|
14 | 14 |
|
15 | 15 | package com.google.devtools.build.lib.bazel.rules.genrule;
|
16 | 16 |
|
17 |
| -import static com.google.devtools.build.lib.analysis.RunfilesProvider.withData; |
18 |
| - |
19 |
| -import com.google.common.collect.ImmutableList; |
20 |
| -import com.google.common.collect.ImmutableMap; |
21 |
| -import com.google.common.collect.ImmutableSet; |
22 |
| -import com.google.common.collect.Iterables; |
23 |
| -import com.google.common.collect.Lists; |
24 |
| -import com.google.common.collect.Maps; |
25 |
| -import com.google.devtools.build.lib.actions.Artifact; |
26 |
| -import com.google.devtools.build.lib.analysis.CommandHelper; |
27 |
| -import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext; |
28 |
| -import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
29 |
| -import com.google.devtools.build.lib.analysis.FileProvider; |
30 |
| -import com.google.devtools.build.lib.analysis.FilesToRunProvider; |
31 |
| -import com.google.devtools.build.lib.analysis.MakeVariableExpander.ExpansionException; |
32 |
| -import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; |
33 |
| -import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; |
34 | 17 | import com.google.devtools.build.lib.analysis.RuleContext;
|
35 |
| -import com.google.devtools.build.lib.analysis.Runfiles; |
36 |
| -import com.google.devtools.build.lib.analysis.RunfilesProvider; |
37 |
| -import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; |
38 |
| -import com.google.devtools.build.lib.cmdline.Label; |
39 |
| -import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
40 |
| -import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
41 |
| -import com.google.devtools.build.lib.collect.nestedset.Order; |
42 |
| -import com.google.devtools.build.lib.packages.TargetUtils; |
43 |
| -import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; |
44 |
| -import com.google.devtools.build.lib.rules.ToolchainProvider; |
| 18 | +import com.google.devtools.build.lib.rules.genrule.GenRuleBase; |
45 | 19 | import com.google.devtools.build.lib.syntax.Type;
|
46 |
| -import com.google.devtools.build.lib.vfs.PathFragment; |
47 |
| -import java.util.List; |
48 |
| -import java.util.Map; |
49 | 20 |
|
50 | 21 | /**
|
51 |
| - * An implementation of genrule. |
| 22 | + * An implementation of genrule for Bazel. |
52 | 23 | */
|
53 |
| -public class BazelGenRule implements RuleConfiguredTargetFactory { |
54 |
| - |
55 |
| - private Artifact getExecutable(RuleContext ruleContext, NestedSet<Artifact> filesToBuild) { |
56 |
| - if (Iterables.size(filesToBuild) == 1) { |
57 |
| - Artifact out = Iterables.getOnlyElement(filesToBuild); |
58 |
| - if (ruleContext.attributes().get("executable", Type.BOOLEAN)) { |
59 |
| - return out; |
60 |
| - } |
61 |
| - } |
62 |
| - return null; |
63 |
| - } |
| 24 | +public class BazelGenRule extends GenRuleBase { |
64 | 25 |
|
65 | 26 | @Override
|
66 |
| - public ConfiguredTarget create(RuleContext ruleContext) |
67 |
| - throws RuleErrorException, InterruptedException { |
68 |
| - final List<Artifact> resolvedSrcs = Lists.newArrayList(); |
69 |
| - |
70 |
| - final NestedSet<Artifact> filesToBuild = |
71 |
| - NestedSetBuilder.wrap(Order.STABLE_ORDER, ruleContext.getOutputArtifacts()); |
72 |
| - if (filesToBuild.isEmpty()) { |
73 |
| - ruleContext.attributeError("outs", "Genrules without outputs don't make sense"); |
74 |
| - } |
75 |
| - if (ruleContext.attributes().get("executable", Type.BOOLEAN) |
76 |
| - && Iterables.size(filesToBuild) > 1) { |
77 |
| - ruleContext.attributeError("executable", |
78 |
| - "if genrules produce executables, they are allowed only one output. " |
79 |
| - + "If you need the executable=1 argument, then you should split this genrule into " |
80 |
| - + "genrules producing single outputs"); |
81 |
| - } |
82 |
| - |
83 |
| - ImmutableMap.Builder<Label, Iterable<Artifact>> labelMap = ImmutableMap.builder(); |
84 |
| - for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("srcs", Mode.TARGET)) { |
85 |
| - Iterable<Artifact> files = dep.getProvider(FileProvider.class).getFilesToBuild(); |
86 |
| - Iterables.addAll(resolvedSrcs, files); |
87 |
| - labelMap.put(dep.getLabel(), files); |
88 |
| - } |
89 |
| - |
90 |
| - CommandHelper commandHelper = |
91 |
| - new CommandHelper( |
92 |
| - ruleContext, ruleContext.getPrerequisites("tools", Mode.HOST), labelMap.build()); |
93 |
| - |
94 |
| - if (ruleContext.hasErrors()) { |
95 |
| - return null; |
96 |
| - } |
97 |
| - |
98 |
| - String baseCommand = commandHelper.resolveCommandAndExpandLabels( |
99 |
| - ruleContext.attributes().get("heuristic_label_expansion", Type.BOOLEAN), false); |
100 |
| - |
101 |
| - // Adds the genrule environment setup script before the actual shell command |
102 |
| - String command = String.format("source %s; %s", |
103 |
| - ruleContext.getPrerequisiteArtifact("$genrule_setup", Mode.HOST).getExecPath(), |
104 |
| - baseCommand); |
105 |
| - |
106 |
| - command = resolveCommand(ruleContext, command, resolvedSrcs, filesToBuild); |
107 |
| - |
108 |
| - String message = ruleContext.attributes().get("message", Type.STRING); |
109 |
| - if (message.isEmpty()) { |
110 |
| - message = "Executing genrule"; |
111 |
| - } |
112 |
| - |
113 |
| - ImmutableMap<String, String> env = ruleContext.getConfiguration().getLocalShellEnvironment(); |
114 |
| - ImmutableSet<String> clientEnvVars = |
115 |
| - ruleContext.getConfiguration().getVariableShellEnvironment(); |
116 |
| - |
117 |
| - Map<String, String> executionInfo = Maps.newLinkedHashMap(); |
118 |
| - executionInfo.putAll(TargetUtils.getExecutionInfo(ruleContext.getRule())); |
119 |
| - |
120 |
| - if (ruleContext.attributes().get("local", Type.BOOLEAN)) { |
121 |
| - executionInfo.put("local", ""); |
122 |
| - } |
123 |
| - |
124 |
| - NestedSetBuilder<Artifact> inputs = NestedSetBuilder.stableOrder(); |
125 |
| - inputs.addAll(resolvedSrcs); |
126 |
| - inputs.addAll(commandHelper.getResolvedTools()); |
127 |
| - FilesToRunProvider genruleSetup = |
128 |
| - ruleContext.getPrerequisite("$genrule_setup", Mode.HOST, FilesToRunProvider.class); |
129 |
| - inputs.addAll(genruleSetup.getFilesToRun()); |
130 |
| - List<String> argv = commandHelper.buildCommandLine(command, inputs, ".genrule_script.sh", |
131 |
| - ImmutableMap.copyOf(executionInfo)); |
132 |
| - |
133 |
| - if (ruleContext.attributes().get("stamp", Type.BOOLEAN)) { |
134 |
| - inputs.add(ruleContext.getAnalysisEnvironment().getStableWorkspaceStatusArtifact()); |
135 |
| - inputs.add(ruleContext.getAnalysisEnvironment().getVolatileWorkspaceStatusArtifact()); |
136 |
| - } |
137 |
| - |
138 |
| - ruleContext.registerAction( |
139 |
| - new BazelGenRuleAction( |
140 |
| - ruleContext.getActionOwner(), |
141 |
| - ImmutableList.copyOf(commandHelper.getResolvedTools()), |
142 |
| - inputs.build(), |
143 |
| - filesToBuild, |
144 |
| - argv, |
145 |
| - env, |
146 |
| - clientEnvVars, |
147 |
| - ImmutableMap.copyOf(executionInfo), |
148 |
| - ImmutableMap.copyOf(commandHelper.getRemoteRunfileManifestMap()), |
149 |
| - message + ' ' + ruleContext.getLabel())); |
150 |
| - |
151 |
| - RunfilesProvider runfilesProvider = withData( |
152 |
| - // No runfiles provided if not a data dependency. |
153 |
| - Runfiles.EMPTY, |
154 |
| - // We only need to consider the outputs of a genrule |
155 |
| - // No need to visit the dependencies of a genrule. They cross from the target into the host |
156 |
| - // configuration, because the dependencies of a genrule are always built for the host |
157 |
| - // configuration. |
158 |
| - new Runfiles.Builder( |
159 |
| - ruleContext.getWorkspaceName(), |
160 |
| - ruleContext.getConfiguration().legacyExternalRunfiles()) |
161 |
| - .addTransitiveArtifacts(filesToBuild) |
162 |
| - .build()); |
163 |
| - |
164 |
| - return new RuleConfiguredTargetBuilder(ruleContext) |
165 |
| - .setFilesToBuild(filesToBuild) |
166 |
| - .setRunfilesSupport(null, getExecutable(ruleContext, filesToBuild)) |
167 |
| - .addProvider(RunfilesProvider.class, runfilesProvider) |
168 |
| - .build(); |
169 |
| - } |
170 |
| - |
171 |
| - private String resolveCommand(final RuleContext ruleContext, final String command, |
172 |
| - final List<Artifact> resolvedSrcs, final NestedSet<Artifact> filesToBuild) { |
173 |
| - return ruleContext.expandMakeVariables( |
174 |
| - "cmd", |
175 |
| - command, |
176 |
| - new ConfigurationMakeVariableContext( |
177 |
| - ruleContext.getRule().getPackage(), ruleContext.getConfiguration(), |
178 |
| - ToolchainProvider.getToolchainMakeVariables(ruleContext, "toolchains")) { |
179 |
| - @Override |
180 |
| - public String lookupMakeVariable(String name) throws ExpansionException { |
181 |
| - if (name.equals("SRCS")) { |
182 |
| - return Artifact.joinExecPaths(" ", resolvedSrcs); |
183 |
| - } else if (name.equals("<")) { |
184 |
| - return expandSingletonArtifact(resolvedSrcs, "$<", "input file"); |
185 |
| - } else if (name.equals("OUTS")) { |
186 |
| - return Artifact.joinExecPaths(" ", filesToBuild); |
187 |
| - } else if (name.equals("@")) { |
188 |
| - return expandSingletonArtifact(filesToBuild, "$@", "output file"); |
189 |
| - } else if (name.equals("@D")) { |
190 |
| - // The output directory. If there is only one filename in outs, |
191 |
| - // this expands to the directory containing that file. If there are |
192 |
| - // multiple filenames, this variable instead expands to the |
193 |
| - // package's root directory in the genfiles tree, even if all the |
194 |
| - // generated files belong to the same subdirectory! |
195 |
| - if (Iterables.size(filesToBuild) == 1) { |
196 |
| - Artifact outputFile = Iterables.getOnlyElement(filesToBuild); |
197 |
| - PathFragment relativeOutputFile = outputFile.getExecPath(); |
198 |
| - if (relativeOutputFile.segmentCount() <= 1) { |
199 |
| - // This should never happen, since the path should contain at |
200 |
| - // least a package name and a file name. |
201 |
| - throw new IllegalStateException( |
202 |
| - "$(@D) for genrule " + ruleContext.getLabel() + " has less than one segment"); |
203 |
| - } |
204 |
| - return relativeOutputFile.getParentDirectory().getPathString(); |
205 |
| - } else { |
206 |
| - PathFragment dir; |
207 |
| - if (ruleContext.getRule().hasBinaryOutput()) { |
208 |
| - dir = ruleContext.getConfiguration().getBinFragment(); |
209 |
| - } else { |
210 |
| - dir = ruleContext.getConfiguration().getGenfilesFragment(); |
211 |
| - } |
212 |
| - PathFragment relPath = |
213 |
| - ruleContext.getRule().getLabel().getPackageIdentifier().getSourceRoot(); |
214 |
| - return dir.getRelative(relPath).getPathString(); |
215 |
| - } |
216 |
| - } else { |
217 |
| - return super.lookupMakeVariable(name); |
218 |
| - } |
219 |
| - } |
220 |
| - }); |
221 |
| - } |
222 |
| - |
223 |
| - // Returns the path of the sole element "artifacts", generating an exception |
224 |
| - // with an informative error message iff the set is not a singleton. |
225 |
| - // |
226 |
| - // Used to expand "$<", "$@" |
227 |
| - private String expandSingletonArtifact(Iterable<Artifact> artifacts, |
228 |
| - String variable, |
229 |
| - String artifactName) |
230 |
| - throws ExpansionException { |
231 |
| - if (Iterables.isEmpty(artifacts)) { |
232 |
| - throw new ExpansionException("variable '" + variable |
233 |
| - + "' : no " + artifactName); |
234 |
| - } else if (Iterables.size(artifacts) > 1) { |
235 |
| - throw new ExpansionException("variable '" + variable |
236 |
| - + "' : more than one " + artifactName); |
| 27 | + protected boolean isStampingEnabled(RuleContext ruleContext) { |
| 28 | + if (!ruleContext.attributes().has("stamp", Type.BOOLEAN)) { |
| 29 | + return false; |
237 | 30 | }
|
238 |
| - return Iterables.getOnlyElement(artifacts).getExecPathString(); |
| 31 | + return ruleContext.attributes().get("stamp", Type.BOOLEAN); |
239 | 32 | }
|
240 | 33 | }
|
0 commit comments