From 4a497d0294d675173eb26b3935fed063d4f86d27 Mon Sep 17 00:00:00 2001 From: Abderrahim Kitouni Date: Thu, 29 Jan 2026 18:11:50 +0100 Subject: [PATCH 1/3] _stream: Don't require runtime deps when building with --deps none Before this change, running a build with `--deps none` would also build / pull runtime dependencies. This commit changes the elements on the command line to not require their runtime dependencies if we're using `--deps none` --- src/buildstream/_stream.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py index 5b28002c0..a33f8b440 100644 --- a/src/buildstream/_stream.py +++ b/src/buildstream/_stream.py @@ -1775,8 +1775,13 @@ def _load( if not required_elements: required_elements = selected + if selection == _PipelineSelection.NONE: + scope = _Scope.NONE + else: + scope = _Scope.RUN + for element in required_elements: - element._set_required() + element._set_required(scope) return selected From 7f01252e05971fb4809e49d8960998d6ea6511c0 Mon Sep 17 00:00:00 2001 From: Abderrahim Kitouni Date: Thu, 29 Jan 2026 20:35:25 +0100 Subject: [PATCH 2/3] Add config option for `dependencies: run` In addition to `none` and `all`, this commit adds `run` and makes it the default instead of `none`. The previous commit changed the meaning of `--deps none` to be different from `--deps run`, so we change the default to be `run`. --- doc/source/using_config.rst | 1 + src/buildstream/_context.py | 4 ++-- src/buildstream/_frontend/cli.py | 3 ++- src/buildstream/_stream.py | 2 +- src/buildstream/data/userconfig.yaml | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/source/using_config.rst b/doc/source/using_config.rst index 8911e3f70..3010eb0b7 100644 --- a/doc/source/using_config.rst +++ b/doc/source/using_config.rst @@ -381,6 +381,7 @@ Attributes values for this attribute are: * ``none``: Only build elements required to generate the expected target artifacts + * ``run``: Build required elements and their their runtime dependencies * ``all``: Build elements even if they are build dependencies of artifacts which are already cached diff --git a/src/buildstream/_context.py b/src/buildstream/_context.py index d11c906d7..48e8eff43 100644 --- a/src/buildstream/_context.py +++ b/src/buildstream/_context.py @@ -465,10 +465,10 @@ def load(self, config: Optional[str] = None) -> None: self.build_retry_failed = build.get_bool("retry-failed") dependencies = build.get_str("dependencies") - if dependencies not in ["none", "all"]: + if dependencies not in ["none", "run", "all"]: provenance = build.get_scalar("dependencies").get_provenance() raise LoadError( - "{}: Invalid value for 'dependencies'. Choose 'none' or 'all'.".format(provenance), + "{}: Invalid value for 'dependencies'. Choose 'none', 'run', or 'all'.".format(provenance), LoadErrorReason.INVALID_DATA, ) self.build_dependencies = _PipelineSelection(dependencies) diff --git a/src/buildstream/_frontend/cli.py b/src/buildstream/_frontend/cli.py index 081a7d46b..4986d6183 100644 --- a/src/buildstream/_frontend/cli.py +++ b/src/buildstream/_frontend/cli.py @@ -455,7 +455,7 @@ def init(app, project_name, min_version, element_path, force, target_directory): default=None, type=FastEnumType( _PipelineSelection, - [_PipelineSelection.NONE, _PipelineSelection.BUILD, _PipelineSelection.ALL], + [_PipelineSelection.NONE, _PipelineSelection.BUILD, _PipelineSelection.RUN, _PipelineSelection.ALL], ), help="The dependencies to build", ) @@ -513,6 +513,7 @@ def build( \b none: No dependencies, just the element itself build: Build time dependencies, excluding the element itself + run: The element and its runtime dependencies all: All dependencies Dependencies that are consequently required to build the requested diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py index a33f8b440..a475bdb41 100644 --- a/src/buildstream/_stream.py +++ b/src/buildstream/_stream.py @@ -1766,7 +1766,7 @@ def _load( # rely on state changes during processing to determine which elements # must be processed. # - if selection == _PipelineSelection.NONE: + if selection in (_PipelineSelection.NONE, _PipelineSelection.RUN): required_elements = elements elif selection == _PipelineSelection.BUILD: required_elements = list(_pipeline.dependencies(elements, _Scope.BUILD, recurse=False)) diff --git a/src/buildstream/data/userconfig.yaml b/src/buildstream/data/userconfig.yaml index a84da10ae..76af0d6c8 100644 --- a/src/buildstream/data/userconfig.yaml +++ b/src/buildstream/data/userconfig.yaml @@ -93,7 +93,7 @@ build: # # Control which dependencies to build # - dependencies: none + dependencies: run # From ee23f26f697c3430d434ed22d6ba3fede8e52d54 Mon Sep 17 00:00:00 2001 From: Abderrahim Kitouni Date: Tue, 3 Feb 2026 15:03:36 +0000 Subject: [PATCH 3/3] Update test for dynamic build plan --- tests/frontend/pull.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/frontend/pull.py b/tests/frontend/pull.py index 87cc2de67..6d8fd482b 100644 --- a/tests/frontend/pull.py +++ b/tests/frontend/pull.py @@ -581,7 +581,15 @@ def test_pull_artifact(cli, tmpdir, datafiles): @pytest.mark.datafiles(DATA_DIR) -def test_dynamic_build_plan(cli, tmpdir, datafiles): +@pytest.mark.parametrize( + "deps, expected_pulled_elements, expected_states", + [ + ("none", ["checkout-deps.bst"], ("cached", "buildable", "buildable")), + ("run", ["import-bin.bst", "checkout-deps.bst"], ("cached", "buildable", "cached")), + ("all", ["import-dev.bst", "import-bin.bst", "checkout-deps.bst"], ("cached", "cached", "cached")), + ], +) +def test_dynamic_build_plan(cli, tmpdir, datafiles, deps, expected_pulled_elements, expected_states): project = str(datafiles) target = "checkout-deps.bst" build_dep = "import-dev.bst" @@ -610,18 +618,14 @@ def test_dynamic_build_plan(cli, tmpdir, datafiles): assert not any(states[e] == "cached" for e in all_elements) # Now try to rebuild target - result = cli.run(project=project, args=["build", target]) + result = cli.run(project=project, args=["build", "--deps", deps, target]) result.assert_success() - # Assert that target and runtime dependency were pulled - # but build dependency was not pulled as it wasn't needed + # Assert that we only pulled the needed dependencies # (dynamic build plan). - assert target in result.get_pulled_elements() - assert runtime_dep in result.get_pulled_elements() - assert build_dep not in result.get_pulled_elements() + assert result.get_pulled_elements() == expected_pulled_elements # And assert that the pulled elements are again in the local cache states = cli.get_element_states(project, all_elements) - assert states[target] == "cached" - assert states[runtime_dep] == "cached" - assert states[build_dep] != "cached" + states_flattended = (states[target], states[build_dep], states[runtime_dep]) + assert states_flattended == expected_states