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 1dc3be132eb9da..6ae48c2b5260c5 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 @@ -38,6 +38,7 @@ import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.runtime.KeepGoingOption; import com.google.devtools.build.lib.runtime.LoadingPhaseThreadsOption; +import com.google.devtools.build.lib.runtime.commands.TargetPatternsHelper; import com.google.devtools.build.lib.runtime.commands.TestCommand; import com.google.devtools.build.lib.server.FailureDetails; import com.google.devtools.build.lib.server.FailureDetails.FailureDetail; @@ -76,10 +77,7 @@ public final class FetchCommand implements BlazeCommand { @Override public void editOptions(OptionsParser optionsParser) { - // We only need to inject these options with fetch target (when there is a residue) - if (!optionsParser.getResidue().isEmpty()) { - TargetFetcher.injectNoBuildOption(optionsParser); - } + TargetFetcher.injectNoBuildOption(optionsParser); } @Override @@ -111,9 +109,20 @@ public BlazeCommandResult exec(CommandEnvironment env, OptionsParsingResult opti BlazeCommandResult result; LoadingPhaseThreadsOption threadsOption = options.getOptions(LoadingPhaseThreadsOption.class); + List targets; + try { + targets = TargetPatternsHelper.readFrom(env, options); + } catch (TargetPatternsHelper.TargetPatternsHelperException e) { + env.getReporter().handle(Event.error(e.getMessage())); + return BlazeCommandResult.failureDetail(e.getFailureDetail()); + } try { - if (!options.getResidue().isEmpty()) { - result = fetchTarget(env, options, options.getResidue()); + if (!targets.isEmpty()) { + if (!fetchOptions.repos.isEmpty()) { + return createFailedBlazeCommandResult( + env.getReporter(), "Target patterns and --repo cannot both be specified"); + } + result = fetchTarget(env, options, targets); } else if (!fetchOptions.repos.isEmpty()) { result = fetchRepos(env, threadsOption, fetchOptions.repos); } else { // --all, --configure, or just 'fetch' diff --git a/src/main/java/com/google/devtools/build/lib/bazel/commands/VendorCommand.java b/src/main/java/com/google/devtools/build/lib/bazel/commands/VendorCommand.java index 64a6e7f2dcf5f2..8c088a45ec09a1 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/commands/VendorCommand.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/commands/VendorCommand.java @@ -44,6 +44,7 @@ import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.runtime.KeepGoingOption; import com.google.devtools.build.lib.runtime.LoadingPhaseThreadsOption; +import com.google.devtools.build.lib.runtime.commands.TargetPatternsHelper; import com.google.devtools.build.lib.runtime.commands.TestCommand; import com.google.devtools.build.lib.server.FailureDetails; import com.google.devtools.build.lib.server.FailureDetails.FailureDetail; @@ -123,10 +124,7 @@ public void setDownloadManager(DownloadManager downloadManager) { @Override public void editOptions(OptionsParser optionsParser) { - // We only need to inject these options with fetch target (when there is a residue) - if (!optionsParser.getResidue().isEmpty()) { - TargetFetcher.injectNoBuildOption(optionsParser); - } + TargetFetcher.injectNoBuildOption(optionsParser); } @Override @@ -158,9 +156,20 @@ public BlazeCommandResult exec(CommandEnvironment env, OptionsParsingResult opti Path vendorDirectory = env.getWorkspace().getRelative(options.getOptions(RepositoryOptions.class).vendorDirectory); this.vendorManager = new VendorManager(vendorDirectory); + List targets; + try { + targets = TargetPatternsHelper.readFrom(env, options); + } catch (TargetPatternsHelper.TargetPatternsHelperException e) { + env.getReporter().handle(Event.error(e.getMessage())); + return BlazeCommandResult.failureDetail(e.getFailureDetail()); + } try { - if (!options.getResidue().isEmpty()) { - result = vendorTargets(env, options, options.getResidue()); + if (!targets.isEmpty()) { + if (!vendorOptions.repos.isEmpty()) { + return createFailedBlazeCommandResult( + env.getReporter(), "Target patterns and --repo cannot both be specified"); + } + result = vendorTargets(env, options, targets); } else if (!vendorOptions.repos.isEmpty()) { result = vendorRepos(env, threadsOption, vendorOptions.repos); } else { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/TargetPatternsHelper.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/TargetPatternsHelper.java index a39db01f57d997..ac0f0421b9b3cd 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/TargetPatternsHelper.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/TargetPatternsHelper.java @@ -33,7 +33,7 @@ import java.util.function.Predicate; /** Provides support for reading target patterns from a file or the command-line. */ -final class TargetPatternsHelper { +public final class TargetPatternsHelper { private TargetPatternsHelper() {} @@ -77,7 +77,7 @@ public static List readFrom(CommandEnvironment env, OptionsParsingResult } /** Thrown when target patterns couldn't be read. */ - static class TargetPatternsHelperException extends Exception { + public static class TargetPatternsHelperException extends Exception { private final TargetPatterns.Code detailedCode; private TargetPatternsHelperException(String message, TargetPatterns.Code detailedCode) { @@ -85,7 +85,7 @@ private TargetPatternsHelperException(String message, TargetPatterns.Code detail this.detailedCode = detailedCode; } - FailureDetail getFailureDetail() { + public FailureDetail getFailureDetail() { return FailureDetail.newBuilder() .setMessage(getMessage()) .setTargetPatterns(TargetPatterns.newBuilder().setCode(detailedCode)) diff --git a/src/test/py/bazel/bzlmod/bazel_fetch_test.py b/src/test/py/bazel/bzlmod/bazel_fetch_test.py index 5a3a925a406b9d..fd35c14b63eee2 100644 --- a/src/test/py/bazel/bzlmod/bazel_fetch_test.py +++ b/src/test/py/bazel/bzlmod/bazel_fetch_test.py @@ -304,6 +304,44 @@ def testFetchTarget(self): _, stdout, _ = self.RunBazel(['run', '//:main', '--nofetch']) self.assertIn('Hello there! => aaa@1.0', stdout) + def testFetchWithTargetPatternFile(self): + self.main_registry.createCcModule('aaa', '1.0').createCcModule( + 'bbb', '1.0', {'aaa': '1.0'} + ) + self.ScratchFile( + 'MODULE.bazel', + [ + 'bazel_dep(name = "bbb", version = "1.0")', + ], + ) + self.ScratchFile( + 'BUILD', + [ + 'cc_binary(', + ' name = "main",', + ' srcs = ["main.cc"],', + ' deps = [', + ' "@bbb//:lib_bbb",', + ' ],', + ')', + ], + ) + self.ScratchFile( + 'main.cc', + [ + '#include "aaa.h"', + 'int main() {', + ' hello_aaa("Hello there!");', + '}', + ], + ) + self.ScratchFile('targets.params', ['//:main']) + self.RunBazel(['fetch', '--target_pattern_file=targets.params']) + # If we can run the target with --nofetch, this means we successfully + # fetched all its needed repos + _, stdout, _ = self.RunBazel(['run', '//:main', '--nofetch']) + self.assertIn('Hello there! => aaa@1.0', stdout) + if __name__ == '__main__': absltest.main() diff --git a/src/test/py/bazel/bzlmod/bazel_vendor_test.py b/src/test/py/bazel/bzlmod/bazel_vendor_test.py index 4dc87a2a9c57b5..12ee461e124adb 100644 --- a/src/test/py/bazel/bzlmod/bazel_vendor_test.py +++ b/src/test/py/bazel/bzlmod/bazel_vendor_test.py @@ -640,6 +640,37 @@ def on_rm_error(func, path, exc_info): self.assertIn('bbb~', os.listdir(self._test_cwd + '/vendor')) self.assertNotIn('ccc~', os.listdir(self._test_cwd + '/vendor')) + def testVendorWithTargetPatternFile(self): + self.main_registry.createCcModule('aaa', '1.0').createCcModule( + 'bbb', '1.0' + ).createCcModule('ccc', '1.0') + self.ScratchFile( + 'MODULE.bazel', + [ + 'bazel_dep(name = "aaa", version = "1.0")', + 'bazel_dep(name = "bbb", version = "1.0")', + 'bazel_dep(name = "ccc", version = "1.0")', + ], + ) + self.ScratchFile('BUILD') + self.ScratchFile( + 'targets.params', + [ + '@aaa//:lib_aaa', + '@bbb//:lib_bbb', + ], + ) + + self.RunBazel([ + 'vendor', + '--target_pattern_file=targets.params', + '--vendor_dir=vendor', + ]) + # Assert aaa & bbb and are vendored + self.assertIn('aaa~', os.listdir(self._test_cwd + '/vendor')) + self.assertIn('bbb~', os.listdir(self._test_cwd + '/vendor')) + self.assertNotIn('ccc~', os.listdir(self._test_cwd + '/vendor')) + def testBuildVendoredTargetOffline(self): self.main_registry.createCcModule('aaa', '1.0').createCcModule( 'bbb', '1.0', {'aaa': '1.0'}