James Ennis pushed to branch jennis/filter-docs at BuildStream / buildstream
Commits:
-
a3e2cdd2
by Tom Pollard at 2019-01-28T12:14:40Z
-
805baf7d
by Tom Pollard at 2019-01-28T12:14:40Z
-
38356932
by Tom Pollard at 2019-01-28T13:44:37Z
-
3590ca8c
by Abderrahim Kitouni at 2019-01-28T14:59:19Z
-
39b952dc
by Abderrahim Kitouni at 2019-01-28T15:19:04Z
-
80b36d0c
by Javier Jardón at 2019-01-28T17:54:52Z
-
a1ab48da
by Valentin David at 2019-01-28T21:30:26Z
-
2fcb4491
by Jürg Billeter at 2019-01-28T22:36:22Z
-
1c05a092
by Valentin David at 2019-01-29T05:58:17Z
-
785da59c
by Jürg Billeter at 2019-01-29T06:49:10Z
-
ddef91ea
by Valentin David at 2019-01-29T07:23:35Z
-
6a4c8611
by Jürg Billeter at 2019-01-29T08:18:45Z
-
86c8e414
by Angelos Evripiotis at 2019-01-29T10:39:29Z
-
aae35e13
by Angelos Evripiotis at 2019-01-29T12:08:07Z
-
03111d39
by Dor Askayo at 2019-01-30T10:35:06Z
-
7256bb0c
by James Ennis at 2019-01-30T11:34:04Z
-
44f72f1a
by James Ennis at 2019-01-31T11:17:01Z
-
eaae5bed
by James Ennis at 2019-01-31T11:17:01Z
15 changed files:
- NEWS
- buildstream/_cas/casserver.py
- buildstream/_context.py
- buildstream/_frontend/cli.py
- buildstream/_scheduler/scheduler.py
- buildstream/_stream.py
- buildstream/data/userconfig.yaml
- buildstream/plugins/elements/filter.py
- buildstream/plugins/elements/filter.yaml
- conftest.py
- requirements/requirements.in
- tests/elements/filter.py
- + tests/elements/filter/basic/elements/input-with-deps.bst
- + tests/elements/filter/basic/elements/output-include-with-indirect-deps.bst
- tests/integration/build-tree.py
Changes:
... | ... | @@ -50,6 +50,11 @@ buildstream 1.3.1 |
50 | 50 |
an error message and a hint instead, to avoid bothering folks that just
|
51 | 51 |
made a mistake.
|
52 | 52 |
|
53 |
+ o BREAKING CHANGE: The unconditional 'Are you sure?' prompts have been
|
|
54 |
+ removed. These would always ask you if you were sure when running
|
|
55 |
+ 'bst workspace close --remove-dir' or 'bst workspace reset'. They got in
|
|
56 |
+ the way too often.
|
|
57 |
+ |
|
53 | 58 |
o Failed builds are included in the cache as well.
|
54 | 59 |
`bst checkout` will provide anything in `%{install-root}`.
|
55 | 60 |
A build including cached fails will cause any dependant elements
|
... | ... | @@ -87,12 +92,6 @@ buildstream 1.3.1 |
87 | 92 |
instead of just a specially-formatted build-root with a `root` and `scratch`
|
88 | 93 |
subdirectory.
|
89 | 94 |
|
90 |
- o The buildstream.conf file learned new
|
|
91 |
- 'prompt.really-workspace-close-remove-dir' and
|
|
92 |
- 'prompt.really-workspace-reset-hard' options. These allow users to suppress
|
|
93 |
- certain confirmation prompts, e.g. double-checking that the user meant to
|
|
94 |
- run the command as typed.
|
|
95 |
- |
|
96 | 95 |
o Due to the element `build tree` being cached in the respective artifact their
|
97 | 96 |
size in some cases has significantly increased. In *most* cases the build trees
|
98 | 97 |
are not utilised when building targets, as such by default bst 'pull' & 'build'
|
... | ... | @@ -324,7 +324,7 @@ class _ContentAddressableStorageServicer(remote_execution_pb2_grpc.ContentAddres |
324 | 324 |
blob_response.digest.size_bytes = digest.size_bytes
|
325 | 325 |
|
326 | 326 |
if len(blob_request.data) != digest.size_bytes:
|
327 |
- blob_response.status.code = grpc.StatusCode.FAILED_PRECONDITION
|
|
327 |
+ blob_response.status.code = code_pb2.FAILED_PRECONDITION
|
|
328 | 328 |
continue
|
329 | 329 |
|
330 | 330 |
try:
|
... | ... | @@ -335,10 +335,10 @@ class _ContentAddressableStorageServicer(remote_execution_pb2_grpc.ContentAddres |
335 | 335 |
out.flush()
|
336 | 336 |
server_digest = self.cas.add_object(path=out.name)
|
337 | 337 |
if server_digest.hash != digest.hash:
|
338 |
- blob_response.status.code = grpc.StatusCode.FAILED_PRECONDITION
|
|
338 |
+ blob_response.status.code = code_pb2.FAILED_PRECONDITION
|
|
339 | 339 |
|
340 | 340 |
except ArtifactTooLargeException:
|
341 |
- blob_response.status.code = grpc.StatusCode.RESOURCE_EXHAUSTED
|
|
341 |
+ blob_response.status.code = code_pb2.RESOURCE_EXHAUSTED
|
|
342 | 342 |
|
343 | 343 |
return response
|
344 | 344 |
|
... | ... | @@ -121,18 +121,10 @@ class Context(): |
121 | 121 |
# Whether or not to attempt to pull build trees globally
|
122 | 122 |
self.pull_buildtrees = None
|
123 | 123 |
|
124 |
- # Boolean, whether we double-check with the user that they meant to
|
|
125 |
- # remove a workspace directory.
|
|
126 |
- self.prompt_workspace_close_remove_dir = None
|
|
127 |
- |
|
128 | 124 |
# Boolean, whether we double-check with the user that they meant to
|
129 | 125 |
# close the workspace when they're using it to access the project.
|
130 | 126 |
self.prompt_workspace_close_project_inaccessible = None
|
131 | 127 |
|
132 |
- # Boolean, whether we double-check with the user that they meant to do
|
|
133 |
- # a hard reset of a workspace, potentially losing changes.
|
|
134 |
- self.prompt_workspace_reset_hard = None
|
|
135 |
- |
|
136 | 128 |
# Whether elements must be rebuilt when their dependencies have changed
|
137 | 129 |
self._strict_build_plan = None
|
138 | 130 |
|
... | ... | @@ -260,16 +252,10 @@ class Context(): |
260 | 252 |
prompt = _yaml.node_get(
|
261 | 253 |
defaults, Mapping, 'prompt')
|
262 | 254 |
_yaml.node_validate(prompt, [
|
263 |
- 'really-workspace-close-remove-dir',
|
|
264 | 255 |
'really-workspace-close-project-inaccessible',
|
265 |
- 'really-workspace-reset-hard',
|
|
266 | 256 |
])
|
267 |
- self.prompt_workspace_close_remove_dir = _node_get_option_str(
|
|
268 |
- prompt, 'really-workspace-close-remove-dir', ['ask', 'yes']) == 'ask'
|
|
269 | 257 |
self.prompt_workspace_close_project_inaccessible = _node_get_option_str(
|
270 | 258 |
prompt, 'really-workspace-close-project-inaccessible', ['ask', 'yes']) == 'ask'
|
271 |
- self.prompt_workspace_reset_hard = _node_get_option_str(
|
|
272 |
- prompt, 'really-workspace-reset-hard', ['ask', 'yes']) == 'ask'
|
|
273 | 259 |
|
274 | 260 |
# Load per-projects overrides
|
275 | 261 |
self._project_overrides = _yaml.node_get(defaults, Mapping, 'projects', default_value={})
|
... | ... | @@ -526,7 +526,7 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) |
526 | 526 |
else:
|
527 | 527 |
scope = Scope.RUN
|
528 | 528 |
|
529 |
- use_buildtree = False
|
|
529 |
+ use_buildtree = None
|
|
530 | 530 |
|
531 | 531 |
with app.initialized():
|
532 | 532 |
if not element:
|
... | ... | @@ -534,7 +534,8 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) |
534 | 534 |
if not element:
|
535 | 535 |
raise AppError('Missing argument "ELEMENT".')
|
536 | 536 |
|
537 |
- dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE)
|
|
537 |
+ dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE,
|
|
538 |
+ use_artifact_config=True)
|
|
538 | 539 |
element = dependencies[0]
|
539 | 540 |
prompt = app.shell_prompt(element)
|
540 | 541 |
mounts = [
|
... | ... | @@ -543,20 +544,31 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) |
543 | 544 |
]
|
544 | 545 |
|
545 | 546 |
cached = element._cached_buildtree()
|
546 |
- if cli_buildtree == "always":
|
|
547 |
- if cached:
|
|
548 |
- use_buildtree = True
|
|
549 |
- else:
|
|
550 |
- raise AppError("No buildtree is cached but the use buildtree option was specified")
|
|
551 |
- elif cli_buildtree == "never":
|
|
552 |
- pass
|
|
553 |
- elif cli_buildtree == "try":
|
|
554 |
- use_buildtree = cached
|
|
547 |
+ if cli_buildtree in ("always", "try"):
|
|
548 |
+ use_buildtree = cli_buildtree
|
|
549 |
+ if not cached and use_buildtree == "always":
|
|
550 |
+ click.echo("WARNING: buildtree is not cached locally, will attempt to pull from available remotes",
|
|
551 |
+ err=True)
|
|
555 | 552 |
else:
|
556 |
- if app.interactive and cached:
|
|
557 |
- use_buildtree = bool(click.confirm('Do you want to use the cached buildtree?'))
|
|
553 |
+ # If the value has defaulted to ask and in non interactive mode, don't consider the buildtree, this
|
|
554 |
+ # being the default behaviour of the command
|
|
555 |
+ if app.interactive and cli_buildtree == "ask":
|
|
556 |
+ if cached and bool(click.confirm('Do you want to use the cached buildtree?')):
|
|
557 |
+ use_buildtree = "always"
|
|
558 |
+ elif not cached:
|
|
559 |
+ try:
|
|
560 |
+ choice = click.prompt("Do you want to pull & use a cached buildtree?",
|
|
561 |
+ type=click.Choice(['try', 'always', 'never']),
|
|
562 |
+ err=True, show_choices=True)
|
|
563 |
+ except click.Abort:
|
|
564 |
+ click.echo('Aborting', err=True)
|
|
565 |
+ sys.exit(-1)
|
|
566 |
+ |
|
567 |
+ if choice != "never":
|
|
568 |
+ use_buildtree = choice
|
|
569 |
+ |
|
558 | 570 |
if use_buildtree and not element._cached_success():
|
559 |
- click.echo("Warning: using a buildtree from a failed build.")
|
|
571 |
+ click.echo("WARNING: using a buildtree from a failed build.", err=True)
|
|
560 | 572 |
|
561 | 573 |
try:
|
562 | 574 |
exitcode = app.stream.shell(element, scope, prompt,
|
... | ... | @@ -829,11 +841,6 @@ def workspace_close(app, remove_dir, all_, elements): |
829 | 841 |
if nonexisting:
|
830 | 842 |
raise AppError("Workspace does not exist", detail="\n".join(nonexisting))
|
831 | 843 |
|
832 |
- if app.interactive and remove_dir and app.context.prompt_workspace_close_remove_dir:
|
|
833 |
- if not click.confirm('This will remove all your changes, are you sure?'):
|
|
834 |
- click.echo('Aborting', err=True)
|
|
835 |
- sys.exit(-1)
|
|
836 |
- |
|
837 | 844 |
for element_name in elements:
|
838 | 845 |
app.stream.workspace_close(element_name, remove_dir=remove_dir)
|
839 | 846 |
|
... | ... | @@ -867,11 +874,6 @@ def workspace_reset(app, soft, track_, all_, elements): |
867 | 874 |
if all_ and not app.stream.workspace_exists():
|
868 | 875 |
raise AppError("No open workspaces to reset")
|
869 | 876 |
|
870 |
- if app.interactive and not soft and app.context.prompt_workspace_reset_hard:
|
|
871 |
- if not click.confirm('This will remove all your changes, are you sure?'):
|
|
872 |
- click.echo('Aborting', err=True)
|
|
873 |
- sys.exit(-1)
|
|
874 |
- |
|
875 | 877 |
if all_:
|
876 | 878 |
elements = tuple(element_name for element_name, _ in app.context.get_workspaces().list())
|
877 | 879 |
|
... | ... | @@ -314,10 +314,10 @@ class Scheduler(): |
314 | 314 |
# job (Job): The job to spawn
|
315 | 315 |
#
|
316 | 316 |
def _spawn_job(self, job):
|
317 |
- job.spawn()
|
|
318 | 317 |
self._active_jobs.append(job)
|
319 | 318 |
if self._job_start_callback:
|
320 | 319 |
self._job_start_callback(job)
|
320 |
+ job.spawn()
|
|
321 | 321 |
|
322 | 322 |
# Callback for the cache size job
|
323 | 323 |
def _cache_size_job_complete(self, status, cache_size):
|
... | ... | @@ -101,19 +101,22 @@ class Stream(): |
101 | 101 |
# targets (list of str): Targets to pull
|
102 | 102 |
# selection (PipelineSelection): The selection mode for the specified targets
|
103 | 103 |
# except_targets (list of str): Specified targets to except from fetching
|
104 |
+ # use_artifact_config (bool): If artifact remote config should be loaded
|
|
104 | 105 |
#
|
105 | 106 |
# Returns:
|
106 | 107 |
# (list of Element): The selected elements
|
107 | 108 |
def load_selection(self, targets, *,
|
108 | 109 |
selection=PipelineSelection.NONE,
|
109 |
- except_targets=()):
|
|
110 |
+ except_targets=(),
|
|
111 |
+ use_artifact_config=False):
|
|
110 | 112 |
|
111 | 113 |
profile_start(Topics.LOAD_SELECTION, "_".join(t.replace(os.sep, '-') for t in targets))
|
112 | 114 |
|
113 | 115 |
elements, _ = self._load(targets, (),
|
114 | 116 |
selection=selection,
|
115 | 117 |
except_targets=except_targets,
|
116 |
- fetch_subprojects=False)
|
|
118 |
+ fetch_subprojects=False,
|
|
119 |
+ use_artifact_config=use_artifact_config)
|
|
117 | 120 |
|
118 | 121 |
profile_end(Topics.LOAD_SELECTION, "_".join(t.replace(os.sep, '-') for t in targets))
|
119 | 122 |
|
... | ... | @@ -131,7 +134,7 @@ class Stream(): |
131 | 134 |
# mounts (list of HostMount): Additional directories to mount into the sandbox
|
132 | 135 |
# isolate (bool): Whether to isolate the environment like we do in builds
|
133 | 136 |
# command (list): An argv to launch in the sandbox, or None
|
134 |
- # usebuildtree (bool): Wheather to use a buildtree as the source.
|
|
137 |
+ # usebuildtree (str): Whether to use a buildtree as the source, given cli option
|
|
135 | 138 |
#
|
136 | 139 |
# Returns:
|
137 | 140 |
# (int): The exit code of the launched shell
|
... | ... | @@ -141,7 +144,7 @@ class Stream(): |
141 | 144 |
mounts=None,
|
142 | 145 |
isolate=False,
|
143 | 146 |
command=None,
|
144 |
- usebuildtree=False):
|
|
147 |
+ usebuildtree=None):
|
|
145 | 148 |
|
146 | 149 |
# Assert we have everything we need built, unless the directory is specified
|
147 | 150 |
# in which case we just blindly trust the directory, using the element
|
... | ... | @@ -156,8 +159,31 @@ class Stream(): |
156 | 159 |
raise StreamError("Elements need to be built or downloaded before staging a shell environment",
|
157 | 160 |
detail="\n".join(missing_deps))
|
158 | 161 |
|
162 |
+ buildtree = False
|
|
163 |
+ # Check if we require a pull queue attempt, with given artifact state and context
|
|
164 |
+ if usebuildtree:
|
|
165 |
+ if not element._cached_buildtree():
|
|
166 |
+ require_buildtree = self._buildtree_pull_required([element])
|
|
167 |
+ # Attempt a pull queue for the given element if remote and context allow it
|
|
168 |
+ if require_buildtree:
|
|
169 |
+ self._message(MessageType.INFO, "Attempting to fetch missing artifact buildtree")
|
|
170 |
+ self._add_queue(PullQueue(self._scheduler))
|
|
171 |
+ self._enqueue_plan(require_buildtree)
|
|
172 |
+ self._run()
|
|
173 |
+ # Now check if the buildtree was successfully fetched
|
|
174 |
+ if element._cached_buildtree():
|
|
175 |
+ buildtree = True
|
|
176 |
+ if not buildtree:
|
|
177 |
+ if usebuildtree == "always":
|
|
178 |
+ raise StreamError("Buildtree is not cached locally or in available remotes")
|
|
179 |
+ else:
|
|
180 |
+ self._message(MessageType.INFO, """Buildtree is not cached locally or in available remotes,
|
|
181 |
+ shell will be loaded without it""")
|
|
182 |
+ else:
|
|
183 |
+ buildtree = True
|
|
184 |
+ |
|
159 | 185 |
return element._shell(scope, directory, mounts=mounts, isolate=isolate, prompt=prompt, command=command,
|
160 |
- usebuildtree=usebuildtree)
|
|
186 |
+ usebuildtree=buildtree)
|
|
161 | 187 |
|
162 | 188 |
# build()
|
163 | 189 |
#
|
... | ... | @@ -112,14 +112,6 @@ logging: |
112 | 112 |
#
|
113 | 113 |
prompt:
|
114 | 114 |
|
115 |
- # Whether to really proceed with 'bst workspace close --remove-dir' removing
|
|
116 |
- # a workspace directory, potentially losing changes.
|
|
117 |
- #
|
|
118 |
- # ask - Ask the user if they are sure.
|
|
119 |
- # yes - Always remove, without asking.
|
|
120 |
- #
|
|
121 |
- really-workspace-close-remove-dir: ask
|
|
122 |
- |
|
123 | 115 |
# Whether to really proceed with 'bst workspace close' when doing so would
|
124 | 116 |
# stop them from running bst commands in this workspace.
|
125 | 117 |
#
|
... | ... | @@ -127,11 +119,3 @@ prompt: |
127 | 119 |
# yes - Always close, without asking.
|
128 | 120 |
#
|
129 | 121 |
really-workspace-close-project-inaccessible: ask
|
130 |
- |
|
131 |
- # Whether to really proceed with 'bst workspace reset' doing a hard reset of
|
|
132 |
- # a workspace, potentially losing changes.
|
|
133 |
- #
|
|
134 |
- # ask - Ask the user if they are sure.
|
|
135 |
- # yes - Always hard reset, without asking.
|
|
136 |
- #
|
|
137 |
- really-workspace-reset-hard: ask
|
... | ... | @@ -20,25 +20,127 @@ |
20 | 20 |
"""
|
21 | 21 |
filter - Extract a subset of files from another element
|
22 | 22 |
=======================================================
|
23 |
-This filters another element by producing an output that is a subset of
|
|
24 |
-the filtered element.
|
|
23 |
+Filter another element by producing an output that is a subset of
|
|
24 |
+the parent element's output. Subsets are defined by the parent element's
|
|
25 |
+:ref:`split rules <public_split_rules>`.
|
|
25 | 26 |
|
26 |
-To specify the element to filter, specify it as the one and only build
|
|
27 |
-dependency to filter. See :ref:`Dependencies <format_dependencies>`
|
|
28 |
-for what dependencies are and how to specify them.
|
|
27 |
+Overview
|
|
28 |
+--------
|
|
29 |
+A filter element must have exactly one *build* dependency, where said
|
|
30 |
+dependency is the 'parent' element which we would like to filter.
|
|
31 |
+Runtime dependencies may also be specified, which can be useful to propagate
|
|
32 |
+forward from this filter element onto its reverse dependencies.
|
|
33 |
+See :ref:`Dependencies <format_dependencies>` to see how we specify dependencies.
|
|
29 | 34 |
|
30 |
-Dependencies aside from the filtered element may be specified, but
|
|
31 |
-they must be runtime dependencies only. This can be useful to propagate
|
|
32 |
-runtime dependencies forward from this filter element onto its reverse
|
|
33 |
-dependencies.
|
|
35 |
+When workspaces are opened, closed or reset on a filter element, or this
|
|
36 |
+element is tracked, the filter element will transparently pass on the command
|
|
37 |
+to its parent element (the sole build-dependency).
|
|
34 | 38 |
|
35 |
-When workspaces are opened, closed or reset on this element, or this
|
|
36 |
-element is tracked, instead of erroring due to a lack of sources, this
|
|
37 |
-element will transparently pass on the command to its sole build-dependency.
|
|
39 |
+Example
|
|
40 |
+-------
|
|
41 |
+Consider a simple import element, ``import.bst`` which imports the local files
|
|
42 |
+'foo', 'bar' and 'baz' (each stored in ``files/``, relative to the project's root):
|
|
38 | 43 |
|
39 |
-The default configuration and possible options are as such:
|
|
40 |
- .. literalinclude:: ../../../buildstream/plugins/elements/filter.yaml
|
|
41 |
- :language: yaml
|
|
44 |
+.. code:: yaml
|
|
45 |
+ |
|
46 |
+ kind: import
|
|
47 |
+ |
|
48 |
+ # Specify sources to import
|
|
49 |
+ sources:
|
|
50 |
+ - kind: local
|
|
51 |
+ path: files
|
|
52 |
+ |
|
53 |
+ # Specify public domain data, visible to other elements
|
|
54 |
+ public:
|
|
55 |
+ bst:
|
|
56 |
+ split-rules:
|
|
57 |
+ foo:
|
|
58 |
+ - /foo
|
|
59 |
+ bar:
|
|
60 |
+ - /bar
|
|
61 |
+ |
|
62 |
+.. note::
|
|
63 |
+ |
|
64 |
+ We can make an element's metadata visible to all reverse dependencies by making use
|
|
65 |
+ of the ``public:`` field. See the :ref:`public data documentation <format_public>`
|
|
66 |
+ for more information.
|
|
67 |
+ |
|
68 |
+In this example, ``import.bst`` will serve as the 'parent' of the filter element, thus
|
|
69 |
+its output will be filtered. It is important to understand that the artifact of the
|
|
70 |
+above element will contain the files: 'foo', 'bar' and 'baz'.
|
|
71 |
+ |
|
72 |
+Now, to produce an element whose artifact contains the file 'foo', and exlusively 'foo',
|
|
73 |
+we can define the following filter, ``filter-foo.bst``:
|
|
74 |
+ |
|
75 |
+.. code:: yaml
|
|
76 |
+ |
|
77 |
+ kind: filter
|
|
78 |
+ |
|
79 |
+ # Declare the sole build-dependency of the filter element
|
|
80 |
+ depends:
|
|
81 |
+ - filename: import.bst
|
|
82 |
+ type: build
|
|
83 |
+ |
|
84 |
+ # Declare a list of domains to include in the filter's artifact
|
|
85 |
+ config:
|
|
86 |
+ include:
|
|
87 |
+ - foo
|
|
88 |
+ |
|
89 |
+.. note::
|
|
90 |
+ |
|
91 |
+ We can also specify build-dependencies with a 'build-depends' field which has been
|
|
92 |
+ available since :ref:`format version 14 <project_format_version>`. See the
|
|
93 |
+ :ref:`Build-Depends documentation <format_build_depends>` for more detail.
|
|
94 |
+ |
|
95 |
+It should be noted that an 'empty' ``include:`` list would, by default, include all
|
|
96 |
+split-rules specified in the parent element, which, in this example, would be the
|
|
97 |
+files 'foo' and 'bar' (the file 'baz' was not covered by any split rules).
|
|
98 |
+ |
|
99 |
+Equally, we can use the ``exclude:`` statement to create the same artifact (which
|
|
100 |
+only contains the file 'foo') by declaring the following element, ``exclude-bar.bst``:
|
|
101 |
+ |
|
102 |
+.. code:: yaml
|
|
103 |
+ |
|
104 |
+ kind: filter
|
|
105 |
+ |
|
106 |
+ # Declare the sole build-dependency of the filter element
|
|
107 |
+ depends:
|
|
108 |
+ - filename: import.bst
|
|
109 |
+ type: build
|
|
110 |
+ |
|
111 |
+ # Declare a list of domains to exclude in the filter's artifact
|
|
112 |
+ config:
|
|
113 |
+ exclude:
|
|
114 |
+ - bar
|
|
115 |
+ |
|
116 |
+In addition to the ``include:`` and ``exclude:`` fields, there exists an ``include-orphans:``
|
|
117 |
+(Boolean) field, which defaults to ``False``. This will determine whether to include files
|
|
118 |
+which are not present in the 'split-rules'. For example, if we wanted to filter out all files
|
|
119 |
+which are not included as split rules we can define the following element, ``filter-misc.bst``:
|
|
120 |
+ |
|
121 |
+.. code:: yaml
|
|
122 |
+ |
|
123 |
+ kind: filter
|
|
124 |
+ |
|
125 |
+ # Declare the sole build-dependency of the filter element
|
|
126 |
+ depends:
|
|
127 |
+ - filename: import.bst
|
|
128 |
+ type: build
|
|
129 |
+ |
|
130 |
+ # Filter out all files which are not declared as split rules
|
|
131 |
+ config:
|
|
132 |
+ exclude:
|
|
133 |
+ - foo
|
|
134 |
+ - bar
|
|
135 |
+ include-orphans: True
|
|
136 |
+ |
|
137 |
+The artifact of ``filter-misc.bst`` will only contain the file 'baz'.
|
|
138 |
+ |
|
139 |
+Below is more information regarding the the default configurations and possible options
|
|
140 |
+of the filter element:
|
|
141 |
+ |
|
142 |
+.. literalinclude:: ../../../buildstream/plugins/elements/filter.yaml
|
|
143 |
+ :language: yaml
|
|
42 | 144 |
"""
|
43 | 145 |
|
44 | 146 |
from buildstream import Element, ElementError, Scope
|
... | ... | @@ -47,6 +149,8 @@ from buildstream import Element, ElementError, Scope |
47 | 149 |
class FilterElement(Element):
|
48 | 150 |
# pylint: disable=attribute-defined-outside-init
|
49 | 151 |
|
152 |
+ BST_ARTIFACT_VERSION = 1
|
|
153 |
+ |
|
50 | 154 |
# The filter element's output is its dependencies, so
|
51 | 155 |
# we must rebuild if the dependencies change even when
|
52 | 156 |
# not in strict build plans.
|
... | ... | @@ -102,7 +206,7 @@ class FilterElement(Element): |
102 | 206 |
|
103 | 207 |
def assemble(self, sandbox):
|
104 | 208 |
with self.timed_activity("Staging artifact", silent_nested=True):
|
105 |
- for dep in self.dependencies(Scope.BUILD):
|
|
209 |
+ for dep in self.dependencies(Scope.BUILD, recurse=False):
|
|
106 | 210 |
dep.stage_artifact(sandbox, include=self.include,
|
107 | 211 |
exclude=self.exclude, orphans=self.include_orphans)
|
108 | 212 |
return ""
|
... | ... | @@ -2,20 +2,21 @@ |
2 | 2 |
# Filter element configuration
|
3 | 3 |
config:
|
4 | 4 |
|
5 |
- # A list of domains to include from each artifact, as
|
|
6 |
- # they were defined in the element's 'split-rules'.
|
|
5 |
+ # A list of domains to include in each artifact, as
|
|
6 |
+ # they were defined as public data in the parent
|
|
7 |
+ # element's 'split-rules'.
|
|
7 | 8 |
#
|
8 | 9 |
# Since domains can be added, it is not an error to
|
9 | 10 |
# specify domains which may not exist for all of the
|
10 | 11 |
# elements in this composition.
|
11 | 12 |
#
|
12 | 13 |
# The default empty list indicates that all domains
|
13 |
- # from each dependency should be included.
|
|
14 |
+ # of the parent's artifact should be included.
|
|
14 | 15 |
#
|
15 | 16 |
include: []
|
16 | 17 |
|
17 | 18 |
# A list of domains to exclude from each artifact, as
|
18 |
- # they were defined in the element's 'split-rules'.
|
|
19 |
+ # they were defined in the parent element's 'split-rules'.
|
|
19 | 20 |
#
|
20 | 21 |
# In the case that a file is spoken for by a domain
|
21 | 22 |
# in the 'include' list and another in the 'exclude'
|
... | ... | @@ -23,7 +24,7 @@ config: |
23 | 24 |
exclude: []
|
24 | 25 |
|
25 | 26 |
# Whether to include orphan files which are not
|
26 |
- # included by any of the 'split-rules' present on
|
|
27 |
- # a given element.
|
|
27 |
+ # included by any of the 'split-rules' present in
|
|
28 |
+ # the parent element.
|
|
28 | 29 |
#
|
29 | 30 |
include-orphans: False
|
... | ... | @@ -54,6 +54,7 @@ class IntegrationCache(): |
54 | 54 |
|
55 | 55 |
def __init__(self, cache):
|
56 | 56 |
cache = os.path.abspath(cache)
|
57 |
+ os.makedirs(cache, exist_ok=True)
|
|
57 | 58 |
|
58 | 59 |
# Use the same sources every time
|
59 | 60 |
self.sources = os.path.join(cache, 'sources')
|
1 |
-Click
|
|
1 |
+Click >= 7.0
|
|
2 | 2 |
grpcio >= 1.10
|
3 | 3 |
Jinja2 >= 2.10
|
4 | 4 |
pluginbase
|
5 |
-protobuf >= 3.5
|
|
5 |
+protobuf >= 3.6
|
|
6 | 6 |
psutil
|
7 | 7 |
# According to ruamel.yaml's PyPI page, we are suppose to use
|
8 | 8 |
# "<=0.15" in production until 0.15 becomes API stable.
|
... | ... | @@ -464,3 +464,23 @@ def test_filter_track_multi_exclude(datafiles, cli, tmpdir): |
464 | 464 |
assert "ref" not in new_input["sources"][0]
|
465 | 465 |
new_input2 = _yaml.load(input2_file)
|
466 | 466 |
assert new_input2["sources"][0]["ref"] == ref
|
467 |
+ |
|
468 |
+ |
|
469 |
+@pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic'))
|
|
470 |
+def test_filter_include_with_indirect_deps(datafiles, cli, tmpdir):
|
|
471 |
+ project = os.path.join(datafiles.dirname, datafiles.basename)
|
|
472 |
+ result = cli.run(project=project, args=[
|
|
473 |
+ 'build', 'output-include-with-indirect-deps.bst'])
|
|
474 |
+ result.assert_success()
|
|
475 |
+ |
|
476 |
+ checkout = os.path.join(tmpdir.dirname, tmpdir.basename, 'checkout')
|
|
477 |
+ result = cli.run(project=project, args=[
|
|
478 |
+ 'artifact', 'checkout', 'output-include-with-indirect-deps.bst', '--directory', checkout])
|
|
479 |
+ result.assert_success()
|
|
480 |
+ |
|
481 |
+ # direct dependencies should be staged and filtered
|
|
482 |
+ assert os.path.exists(os.path.join(checkout, "baz"))
|
|
483 |
+ |
|
484 |
+ # indirect dependencies shouldn't be staged and filtered
|
|
485 |
+ assert not os.path.exists(os.path.join(checkout, "foo"))
|
|
486 |
+ assert not os.path.exists(os.path.join(checkout, "bar"))
|
1 |
+kind: import
|
|
2 |
+ |
|
3 |
+depends:
|
|
4 |
+- filename: input.bst
|
|
5 |
+ |
|
6 |
+sources:
|
|
7 |
+- kind: local
|
|
8 |
+ path: files
|
|
9 |
+ |
|
10 |
+public:
|
|
11 |
+ bst:
|
|
12 |
+ split-rules:
|
|
13 |
+ baz:
|
|
14 |
+ - /baz
|
1 |
+kind: filter
|
|
2 |
+ |
|
3 |
+depends:
|
|
4 |
+- filename: input-with-deps.bst
|
|
5 |
+ type: build
|
... | ... | @@ -101,7 +101,7 @@ def test_buildtree_from_failure(cli_integration, tmpdir, datafiles): |
101 | 101 |
'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
|
102 | 102 |
])
|
103 | 103 |
res.assert_success()
|
104 |
- assert "Warning: using a buildtree from a failed build" in res.output
|
|
104 |
+ assert "WARNING: using a buildtree from a failed build" in res.stderr
|
|
105 | 105 |
assert 'Hi' in res.output
|
106 | 106 |
|
107 | 107 |
|
... | ... | @@ -141,7 +141,7 @@ def test_buildtree_pulled(cli, tmpdir, datafiles): |
141 | 141 |
res.assert_success()
|
142 | 142 |
|
143 | 143 |
|
144 |
-# This test checks for correct behaviour if a buildtree is not present.
|
|
144 |
+# This test checks for correct behaviour if a buildtree is not present in the local cache.
|
|
145 | 145 |
@pytest.mark.datafiles(DATA_DIR)
|
146 | 146 |
@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
|
147 | 147 |
def test_buildtree_options(cli, tmpdir, datafiles):
|
... | ... | @@ -156,6 +156,7 @@ def test_buildtree_options(cli, tmpdir, datafiles): |
156 | 156 |
result = cli.run(project=project, args=['build', element_name])
|
157 | 157 |
result.assert_success()
|
158 | 158 |
assert cli.get_element_state(project, element_name) == 'cached'
|
159 |
+ assert share.has_artifact('test', element_name, cli.get_element_key(project, element_name))
|
|
159 | 160 |
|
160 | 161 |
# Discard the cache
|
161 | 162 |
cli.configure({
|
... | ... | @@ -168,8 +169,6 @@ def test_buildtree_options(cli, tmpdir, datafiles): |
168 | 169 |
result = cli.run(project=project, args=['artifact', 'pull', '--deps', 'all', element_name])
|
169 | 170 |
result.assert_success()
|
170 | 171 |
|
171 |
- # The above is the simplest way I know to create a local cache without any buildtrees.
|
|
172 |
- |
|
173 | 172 |
# Check it's not using the cached build tree
|
174 | 173 |
res = cli.run(project=project, args=[
|
175 | 174 |
'shell', '--build', element_name, '--use-buildtree', 'never', '--', 'cat', 'test'
|
... | ... | @@ -177,24 +176,51 @@ def test_buildtree_options(cli, tmpdir, datafiles): |
177 | 176 |
res.assert_shell_error()
|
178 | 177 |
assert 'Hi' not in res.output
|
179 | 178 |
|
180 |
- # Check it's not correctly handling the lack of buildtree
|
|
179 |
+ # Check it's not using the cached build tree, default is to ask, and fall back to not
|
|
180 |
+ # for non interactive behavior
|
|
181 | 181 |
res = cli.run(project=project, args=[
|
182 |
- 'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
|
|
182 |
+ 'shell', '--build', element_name, '--', 'cat', 'test'
|
|
183 | 183 |
])
|
184 | 184 |
res.assert_shell_error()
|
185 | 185 |
assert 'Hi' not in res.output
|
186 | 186 |
|
187 |
- # Check it's not using the cached build tree, default is to ask, and fall back to not
|
|
188 |
- # for non interactive behavior
|
|
187 |
+ # Check correctly handling the lack of buildtree, with 'try' not attempting to
|
|
188 |
+ # pull the buildtree as the user context is by default set to not pull them
|
|
189 | 189 |
res = cli.run(project=project, args=[
|
190 |
- 'shell', '--build', element_name, '--', 'cat', 'test'
|
|
190 |
+ 'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
|
|
191 | 191 |
])
|
192 |
- res.assert_shell_error()
|
|
193 | 192 |
assert 'Hi' not in res.output
|
193 |
+ assert 'Attempting to fetch missing artifact buildtrees' not in res.stderr
|
|
194 |
+ assert """Buildtree is not cached locally or in available remotes,
|
|
195 |
+ shell will be loaded without it"""
|
|
194 | 196 |
|
195 |
- # Check it's using the cached build tree
|
|
197 |
+ # Check correctly handling the lack of buildtree, with 'try' attempting and succeeding
|
|
198 |
+ # to pull the buildtree as the user context allow the pulling of buildtrees and it is
|
|
199 |
+ # available in the remote
|
|
200 |
+ res = cli.run(project=project, args=[
|
|
201 |
+ '--pull-buildtrees', 'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
|
|
202 |
+ ])
|
|
203 |
+ assert 'Attempting to fetch missing artifact buildtree' in res.stderr
|
|
204 |
+ assert 'Hi' in res.output
|
|
205 |
+ shutil.rmtree(os.path.join(os.path.join(cli.directory, 'artifacts2')))
|
|
206 |
+ assert cli.get_element_state(project, element_name) != 'cached'
|
|
207 |
+ |
|
208 |
+ # Check it's not loading the shell at all with always set for the buildtree, when the
|
|
209 |
+ # user context does not allow for buildtree pulling
|
|
210 |
+ result = cli.run(project=project, args=['artifact', 'pull', '--deps', 'all', element_name])
|
|
211 |
+ result.assert_success()
|
|
196 | 212 |
res = cli.run(project=project, args=[
|
197 | 213 |
'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
|
198 | 214 |
])
|
199 | 215 |
res.assert_main_error(ErrorDomain.PROG_NOT_FOUND, None)
|
216 |
+ assert 'Buildtree is not cached locally or in available remotes' in res.stderr
|
|
200 | 217 |
assert 'Hi' not in res.output
|
218 |
+ assert 'Attempting to fetch missing artifact buildtree' not in res.stderr
|
|
219 |
+ |
|
220 |
+ # Check that when user context is set to pull buildtrees and a remote has the buildtree,
|
|
221 |
+ # 'always' will attempt and succeed at pulling the missing buildtree.
|
|
222 |
+ res = cli.run(project=project, args=[
|
|
223 |
+ '--pull-buildtrees', 'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
|
|
224 |
+ ])
|
|
225 |
+ assert 'Hi' in res.output
|
|
226 |
+ assert 'Attempting to fetch missing artifact buildtree' in res.stderr
|