aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/android/AndroidTools.java
blob: f7a2ab878399057c4081b4a349dd30a52c5335dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.rules.android;

import com.google.devtools.build.lib.Constants;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.rules.java.BaseJavaCompilationHelper;
import com.google.devtools.build.lib.rules.java.JavaCompilationHelper;
import com.google.devtools.build.lib.rules.java.Jvm;

import java.util.List;

/** A common interface for all the tools used by {@code android_*} rules. */
public class AndroidTools {
  private final RuleContext ruleContext;
  private final Artifact dxJar;
  private final FilesToRunProvider aapt;
  private final Artifact apkBuilderTool;
  private final Artifact aidlTool;
  private final Artifact frameworkAidl;
  private final FilesToRunProvider adb;
  private final FilesToRunProvider toolRunner;
  private final FilesToRunProvider aaptJavaGenerator;
  private final FilesToRunProvider apkGenerator;
  private final FilesToRunProvider resourceProcessor;
  private final FilesToRunProvider aarGenerator;
  private final FilesToRunProvider zipalign;
  private final FilesToRunProvider proguard;
  private final Artifact androidJar;
  private final Artifact shrinkedAndroidJar;
  private final Artifact annotationsJar;
  private final Artifact mainDexClasses;
  private final AndroidSdkProvider androidSdk;

  public static AndroidTools fromRuleContext(RuleContext ruleContext) {
    TransitiveInfoCollection androidSdkDep =
        ruleContext.getPrerequisite(":android_sdk", Mode.TARGET);
    AndroidSdkProvider androidSdk = androidSdkDep == null
        ? null
        : androidSdkDep.getProvider(AndroidSdkProvider.class);
    if (androidSdk == null && !Constants.ANDROID_ALLOW_SDK_FILEGROUP) {
      ruleContext.ruleError(
          "No Android SDK found. Use the --android_sdk command line option to specify one.");
      return null;
    }

    return new AndroidTools(
        ruleContext,
        androidSdk,
        getOptionalArtifact(ruleContext, "$android_dx_jar"),
        getOptionalArtifact(ruleContext, "$android_jar", Mode.TARGET),
        getOptionalArtifact(ruleContext, "$shrinked_android"),
        getOptionalArtifact(ruleContext, "$android_annotations_jar"),
        getOptionalArtifact(ruleContext, "$multidex_keep_classes"),
        getOptionalArtifact(ruleContext, "$android_apkbuilder_tool"),
        getOptionalArtifact(ruleContext, "$android_aidl_tool"),
        getOptionalArtifact(ruleContext, "$android_aidl_framework"),
        getOptionalToolFromArtifact(ruleContext, "$android_aapt"),
        getOptionalToolFromArtifact(ruleContext, "$adb"),
        getOptionalTool(ruleContext, "$android_tool_runner"),
        getOptionalTool(ruleContext, "$android_aapt_java_generator"),
        getOptionalTool(ruleContext, "$android_aapt_apk_generator"),
        getOptionalTool(ruleContext, ":android_resources_processor"),
        getOptionalTool(ruleContext, ":android_aar_generator"),
        getOptionalTool(ruleContext, "$zipalign_tool"),
        getOptionalTool(ruleContext, ":proguard"));
  }

  private static Artifact getOptionalArtifact(RuleContext ruleContext, String attribute) {
    return getOptionalArtifact(ruleContext, attribute, Mode.HOST);
  }

  private static Artifact getOptionalArtifact(
      RuleContext ruleContext, String attribute, Mode mode) {
    if (!ruleContext.getRule().isAttrDefined(attribute, Type.LABEL)) {
      return null;
    }

    List<Artifact> prerequisites =
        ruleContext.getPrerequisiteArtifacts(attribute, mode).list();

    if (prerequisites.isEmpty()) {
      return null;
    } else if (prerequisites.size() == 1) {
      return prerequisites.get(0);
    } else {
      ruleContext.attributeError(attribute, "expected a single artifact");
      return null;
    }
  }

  private static FilesToRunProvider getOptionalToolFromArtifact(
      RuleContext ruleContext, String attribute) {
    if (!ruleContext.getRule().isAttrDefined(attribute, Type.LABEL)) {
      return null;
    }

    Artifact artifact = getOptionalArtifact(ruleContext, attribute);
    if (artifact == null) {
      return null;
    }

    return FilesToRunProvider.fromSingleArtifact(
        ruleContext.attributes().get(attribute, Type.LABEL),
        artifact);
  }

  private static FilesToRunProvider getOptionalTool(RuleContext ruleContext, String attribute) {
    if (!ruleContext.getRule().isAttrDefined(attribute, Type.LABEL)) {
      return null;
    }

    TransitiveInfoCollection prerequisite = ruleContext.getPrerequisite(attribute, Mode.HOST);
    if (prerequisite == null) {
      return null;
    }

    return prerequisite.getProvider(FilesToRunProvider.class);
  }

  public AndroidTools(
      RuleContext ruleContext,
      AndroidSdkProvider androidSdk,
      Artifact dxJar,
      Artifact androidJar,
      Artifact shrinkedAndroidJar,
      Artifact annotationsJar,
      Artifact mainDexClasses,
      Artifact apkBuilderTool,
      Artifact aidlTool,
      Artifact frameworkAidl,
      FilesToRunProvider aapt,
      FilesToRunProvider adb,
      FilesToRunProvider toolRunner,
      FilesToRunProvider aaptJavaGenerator,
      FilesToRunProvider apkGenerator,
      FilesToRunProvider resourceProcessor,
      FilesToRunProvider aarGenerator,
      FilesToRunProvider zipalign,
      FilesToRunProvider proguard) {
    this.ruleContext = ruleContext;
    this.androidSdk = androidSdk;
    this.dxJar = dxJar;
    this.androidJar = androidJar;
    this.shrinkedAndroidJar = shrinkedAndroidJar;
    this.mainDexClasses = mainDexClasses;
    this.aapt = aapt;
    this.annotationsJar = annotationsJar;
    this.apkBuilderTool = apkBuilderTool;
    this.aidlTool = aidlTool;
    this.frameworkAidl = frameworkAidl;
    this.adb = adb;
    this.toolRunner = toolRunner;
    this.aaptJavaGenerator = aaptJavaGenerator;
    this.apkGenerator = apkGenerator;
    this.resourceProcessor = resourceProcessor;
    this.aarGenerator = aarGenerator;
    this.zipalign = zipalign;
    this.proguard = proguard;
  }

  public Artifact getFrameworkAidl() {
    return androidSdk != null ? androidSdk.getFrameworkAidl() : frameworkAidl;
  }

  public Artifact getAndroidJar() {
    return androidSdk != null ? androidSdk.getAndroidJar() : androidJar;
  }

  public Artifact getShrinkedAndroidJar() {
    return androidSdk != null ? androidSdk.getShrinkedAndroidJar() : shrinkedAndroidJar;
  }

  public Artifact getAnnotationsJar() {
    return androidSdk != null ? androidSdk.getAnnotationsJar() : annotationsJar;
  }

  public Artifact getMainDexClasses() {
    return androidSdk != null ? androidSdk.getMainDexClasses() : mainDexClasses;
  }

  public FilesToRunProvider getAapt() {
    return androidSdk != null ? androidSdk.getAapt() : aapt;
  }

  public FilesToRunProvider getAdb() {
    return androidSdk != null ? androidSdk.getAdb() : adb;
  }

  public FilesToRunProvider getToolRunner() {
    return toolRunner;
  }

  public FilesToRunProvider getAaptJavaGenerator() {
    return aaptJavaGenerator;
  }

  public FilesToRunProvider getApkGenerator() {
    return apkGenerator;
  }

  public FilesToRunProvider getAndroidResourceProcessor() {
    return resourceProcessor;
  }

  public FilesToRunProvider getAarGenerator() {
    return aarGenerator;
  }

  public FilesToRunProvider getZipalign() {
    return androidSdk != null ? androidSdk.getZipalign() : zipalign;
  }

  public FilesToRunProvider getProguard() {
    return androidSdk != null ? androidSdk.getProguard() : proguard;
  }

  /**
   * Creates a {@link com.google.devtools.build.lib.analysis.actions.SpawnAction.Builder} that has
   * its executable already set to invoke dx.
   */
  public SpawnAction.Builder dxAction(AndroidSemantics semantics) {
    return androidSdk != null
        ? new SpawnAction.Builder()
            .setExecutable(androidSdk.getDx())
        : new SpawnAction.Builder()
            .addTransitiveInputs(BaseJavaCompilationHelper.getHostJavabaseInputs(ruleContext))
            .setExecutable(ruleContext.getHostConfiguration()
                .getFragment(Jvm.class).getJavaExecutable())
            .addArguments(semantics.getDxJvmArguments())
            .addArgument("-jar")
            .addInputArgument(dxJar);
  }

  /**
   * Creates a {@link com.google.devtools.build.lib.analysis.actions.SpawnAction.Builder} that has
   * its executable already set to invoke the main dex list creator.
   */
  public Action[] mainDexListAction(Artifact jar, Artifact strippedJar, Artifact mainDexList) {
    SpawnAction.Builder builder = new SpawnAction.Builder()
        .setMnemonic("MainDexClasses")
        .setProgressMessage("Generating main dex classes list");

    if (androidSdk != null) {
      return builder
          .setExecutable(androidSdk.getMainDexListCreator())
          .addOutputArgument(mainDexList)
          .addInputArgument(strippedJar)
          .addInputArgument(jar)
          .addArguments(ruleContext.getTokenizedStringListAttr("main_dex_list_opts"))
          .build(ruleContext);
    } else {
      StringBuilder shellCommandBuilder = new StringBuilder()
          .append(ruleContext.getHostConfiguration().getFragment(Jvm.class).getJavaExecutable()
              .getPathString())
          .append(" -cp ").append(dxJar.getExecPathString())
          .append(" ").append(AndroidBinary.MAIN_DEX_CLASS_BUILDER);
      for (String opt : ruleContext.getTokenizedStringListAttr("main_dex_list_opts")) {
        shellCommandBuilder.append(" ").append(opt);
      }
      shellCommandBuilder
          .append(" ").append(strippedJar.getExecPathString())
          .append(" ").append(jar.getExecPathString())
          .append(" >").append(mainDexList.getExecPathString());

      return builder
          .addInput(strippedJar)
          .addInput(jar)
          .addInput(dxJar)
          .addTransitiveInputs(BaseJavaCompilationHelper.getHostJavabaseInputs(ruleContext))
          .addOutput(mainDexList)
          .setShellCommand(shellCommandBuilder.toString())
          .build(ruleContext);
    }
  }

    /**
   * Creates a {@link com.google.devtools.build.lib.analysis.actions.SpawnAction.Builder} that has
   * its executable already set to invoke apkbuilder.
   */
  public SpawnAction.Builder apkBuilderAction() {
    return androidSdk != null
        ? new SpawnAction.Builder()
            .setExecutable(androidSdk.getApkBuilder())
        : new SpawnAction.Builder()
          .setExecutable(
              ruleContext.getHostConfiguration().getFragment(Jvm.class).getJavaExecutable())
          .addTransitiveInputs(JavaCompilationHelper.getHostJavabaseInputs(ruleContext))
          .addArgument("-jar")
          .addInputArgument(apkBuilderTool);
  }

  /**
   * Creates a {@link com.google.devtools.build.lib.analysis.actions.SpawnAction.Builder} that has
   * its executable already set to invoke aidl.
   */
  public SpawnAction.Builder aidlAction() {
    return androidSdk != null
        ? new SpawnAction.Builder()
            .setExecutable(androidSdk.getAidl())
        : new SpawnAction.Builder()
            // Note the below may be an overapproximation of the actual runfiles, due to
            // "conditional artifacts" (see Runfiles.PruningManifest).
            // TODO(bazel-team): When using getFilesToRun(), the middleman is
            // not expanded. Fix by providing code to expand and use getFilesToRun here.
            .addInputs(toolRunner.getRunfilesSupport().getRunfilesArtifactsWithoutMiddlemen())
            .setExecutable(toolRunner.getExecutable())
            .addInputArgument(aidlTool);
  }
}