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 // ProGuard's method inlining causes code to break! So disable it
46 // otherwise it generates an incorrect StackMapTable... *facepalm*
47 "-optimizations", "!method/inlining/*",
49 // Adjust manifest resources
50 "-adaptresourcefilenames", "**",
51 "-adaptresourcefilecontents",
52 "META-INF/MANIFEST.MF,META-INF/services/**",
54 // Consumers of the libraries/APIs need to see the annotation
55 // information if it is there, to make sure it is retained
56 "-keepattributes", "*Annotation*",
58 // Keep anything with main in it
59 "-keepclasseswithmembers", "class", "*", "{",
60 "public", "static", "void", "main", "(",
61 "java.lang.String[]", ")", ";",
65 "-keep", "class", "*", "extends",
66 "javax.microedition.midlet.MIDlet",
68 // Keep classes annotation with @Api and
70 "@cc.squirreljme.runtime.cldc.annotation.Api",
72 "public", "protected", "*", ";",
75 "@cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi",
77 "public", "protected", "*", ";",
80 // Keep the names of these classes as well
81 "-keepnames", "public",
82 "@cc.squirreljme.runtime.cldc.annotation.Api",
84 "-keepnames", "public",
85 "@cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi",
88 // Keep members with these two annotations
89 "-keepclassmembers", "public", "class", "*", "{",
90 "@cc.squirreljme.runtime.cldc.annotation.Api",
91 "public", "protected", "*", ";",
93 "-keepclassmembers", "public", "class", "*", "{",
94 "@cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi",
95 "public", "protected", "*", ";",
98 // Keep all clone methods
99 "-keepclassmembers", "class", "*", "{",
100 "public", "java.lang.Object", "clone", "(", ")", ";",
102 "-keepclassmembernames", "class", "*", "{",
103 "public", "java.lang.Object", "clone", "(", ")", ";",
106 // Keep names as well
107 "-keepclassmembernames", "public", "class", "*", "{",
108 "@cc.squirreljme.runtime.cldc.annotation.Api",
109 "public", "protected", "*", ";",
111 "-keepclassmembernames", "public", "class", "*", "{",
112 "@cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi",
113 "public", "protected", "*", ";",
116 // Do not trash enumerations as we need those to work properly
117 "-keepclassmembers", "class", "*",
118 "extends", "java.lang.Enum", "{",
120 "public", "static", "**[]", "values",
122 "public", "static", "**", "valueOf",
123 "(", "java.lang.String", ")", ";",
125 "-keepclassmembernames", "class", "*",
126 "extends", "java.lang.Enum", "{",
128 "public", "static", "**[]", "values",
130 "public", "static", "**", "valueOf",
131 "(", "java.lang.String", ")", ";",
135 /** Settings for tests. */
136 static final String
[] _TEST_SETTINGS
=
138 // This keeps everything about tests but will use pre-existing
139 // mappings and otherwise if we are using obfuscated classes
140 // This is the only thing I have found that works
141 "-keep", "class", "*",
142 "-keepnames", "class", "*",
143 "-keepclassmembernames", "class", "*",
146 /** The source set used. */
147 public final String sourceSet
;
150 * Initializes the task action.
152 * @param __sourceSet The source set used.
153 * @throws NullPointerException On null arguments.
156 public VMCompactLibraryTaskAction(String __sourceSet
)
157 throws NullPointerException
159 if (__sourceSet
== null)
160 throw new NullPointerException("NARG");
162 this.sourceSet
= __sourceSet
;
170 public void execute(Task __task
)
172 VMCompactLibraryTask compactTask
= (VMCompactLibraryTask
)__task
;
174 // Where are we reading/writing to/from?
175 Path inputPath
= compactTask
.inputBaseJarPath().get();
176 Path outputJarPath
= compactTask
.outputJarPath().get();
177 Path outputMapPath
= compactTask
.outputMapPath().get();
179 // Some settings may be configured
180 SquirrelJMEPluginConfiguration projectConfig
=
181 SquirrelJMEPluginConfiguration
.configuration(__task
.getProject());
184 Path tempJarFile
= null;
185 Path tempInputMapFile
= null;
186 Path tempOutputMapFile
= null;
189 // Look into the Jar file and check if there are class files, if
190 // there are none then there is nothing to compact
191 boolean atLeastOneClass
= false;
192 try (InputStream in
= Files
.newInputStream(inputPath
,
193 StandardOpenOption
.READ
);
194 ZipInputStream zip
= new ZipInputStream(in
))
198 // Get the next entry
199 ZipEntry entry
= zip
.getNextEntry();
203 String name
= entry
.getName();
204 if (name
.endsWith(".class"))
205 atLeastOneClass
= true;
209 // No classes were found, so do nothing
210 if (!atLeastOneClass
)
212 Files
.copy(inputPath
, outputJarPath
,
213 StandardCopyOption
.REPLACE_EXISTING
);
218 // Setup temporary file to output to when finished
219 tempJarFile
= Files
.createTempFile("out", ".jar");
220 tempInputMapFile
= Files
.createTempFile("in", ".map");
221 tempOutputMapFile
= Files
.createTempFile("out", ".map");
223 // Need to delete the created temporary file, otherwise Proguard
224 // will just say "The output appears up to date" and do nothing
225 Files
.delete(tempJarFile
);
226 Files
.delete(tempOutputMapFile
);
228 // We need to include all the inputs that were already ran through
229 // ProGuard, so we basically need to look at the dependencies and
230 // map them around accordingly
231 // We also need to combine the mapping files as well
232 ClassPath libraryJars
= new ClassPath();
233 boolean applyMapping
= false;
234 for (VMCompactLibraryTask compactDep
:
235 VMHelpers
.compactLibTaskDepends(__task
.getProject(),
238 Path baseJarFile
= compactDep
.baseJar
.getOutputs().getFiles()
239 .getSingleFile().toPath();
241 // Add the library, but the pre-obfuscated form since we need
242 // to know what it is
243 if (Files
.exists(baseJarFile
))
244 libraryJars
.add(new ClassPathEntry(
245 compactDep
.baseJar
.getOutputs().getFiles()
246 .getSingleFile(), false));
248 // If the mapping file exists, concatenate it
249 if (Files
.exists(compactDep
.outputMapPath().get()))
251 // Do use mapping now
254 // Add all the information
255 Files
.write(tempInputMapFile
,
256 Files
.readAllLines(compactDep
.outputMapPath().get()),
257 StandardOpenOption
.APPEND
, StandardOpenOption
.WRITE
);
261 // Base options to use
262 List
<String
> proGuardOptions
= new ArrayList
<>();
263 proGuardOptions
.addAll(
264 Arrays
.asList(VMCompactLibraryTaskAction
._PARSE_SETTINGS
));
268 SourceSet
.TEST_SOURCE_SET_NAME
.equals(this.sourceSet
) ||
269 VMHelpers
.TEST_FIXTURES_SOURCE_SET_NAME
.equals(this.sourceSet
);
273 proGuardOptions
.addAll(Arrays
.asList(
274 VMCompactLibraryTaskAction
._TEST_SETTINGS
));
276 // Add any additional options as needed
277 if (projectConfig
.proGuardOptions
!= null &&
278 !projectConfig
.proGuardOptions
.isEmpty())
279 proGuardOptions
.addAll(projectConfig
.proGuardOptions
);
281 // Parse initial configuration with settings
282 Configuration config
= new Configuration();
283 try (ConfigurationParser parser
= new ConfigurationParser(
284 proGuardOptions
.toArray(new String
[proGuardOptions
.size()]),
287 parser
.parse(config
);
290 // We are neither of these platforms, we say we are not Java ME
291 // because it will remove StackMapTable and instead use StackMap
292 // which is not what we want
293 config
.android
= false;
294 config
.microEdition
= false;
296 // Reduce space and obfuscate, but we cannot remove everything at
298 config
.shrink
= false;
299 config
.optimize
= false;
300 config
.flattenPackageHierarchy
= "$" +
301 (projectConfig
.javaDocErrorCode
== null ?
"??" :
302 projectConfig
.javaDocErrorCode
);
304 // For mapping files, members do need to be unique
305 config
.useUniqueClassMemberNames
= true;
307 // Do not use mix case class names, so that more strings can
308 // be compacted together accordingly
309 config
.useMixedCaseClassNames
= false;
311 // Write mapping to the output file, since we will use it later on
312 config
.printMapping
= tempOutputMapFile
.toFile();
314 // Utilize the combined mapping file that was made so that we can
315 // use everything we have?
317 config
.applyMapping
= tempInputMapFile
.toFile();
320 config
.verbose
= true;
321 //config.dump = Configuration.STD_OUT;
322 //config.printUsage = Configuration.STD_OUT;
323 config
.printConfiguration
= Configuration
.STD_OUT
;
325 // Use whatever libraries were found
326 config
.libraryJars
= libraryJars
;
328 // Setup input and output Jar
329 ClassPath programJars
= new ClassPath();
330 config
.programJars
= programJars
;
334 new ClassPathEntry(inputPath
.toFile(), false));
336 // Output temporary Jar
337 programJars
.add(new ClassPathEntry(
338 tempJarFile
.toFile(), true));
340 // Run the shrinking/obfuscation
343 new ProGuard(config
).execute();
347 Files
.move(tempInputMapFile
,
348 outputMapPath
.resolveSibling(
349 outputMapPath
.getFileName() + ".in"),
350 StandardCopyOption
.REPLACE_EXISTING
);
354 if (Files
.size(tempJarFile
) <= 12)
355 throw new RuntimeException("Nothing happened?");
358 Files
.move(tempJarFile
,
360 StandardCopyOption
.REPLACE_EXISTING
);
362 if (Files
.exists(tempOutputMapFile
))
363 Files
.move(tempOutputMapFile
,
365 StandardCopyOption
.REPLACE_EXISTING
);
367 catch (Exception __e
)
369 throw new RuntimeException("Failed to shrink/obfuscate.", __e
);
372 // Cleanup anything left over
375 if (tempJarFile
!= null)
378 Files
.delete(tempJarFile
);
380 catch (IOException ignored
)
384 if (tempInputMapFile
!= null)
387 Files
.delete(tempInputMapFile
);
389 catch (IOException ignored
)
393 if (tempOutputMapFile
!= null)
396 Files
.delete(tempOutputMapFile
);
398 catch (IOException ignored
)