1313// limitations under the License.
1414package com .google .devtools .build .lib .analysis ;
1515
16+ import static com .google .common .collect .ImmutableSet .toImmutableSet ;
17+ import static com .google .common .collect .ImmutableSortedMap .toImmutableSortedMap ;
1618import static java .nio .charset .StandardCharsets .ISO_8859_1 ;
1719import static java .util .Comparator .comparing ;
1820
19- import com .google .auto . value . AutoValue ;
20- import com .google .common .collect .ImmutableList ;
21+ import com .google .common . collect . ImmutableSet ;
22+ import com .google .common .collect .ImmutableSortedMap ;
2123import com .google .devtools .build .lib .actions .ActionExecutionContext ;
2224import com .google .devtools .build .lib .actions .ActionKeyContext ;
2325import com .google .devtools .build .lib .actions .ActionOwner ;
2426import com .google .devtools .build .lib .actions .Artifact ;
2527import com .google .devtools .build .lib .actions .Artifact .ArtifactExpander ;
2628import com .google .devtools .build .lib .actions .CommandLineExpansionException ;
29+ import com .google .devtools .build .lib .actions .CommandLineItem .MapFn ;
2730import com .google .devtools .build .lib .actions .ExecException ;
2831import com .google .devtools .build .lib .analysis .actions .AbstractFileWriteAction ;
2932import com .google .devtools .build .lib .analysis .actions .DeterministicWriter ;
33+ import com .google .devtools .build .lib .cmdline .RepositoryMapping ;
3034import com .google .devtools .build .lib .cmdline .RepositoryName ;
35+ import com .google .devtools .build .lib .collect .nestedset .NestedSet ;
3136import com .google .devtools .build .lib .collect .nestedset .NestedSetBuilder ;
3237import com .google .devtools .build .lib .collect .nestedset .Order ;
38+ import com .google .devtools .build .lib .packages .Package ;
3339import com .google .devtools .build .lib .util .Fingerprint ;
3440import java .io .PrintWriter ;
35- import java .util .List ;
41+ import java .util .Map . Entry ;
3642import java .util .UUID ;
3743import javax .annotation .Nullable ;
3844import net .starlark .java .eval .EvalException ;
3945
4046/** Creates a manifest file describing the repos and mappings relevant for a runfile tree. */
41- public class RepoMappingManifestAction extends AbstractFileWriteAction {
42- private static final UUID MY_UUID = UUID .fromString ("458e351c-4d30-433d-b927-da6cddd4737f" );
43-
44- private final ImmutableList <Entry > entries ;
45- private final String workspaceName ;
47+ public final class RepoMappingManifestAction extends AbstractFileWriteAction {
4648
47- /** An entry in the repo mapping manifest file. */
48- @ AutoValue
49- public abstract static class Entry {
50- public static Entry of (
51- RepositoryName sourceRepo , String targetRepoApparentName , RepositoryName targetRepo ) {
52- return new AutoValue_RepoMappingManifestAction_Entry (
53- sourceRepo , targetRepoApparentName , targetRepo );
54- }
49+ private static final UUID MY_UUID = UUID .fromString ("458e351c-4d30-433d-b927-da6cddd4737f" );
5550
56- public abstract RepositoryName sourceRepo ();
51+ // Uses MapFn's args parameter just like Fingerprint#addString to compute a cacheable fingerprint
52+ // of just the repo name and mapping of a given Package.
53+ private static final MapFn <Package > REPO_AND_MAPPING_DIGEST_FN =
54+ (pkg , args ) -> {
55+ args .accept (pkg .getPackageIdentifier ().getRepository ().getName ());
5756
58- public abstract String targetRepoApparentName ();
57+ var mapping = pkg .getRepositoryMapping ().entries ();
58+ args .accept (String .valueOf (mapping .size ()));
59+ mapping .forEach (
60+ (apparentName , canonicalName ) -> {
61+ args .accept (apparentName );
62+ args .accept (canonicalName .getName ());
63+ });
64+ };
5965
60- public abstract RepositoryName targetRepo ();
61- }
66+ private final NestedSet <Package > transitivePackages ;
67+ private final NestedSet <Artifact > runfilesArtifacts ;
68+ private final String workspaceName ;
6269
6370 public RepoMappingManifestAction (
64- ActionOwner owner , Artifact output , List <Entry > entries , String workspaceName ) {
71+ ActionOwner owner ,
72+ Artifact output ,
73+ NestedSet <Package > transitivePackages ,
74+ NestedSet <Artifact > runfilesArtifacts ,
75+ String workspaceName ) {
6576 super (owner , NestedSetBuilder .emptySet (Order .STABLE_ORDER ), output , /*makeExecutable=*/ false );
66- this .entries =
67- ImmutableList .sortedCopyOf (
68- comparing ((Entry e ) -> e .sourceRepo ().getName ())
69- .thenComparing (Entry ::targetRepoApparentName ),
70- entries );
77+ this .transitivePackages = transitivePackages ;
78+ this .runfilesArtifacts = runfilesArtifacts ;
7179 this .workspaceName = workspaceName ;
7280 }
7381
@@ -78,7 +86,7 @@ public String getMnemonic() {
7886
7987 @ Override
8088 protected String getRawProgressMessage () {
81- return "writing repo mapping manifest for " + getOwner ().getLabel ();
89+ return "Writing repo mapping manifest for " + getOwner ().getLabel ();
8290 }
8391
8492 @ Override
@@ -88,35 +96,61 @@ protected void computeKey(
8896 Fingerprint fp )
8997 throws CommandLineExpansionException , EvalException , InterruptedException {
9098 fp .addUUID (MY_UUID );
99+ actionKeyContext .addNestedSetToFingerprint (REPO_AND_MAPPING_DIGEST_FN , fp , transitivePackages );
100+ actionKeyContext .addNestedSetToFingerprint (fp , runfilesArtifacts );
91101 fp .addString (workspaceName );
92- for (Entry entry : entries ) {
93- fp .addString (entry .sourceRepo ().getName ());
94- fp .addString (entry .targetRepoApparentName ());
95- fp .addString (entry .targetRepo ().getName ());
96- }
97102 }
98103
99104 @ Override
100105 public DeterministicWriter newDeterministicWriter (ActionExecutionContext ctx )
101106 throws InterruptedException , ExecException {
102107 return out -> {
103- PrintWriter writer = new PrintWriter (out , /*autoFlush=*/ false , ISO_8859_1 );
104- for (Entry entry : entries ) {
105- if (entry .targetRepoApparentName ().isEmpty ()) {
106- // The apparent repo name can only be empty for the main repo. We skip this line as
107- // Rlocation paths can't reference an empty apparent name anyway.
108- continue ;
109- }
110- // The canonical name of the main repo is the empty string, which is not a valid name for a
111- // directory, so the "workspace name" is used the name of the directory under the runfiles
112- // tree for it.
113- String targetRepoDirectoryName =
114- entry .targetRepo ().isMain () ? workspaceName : entry .targetRepo ().getName ();
115- writer .format (
116- "%s,%s,%s\n " ,
117- entry .sourceRepo ().getName (), entry .targetRepoApparentName (), targetRepoDirectoryName );
118- }
108+ PrintWriter writer = new PrintWriter (out , /* autoFlush= */ false , ISO_8859_1 );
109+
110+ ImmutableSet <RepositoryName > reposContributingRunfiles =
111+ runfilesArtifacts .toList ().stream ()
112+ .filter (a -> a .getOwner () != null )
113+ .map (a -> a .getOwner ().getRepository ())
114+ .collect (toImmutableSet ());
115+ transitivePackages .toList ().stream ()
116+ .collect (
117+ toImmutableSortedMap (
118+ comparing (RepositoryName ::getName ),
119+ pkg -> pkg .getPackageIdentifier ().getRepository (),
120+ Package ::getRepositoryMapping ,
121+ // All packages in a given repository have the same repository mapping, so the
122+ // particular way of resolving duplicates does not matter.
123+ (first , second ) -> first ))
124+ .forEach (
125+ (repoName , mapping ) ->
126+ writeRepoMapping (writer , reposContributingRunfiles , repoName , mapping ));
119127 writer .flush ();
120128 };
121129 }
130+
131+ private void writeRepoMapping (
132+ PrintWriter writer ,
133+ ImmutableSet <RepositoryName > reposContributingRunfiles ,
134+ RepositoryName repoName ,
135+ RepositoryMapping repoMapping ) {
136+ for (Entry <String , RepositoryName > mappingEntry :
137+ ImmutableSortedMap .copyOf (repoMapping .entries ()).entrySet ()) {
138+ if (mappingEntry .getKey ().isEmpty ()) {
139+ // The apparent repo name can only be empty for the main repo. We skip this line as
140+ // Rlocation paths can't reference an empty apparent name anyway.
141+ continue ;
142+ }
143+ if (!reposContributingRunfiles .contains (mappingEntry .getValue ())) {
144+ // We only write entries for repos that actually contribute runfiles.
145+ continue ;
146+ }
147+ // The canonical name of the main repo is the empty string, which is not a valid name for a
148+ // directory, so the "workspace name" is used the name of the directory under the runfiles
149+ // tree for it.
150+ String targetRepoDirectoryName =
151+ mappingEntry .getValue ().isMain () ? workspaceName : mappingEntry .getValue ().getName ();
152+ writer .format (
153+ "%s,%s,%s\n " , repoName .getName (), mappingEntry .getKey (), targetRepoDirectoryName );
154+ }
155+ }
122156}
0 commit comments