1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package vogar.commands;
18
19import com.google.common.collect.Lists;
20
21import java.io.File;
22import java.util.Collection;
23import java.util.List;
24
25import vogar.Log;
26import vogar.util.Strings;
27
28/**
29 * Runs the Jack compiler to generate dex files.
30 */
31public class Jack {
32    private static final File JACK_SCRIPT;
33    private static final File JACK_JAR;
34
35    // Initialise the files for jack and jill, letting them be null if the files
36    // cannot be found.
37    static {
38        String sdkTop = System.getenv("ANDROID_BUILD_TOP");
39
40        final File jackScript = new File(sdkTop + "/prebuilts/sdk/tools/jack");
41        final File jackJar = new File(sdkTop + "/prebuilts/sdk/tools/jack.jar");
42
43        // If the system environment variable JACK_JAR is set then use that,
44        // otherwise find the jar relative to the AOSP source.
45        String jackJarEnv = System.getenv("JACK_JAR");
46
47        final File jackJarFromEnv = (jackJarEnv != null) ? new File(jackJarEnv) : null;
48
49        if (!jackScript.exists()) {
50            JACK_SCRIPT = null;
51        } else {
52            JACK_SCRIPT = jackScript;
53        }
54
55        if (jackJarEnv != null && jackJarFromEnv.exists()) {
56            JACK_JAR = jackJarFromEnv;
57        } else {
58            if (!jackJar.exists()) {
59                JACK_JAR = null;
60            } else {
61                JACK_JAR = jackJar;
62            }
63        }
64    }
65
66    /**
67     * Get an instance of the jack command with appropriate path settings.
68     *
69     * @return an instance of a jack command with appropriate paths to its dependencies if needed.
70     * @throws IllegalStateException when the jack command cannot be found.
71     */
72    public static Jack getJackCommand(Log log) throws IllegalStateException {
73        if (JACK_SCRIPT != null) {
74            // Configure jack compiler with right JACK_SCRIPT path.
75            return new Jack(log, Lists.newArrayList(JACK_SCRIPT.getAbsolutePath()));
76        }
77        if (JACK_JAR != null) {
78            // Fallback to jack.jar, for previous releases.
79            return new Jack(log, Lists.newArrayList("java", "-jar", JACK_JAR.getAbsolutePath()));
80        }
81        throw new IllegalStateException("Jack library not found, cannot use jack.");
82    }
83
84    private final Command.Builder builder;
85
86    private Jack(Log log, Collection<String> jackArgs) {
87        this.builder = new Command.Builder(log);
88        builder.args(jackArgs);
89    }
90
91    public Jack importFile(String path) {
92        builder.args("--import", path);
93        return this;
94    }
95
96    public Jack importMeta(String dir) {
97        builder.args("--import-meta", dir);
98        return this;
99    }
100
101    public Jack importResource(String dir) {
102        builder.args("--import-resource", dir);
103        return this;
104    }
105
106    public Jack incrementalFolder(String dir) {
107        builder.args("--incremental--folder", dir);
108        return this;
109    }
110
111    public Jack multiDex(String mode) {
112        builder.args("--multi-dex", mode);
113        return this;
114    }
115
116    public Jack sourceVersion(String version) {
117        setProperty("jack.java.source.version=" + version);
118        return this;
119    }
120
121    public Jack minApiLevel(String minApiLevel) {
122        setProperty("jack.android.min-api-level=" + minApiLevel);
123        return this;
124    }
125
126    public Jack outputDex(String dir) {
127        builder.args("--output-dex", dir);
128        return this;
129    }
130
131    public Jack outputDexZip(String zipFile) {
132        builder.args("--output-dex-zip", zipFile);
133        return this;
134    }
135
136    public Jack outputJack(String path) {
137        builder.args("--output-jack", path);
138        return this;
139    }
140
141    public Jack processor(String names) {
142        builder.args("--processor", names);
143        return this;
144    }
145
146    public Jack processorPath(String path) {
147        builder.args("--processorpath", path);
148        return this;
149    }
150
151    public Jack verbose(String mode) {
152        builder.args("--verbose", mode);
153        return this;
154    }
155
156    public Jack addAnnotationProcessor(String processor) {
157        builder.args("-A", processor);
158        return this;
159    }
160
161    public Jack setProperty(String property) {
162        builder.args("-D", property);
163        return this;
164    }
165
166    public Jack setClassPath(String classPath) {
167        builder.args("-cp", classPath);
168        return this;
169    }
170
171    public Jack setDebug() {
172        builder.args("-g");
173        return this;
174    }
175
176    public Jack setEnvVar(String key, String value) {
177        builder.env(key, value);
178        return this;
179    }
180
181    /**
182     * Runs the command with the preconfigured options on Jack, and returns the outcome.
183     *
184     * @return A list of output lines from running the command.
185     */
186    public List<String> invoke() {
187        return builder.execute();
188    }
189
190    /**
191     * Runs the command with the preconfigured options on Jack, and returns the outcome.
192     * This method does not dirty the existing Jack instance, and can be safely reused
193     * to compile other files.
194     * @param files The files to compile.
195     * @return A list of output lines from running the command.
196     */
197    public List<String> compile(Collection<File> files) {
198        return new Command.Builder(builder)
199                .args((Object[]) Strings.objectsToStrings(files))
200                .execute();
201    }
202}
203