From eb92e02be91f7bb64cfc2d745e9ae2212a07d38e Mon Sep 17 00:00:00 2001 From: salma-samy <salmasamy@google.com> Date: Thu, 19 Oct 2023 03:57:06 -0700 Subject: [PATCH] Add --force option to fetch This option can be used alongside any fetch operation to compel the fetching process. PiperOrigin-RevId: 574810048 Change-Id: I3df8fc18ac701cf0ca7a0ce68f1565fb064d59b5 --- .../lib/bazel/commands/FetchCommand.java | 12 ++++- .../lib/bazel/commands/FetchOptions.java | 12 +++-- .../build/lib/bazel/commands/fetch.txt | 1 + src/test/py/bazel/bzlmod/bazel_fetch_test.py | 46 +++++++++++++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchCommand.java b/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchCommand.java index e74b9ac6c03..d3a79e668c5 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchCommand.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchCommand.java @@ -41,6 +41,7 @@ import com.google.devtools.build.lib.query2.engine.QueryException; import com.google.devtools.build.lib.query2.engine.QueryExpression; import com.google.devtools.build.lib.query2.engine.QuerySyntaxException; import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback; +import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction; import com.google.devtools.build.lib.rules.repository.RepositoryDirectoryValue; import com.google.devtools.build.lib.runtime.BlazeCommand; import com.google.devtools.build.lib.runtime.BlazeCommandResult; @@ -52,6 +53,7 @@ import com.google.devtools.build.lib.runtime.commands.QueryCommand; import com.google.devtools.build.lib.server.FailureDetails; import com.google.devtools.build.lib.server.FailureDetails.FailureDetail; import com.google.devtools.build.lib.server.FailureDetails.FetchCommand.Code; +import com.google.devtools.build.lib.skyframe.PrecomputedValue; import com.google.devtools.build.lib.skyframe.RepositoryMappingValue.RepositoryMappingResolutionException; import com.google.devtools.build.lib.skyframe.SkyframeExecutor; import com.google.devtools.build.lib.util.AbruptExitException; @@ -82,7 +84,6 @@ import net.starlark.java.eval.EvalException; allowResidue = true, completion = "label") public final class FetchCommand implements BlazeCommand { - // TODO(kchodorow): add an option to force-fetch targets, even if they're already downloaded. // TODO(kchodorow): this would be a great time to check for difference and invalidate the upward // transitive closure for local repositories. @@ -119,6 +120,15 @@ public final class FetchCommand implements BlazeCommand { /* showProgress= */ true, /* id= */ null)); BlazeCommandResult result; + if (fetchOptions.force) { + // Using commandId as the value -instead of true/false- to make sure to invalidate skyframe + // and to actually force fetch each time + env.getSkyframeExecutor() + .injectExtraPrecomputedValues( + ImmutableList.of( + PrecomputedValue.injected( + RepositoryDelegatorFunction.FORCE_FETCH, env.getCommandId().toString()))); + } if (fetchOptions.all || fetchOptions.configure) { result = fetchAll(env, options, threadsOption, fetchOptions.configure); } else if (!fetchOptions.repos.isEmpty()) { diff --git a/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchOptions.java b/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchOptions.java index c35ebf21878..e5801a1581a 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchOptions.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchOptions.java @@ -52,7 +52,13 @@ public class FetchOptions extends OptionsBase { + " {@@canonical_repo_name}. Only works when --enable_bzlmod is on.") public List<String> repos; - /*TODO(salmasamy) add more options: - * force: to force fetch even if a repo exists - */ + @Option( + name = "force", + defaultValue = "false", + documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, + effectTags = {OptionEffectTag.CHANGES_INPUTS}, + help = + "Ignore existing repository if any and force fetch the repository again. Only works when " + + "--enable_bzlmod is on.") + public boolean force; } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/commands/fetch.txt b/src/main/java/com/google/devtools/build/lib/bazel/commands/fetch.txt index 8dd87ff0bb5..ce584f7fd5a 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/commands/fetch.txt +++ b/src/main/java/com/google/devtools/build/lib/bazel/commands/fetch.txt @@ -5,6 +5,7 @@ Usage: %{product} %{command} [<option> ...] - --configure: Only fetches repositories marked as 'configure', only works with bzlmod. - --repo={repo}: Only fetches the specified repository, which can be either {@apparent_repo_name} or {@@canonical_repo_name} , only works with bzlmod. + - --force: Ignore existing repository if any and force fetch the repository again, only works with bzlmod. - <targets>: Fetches dependencies only for given targets. %{options} diff --git a/src/test/py/bazel/bzlmod/bazel_fetch_test.py b/src/test/py/bazel/bzlmod/bazel_fetch_test.py index a6f1a728837..22fba4125b2 100644 --- a/src/test/py/bazel/bzlmod/bazel_fetch_test.py +++ b/src/test/py/bazel/bzlmod/bazel_fetch_test.py @@ -219,6 +219,52 @@ class BazelFetchTest(test_base.TestBase): stderr, ) + def testForceFetch(self): + self.ScratchFile( + 'MODULE.bazel', + [ + 'ext = use_extension("extension.bzl", "ext")', + 'use_repo(ext, "hello")', + 'local_path_override(module_name="bazel_tools", path="tools_mock")', + 'local_path_override(module_name="local_config_platform", ', + 'path="platforms_mock")', + ], + ) + self.ScratchFile('BUILD') + self.ScratchFile('orange_juice.txt', ['Orange Juice']) + file_path = self.Path('orange_juice.txt').replace('\\', '\\\\') + self.ScratchFile( + 'extension.bzl', + [ + 'def _repo_rule_impl(ctx):', + ' file_content = ctx.read("' + file_path + '").strip()', + ' print(file_content)', + ' ctx.file("WORKSPACE")', + ' ctx.file("BUILD")', + 'repo_rule = repository_rule(implementation=_repo_rule_impl)', + '', + 'def _ext_impl(ctx):', + ' repo_rule(name="hello")', + 'ext = module_extension(implementation=_ext_impl)', + ], + ) + + _, _, stderr = self.RunBazel(['fetch', '--repo=@hello']) + self.assertIn('Orange Juice', ''.join(stderr)) + + # Change file content and run WITHOUT force, assert no fetching! + self.ScratchFile('orange_juice.txt', ['No more Orange Juice!']) + _, _, stderr = self.RunBazel(['fetch', '--repo=@hello']) + self.assertNotIn('No more Orange Juice!', ''.join(stderr)) + + # Run again WITH --force and assert fetching + _, _, stderr = self.RunBazel(['fetch', '--repo=@hello', '--force']) + self.assertIn('No more Orange Juice!', ''.join(stderr)) + + # One more time to validate force is invoked and not cached by skyframe + _, _, stderr = self.RunBazel(['fetch', '--repo=@hello', '--force']) + self.assertIn('No more Orange Juice!', ''.join(stderr)) + if __name__ == '__main__': absltest.main() -- GitLab