|
| 1 | +import org.gradle.jvm.toolchain.JavaLanguageVersion |
| 2 | +import org.gradle.jvm.toolchain.JvmVendorSpec |
| 3 | + |
| 4 | +import groovy.json.JsonOutput |
| 5 | +import java.nio.file.Files |
| 6 | + |
| 7 | +plugins { |
| 8 | + id 'org.graalvm.buildtools.native' version '0.10.6' |
| 9 | +} |
| 10 | + |
1 | 11 | ext.mainClassName = 'jme3test.TestChooser' |
| 12 | +ext.nativeMainClassName = 'jme3test.TestChooserCli' |
| 13 | +ext.jmeNativeMetadataAdditionalResourceGlobs = [] |
| 14 | + |
| 15 | +def generatedTestChooserResourcesDir = layout.buildDirectory.dir('generated/testchooser/resources') |
| 16 | +def testChooserClassListFile = generatedTestChooserResourcesDir.map { it.file('jme3test/test-classes.txt') } |
| 17 | +def testChooserReflectionConfigFile = layout.buildDirectory.file('generated/testchooser/reflect-config.json') |
| 18 | +def nativeImageBuildOutputJsonFile = layout.buildDirectory.file('reports/native-image/jme3-testchooser-build-output.json') |
| 19 | +def testChooserReachabilityMetadataFile = generatedTestChooserResourcesDir.map { |
| 20 | + it.file('META-INF/native-image/org.jmonkeyengine/jme3-examples-testchooser/reachability-metadata.json') |
| 21 | +} |
| 22 | + |
| 23 | +tasks.register('generateTestChooserClassList') { |
| 24 | + group = 'build' |
| 25 | + description = 'Generates a resource list of jme3test classes for TestChooserCli fallback discovery.' |
| 26 | + dependsOn 'compileJava' |
| 27 | + outputs.files(testChooserClassListFile, testChooserReachabilityMetadataFile, testChooserReflectionConfigFile) |
| 28 | + |
| 29 | + doLast { |
| 30 | + Set<String> allJme3TestClassNames = new TreeSet<String>() |
| 31 | + Set<String> launcherClassNames = new TreeSet<String>() |
| 32 | + sourceSets.main.output.classesDirs.files.findAll { it.exists() }.each { classesDir -> |
| 33 | + fileTree(classesDir).matching { |
| 34 | + include 'jme3test/**/*.class' |
| 35 | + exclude '**/*$*' |
| 36 | + exclude '**/module-info.class' |
| 37 | + exclude '**/package-info.class' |
| 38 | + exclude '**/TestChooser.class' |
| 39 | + exclude '**/TestChooserCli.class' |
| 40 | + }.files.each { classFile -> |
| 41 | + String relativePath = classesDir.toPath().relativize(classFile.toPath()).toString().replace(File.separatorChar, (char) '/') |
| 42 | + if (relativePath.endsWith('.class')) { |
| 43 | + String className = relativePath.substring(0, relativePath.length() - '.class'.length()).replace('/', '.') |
| 44 | + allJme3TestClassNames.add(className) |
| 45 | + if (relativePath.contains('Test')) { |
| 46 | + launcherClassNames.add(className) |
| 47 | + } |
| 48 | + } |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + File classListFile = testChooserClassListFile.get().asFile |
| 53 | + classListFile.parentFile.mkdirs() |
| 54 | + classListFile.text = launcherClassNames.isEmpty() |
| 55 | + ? '' |
| 56 | + : launcherClassNames.join(System.lineSeparator()) + System.lineSeparator() |
| 57 | + |
| 58 | + List<Map<String, Object>> reflectionEntries = allJme3TestClassNames.collect { className -> |
| 59 | + [ |
| 60 | + type : className, |
| 61 | + allDeclaredConstructors: true, |
| 62 | + allDeclaredMethods : true, |
| 63 | + allPublicMethods : true |
| 64 | + ] |
| 65 | + } |
| 66 | + Map<String, Object> metadata = [ |
| 67 | + reflection: reflectionEntries, |
| 68 | + resources : [] |
| 69 | + ] |
| 70 | + |
| 71 | + File reachabilityFile = testChooserReachabilityMetadataFile.get().asFile |
| 72 | + reachabilityFile.parentFile.mkdirs() |
| 73 | + reachabilityFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(metadata)) + System.lineSeparator() |
| 74 | + |
| 75 | + List<Map<String, Object>> reflectionConfigEntries = allJme3TestClassNames.collect { className -> |
| 76 | + [ |
| 77 | + name : className, |
| 78 | + allDeclaredConstructors: true, |
| 79 | + allDeclaredMethods : true, |
| 80 | + allPublicMethods : true |
| 81 | + ] |
| 82 | + } |
| 83 | + File reflectionConfigFile = testChooserReflectionConfigFile.get().asFile |
| 84 | + reflectionConfigFile.parentFile.mkdirs() |
| 85 | + reflectionConfigFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(reflectionConfigEntries)) + System.lineSeparator() |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +sourceSets.main.resources.srcDir(generatedTestChooserResourcesDir) |
| 90 | + |
| 91 | +tasks.named('processResources') { |
| 92 | + dependsOn 'generateTestChooserClassList' |
| 93 | +} |
2 | 94 |
|
3 | 95 | def androidProject = project(':jme3-android') |
4 | 96 | def androidNativeProject = project(':jme3-android-native') |
@@ -37,6 +129,74 @@ task runExamples(dependsOn: 'build', type: JavaExec) { |
37 | 129 | } |
38 | 130 | } |
39 | 131 |
|
| 132 | +def graalLauncher = javaToolchains.launcherFor { |
| 133 | + languageVersion = JavaLanguageVersion.of(21) |
| 134 | + vendor = JvmVendorSpec.GRAAL_VM |
| 135 | + nativeImageCapable = true |
| 136 | +} |
| 137 | + |
| 138 | +graalvmNative { |
| 139 | + binaries { |
| 140 | + named('main') { |
| 141 | + imageName = 'jme3-testchooser' |
| 142 | + mainClass = nativeMainClassName |
| 143 | + sharedLibrary = false |
| 144 | + javaLauncher = graalLauncher |
| 145 | + project.ext.jmeApplyDefaultNativeImageResourceSettings(delegate) |
| 146 | + buildArgs.add("-H:ReflectionConfigurationFiles=${testChooserReflectionConfigFile.get().asFile.absolutePath}") |
| 147 | + buildArgs.add('-H:+BuildOutputBreakdowns') |
| 148 | + buildArgs.add("-H:BuildOutputJSONFile=${nativeImageBuildOutputJsonFile.get().asFile.absolutePath}") |
| 149 | + } |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +tasks.named('nativeCompile') { |
| 154 | + dependsOn 'processResources' |
| 155 | + |
| 156 | + doFirst { |
| 157 | + // Gradle's Copy task cannot preserve symlinks in some provisioned toolchains. |
| 158 | + // Recreate likely broken links in the GraalVM toolchain before native-image runs. |
| 159 | + File graalvmHomeDir = System.getenv('GRAALVM_HOME') ? file(System.getenv('GRAALVM_HOME')) : null |
| 160 | + def nativeImageLauncher = javaToolchains.launcherFor { |
| 161 | + languageVersion = JavaLanguageVersion.of(21) |
| 162 | + vendor = JvmVendorSpec.GRAAL_VM |
| 163 | + nativeImageCapable = true |
| 164 | + } |
| 165 | + |
| 166 | + if (delegate.hasProperty('options') && delegate.options.hasProperty('javaLauncher')) { |
| 167 | + delegate.options.javaLauncher.set(nativeImageLauncher) |
| 168 | + } |
| 169 | + |
| 170 | + File toolchainDir = graalvmHomeDir |
| 171 | + if (toolchainDir == null) { |
| 172 | + File executableParent = nativeImageLauncher.get().executablePath.asFile.parentFile |
| 173 | + toolchainDir = executableParent.name == 'bin' ? executableParent.parentFile : executableParent |
| 174 | + } |
| 175 | + |
| 176 | + def toolchainFiles = project.fileTree(toolchainDir).files.findAll { it.isFile() } |
| 177 | + def emptyFiles = toolchainFiles.findAll { it.length() == 0L } |
| 178 | + Set<File> emptyFileSet = emptyFiles as Set<File> |
| 179 | + |
| 180 | + toolchainFiles.groupBy { it.name }.each { ignoredName, sameNamedFiles -> |
| 181 | + List<File> nonEmptyCandidates = sameNamedFiles.findAll { it.length() > 0L } |
| 182 | + List<File> emptyCandidates = sameNamedFiles.findAll { emptyFileSet.contains(it) } |
| 183 | + if (!nonEmptyCandidates.isEmpty() && !emptyCandidates.isEmpty()) { |
| 184 | + File target = nonEmptyCandidates.first() |
| 185 | + emptyCandidates.each { File link -> |
| 186 | + if (link != target) { |
| 187 | + logger.quiet("Fixing up '${link}' to link to '${target}'.") |
| 188 | + if (link.delete()) { |
| 189 | + Files.createSymbolicLink(link.toPath(), target.toPath()) |
| 190 | + } else { |
| 191 | + logger.warn("Unable to delete '${link}'.") |
| 192 | + } |
| 193 | + } |
| 194 | + } |
| 195 | + } |
| 196 | + } |
| 197 | + } |
| 198 | +} |
| 199 | + |
40 | 200 | dependencies { |
41 | 201 | implementation project(':jme3-core') |
42 | 202 | implementation project(':jme3-desktop') |
|
0 commit comments