• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 package vogar.android;
18 
19 import java.io.File;
20 import java.util.LinkedList;
21 
22 import vogar.Action;
23 import vogar.Classpath;
24 import vogar.Md5Cache;
25 import vogar.Result;
26 import vogar.Run;
27 import vogar.commands.Command;
28 import vogar.commands.Jack;
29 import vogar.tasks.Task;
30 
31 /**
32  * Generates .dex.jar files using Jack.
33  */
34 public final class JackDexTask extends Task {
35     private final Run run;
36     private final Classpath classpath;
37     private final boolean benchmark;
38     private final File inputFile;
39     private final Action action;
40     private final File localDex;
41 
JackDexTask(Run run, Classpath classpath, boolean benchmark, String name, File inputFile, Action action, File localDex)42     public JackDexTask(Run run, Classpath classpath, boolean benchmark, String name,
43             File inputFile, Action action, File localDex) {
44         super("jackdex " + name);
45         this.run = run;
46         this.classpath = classpath;
47         this.benchmark = benchmark;
48         this.inputFile = inputFile;
49         this.action = action;
50         this.localDex = localDex;
51     }
52 
execute()53     @Override protected Result execute() throws Exception {
54         run.mkdir.mkdirs(localDex.getParentFile());
55 
56         // What we do depends on the nature of the nature of inputFile. Ultimately we want a
57         // .dex.jar containing a classes.dex and resources.
58         // For the inputFile we might be given:
59         // 1) A .jack file: We must convert it to a dex.jar. Jack will handle including resources
60         // for us.
61         // 2) A .jar file containing .class files: We must ask Jack to convert it to a .dex.jar and
62         // we have to handle resources.
63 
64         // There are several things below that seem fishy and could be improved with a different
65         // task workflow:
66         // 1) Having to deal with multiple classpath entries for inclusion: if the purpose is
67         // to convert a .jack or .jar to a .dex.jar file we *may* not need supporting classes.
68         // 2) The resource inclusion behavior is almost certainly incorrect and may need a change in
69         // Jack if we persist with including the entire classpath (2).
70 
71         // Check the jack cache first.
72         Md5Cache jackCache = run.jackCache;
73         String classpathSubKey = jackCache.makeKey(classpath);
74         String cacheKey = null;
75         if (classpathSubKey != null) {
76             // If the classpath contains things that are not .jar or .jack files we get null
77             // classpathSubKey and do not cache.
78 
79             // cacheKey includes all the arguments that could affect the output.
80             cacheKey = jackCache.makeKey(inputFile.toString(), classpathSubKey,
81                 run.language.toString(), Boolean.toString(run.debugging));
82 
83             if (jackCache.getFromCache(localDex, cacheKey)) {
84                 run.log.verbose("JackDexTask: Obtained " + localDex + " from jackCache");
85                 return Result.SUCCESS;
86             }
87         }
88         run.log.verbose("JackDexTask: Could not obtain " + localDex + " from jackCache");
89 
90         Jack jack = Jack.getJackCommand(run.log).outputDexZip(localDex.getPath());
91         jack.sourceVersion(run.language.getJackSourceVersion());
92         jack.minApiLevel(String.valueOf(run.language.getJackMinApilevel()));
93         if (run.debugging) {
94             jack.setDebug();
95         }
96 
97         // Jack imports resources from .jack files but not .jar files. We keep track of the .jar
98         // files so we can unpack them in reverse order (to maintain classpath ordering).
99         // Unfortunately, the inconsistency between .jack and .jar behavior makes it incorrect
100         // in some cases.
101         LinkedList<File> resourcesReverseClasspath = new LinkedList<>();
102         addClassPathEntryToJack(jack, resourcesReverseClasspath, inputFile);
103         if (benchmark && action != null) {
104             for (File classpathElement : classpath.getElements()) {
105                 addClassPathEntryToJack(jack, resourcesReverseClasspath, classpathElement);
106             }
107         }
108 
109         if (!resourcesReverseClasspath.isEmpty()) {
110             File resourcesDir = run.localFile(localDex.getName() + "_resources");
111             run.mkdir.mkdirs(resourcesDir);
112             // Unpack each classpath entry into resourcesDir.
113             for (File classpathEntry : resourcesReverseClasspath) {
114                 unpackJar(classpathEntry, resourcesDir);
115             }
116             jack.importResource(resourcesDir.getPath());
117         }
118         jack.invoke();
119 
120         if (cacheKey != null) {
121             // Store the result of the jack invocation in the jackCache.
122             jackCache.insert(cacheKey, localDex);
123         }
124 
125         return Result.SUCCESS;
126     }
127 
addClassPathEntryToJack(Jack jack, LinkedList<File> resourcesReverseClasspath, File classpathElement)128     private static void addClassPathEntryToJack(Jack jack,
129             LinkedList<File> resourcesReverseClasspath, File classpathElement) {
130         jack.importFile(classpathElement.getPath());
131         if (!classpathElement.getName().endsWith(".jack")) {
132             resourcesReverseClasspath.addFirst(classpathElement);
133         }
134     }
135 
unpackJar(File classpathEntry, File resourcesDir)136     private void unpackJar(File classpathEntry, File resourcesDir) {
137         new Command.Builder(run.log)
138                 .workingDir(resourcesDir)
139                 .args(run.javaPath("jar"), "xf", classpathEntry.getPath())
140                 .execute();
141     }
142 }
143