1 import cc.squirreljme.plugin.multivm.ident.SourceTargetClassifier
2 import cc.squirreljme.plugin.multivm.BangletVariant
3 import cc.squirreljme.plugin.multivm.ClutterLevel
4 import cc.squirreljme.plugin.multivm.VMFullSuiteDepends
5 import cc.squirreljme.plugin.multivm.VMHelpers
6 import cc.squirreljme.plugin.multivm.VMRunUpToDateWhen
7 import cc.squirreljme.plugin.multivm.VMType
9 import java.nio.file.Files
10 import java.nio.file.Paths
11 import java.nio.file.StandardOpenOption
12 import java.util.regex.Pattern
13 import java.util.zip.ZipEntry
14 import java.util.zip.ZipInputStream
20 id "com.github.johnrengelman.shadow" version "7.1.2"
23 description = "Standalone SquirrelJME virtual machine on Java."
24 mainClassName = "cc.squirreljme.vm.standalone.main.Main"
27 implementation project(":emulators:springcoat-vm")
31 // Use a fixed version of the JVM
34 sourceCompatibility = JavaVersion.VERSION_1_8
35 targetCompatibility = JavaVersion.VERSION_1_8
37 // Use the default bootstrap classpath
38 options.bootstrapClasspath = null
43 compileJava.options.debug = true
44 compileJava.options.debugOptions.setDebugLevel("source,lines,vars")
47 compileTestJava.options.debug = compileJava.options.debug
48 compileTestJava.options.debugOptions = compileJava.options.debugOptions
51 // Mapper for Jar names on files to straight file names
52 static Iterable<java.nio.file.Path> mapBaseNameP(Iterable<Path> input) {
53 List<java.nio.file.Path> result = new ArrayList<>()
55 input.forEach({path -> result.add(path.getFileName())})
60 // Mapper for Jar names on files to straight file names
61 static Iterable<java.nio.file.Path> mapBaseNameF(Iterable<File> input) {
62 List<java.nio.file.Path> mapped = new ArrayList<>()
64 input.forEach({file -> mapped.add(file.toPath())})
66 return mapBaseNameP(mapped as Iterable<Path>)
69 static String flatClasspath(Project project) {
70 return VMHelpers.classpathAsString(mapBaseNameP(VMHelpers
71 .runClassPath(project, new SourceTargetClassifier(
72 SourceSet.MAIN_SOURCE_SET_NAME,
73 VMType.SPRINGCOAT, BangletVariant.NONE,
74 ClutterLevel.DEBUG)) as List<Path>))
77 static java.nio.file.Path nameToDiskFile(java.nio.file.Path jarOut,
79 java.nio.file.Path diskFile = jarOut
80 for (String splice : name.split("[\\\\/]"))
81 diskFile = diskFile.resolve(splice)
86 // Prefix for included JAR resources
87 String MERGED_PREFIX = "SQUIRRELJME.SQC"
89 task collateResourceJars {
90 dependsOn new VMFullSuiteDepends(collateResourceJars,
91 new SourceTargetClassifier(SourceSet.MAIN_SOURCE_SET_NAME,
92 VMType.SPRINGCOAT, BangletVariant.NONE, ClutterLevel.DEBUG))
93 mustRunAfter processResources
97 description "Collates all files for standalone JARs."
99 // Inputs for this, it is just all of the input JARs and such
100 inputs.files(project.provider({ -> VMHelpers.fullSuiteLibraries(
101 rootProject.tasks.getByName("fullSpringCoatRelease")) as Iterable<Path>}))
102 outputs.files(project.provider({ ->
103 java.nio.file.Path outBase = processResources.getOutputs().
104 files.getSingleFile().toPath().resolve(MERGED_PREFIX)
106 Set<java.nio.file.Path> result = new LinkedHashSet<>()
109 result.add(outBase.resolve("suites.list"))
112 for (File jarFile : collateResourceJars.getInputs()
114 java.nio.file.Path jarPath = jarFile.toPath()
115 java.nio.file.Path jarName = jarPath.getFileName()
116 java.nio.file.Path jarOut = outBase.resolve(jarName)
119 result.add(jarOut.resolve("META-INF")
120 .resolve("squirreljme").resolve("resources.list"))
122 // Contents of the JAR
123 try (InputStream jarIn = Files.newInputStream(jarPath,
124 StandardOpenOption.READ);
125 ZipInputStream jar = new ZipInputStream(jarIn)) {
127 ZipEntry entry = jar.getNextEntry()
131 if (entry.isDirectory())
134 // Where is this file going?
135 result.add(nameToDiskFile(jarOut, name))
143 // Up to date when dependencies are updated
144 outputs.upToDateWhen(new VMRunUpToDateWhen(new SourceTargetClassifier(
145 SourceSet.MAIN_SOURCE_SET_NAME, VMType.SPRINGCOAT,
146 BangletVariant.NONE, ClutterLevel.DEBUG)))
148 // Explode the JARs into the resource root
150 java.nio.file.Path outBase = processResources.getOutputs().
151 files.getSingleFile().toPath().resolve(MERGED_PREFIX)
153 // Delete old directory set first since it will have a bunch of
154 // old files in it and such...
155 if (Files.isDirectory(outBase)) {
156 Set<java.nio.file.Path> deleteFiles = new LinkedHashSet<>()
157 Set<java.nio.file.Path> deleteDirs = new LinkedHashSet<>()
158 Files.walk(outBase).forEach({
159 if (Files.isDirectory(it))
164 for (Set<java.nio.file.Path> rawByes : [deleteFiles, deleteDirs]) {
165 List<java.nio.file.Path> byes = new ArrayList<>(rawByes)
166 Collections.reverse(byes)
168 for (java.nio.file.Path bye : byes) {
169 logger.lifecycle(String.format("Cleaning %s...", bye))
172 Files.deleteIfExists(bye)
173 } catch (IOException e) {
180 // Make sure it exists
181 Files.createDirectories(outBase)
184 List<String> suiteList = new ArrayList<>()
186 // Go through all of the files
187 byte[] buf = new byte[524288];
188 for (File jarFile : collateResourceJars.getInputs().files.asList()) {
189 // Where is everything?
190 java.nio.file.Path jarPath = jarFile.toPath()
191 java.nio.file.Path jarName = jarPath.getFileName()
193 // Add to the suite list for later
194 suiteList.add(jarName.toString())
196 // Where does the exploded JAR content go?
197 java.nio.file.Path jarOut = outBase.resolve(jarName)
200 Files.createDirectories(jarOut)
202 // Read the ZIP and process
203 List<String> jarContent = new ArrayList<>()
204 try (InputStream jarIn = Files.newInputStream(jarPath,
205 StandardOpenOption.READ);
206 ZipInputStream jar = new ZipInputStream(jarIn)) {
208 ZipEntry entry = jar.getNextEntry()
212 if (entry.isDirectory())
216 String name = entry.getName()
219 // Find where it goes
220 java.nio.file.Path diskFile = nameToDiskFile(jarOut, name)
223 Files.createDirectories(diskFile.getParent())
226 logger.lifecycle(String.format("Adding %s...",
227 outBase.relativize(diskFile)))
230 try (OutputStream out = Files.newOutputStream(diskFile,
231 StandardOpenOption.WRITE,
232 StandardOpenOption.TRUNCATE_EXISTING,
233 StandardOpenOption.CREATE)) {
235 int rc = jar.read(buf)
240 out.write(buf, 0, rc)
246 // Write resource list
247 java.nio.file.Path rcListBase =
248 jarOut.resolve("META-INF").resolve("squirreljme")
249 java.nio.file.Path rcListPath =
250 rcListBase.resolve("resources.list")
251 Files.createDirectories(rcListBase)
252 Files.write(rcListPath, jarContent,
253 StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING,
254 StandardOpenOption.CREATE)
257 // Write the suite list
258 Files.write(outBase.resolve("suites.list"), suiteList,
259 StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING,
260 StandardOpenOption.CREATE)
266 dependsOn processResources, collateResourceJars
267 mustRunAfter collateResourceJars
269 // We need to set specific manifest properties
272 "X-SquirrelJME-Standalone-Main-Class":
273 "javax.microedition.midlet.__MainHandler__",
274 "X-SquirrelJME-Standalone-Parameter":
275 "cc.squirreljme.runtime.launcher.ui.MidletMain",
276 "X-SquirrelJME-Standalone-Classpath": project.provider({ ->
277 return flatClasspath(project(":modules:launcher"))
279 "X-SquirrelJME-Standalone-Library": project.provider({ ->
280 return VMHelpers.classpathAsString(
281 mapBaseNameP(VMHelpers.fullSuiteLibraries(
282 rootProject.tasks.getByName("fullSpringCoatRelease"))
285 "X-SquirrelJME-Standalone-Internal-Jar-Root": project.provider({ ->
286 "/" + MERGED_PREFIX + "/"})
290 // Configuration for ShadowJar
292 dependsOn collateResourceJars
293 mustRunAfter collateResourceJars
295 // Always SquirrelJME
296 archiveBaseName.set("squirreljme-standalone")
298 // Set the suffix of the JAR to be the OS name and arch, since there is
299 // a dynamic library within for it
300 archiveClassifier.set(project.provider({ ->
301 String osName = System.getProperty("os.name").toLowerCase()
302 String osArch = System.getProperty("os.arch").toLowerCase()
304 // Normalize OS names
305 if (osName.contains("windows"))
307 else if (osName.contains("mac os") || osName.contains("macos"))
310 // Make sure there are no spaces or weird characters such as for
312 return (osName + "-" + osArch).replaceAll(
313 Pattern.compile("[\\s<>:\"/\\\\|?*]"), "")