blob: fa26e5c5980539f509c64d02a481880e7add1561 [file] [log] [blame]
Dmitry Lomov9d0f9142015-09-01 17:56:32 +00001// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.ideinfo;
16
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000017import static com.google.common.collect.Iterables.transform;
18
19import com.google.common.base.Function;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000020import com.google.common.collect.ImmutableList;
21import com.google.common.io.ByteSource;
Dmitry Lomov745e4192015-09-09 12:00:07 +000022import com.google.devtools.build.lib.actions.ActionOwner;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000023import com.google.devtools.build.lib.actions.Artifact;
24import com.google.devtools.build.lib.actions.Root;
25import com.google.devtools.build.lib.analysis.Aspect;
Dmitry Lomov745e4192015-09-09 12:00:07 +000026import com.google.devtools.build.lib.analysis.Aspect.Builder;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000027import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory;
28import com.google.devtools.build.lib.analysis.ConfiguredTarget;
29import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
30import com.google.devtools.build.lib.analysis.RuleContext;
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000031import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000032import com.google.devtools.build.lib.analysis.actions.BinaryFileWriteAction;
33import com.google.devtools.build.lib.collect.nestedset.NestedSet;
34import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
Dmitry Lomov745e4192015-09-09 12:00:07 +000035import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.AndroidSdkRuleInfo;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000036import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.ArtifactLocation;
37import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.JavaRuleIdeInfo;
38import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.LibraryArtifact;
39import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.RuleIdeInfo;
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000040import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.RuleIdeInfo.Kind;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000041import com.google.devtools.build.lib.packages.AspectDefinition;
Marian Lobur702cad72015-09-02 09:53:58 +000042import com.google.devtools.build.lib.packages.AspectParameters;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000043import com.google.devtools.build.lib.packages.Rule;
Dmitry Lomov745e4192015-09-09 12:00:07 +000044import com.google.devtools.build.lib.packages.Type;
45import com.google.devtools.build.lib.rules.android.AndroidSdkProvider;
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000046import com.google.devtools.build.lib.rules.java.JavaExportsProvider;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000047import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
48import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider;
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000049import com.google.devtools.build.lib.syntax.Label;
Dmitry Lomov745e4192015-09-09 12:00:07 +000050import com.google.devtools.build.lib.vfs.Path;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000051import com.google.devtools.build.lib.vfs.PathFragment;
Dmitry Lomov745e4192015-09-09 12:00:07 +000052import com.google.protobuf.MessageLite;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000053
54import java.io.IOException;
55import java.io.InputStream;
56import java.util.Collection;
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000057import java.util.List;
58
59import javax.annotation.Nullable;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000060
61/**
62 * Generates ide-build information for Android Studio.
63 */
64public class AndroidStudioInfoAspect implements ConfiguredAspectFactory {
65 public static final String NAME = "AndroidStudioInfoAspect";
Dmitry Lomov745e4192015-09-09 12:00:07 +000066
67 // Output groups.
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000068 public static final String IDE_RESOLVE = "ide-resolve";
69 public static final String IDE_BUILD = "ide-build";
Dmitry Lomov745e4192015-09-09 12:00:07 +000070
71 // File suffixes.
72 public static final String ASWB_BUILD_SUFFIX = ".aswb-build";
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000073 public static final Function<Label, String> LABEL_TO_STRING = new Function<Label, String>() {
74 @Nullable
75 @Override
76 public String apply(Label label) {
77 return label.toString();
78 }
79 };
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000080
81 @Override
82 public AspectDefinition getDefinition() {
83 return new AspectDefinition.Builder(NAME)
84 .requireProvider(JavaSourceInfoProvider.class)
85 .attributeAspect("deps", AndroidStudioInfoAspect.class)
86 .build();
87 }
88
89 @Override
Marian Lobur702cad72015-09-02 09:53:58 +000090 public Aspect create(ConfiguredTarget base, RuleContext ruleContext,
91 AspectParameters parameters) {
Dmitry Lomov745e4192015-09-09 12:00:07 +000092 Aspect.Builder builder = new Builder(NAME);
93
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000094 // Collect ide build files and calculate dependencies.
95 NestedSetBuilder<Label> transitiveDependenciesBuilder = NestedSetBuilder.stableOrder();
96 NestedSetBuilder<Label> dependenciesBuilder = NestedSetBuilder.stableOrder();
97
Dmitry Lomov9d0f9142015-09-01 17:56:32 +000098 NestedSetBuilder<Artifact> ideBuildFilesBuilder = NestedSetBuilder.stableOrder();
Dmitry Lomov2473b9f2015-09-14 08:53:10 +000099
100 // todo(dslomov,tomlu): following current build info logic, this code enumerates dependencies
101 // directly by iterating over deps attribute. The more robust way to do this might be
102 // to iterate classpath as provided to build action.
Dmitry Lomov745e4192015-09-09 12:00:07 +0000103 if (ruleContext.attributes().has("deps", Type.LABEL_LIST)) {
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000104 Iterable<AndroidStudioInfoFilesProvider> androidStudioInfoFilesProviders =
Dmitry Lomov745e4192015-09-09 12:00:07 +0000105 ruleContext.getPrerequisites("deps", Mode.TARGET, AndroidStudioInfoFilesProvider.class);
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000106 for (AndroidStudioInfoFilesProvider depProvider : androidStudioInfoFilesProviders) {
107 ideBuildFilesBuilder.addTransitive(depProvider.getIdeBuildFiles());
108 transitiveDependenciesBuilder.addTransitive(depProvider.getTransitiveDependencies());
109 }
110 List<? extends TransitiveInfoCollection> deps =
111 ruleContext.getPrerequisites("deps", Mode.TARGET);
112 for (TransitiveInfoCollection dep : deps) {
113 dependenciesBuilder.add(dep.getLabel());
114 }
115
116 Iterable<JavaExportsProvider> javaExportsProviders = ruleContext
117 .getPrerequisites("deps", Mode.TARGET, JavaExportsProvider.class);
118 for (JavaExportsProvider javaExportsProvider : javaExportsProviders) {
119 dependenciesBuilder.addTransitive(javaExportsProvider.getTransitiveExports());
Dmitry Lomov745e4192015-09-09 12:00:07 +0000120 }
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000121 }
122
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000123 NestedSet<Label> directDependencies = dependenciesBuilder.build();
124 transitiveDependenciesBuilder.addTransitive(directDependencies);
125 NestedSet<Label> transitiveDependencies = transitiveDependenciesBuilder.build();
126
Dmitry Lomov745e4192015-09-09 12:00:07 +0000127 RuleIdeInfo.Kind ruleKind = getRuleKind(ruleContext.getRule(), base);
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000128
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000129 if (ruleKind != RuleIdeInfo.Kind.UNRECOGNIZED) {
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000130 Artifact ideBuildFile =
131 createIdeBuildArtifact(base, ruleContext, ruleKind,
132 directDependencies,
133 transitiveDependencies);
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000134 ideBuildFilesBuilder.add(ideBuildFile);
135 }
136
137 NestedSet<Artifact> ideBuildFiles = ideBuildFilesBuilder.build();
Dmitry Lomov745e4192015-09-09 12:00:07 +0000138 builder
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000139 .addOutputGroup(IDE_BUILD, ideBuildFiles)
140 .addProvider(
Dmitry Lomov745e4192015-09-09 12:00:07 +0000141 AndroidStudioInfoFilesProvider.class,
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000142 new AndroidStudioInfoFilesProvider(ideBuildFiles, transitiveDependencies));
Dmitry Lomov745e4192015-09-09 12:00:07 +0000143
144 return builder.build();
145 }
146
147 private static AndroidSdkRuleInfo makeAndroidSdkRuleInfo(RuleContext ruleContext,
148 AndroidSdkProvider provider) {
149 AndroidSdkRuleInfo.Builder sdkInfoBuilder = AndroidSdkRuleInfo.newBuilder();
150
151 Path androidSdkDirectory = provider.getAndroidJar().getPath().getParentDirectory();
152 sdkInfoBuilder.setAndroidSdkPath(androidSdkDirectory.toString());
153
154 Root genfilesDirectory = ruleContext.getConfiguration().getGenfilesDirectory();
155 sdkInfoBuilder.setGenfilesPath(genfilesDirectory.getPath().toString());
156
157 Path binfilesPath = ruleContext.getConfiguration().getBinDirectory().getPath();
158 sdkInfoBuilder.setBinPath(binfilesPath.toString());
159
160 return sdkInfoBuilder.build();
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000161 }
162
163 private Artifact createIdeBuildArtifact(
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000164 ConfiguredTarget base,
165 RuleContext ruleContext,
166 Kind ruleKind,
167 NestedSet<Label> directDependencies, NestedSet<Label> transitiveDependencies) {
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000168 PathFragment ideBuildFilePath = getOutputFilePath(base, ruleContext);
169 Root genfilesDirectory = ruleContext.getConfiguration().getGenfilesDirectory();
170 Artifact ideBuildFile =
171 ruleContext
172 .getAnalysisEnvironment()
173 .getDerivedArtifact(ideBuildFilePath, genfilesDirectory);
174
175 RuleIdeInfo.Builder outputBuilder = RuleIdeInfo.newBuilder();
176
177 outputBuilder.setLabel(base.getLabel().toString());
178
179 outputBuilder.setBuildFile(
180 ruleContext
181 .getRule()
182 .getPackage()
183 .getBuildFile()
184 .getPath()
185 .toString());
186
187 outputBuilder.setKind(ruleKind);
188
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000189 outputBuilder.addAllDependencies(transform(directDependencies, LABEL_TO_STRING));
190 outputBuilder.addAllTransitiveDependencies(transform(transitiveDependencies, LABEL_TO_STRING));
191
192 if (ruleKind == Kind.JAVA_LIBRARY) {
Dmitry Lomov745e4192015-09-09 12:00:07 +0000193 outputBuilder.setJavaRuleIdeInfo(makeJavaRuleIdeInfo(base));
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000194 } else if (ruleKind == Kind.ANDROID_SDK) {
Dmitry Lomov745e4192015-09-09 12:00:07 +0000195 outputBuilder.setAndroidSdkRuleInfo(
196 makeAndroidSdkRuleInfo(ruleContext, base.getProvider(AndroidSdkProvider.class)));
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000197 }
198
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000199
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000200 final RuleIdeInfo ruleIdeInfo = outputBuilder.build();
201 ruleContext.registerAction(
Dmitry Lomov745e4192015-09-09 12:00:07 +0000202 makeProtoWriteAction(ruleContext.getActionOwner(), ruleIdeInfo, ideBuildFile));
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000203
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000204 return ideBuildFile;
205 }
206
Dmitry Lomov745e4192015-09-09 12:00:07 +0000207 private static BinaryFileWriteAction makeProtoWriteAction(
208 ActionOwner actionOwner, final MessageLite message, Artifact artifact) {
209 return new BinaryFileWriteAction(
210 actionOwner,
211 artifact,
212 new ByteSource() {
213 @Override
214 public InputStream openStream() throws IOException {
215 return message.toByteString().newInput();
216 }
217 },
218 /*makeExecutable =*/ false);
219 }
220
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000221 private static ArtifactLocation makeArtifactLocation(Artifact artifact) {
222 return ArtifactLocation.newBuilder()
223 .setRootPath(artifact.getRoot().getPath().toString())
224 .setRelativePath(artifact.getRootRelativePathString())
225 .build();
226 }
227
228 private static JavaRuleIdeInfo makeJavaRuleIdeInfo(ConfiguredTarget base) {
229 JavaRuleIdeInfo.Builder builder = JavaRuleIdeInfo.newBuilder();
230 JavaRuleOutputJarsProvider outputJarsProvider =
231 base.getProvider(JavaRuleOutputJarsProvider.class);
232 if (outputJarsProvider != null) {
233 {
234 LibraryArtifact.Builder jarsBuilder = LibraryArtifact.newBuilder();
235 Artifact classJar = outputJarsProvider.getClassJar();
236 if (classJar != null) {
237 jarsBuilder.setJar(makeArtifactLocation(classJar));
238 }
239 Artifact srcJar = outputJarsProvider.getSrcJar();
240 if (srcJar != null) {
241 jarsBuilder.setSourceJar(makeArtifactLocation(srcJar));
242 }
243 if (jarsBuilder.hasJar() || jarsBuilder.hasSourceJar()) {
244 builder.addJars(jarsBuilder.build());
245 }
246 }
247
248 {
249 LibraryArtifact.Builder genjarsBuilder = LibraryArtifact.newBuilder();
250
251 Artifact genClassJar = outputJarsProvider.getGenClassJar();
252 if (genClassJar != null) {
253 genjarsBuilder.setJar(makeArtifactLocation(genClassJar));
254 }
255 Artifact gensrcJar = outputJarsProvider.getGensrcJar();
256 if (gensrcJar != null) {
257 genjarsBuilder.setSourceJar(makeArtifactLocation(gensrcJar));
258 }
259 if (genjarsBuilder.hasJar() || genjarsBuilder.hasSourceJar()) {
260 builder.addJars(genjarsBuilder.build());
261 }
262 }
263 }
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000264 Collection<Artifact> sourceFiles = getSources(base);
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000265
Dmitry Lomov745e4192015-09-09 12:00:07 +0000266 for (Artifact sourceFile : sourceFiles) {
267 builder.addSources(makeArtifactLocation(sourceFile));
268 }
269
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000270 return builder.build();
271 }
272
Dmitry Lomov2473b9f2015-09-14 08:53:10 +0000273 private static Collection<Artifact> getSources(ConfiguredTarget base) {
274 // Calculate source files.
275 JavaSourceInfoProvider sourceInfoProvider = base.getProvider(JavaSourceInfoProvider.class);
276 return sourceInfoProvider != null
277 ? sourceInfoProvider.getSourceFiles()
278 : ImmutableList.<Artifact>of();
279 }
280
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000281 private PathFragment getOutputFilePath(ConfiguredTarget base, RuleContext ruleContext) {
282 PathFragment packagePathFragment =
283 ruleContext.getLabel().getPackageIdentifier().getPathFragment();
284 String name = base.getLabel().getName();
Dmitry Lomov745e4192015-09-09 12:00:07 +0000285 return new PathFragment(packagePathFragment, new PathFragment(name + ASWB_BUILD_SUFFIX));
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000286 }
287
Dmitry Lomov745e4192015-09-09 12:00:07 +0000288 private RuleIdeInfo.Kind getRuleKind(Rule rule, ConfiguredTarget base) {
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000289 RuleIdeInfo.Kind kind;
290 if ("java_library".equals(rule.getRuleClassObject().getName())) {
291 kind = RuleIdeInfo.Kind.JAVA_LIBRARY;
Dmitry Lomov745e4192015-09-09 12:00:07 +0000292 } else if (base.getProvider(AndroidSdkProvider.class) != null) {
293 kind = RuleIdeInfo.Kind.ANDROID_SDK;
Dmitry Lomov9d0f9142015-09-01 17:56:32 +0000294 } else {
295 kind = RuleIdeInfo.Kind.UNRECOGNIZED;
296 }
297 return kind;
298 }
299}