1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // Multi-Phasic Applications: SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the GNU General Public License v3+, or later.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package cc
.squirreljme
.plugin
.multivm
;
12 import cc
.squirreljme
.plugin
.SquirrelJMEPluginConfiguration
;
13 import java
.io
.IOException
;
14 import java
.io
.InputStream
;
15 import java
.nio
.file
.Files
;
16 import java
.nio
.file
.Path
;
17 import java
.nio
.file
.StandardCopyOption
;
18 import java
.nio
.file
.StandardOpenOption
;
19 import java
.util
.ArrayList
;
20 import java
.util
.Arrays
;
21 import java
.util
.List
;
22 import java
.util
.Properties
;
23 import java
.util
.zip
.ZipEntry
;
24 import java
.util
.zip
.ZipInputStream
;
25 import org
.gradle
.api
.Action
;
26 import org
.gradle
.api
.Task
;
27 import org
.gradle
.api
.tasks
.SourceSet
;
28 import proguard
.ClassPath
;
29 import proguard
.ClassPathEntry
;
30 import proguard
.Configuration
;
31 import proguard
.ConfigurationParser
;
32 import proguard
.ProGuard
;
35 * Performs the actual compaction of the Jar.
39 public class VMCompactLibraryTaskAction
40 implements Action
<Task
>
42 /** Settings to use in the configuration for keeping, etc. */
43 static final String
[] _PARSE_SETTINGS
= new String
[]
45 // Adjust manifest resources
46 "-adaptresourcefilenames", "**",
47 "-adaptresourcefilecontents",
48 "META-INF/MANIFEST.MF,META-INF/services/**",
50 // Consumers of the libraries/APIs need to see the annotation
51 // information if it is there, to make sure it is retained
52 "-keepattributes", "*Annotation*",
54 // Keep anything with main in it
55 "-keepclasseswithmembers", "class", "*", "{",
56 "public", "static", "void", "main", "(",
57 "java.lang.String[]", ")", ";",
61 "-keep", "class", "*", "extends",
62 "javax.microedition.midlet.MIDlet",
64 // Keep classes annotation with @Api and
66 "@cc.squirreljme.runtime.cldc.annotation.Api",
68 "public", "protected", "*", ";",
71 "@cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi",
73 "public", "protected", "*", ";",
76 // Keep the names of these classes as well
77 "-keepnames", "public",
78 "@cc.squirreljme.runtime.cldc.annotation.Api",
80 "-keepnames", "public",
81 "@cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi",
84 // Keep members with these two annotations
85 "-keepclassmembers", "public", "class", "*", "{",
86 "@cc.squirreljme.runtime.cldc.annotation.Api",
87 "public", "protected", "*", ";",
89 "-keepclassmembers", "public", "class", "*", "{",
90 "@cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi",
91 "public", "protected", "*", ";",
95 "-keepclassmembernames", "public", "class", "*", "{",
96 "@cc.squirreljme.runtime.cldc.annotation.Api",
97 "public", "protected", "*", ";",
99 "-keepclassmembernames", "public", "class", "*", "{",
100 "@cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi",
101 "public", "protected", "*", ";",
105 /** Settings for tests. */
106 static final String
[] _TEST_SETTINGS
=
108 // This keeps everything about tests but will use pre-existing
109 // mappings and otherwise if we are using obfuscated classes
110 // This is the only thing I have found that works
111 "-keep", "class", "*",
112 "-keepnames", "class", "*",
113 "-keepclassmembernames", "class", "*",
116 /** The source set used. */
117 public final String sourceSet
;
120 * Initializes the task action.
122 * @param __sourceSet The source set used.
123 * @throws NullPointerException On null arguments.
126 public VMCompactLibraryTaskAction(String __sourceSet
)
127 throws NullPointerException
129 if (__sourceSet
== null)
130 throw new NullPointerException("NARG");
132 this.sourceSet
= __sourceSet
;
140 public void execute(Task __task
)
142 VMCompactLibraryTask compactTask
= (VMCompactLibraryTask
)__task
;
144 // Where are we reading/writing to/from?
145 Path inputPath
= compactTask
.inputBaseJarPath().get();
146 Path outputJarPath
= compactTask
.outputJarPath().get();
147 Path outputMapPath
= compactTask
.outputMapPath().get();
149 // Some settings may be configured
150 SquirrelJMEPluginConfiguration projectConfig
=
151 SquirrelJMEPluginConfiguration
.configuration(__task
.getProject());
154 Path tempJarFile
= null;
155 Path tempInputMapFile
= null;
156 Path tempOutputMapFile
= null;
159 // Look into the Jar file and check if there are class files, if
160 // there are none then there is nothing to compact
161 boolean atLeastOneClass
= false;
162 try (InputStream in
= Files
.newInputStream(inputPath
,
163 StandardOpenOption
.READ
);
164 ZipInputStream zip
= new ZipInputStream(in
))
168 // Get the next entry
169 ZipEntry entry
= zip
.getNextEntry();
173 String name
= entry
.getName();
174 if (name
.endsWith(".class"))
175 atLeastOneClass
= true;
179 // No classes were found, so do nothing
180 if (!atLeastOneClass
)
182 Files
.copy(inputPath
, outputJarPath
,
183 StandardCopyOption
.REPLACE_EXISTING
);
188 // Setup temporary file to output to when finished
189 tempJarFile
= Files
.createTempFile("out", ".jar");
190 tempInputMapFile
= Files
.createTempFile("in", ".map");
191 tempOutputMapFile
= Files
.createTempFile("out", ".map");
193 // Need to delete the created temporary file, otherwise Proguard
194 // will just say "The output appears up to date" and do nothing
195 Files
.delete(tempJarFile
);
196 Files
.delete(tempOutputMapFile
);
198 // We need to include all the inputs that were already ran through
199 // ProGuard, so we basically need to look at the dependencies and
200 // map them around accordingly
201 // We also need to combine the mapping files as well
202 ClassPath libraryJars
= new ClassPath();
203 boolean applyMapping
= false;
204 for (VMCompactLibraryTask compactDep
:
205 VMHelpers
.compactLibTaskDepends(__task
.getProject(),
208 Path baseJarFile
= compactDep
.baseJar
.getOutputs().getFiles()
209 .getSingleFile().toPath();
211 // Add the library, but the pre-obfuscated form since we need
212 // to know what it is
213 if (Files
.exists(baseJarFile
))
214 libraryJars
.add(new ClassPathEntry(
215 compactDep
.baseJar
.getOutputs().getFiles()
216 .getSingleFile(), false));
218 // If the mapping file exists, concatenate it
219 if (Files
.exists(compactDep
.outputMapPath().get()))
221 // Do use mapping now
224 // Add all the information
225 Files
.write(tempInputMapFile
,
226 Files
.readAllLines(compactDep
.outputMapPath().get()),
227 StandardOpenOption
.APPEND
, StandardOpenOption
.WRITE
);
231 // Base options to use
232 List
<String
> proGuardOptions
= new ArrayList
<>();
233 proGuardOptions
.addAll(
234 Arrays
.asList(VMCompactLibraryTaskAction
._PARSE_SETTINGS
));
238 SourceSet
.TEST_SOURCE_SET_NAME
.equals(this.sourceSet
) ||
239 VMHelpers
.TEST_FIXTURES_SOURCE_SET_NAME
.equals(this.sourceSet
);
243 proGuardOptions
.addAll(Arrays
.asList(
244 VMCompactLibraryTaskAction
._TEST_SETTINGS
));
246 // Add any additional options as needed
247 if (projectConfig
.proGuardOptions
!= null &&
248 !projectConfig
.proGuardOptions
.isEmpty())
249 proGuardOptions
.addAll(projectConfig
.proGuardOptions
);
251 // Parse initial configuration with settings
252 Configuration config
= new Configuration();
253 try (ConfigurationParser parser
= new ConfigurationParser(
254 proGuardOptions
.toArray(new String
[proGuardOptions
.size()]),
257 parser
.parse(config
);
260 // We are neither of these platforms, we say we are not Java ME
261 // because it will remove StackMapTable and instead use StackMap
262 // which is not what we want
263 config
.android
= false;
264 config
.microEdition
= false;
266 // Reduce space and obfuscate, but we cannot remove everything at
268 config
.shrink
= false;
269 config
.optimize
= false;
270 config
.flattenPackageHierarchy
= "$" +
271 (projectConfig
.javaDocErrorCode
== null ?
"??" :
272 projectConfig
.javaDocErrorCode
);
274 // For mapping files, members do need to be unique
275 config
.useUniqueClassMemberNames
= true;
277 // Do not use mix case class names, so that more strings can
278 // be compacted together accordingly
279 config
.useMixedCaseClassNames
= false;
281 // Write mapping to the output file, since we will use it later on
282 config
.printMapping
= tempOutputMapFile
.toFile();
284 // Utilize the combined mapping file that was made so that we can
285 // use everything we have?
287 config
.applyMapping
= tempInputMapFile
.toFile();
290 config
.verbose
= true;
291 //config.dump = Configuration.STD_OUT;
292 //config.printUsage = Configuration.STD_OUT;
293 config
.printConfiguration
= Configuration
.STD_OUT
;
295 // Use whatever libraries were found
296 config
.libraryJars
= libraryJars
;
298 // Setup input and output Jar
299 ClassPath programJars
= new ClassPath();
300 config
.programJars
= programJars
;
304 new ClassPathEntry(inputPath
.toFile(), false));
306 // Output temporary Jar
307 programJars
.add(new ClassPathEntry(
308 tempJarFile
.toFile(), true));
310 // Run the shrinking/obfuscation
313 new ProGuard(config
).execute();
317 Files
.move(tempInputMapFile
,
318 outputMapPath
.resolveSibling(
319 outputMapPath
.getFileName() + ".in"),
320 StandardCopyOption
.REPLACE_EXISTING
);
324 if (Files
.size(tempJarFile
) <= 12)
325 throw new RuntimeException("Nothing happened?");
328 Files
.move(tempJarFile
,
330 StandardCopyOption
.REPLACE_EXISTING
);
332 if (Files
.exists(tempOutputMapFile
))
333 Files
.move(tempOutputMapFile
,
335 StandardCopyOption
.REPLACE_EXISTING
);
337 catch (Exception __e
)
339 throw new RuntimeException("Failed to shrink/obfuscate.", __e
);
342 // Cleanup anything left over
345 if (tempJarFile
!= null)
348 Files
.delete(tempJarFile
);
350 catch (IOException ignored
)
354 if (tempInputMapFile
!= null)
357 Files
.delete(tempInputMapFile
);
359 catch (IOException ignored
)
363 if (tempOutputMapFile
!= null)
366 Files
.delete(tempOutputMapFile
);
368 catch (IOException ignored
)