forked from canonical/snapcraft
/
__init__.py
271 lines (227 loc) · 9.45 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright (C) 2015 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Plugins drive the build process for a part.
Each part can use an individual plugin that understands how to work with
the declared sources.
These plugins have a lifecycle that consists of the following steps:
- pull
- build
- stage
- strip
- snap
# Lifecycle
## Pull
This is the first step. This is where content is downloaded, e.g. checkout a
git repository or download a binary component like the Java SDK. Snapcraft
will place the downloaded content for each part in that part's
`parts/<part-name>/src` directory.
## Build
This is the step that follows pull. Each part is built in its
`parts/part-name/build` directory and installs itself into
`parts/part-name/install`.
## Stage
After the build step of each part, the parts are combined into a single
directory tree that is called the "staging area". It can be found
under the `./stage` directory.
This is the area where all parts can share assets such as libraries to link
against.
## Strip
The strip step moves the data into a `./snap` directory. It contains only
the content that will be put into the final snap package, unlike the staging
area which may include some development files not destined for your package.
The Snappy metadata information about your project will also now be placed
in `./snap/meta`.
This `./snap` directory is useful for inspecting what is going into your
snap and to make any final post-processing on snapcraft's output.
## Snap
The final step builds a snap package out of the `snap` directory.
# Common keywords
There are common builtin keywords provided to a snapcraft plugin which can
be used in any part irrespective of the plugin, these are
- after:
(list of strings)
Specifies any parts that should be built before this part is. This
is mostly useful when a part needs a library or build tool built by
another part.
If a part listed in `after` is not defined locally, it will be
searched for in the wiki (https://wiki.ubuntu.com/Snappy/Wiki)
- stage-packages:
(list of strings)
A list of Ubuntu packages to use that are needed to support the part
creation.
- build-packages:
(list of strings)
A list of Ubuntu packages to be installed on the host to aid in
building the part. These packages will not go into the final snap.
- organize:
(yaml subsection)
A dictionary exposing replacements, the key is the internal filename
whilst the value is the exposed filename, filesets will refer to the
exposed named applied after organization is applied.
This can be used to avoid conflicts by renaming files or using a
different layout from what came out of the build, e.g.;
`/usr/local/share/icon.png` -> `/usr/share/icon.png`.
- filesets:
(yaml subsection)
A dictionary with filesets, the key being a recognizable user defined
string and its value a list of filenames to be included or
excluded. Globbing is achieved with * for either inclusions or
exclusion. Exclusions are denoted by an initial `-`.
Globbing is computed from the part's install directory in
`parts/<part-name>/install`.
- stage:
(list of strings)
A list of files from a part’s installation to expose in `stage`.
Rules applying to the list here are the same as those of filesets.
Referencing of fileset keys is done with a $ prefixing the fileset
key, which will expand with the value of such key.
- snap:
(list of strings)
A list of files from a part’s installation to expose in `snap`.
Rules applying to the list here are the same as those of filesets.
Referencing of fileset keys is done with a $ prefixing the fileset
key, which will expand with the value of such key.
"""
import contextlib
import os
import shutil
from snapcraft import common
from snapcraft import sources
class BasePlugin:
@classmethod
def schema(cls):
"""Return a json-schema for the plugin's properties as a dictionary.
Of importance to plugin authors is the 'properties' keyword and
optionally the 'requires' keyword with a list of required
'properties'.
By default the properties will be that of a standard VCS,
override in custom implementations if required.
"""
return {
'$schema': 'http://json-schema.org/draft-04/schema#',
'type': 'object',
'properties': {
'source': {
'type': 'string',
},
'source-type': {
'type': 'string',
'default': '',
},
'source-branch': {
'type': 'string',
'default': '',
},
'source-tag': {
'type:': 'string',
'default': '',
},
'source-subdir': {
'type': 'string',
'default': None,
}
},
'required': [
'source',
]
}
@property
def PLUGIN_STAGE_SOURCES(self):
"""Define additional sources.list."""
return getattr(self, '_PLUGIN_STAGE_SOURCES', [])
def __init__(self, name, options):
self.name = name
self.build_packages = []
self.stage_packages = []
with contextlib.suppress(AttributeError):
self.stage_packages = options.stage_packages
with contextlib.suppress(AttributeError):
self.build_packages = options.build_packages
self.options = options
self.partdir = os.path.join(common.get_partsdir(), self.name)
self.sourcedir = os.path.join(self.partdir, 'src')
self.builddir = os.path.join(self.partdir, 'build')
self.installdir = os.path.join(self.partdir, 'install')
# The API
def pull(self):
"""Pull the source code and/or internal prereqs to build the part.
By default, the base implementation for pull will use the following
part properties to retrieve source code:
- source
- source-branch
- source-tag
- source-type
If source is empty or does not exist, the phase will be skipped.
Override or inherit from this method if you need to implement or
enhance with custom pull logic.
"""
if getattr(self.options, 'source', None):
sources.get(self.sourcedir, self.builddir, self.options)
def build(self):
"""Build the source code retrieved from the pull phase.
The base implementation only copies sourcedir to builddir. Override
this method if you need to process the source code to make it runnable.
"""
if os.path.exists(self.builddir):
shutil.rmtree(self.builddir)
source_subdir = getattr(self.options, 'source_subdir', None)
if source_subdir:
sourcedir = os.path.join(self.sourcedir, source_subdir)
else:
sourcedir = self.sourcedir
shutil.copytree(
sourcedir, self.builddir, symlinks=True,
ignore=lambda d, s: common.SNAPCRAFT_FILES
if d is self.sourcedir else [])
def snap_fileset(self):
"""Return a list of files to include or exclude in the resulting snap
The staging phase of a plugin's lifecycle may populate many things
into the staging directory in order to succeed in building a
project.
During the stripping phase and in order to have a clean snap, the
plugin can provide additional logic for stripping build components
from the final snap and alleviate the part author from doing so for
repetetive filesets.
These are the rules to honor when creating such list:
- includes can be just listed
- excludes must be preceded by -
For example::
(['bin', 'lib', '-include'])
"""
return ([])
def env(self, root):
"""Return a list with the execution environment for building.
Plugins often need special environment variables exported to the
system for some builds to take place. This is a list of strings
of the form key=value. The parameter root is the path to this part.
:param str root: The root for the part
"""
return []
# Helpers
def run(self, cmd, cwd=None, **kwargs):
if cwd is None:
cwd = self.builddir
if True:
print(' '.join(cmd))
os.makedirs(cwd, exist_ok=True)
return common.run(cmd, cwd=cwd, **kwargs)
def run_output(self, cmd, cwd=None, **kwargs):
if cwd is None:
cwd = self.builddir
if True:
print(' '.join(cmd))
os.makedirs(cwd, exist_ok=True)
return common.run_output(cmd, cwd=cwd, **kwargs)