13
13
// limitations under the License.
14
14
package com .google .devtools .build .lib .analysis ;
15
15
16
+ import static com .google .common .collect .ImmutableSet .toImmutableSet ;
17
+ import static com .google .common .collect .ImmutableSortedMap .toImmutableSortedMap ;
16
18
import static java .nio .charset .StandardCharsets .ISO_8859_1 ;
17
19
import static java .util .Comparator .comparing ;
18
20
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 ;
21
23
import com .google .devtools .build .lib .actions .ActionExecutionContext ;
22
24
import com .google .devtools .build .lib .actions .ActionKeyContext ;
23
25
import com .google .devtools .build .lib .actions .ActionOwner ;
24
26
import com .google .devtools .build .lib .actions .Artifact ;
25
27
import com .google .devtools .build .lib .actions .Artifact .ArtifactExpander ;
26
28
import com .google .devtools .build .lib .actions .CommandLineExpansionException ;
29
+ import com .google .devtools .build .lib .actions .CommandLineItem .MapFn ;
27
30
import com .google .devtools .build .lib .actions .ExecException ;
28
31
import com .google .devtools .build .lib .analysis .actions .AbstractFileWriteAction ;
29
32
import com .google .devtools .build .lib .analysis .actions .DeterministicWriter ;
33
+ import com .google .devtools .build .lib .cmdline .RepositoryMapping ;
30
34
import com .google .devtools .build .lib .cmdline .RepositoryName ;
35
+ import com .google .devtools .build .lib .collect .nestedset .NestedSet ;
31
36
import com .google .devtools .build .lib .collect .nestedset .NestedSetBuilder ;
32
37
import com .google .devtools .build .lib .collect .nestedset .Order ;
38
+ import com .google .devtools .build .lib .packages .Package ;
33
39
import com .google .devtools .build .lib .util .Fingerprint ;
34
40
import java .io .PrintWriter ;
35
- import java .util .List ;
41
+ import java .util .Map . Entry ;
36
42
import java .util .UUID ;
37
43
import javax .annotation .Nullable ;
38
44
import net .starlark .java .eval .EvalException ;
39
45
40
46
/** 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 {
46
48
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" );
55
50
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 ());
57
56
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
+ };
59
65
60
- public abstract RepositoryName targetRepo ();
61
- }
66
+ private final NestedSet <Package > transitivePackages ;
67
+ private final NestedSet <Artifact > runfilesArtifacts ;
68
+ private final String workspaceName ;
62
69
63
70
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 ) {
65
76
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 ;
71
79
this .workspaceName = workspaceName ;
72
80
}
73
81
@@ -78,7 +86,7 @@ public String getMnemonic() {
78
86
79
87
@ Override
80
88
protected String getRawProgressMessage () {
81
- return "writing repo mapping manifest for " + getOwner ().getLabel ();
89
+ return "Writing repo mapping manifest for " + getOwner ().getLabel ();
82
90
}
83
91
84
92
@ Override
@@ -88,35 +96,61 @@ protected void computeKey(
88
96
Fingerprint fp )
89
97
throws CommandLineExpansionException , EvalException , InterruptedException {
90
98
fp .addUUID (MY_UUID );
99
+ actionKeyContext .addNestedSetToFingerprint (REPO_AND_MAPPING_DIGEST_FN , fp , transitivePackages );
100
+ actionKeyContext .addNestedSetToFingerprint (fp , runfilesArtifacts );
91
101
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
- }
97
102
}
98
103
99
104
@ Override
100
105
public DeterministicWriter newDeterministicWriter (ActionExecutionContext ctx )
101
106
throws InterruptedException , ExecException {
102
107
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 ));
119
127
writer .flush ();
120
128
};
121
129
}
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
+ }
122
156
}
0 commit comments