20
20
import static com .google .common .collect .ImmutableSet .toImmutableSet ;
21
21
import static java .nio .charset .StandardCharsets .UTF_8 ;
22
22
23
+ import com .github .difflib .patch .PatchFailedException ;
23
24
import com .google .common .collect .ImmutableList ;
24
25
import com .google .common .collect .ImmutableMap ;
25
26
import com .google .common .collect .ImmutableSet ;
27
+ import com .google .common .collect .Iterables ;
26
28
import com .google .common .collect .Maps ;
27
29
import com .google .devtools .build .lib .actions .FileValue ;
28
30
import com .google .devtools .build .lib .bazel .bzlmod .CompiledModuleFile .IncludeStatement ;
29
31
import com .google .devtools .build .lib .bazel .bzlmod .ModuleFileValue .NonRootModuleFileValue ;
30
32
import com .google .devtools .build .lib .bazel .bzlmod .ModuleFileValue .RootModuleFileValue ;
33
+ import com .google .devtools .build .lib .bazel .repository .PatchUtil ;
31
34
import com .google .devtools .build .lib .bazel .repository .downloader .Checksum .MissingChecksumException ;
32
35
import com .google .devtools .build .lib .cmdline .Label ;
33
36
import com .google .devtools .build .lib .cmdline .LabelConstants ;
50
53
import com .google .devtools .build .lib .skyframe .PackageLookupValue ;
51
54
import com .google .devtools .build .lib .skyframe .PrecomputedValue ;
52
55
import com .google .devtools .build .lib .skyframe .PrecomputedValue .Precomputed ;
56
+ import com .google .devtools .build .lib .vfs .DigestHashFunction ;
53
57
import com .google .devtools .build .lib .vfs .FileSystemUtils ;
54
58
import com .google .devtools .build .lib .vfs .Path ;
55
59
import com .google .devtools .build .lib .vfs .PathFragment ;
56
60
import com .google .devtools .build .lib .vfs .Root ;
57
61
import com .google .devtools .build .lib .vfs .RootedPath ;
62
+ import com .google .devtools .build .lib .vfs .inmemoryfs .InMemoryFileSystem ;
58
63
import com .google .devtools .build .skyframe .SkyFunction ;
59
64
import com .google .devtools .build .skyframe .SkyFunctionException ;
60
65
import com .google .devtools .build .skyframe .SkyFunctionException .Transience ;
@@ -613,11 +618,15 @@ private GetModuleFileResult getModuleFile(
613
618
StoredEventHandler downloadEventHandler = new StoredEventHandler ();
614
619
for (Registry registry : registryObjects ) {
615
620
try {
616
- Optional <ModuleFile > moduleFile = registry .getModuleFile (key , downloadEventHandler );
617
- if (moduleFile .isEmpty ()) {
621
+ Optional <ModuleFile > maybeModuleFile = registry .getModuleFile (key , downloadEventHandler );
622
+ if (maybeModuleFile .isEmpty ()) {
618
623
continue ;
619
624
}
620
- return new GetModuleFileResult (moduleFile .get (), registry , downloadEventHandler );
625
+ ModuleFile moduleFile = maybePatchModuleFile (maybeModuleFile .get (), override , env );
626
+ if (moduleFile == null ) {
627
+ return null ;
628
+ }
629
+ return new GetModuleFileResult (moduleFile , registry , downloadEventHandler );
621
630
} catch (MissingChecksumException e ) {
622
631
throw new ModuleFileFunctionException (
623
632
ExternalDepsException .withCause (Code .BAD_LOCKFILE , e ));
@@ -630,6 +639,114 @@ private GetModuleFileResult getModuleFile(
630
639
throw errorf (Code .MODULE_NOT_FOUND , "module not found in registries: %s" , key );
631
640
}
632
641
642
+ /**
643
+ * Applies any patches specified in registry overrides.
644
+ *
645
+ * <p>This allows users to modify MODULE.bazel files and thus influence resolution and visibility
646
+ * for modules via patches without having to replace the entire module via a non-registry
647
+ * override.
648
+ *
649
+ * <p>Note: Only patch files from the main repo are applied, all other patches are ignored. This
650
+ * is necessary as we can't load other repos during resolution (unless they are subject to a
651
+ * non-registry override). Patch commands are also not supported as they cannot be selectively
652
+ * applied to the module file only.
653
+ */
654
+ @ Nullable
655
+ private ModuleFile maybePatchModuleFile (
656
+ ModuleFile moduleFile , ModuleOverride override , Environment env )
657
+ throws InterruptedException , ModuleFileFunctionException {
658
+ if (!(override instanceof SingleVersionOverride singleVersionOverride )) {
659
+ return moduleFile ;
660
+ }
661
+ var patchesInMainRepo =
662
+ singleVersionOverride .getPatches ().stream ()
663
+ .filter (label -> label .getRepository ().isMain ())
664
+ .collect (toImmutableList ());
665
+ if (patchesInMainRepo .isEmpty ()) {
666
+ return moduleFile ;
667
+ }
668
+
669
+ // Get the patch paths.
670
+ var patchPackageLookupKeys =
671
+ patchesInMainRepo .stream ()
672
+ .map (Label ::getPackageIdentifier )
673
+ .map (pkg -> (SkyKey ) PackageLookupValue .key (pkg ))
674
+ .collect (toImmutableList ());
675
+ var patchPackageLookupResult = env .getValuesAndExceptions (patchPackageLookupKeys );
676
+ if (env .valuesMissing ()) {
677
+ return null ;
678
+ }
679
+ List <RootedPath > patchPaths = new ArrayList <>();
680
+ for (int i = 0 ; i < patchesInMainRepo .size (); i ++) {
681
+ var pkgLookupValue =
682
+ (PackageLookupValue ) patchPackageLookupResult .get (patchPackageLookupKeys .get (i ));
683
+ if (pkgLookupValue == null ) {
684
+ return null ;
685
+ }
686
+ if (!pkgLookupValue .packageExists ()) {
687
+ Label label = patchesInMainRepo .get (i );
688
+ throw errorf (
689
+ Code .BAD_MODULE ,
690
+ "unable to load package for single_version_override patch %s: %s" ,
691
+ label ,
692
+ PackageLookupFunction .explainNoBuildFileValue (label .getPackageIdentifier (), env ));
693
+ }
694
+ patchPaths .add (
695
+ RootedPath .toRootedPath (
696
+ pkgLookupValue .getRoot (), patchesInMainRepo .get (i ).toPathFragment ()));
697
+ }
698
+
699
+ // Register dependencies on the patch files and ensure that they exist.
700
+ var fileKeys = Iterables .transform (patchPaths , FileValue ::key );
701
+ var fileValueResult = env .getValuesAndExceptions (fileKeys );
702
+ if (env .valuesMissing ()) {
703
+ return null ;
704
+ }
705
+ for (var key : fileKeys ) {
706
+ var fileValue = (FileValue ) fileValueResult .get (key );
707
+ if (fileValue == null ) {
708
+ return null ;
709
+ }
710
+ if (!fileValue .isFile ()) {
711
+ throw errorf (
712
+ Code .BAD_MODULE ,
713
+ "error reading single_version_override patch %s: not a regular file" ,
714
+ key .argument ());
715
+ }
716
+ }
717
+
718
+ // Apply the patches to the module file only.
719
+ InMemoryFileSystem patchFs = new InMemoryFileSystem (DigestHashFunction .SHA256 );
720
+ try {
721
+ Path moduleRoot = patchFs .getPath ("/module" );
722
+ moduleRoot .createDirectoryAndParents ();
723
+ Path moduleFilePath = moduleRoot .getChild ("MODULE.bazel" );
724
+ FileSystemUtils .writeContent (moduleFilePath , moduleFile .getContent ());
725
+ for (var patchPath : patchPaths ) {
726
+ try {
727
+ PatchUtil .applyToSingleFile (
728
+ patchPath .asPath (),
729
+ singleVersionOverride .getPatchStrip (),
730
+ moduleRoot ,
731
+ moduleFilePath );
732
+ } catch (PatchFailedException e ) {
733
+ throw errorf (
734
+ Code .BAD_MODULE ,
735
+ "error applying single_version_override patch %s to module file: %s" ,
736
+ patchPath .asPath (),
737
+ e .getMessage ());
738
+ }
739
+ }
740
+ return ModuleFile .create (
741
+ FileSystemUtils .readContent (moduleFilePath ), moduleFile .getLocation ());
742
+ } catch (IOException e ) {
743
+ throw errorf (
744
+ Code .BAD_MODULE ,
745
+ "error applying single_version_override patches to module file: %s" ,
746
+ e .getMessage ());
747
+ }
748
+ }
749
+
633
750
private static byte [] readModuleFile (Path path ) throws ModuleFileFunctionException {
634
751
try {
635
752
return FileSystemUtils .readWithKnownFileSize (path , path .getFileSize ());
0 commit comments