Rewrite package names in compiled script files

This commit is contained in:
Gary Tierney
2017-09-17 18:42:19 +01:00
parent 112b8aab57
commit 89516dab63
7 changed files with 89 additions and 19 deletions
+1
View File
@@ -20,6 +20,7 @@ repositories {
dependencies { dependencies {
compile gradleApi() 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-stdlib-jre8', version: "$kotlinVersion"
compile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler-embeddable', version: "$kotlinVersion" compile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler-embeddable', version: "$kotlinVersion"
} }
@@ -109,7 +109,6 @@ class ApolloPluginExtension {
compileClasspath = mainSources.compileClasspath + mainSources.runtimeClasspath + mainSources.output compileClasspath = mainSources.compileClasspath + mainSources.runtimeClasspath + mainSources.output
scriptDefinitionClass = "org.apollo.game.plugin.kotlin.KotlinPluginScript" scriptDefinitionClass = "org.apollo.game.plugin.kotlin.KotlinPluginScript"
mustRunAfter buildTask mustRunAfter buildTask
} }
@@ -41,11 +41,10 @@ class KotlinCompilerConfigurationFactory {
classpath.addAll(bootClasspath.split(File.pathSeparatorChar.toString()).collect { new File(it) }) classpath.addAll(bootClasspath.split(File.pathSeparatorChar.toString()).collect { new File(it) })
} }
def classpathFiles = classpath.findAll { it.exists() }
def classLoader = new URLClassLoader(classpath.collect { it.toURL() }.toArray(new URL[classpath.size()])) def classLoader = new URLClassLoader(classpathFiles.collect { it.toURL() }.toArray(new URL[classpath.size()]))
def configuration = new CompilerConfiguration() def configuration = new CompilerConfiguration()
def scriptDefinitionClass = classLoader.loadClass(scriptDefinitionClassName) def scriptDefinitionClass = classLoader.loadClass(scriptDefinitionClassName)
def classpathFiles = classpath.collect { it }
def scriptDefinition = new KotlinScriptDefinitionFromAnnotatedTemplate(JvmClassMappingKt.getKotlinClass(scriptDefinitionClass), def scriptDefinition = new KotlinScriptDefinitionFromAnnotatedTemplate(JvmClassMappingKt.getKotlinClass(scriptDefinitionClass),
null, null, null, classpathFiles) null, null, null, classpathFiles)
@@ -2,12 +2,22 @@ package org.apollo.build.plugin.compiler
import java.nio.file.Path import java.nio.file.Path
class KotlinScriptBinary { class KotlinScriptBinaryArtifact {
final String fullyQualifiedName final String relativePath
final Path output final byte[] data
KotlinScriptBinary(String fullyQualifiedName, Path output) { KotlinScriptBinaryArtifact(String relativePath, byte[] data) {
this.output = output this.relativePath = relativePath
this.fullyQualifiedName = fullyQualifiedName this.data = data
}
}
class KotlinScriptBinary {
final String mainClassName
final List<KotlinScriptBinaryArtifact> artifacts
KotlinScriptBinary(String mainClassName, List<KotlinScriptBinaryArtifact> artifacts) {
this.mainClassName = mainClassName
this.artifacts = artifacts
} }
} }
@@ -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())
}
}
@@ -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.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer 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.CommonConfigurationKeys
import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.KotlinSourceRoot import org.jetbrains.kotlin.config.KotlinSourceRoot
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardOpenOption
class KotlinScriptCompiler { class KotlinScriptCompiler {
private String scriptDefinitionClass private String scriptDefinitionClass
@@ -24,7 +23,7 @@ class KotlinScriptCompiler {
this.messageCollector = messageCollector this.messageCollector = messageCollector
} }
KotlinScriptBinary compile(Path input, Path output) { KotlinScriptBinary compile(Path input) {
def compilerConfiguration = KotlinCompilerConfigurationFactory.create( def compilerConfiguration = KotlinCompilerConfigurationFactory.create(
scriptDefinitionClass, scriptDefinitionClass,
classpath, classpath,
@@ -34,7 +33,6 @@ class KotlinScriptCompiler {
def rootDisposable = Disposer.newDisposable() def rootDisposable = Disposer.newDisposable()
def configuration = compilerConfiguration.copy() def configuration = compilerConfiguration.copy()
output.toFile().mkdirs()
configuration.put(CommonConfigurationKeys.MODULE_NAME, input.toString()) configuration.put(CommonConfigurationKeys.MODULE_NAME, input.toString())
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, new KotlinSourceRoot(input.toAbsolutePath().toString())) configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, new KotlinSourceRoot(input.toAbsolutePath().toString()))
@@ -50,6 +48,7 @@ class KotlinScriptCompiler {
def sourceFiles = environment.getSourceFiles() def sourceFiles = environment.getSourceFiles()
def script = sourceFiles[0].script def script = sourceFiles[0].script
if (script == null) { if (script == null) {
throw new KotlinScriptCompilerException("Main source file is not a script") 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") throw new KotlinScriptCompilerException("Unable to find compiled plugin class file $scriptFilePath")
} }
generationState.factory.asList().forEach { def outputs = generationState.factory.asList()
Files.write(output.resolve(it.relativePath), it.asByteArray(), StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING) 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) { } catch (ex) {
throw new KotlinScriptCompilerException("Compilation failed", ex) throw new KotlinScriptCompilerException("Compilation failed", ex)
} finally { } finally {
@@ -1,5 +1,7 @@
package org.apollo.build.plugin.tasks 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.apollo.build.plugin.compiler.KotlinScriptCompiler
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.file.FileCollection 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.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import java.nio.file.Files
import java.nio.file.StandardOpenOption
class ApolloScriptCompileTask extends DefaultTask { class ApolloScriptCompileTask extends DefaultTask {
@OutputDirectory @OutputDirectory
File outputsDir File outputsDir
@@ -22,6 +27,9 @@ class ApolloScriptCompileTask extends DefaultTask {
@TaskAction @TaskAction
def execute(IncrementalTaskInputs inputs) { def execute(IncrementalTaskInputs inputs) {
def extension = getProject().getExtensions().getByType(ApolloPluginExtension.class);
def packageName = extension.packageName
if (scriptDefinitionClass == null) { if (scriptDefinitionClass == null) {
throw new Exception("No script definition class given") 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 messageCollector = new PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, true);
def compiler = new KotlinScriptCompiler(scriptDefinitionClass, classpath, messageCollector) def compiler = new KotlinScriptCompiler(scriptDefinitionClass, classpath, messageCollector)
outputsDir.mkdirs()
inputs.outOfDate { inputs.outOfDate {
removeBinariesFor(it.file) 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 { inputs.removed {