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 5b28002c0..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)) @@ -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 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 # 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