diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index fe67c434..404e3214 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -20,6 +20,7 @@ repositories { dependencies { compile gradleApi() + compile group: 'org.ow2.asm', name: 'asm-all', version: '5.0.3' compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jre8', version: "$kotlinVersion" compile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler-embeddable', version: "$kotlinVersion" } \ No newline at end of file diff --git a/buildSrc/src/main/groovy/org/apollo/build/plugin/ApolloPluginExtension.groovy b/buildSrc/src/main/groovy/org/apollo/build/plugin/ApolloPluginExtension.groovy index 796534cc..ea4c910e 100644 --- a/buildSrc/src/main/groovy/org/apollo/build/plugin/ApolloPluginExtension.groovy +++ b/buildSrc/src/main/groovy/org/apollo/build/plugin/ApolloPluginExtension.groovy @@ -109,7 +109,6 @@ class ApolloPluginExtension { compileClasspath = mainSources.compileClasspath + mainSources.runtimeClasspath + mainSources.output scriptDefinitionClass = "org.apollo.game.plugin.kotlin.KotlinPluginScript" - mustRunAfter buildTask } diff --git a/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinCompilerConfigurationFactory.groovy b/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinCompilerConfigurationFactory.groovy index 2be6e28b..04ce0178 100644 --- a/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinCompilerConfigurationFactory.groovy +++ b/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinCompilerConfigurationFactory.groovy @@ -41,11 +41,10 @@ class KotlinCompilerConfigurationFactory { classpath.addAll(bootClasspath.split(File.pathSeparatorChar.toString()).collect { new File(it) }) } - - def classLoader = new URLClassLoader(classpath.collect { it.toURL() }.toArray(new URL[classpath.size()])) + def classpathFiles = classpath.findAll { it.exists() } + def classLoader = new URLClassLoader(classpathFiles.collect { it.toURL() }.toArray(new URL[classpath.size()])) def configuration = new CompilerConfiguration() def scriptDefinitionClass = classLoader.loadClass(scriptDefinitionClassName) - def classpathFiles = classpath.collect { it } def scriptDefinition = new KotlinScriptDefinitionFromAnnotatedTemplate(JvmClassMappingKt.getKotlinClass(scriptDefinitionClass), null, null, null, classpathFiles) diff --git a/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptBinary.groovy b/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptBinary.groovy index e4df437d..398ed251 100644 --- a/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptBinary.groovy +++ b/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptBinary.groovy @@ -2,12 +2,22 @@ package org.apollo.build.plugin.compiler import java.nio.file.Path -class KotlinScriptBinary { - final String fullyQualifiedName - final Path output +class KotlinScriptBinaryArtifact { + final String relativePath + final byte[] data - KotlinScriptBinary(String fullyQualifiedName, Path output) { - this.output = output - this.fullyQualifiedName = fullyQualifiedName + KotlinScriptBinaryArtifact(String relativePath, byte[] data) { + this.relativePath = relativePath + this.data = data + } +} + +class KotlinScriptBinary { + final String mainClassName + final List artifacts + + KotlinScriptBinary(String mainClassName, List artifacts) { + this.mainClassName = mainClassName + this.artifacts = artifacts } } diff --git a/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptBinaryArtifactRemapper.groovy b/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptBinaryArtifactRemapper.groovy new file mode 100644 index 00000000..7216c1ba --- /dev/null +++ b/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptBinaryArtifactRemapper.groovy @@ -0,0 +1,39 @@ +package org.apollo.build.plugin.compiler + +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.commons.Remapper +import org.objectweb.asm.commons.RemappingClassAdapter + +class KotlinScriptBinaryArtifactRemapper { + String mainClassName + + KotlinScriptBinaryArtifactRemapper(String mainClassName) { + this.mainClassName = mainClassName + } + + KotlinScriptBinaryArtifact remapToPackage(KotlinScriptBinaryArtifact artifact, String packageName) { + def reader = new ClassReader(new ByteArrayInputStream(artifact.data)) + def writer = new ClassWriter(0) + def normalizedPackageName = packageName.replace('.', '/') + def oldClassName = reader.getClassName() + def newClassName = artifact.relativePath.replace(oldClassName, "$normalizedPackageName/$oldClassName") + + def remapper = new Remapper() { + @Override + String map(String typeName) { + if (typeName.equals(mainClassName) || typeName.startsWith("$mainClassName\$")) { + return "$normalizedPackageName/$typeName" + } + + return super.map(typeName); + } + } + + def remappingAdapter = new RemappingClassAdapter(writer, remapper) + reader.accept(remappingAdapter, ClassReader.EXPAND_FRAMES) + writer.visitEnd() + + return new KotlinScriptBinaryArtifact(newClassName, writer.toByteArray()) + } +} diff --git a/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptCompiler.groovy b/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptCompiler.groovy index 20f510d2..98a1716c 100644 --- a/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptCompiler.groovy +++ b/buildSrc/src/main/groovy/org/apollo/build/plugin/compiler/KotlinScriptCompiler.groovy @@ -5,13 +5,12 @@ import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer +import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.java.PsiPackageStatementImpl import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.KotlinSourceRoot -import java.nio.file.Files import java.nio.file.Path -import java.nio.file.StandardOpenOption class KotlinScriptCompiler { private String scriptDefinitionClass @@ -24,7 +23,7 @@ class KotlinScriptCompiler { this.messageCollector = messageCollector } - KotlinScriptBinary compile(Path input, Path output) { + KotlinScriptBinary compile(Path input) { def compilerConfiguration = KotlinCompilerConfigurationFactory.create( scriptDefinitionClass, classpath, @@ -34,7 +33,6 @@ class KotlinScriptCompiler { def rootDisposable = Disposer.newDisposable() def configuration = compilerConfiguration.copy() - output.toFile().mkdirs() configuration.put(CommonConfigurationKeys.MODULE_NAME, input.toString()) configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, new KotlinSourceRoot(input.toAbsolutePath().toString())) @@ -50,6 +48,7 @@ class KotlinScriptCompiler { def sourceFiles = environment.getSourceFiles() def script = sourceFiles[0].script + if (script == null) { throw new KotlinScriptCompilerException("Main source file is not a script") } @@ -61,11 +60,10 @@ class KotlinScriptCompiler { throw new KotlinScriptCompilerException("Unable to find compiled plugin class file $scriptFilePath") } - generationState.factory.asList().forEach { - Files.write(output.resolve(it.relativePath), it.asByteArray(), StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING) - } + def outputs = generationState.factory.asList() + def artifacts = outputs.collect { new KotlinScriptBinaryArtifact(it.relativePath, it.asByteArray()) } - return new KotlinScriptBinary(script.fqName.asString(), output.resolve(scriptFileClass.relativePath)) + return new KotlinScriptBinary(script.fqName.asString(), artifacts) } catch (ex) { throw new KotlinScriptCompilerException("Compilation failed", ex) } finally { diff --git a/buildSrc/src/main/groovy/org/apollo/build/plugin/tasks/ApolloScriptCompileTask.groovy b/buildSrc/src/main/groovy/org/apollo/build/plugin/tasks/ApolloScriptCompileTask.groovy index 0092fa06..d1e06c12 100644 --- a/buildSrc/src/main/groovy/org/apollo/build/plugin/tasks/ApolloScriptCompileTask.groovy +++ b/buildSrc/src/main/groovy/org/apollo/build/plugin/tasks/ApolloScriptCompileTask.groovy @@ -1,5 +1,7 @@ package org.apollo.build.plugin.tasks +import org.apollo.build.plugin.ApolloPluginExtension +import org.apollo.build.plugin.compiler.KotlinScriptBinaryArtifactRemapper import org.apollo.build.plugin.compiler.KotlinScriptCompiler import org.gradle.api.DefaultTask import org.gradle.api.file.FileCollection @@ -10,6 +12,9 @@ import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector +import java.nio.file.Files +import java.nio.file.StandardOpenOption + class ApolloScriptCompileTask extends DefaultTask { @OutputDirectory File outputsDir @@ -22,6 +27,9 @@ class ApolloScriptCompileTask extends DefaultTask { @TaskAction def execute(IncrementalTaskInputs inputs) { + def extension = getProject().getExtensions().getByType(ApolloPluginExtension.class); + def packageName = extension.packageName + if (scriptDefinitionClass == null) { throw new Exception("No script definition class given") } @@ -34,9 +42,25 @@ class ApolloScriptCompileTask extends DefaultTask { def messageCollector = new PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, true); def compiler = new KotlinScriptCompiler(scriptDefinitionClass, classpath, messageCollector) + outputsDir.mkdirs() + inputs.outOfDate { removeBinariesFor(it.file) - compiler.compile(it.file.toPath(), outputsDir.toPath()) + + def binary = compiler.compile(it.file.toPath()) + def binaryArtifactRemapper = new KotlinScriptBinaryArtifactRemapper(binary.mainClassName) + def artifacts = binary.artifacts.collect { binaryArtifactRemapper.remapToPackage(it, packageName) } + + artifacts.each { + def artifactOutput = outputsDir.toPath().resolve(it.relativePath) + + Files.createDirectories(artifactOutput.getParent()) + Files.write(artifactOutput, it.data, + StandardOpenOption.CREATE, + StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING + ) + } } inputs.removed {