diff --git a/.gitignore b/.gitignore index d1635d121..bc21d8fea 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,7 @@ dependency-reduced-pom.xml # Compiled # ############ bin -/build -/build-logic/build +build dist out run diff --git a/build-logic/settings.gradle b/build-logic/settings.gradle deleted file mode 100644 index 32b6fd94f..000000000 --- a/build-logic/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'Mixin-build-logic' diff --git a/build-logic/src/main/java/org/spongepowered/asm/mixin/build/GenerateModule.java b/build-logic/src/main/java/org/spongepowered/asm/mixin/build/GenerateModule.java deleted file mode 100644 index 71fbff2e3..000000000 --- a/build-logic/src/main/java/org/spongepowered/asm/mixin/build/GenerateModule.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.spongepowered.asm.mixin.build; - -import org.gradle.api.DefaultTask; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.TaskAction; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.ModuleVisitor; -import org.objectweb.asm.Opcodes; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.jar.JarOutputStream; -import java.util.zip.ZipEntry; - -public abstract class GenerateModule extends DefaultTask { - - private static final long STABLE_TIMESTAMP = 0x386D4380; // 01/01/2000 00:00:00 java 8 breaks when using 0. - - @OutputFile - public abstract RegularFileProperty getGeneratedJar(); - - @Input - public abstract Property getModuleName(); - - @TaskAction - public void generateSyntheticModule() throws IOException { - this.getGeneratedJar().get().getAsFile().getParentFile().mkdirs(); // create directories if needed - - // write a jar with a single file, of the synthetic module-info - try (final JarOutputStream jos = new JarOutputStream(new FileOutputStream(this.getGeneratedJar().get().getAsFile()))) { - final ZipEntry entry = new ZipEntry("module-info.class"); - entry.setTime(STABLE_TIMESTAMP); - jos.putNextEntry(entry); - this.writeModule(jos); - jos.closeEntry(); - } - } - - private void writeModule(final OutputStream os) throws IOException { - final ClassWriter cw = new ClassWriter(0); - cw.visit(Opcodes.V9, Opcodes.ACC_MODULE, "module-info", null, null, null); - final ModuleVisitor mv = cw.visitModule(this.getModuleName().get(), 0, null); - mv.visitRequire("java.base", Opcodes.ACC_MANDATED, null); - mv.visitEnd(); - cw.visitEnd(); - os.write(cw.toByteArray()); - } - -} diff --git a/build-logic/src/main/java/org/spongepowered/asm/mixin/build/SyntheticModulesExtension.java b/build-logic/src/main/java/org/spongepowered/asm/mixin/build/SyntheticModulesExtension.java deleted file mode 100644 index 5b1f907df..000000000 --- a/build-logic/src/main/java/org/spongepowered/asm/mixin/build/SyntheticModulesExtension.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.spongepowered.asm.mixin.build; - -import org.gradle.api.Project; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.tasks.TaskProvider; - -import javax.inject.Inject; - -public class SyntheticModulesExtension { - private final Project project; - private final DirectoryProperty outputDirectory; - - @Inject - public SyntheticModulesExtension(final ObjectFactory objects, final Project project) { - this.project = project; - this.outputDirectory = objects.directoryProperty(); - } - - public DirectoryProperty getOutputDirectory() { - return this.outputDirectory; - } - - // This is far from idiomatic gradle -- we should have a NamedDomainObjectCollection for the - // data model, and create tasks based on the contents of that collection - // however, this is a simple thing for internal use, so instead we just create tasks directly - public void module(final String moduleName) { - final String sanitizedName = moduleName.replace('.', '_'); - final TaskProvider t = this.project.getTasks().register("generateModule" + sanitizedName, GenerateModule.class, task -> { - task.getModuleName().set(moduleName); - task.getGeneratedJar().set(this.getOutputDirectory().file(sanitizedName + ".jar")); - }); - - this.project.getPlugins().withType(JavaPlugin.class, $ -> { - this.project.getDependencies().add(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, this.project.files(t.map(it -> it.getOutputs()))); - }); - } - -} diff --git a/build-logic/src/main/java/org/spongepowered/asm/mixin/build/SyntheticModulesPlugin.java b/build-logic/src/main/java/org/spongepowered/asm/mixin/build/SyntheticModulesPlugin.java deleted file mode 100644 index 4f8a5c910..000000000 --- a/build-logic/src/main/java/org/spongepowered/asm/mixin/build/SyntheticModulesPlugin.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.spongepowered.asm.mixin.build; - -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.tasks.TaskContainer; -import org.gradle.api.tasks.TaskProvider; -import org.gradle.plugins.ide.eclipse.EclipsePlugin; -import org.gradle.plugins.ide.eclipse.model.EclipseModel; - -public class SyntheticModulesPlugin implements Plugin { - - @Override - public void apply(final Project project) { - this.createExtension(project); - - // create aggregator task - final TaskProvider aggregator = this.createAggregatorTask(project.getTasks()); - - // setup IDE integration - this.configureIdes(project, aggregator); - } - - private void createExtension(final Project project) { - final SyntheticModulesExtension extension = project.getExtensions() - .create("syntheticModules", SyntheticModulesExtension.class, project); - - extension.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir("generated-modules")); - } - - private TaskProvider createAggregatorTask(final TaskContainer tasks) { - return tasks.register("generateAllSyntheticModules", task -> { - task.dependsOn(tasks.withType(GenerateModule.class)); - }); - } - - private void configureIdes(final Project project, final TaskProvider aggregatorTask) { - project.getPlugins().withType(EclipsePlugin.class, pl -> { - project.getExtensions().getByType(EclipseModel.class).synchronizationTasks(aggregatorTask); - }); - } - -} diff --git a/build.gradle b/build.gradle index e204b4f41..aa9faad8e 100644 --- a/build.gradle +++ b/build.gradle @@ -9,11 +9,6 @@ buildscript { } } -plugins { - id 'java' - id 'synthetic-modules' -} - import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar // Apply plugin @@ -24,6 +19,7 @@ apply plugin: 'maven-publish' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'potemkin-modules' // Default tasks defaultTasks 'licenseFormat', 'check', 'build' @@ -152,11 +148,11 @@ sourceSets { } } -// Because Mixin aims to support a variety of environments, we have to be able to run with older versions of GSON and Guava that lack -// official module names. This means the same library may appear with multiple module names. We want to be able to link our module with -// either of these two at runtime, without having to have two versions of the library on our compile-time module path. -// To do this, we generate empty jars with *only* a module descriptor for the module we want to be able to compile against. -syntheticModules { +// Because Mixin aims to support a variety of environments, we have to be able to run with older versions of GSON and Guava that lack official module +// names. This means the same library may appear with multiple module names. We want to be able to link our module with either of these two at +// runtime, without having to have two versions of the library on our compile-time module path. To do this, we generate empty "potemkin" jars with +// *only* a module descriptor for the module we want to be able to compile against. +potemkinModules { module 'com.google.gson' module 'com.google.common' } @@ -256,6 +252,17 @@ eclipse { } } + project { + resourceFilter { + appliesTo = 'FOLDERS' + type = 'EXCLUDE_ALL' + matcher { + id = 'org.eclipse.ui.ide.multiFilter' + arguments = '1.0-name-matches-false-false-buildSrc' + } + } + } + // Build service task outputs for test projects autoBuildTasks compileModlauncher4Java, compileModlauncher9Java, compileLaunchwrapperJava } diff --git a/build-logic/build.gradle b/buildSrc/build.gradle similarity index 59% rename from build-logic/build.gradle rename to buildSrc/build.gradle index 513c5dca4..e7001a077 100644 --- a/build-logic/build.gradle +++ b/buildSrc/build.gradle @@ -1,10 +1,5 @@ plugins { - id 'java-gradle-plugin' -} - -java { - sourceCompatibility 'VERSION_1_8' - targetCompatibility 'VERSION_1_8' + id 'groovy-gradle-plugin' } repositories { @@ -20,9 +15,9 @@ dependencies { gradlePlugin { plugins { - syntheticModules { - id = 'synthetic-modules' - implementationClass = 'org.spongepowered.asm.mixin.build.SyntheticModulesPlugin' + potemkinModules { + id = 'potemkin-modules' + implementationClass = 'org.spongepowered.asm.mixin.build.PotemkinModulesPlugin' } } } diff --git a/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/GeneratePotemkinModule.groovy b/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/GeneratePotemkinModule.groovy new file mode 100644 index 000000000..f8710ed54 --- /dev/null +++ b/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/GeneratePotemkinModule.groovy @@ -0,0 +1,95 @@ +/* + * This file is part of Mixin, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.asm.mixin.build + +import groovy.transform.PackageScope +import org.gradle.api.DefaultTask +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.ModuleVisitor +import org.objectweb.asm.Opcodes + +import java.io.FileOutputStream +import java.io.IOException +import java.io.OutputStream +import java.util.jar.JarOutputStream +import java.util.zip.ZipEntry + +/** + * Task which generates a "Potemkin Module" which is a jar which only contains + * a module descriptor for the module it's pretending to be. This is just to + * fool the compiler into thinking that the module exists without needing to + * import the module itself. + */ +public abstract class GeneratePotemkinModule extends DefaultTask { + + /** + * 01/01/2000 00:00:00 java 8 breaks when using 0 + */ + private static final long STABLE_TIMESTAMP = 0x386D4380 + + /** + * File property containing the path to the generated jar + */ + @OutputFile + abstract RegularFileProperty getGeneratedJar() + + /** + * Property defining the module name for which the potemkin is being + * generated + */ + @Input + abstract Property getModuleName() + + @TaskAction + void generatePotemkinModule() { + def moduleJarFile = this.generatedJar.get().asFile + moduleJarFile.parentFile.mkdirs() // create directories if needed + + // write a jar with a single file, of the synthetic module-info + try (final JarOutputStream jos = new JarOutputStream(new FileOutputStream(moduleJarFile))) { + def entry = new ZipEntry("module-info.class") + entry.time = STABLE_TIMESTAMP + jos.putNextEntry(entry) + GeneratePotemkinModule.writeModule(jos, this.moduleName.get()) + jos.closeEntry() + } + } + + private static void writeModule(final OutputStream os, String moduleName) { + def cw = new ClassWriter(0) + cw.visit(Opcodes.V9, Opcodes.ACC_MODULE, "module-info", null, null, null) + def mv = cw.visitModule(moduleName, 0, null) + mv.visitRequire("java.base", Opcodes.ACC_MANDATED, null) + mv.visitEnd() + cw.visitEnd() + os.write(cw.toByteArray()) + } + +} diff --git a/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/PotemkinModulesExtension.groovy b/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/PotemkinModulesExtension.groovy new file mode 100644 index 000000000..43cf5e735 --- /dev/null +++ b/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/PotemkinModulesExtension.groovy @@ -0,0 +1,69 @@ +/* + * This file is part of Mixin, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.asm.mixin.build + +import groovy.transform.PackageScope +import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.TaskProvider + +import javax.inject.Inject + +/** + * Extension to allow the potemkin modules to be specified in the build script + */ +public class PotemkinModulesExtension { + + @PackageScope final Project project + private final DirectoryProperty outputDirectory + + @Inject + PotemkinModulesExtension(final Project project, final ObjectFactory objects) { + this.project = project + this.outputDirectory = objects.directoryProperty() + } + + DirectoryProperty getOutputDirectory() { + return this.outputDirectory + } + + // This is far from idiomatic gradle -- we should have a NamedDomainObjectCollection for the + // data model, and create tasks based on the contents of that collection + // however, this is a simple thing for internal use, so instead we just create tasks directly + void module(final String moduleName) { + final String sanitisedName = moduleName.replace('.', '_').replaceAll('(^|_)([a-z])', { it[2].toUpperCase() }) + final TaskProvider taskProvider = this.project.tasks.register("generatePotemkinFor${sanitisedName}", GeneratePotemkinModule.class, task -> { + task.moduleName.set(moduleName) + task.generatedJar.set(this.outputDirectory.file("${moduleName}-potemkin.jar")) + }) + + project.plugins.withType(JavaPlugin.class, { + project.dependencies.add(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, project.files(taskProvider.map(task -> task.outputs))) + }) + } + +} diff --git a/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/PotemkinModulesPlugin.groovy b/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/PotemkinModulesPlugin.groovy new file mode 100644 index 000000000..3cb0a3c82 --- /dev/null +++ b/buildSrc/src/main/groovy/org/spongepowered/asm/mixin/build/PotemkinModulesPlugin.groovy @@ -0,0 +1,72 @@ +/* + * This file is part of Mixin, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.asm.mixin.build + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.plugins.ide.eclipse.EclipsePlugin +import org.gradle.plugins.ide.eclipse.model.EclipseModel + +/** + * Plugin which creates "potemkin" modules specified in the build script, this + * is to facilitate import directives for modules which aren't + * available at compile time, or rather are available but have a different name. + * This is needed so that modules which have changed name over time (eg. + * previously had an automatic module name but later versions have an explicit + * module name) can be supported without things breaking at runtime. + */ +public class PotemkinModulesPlugin implements Plugin { + + @Override + void apply(final Project project) { + this.createExtension(project) + + // create aggregator task + final TaskProvider aggregator = this.createAggregatorTask(project.tasks) + + // setup IDE integration + this.configureIdes(project, aggregator) + } + + private void createExtension(final Project project) { + def extension = project.extensions.create("potemkinModules", PotemkinModulesExtension.class, project) + extension.outputDirectory.set(project.layout.buildDirectory.dir("generated-modules")) + } + + private TaskProvider createAggregatorTask(final TaskContainer tasks) { + return tasks.register("generateAllPotemkinModules", task -> { + task.dependsOn(tasks.withType(GeneratePotemkinModule.class)) + }) + } + + private void configureIdes(final Project project, final TaskProvider aggregatorTask) { + project.plugins.withType(EclipsePlugin.class, pl -> { + project.extensions.getByType(EclipseModel.class).synchronizationTasks(aggregatorTask) + }) + } + +} diff --git a/settings.gradle b/settings.gradle index b062121dc..89f411069 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1 @@ rootProject.name = name - -includeBuild 'build-logic' \ No newline at end of file diff --git a/src/modularity/java/module-info.java b/src/modularity/java/module-info.java index 5dd080252..cfba2cb02 100644 --- a/src/modularity/java/module-info.java +++ b/src/modularity/java/module-info.java @@ -26,7 +26,6 @@ /** * Mixin module declaration */ -@SuppressWarnings("module") // Suppress the warnings about gson and gson below. Damn gsons, you ruined gson! module org.spongepowered.mixin { // @@ -40,7 +39,7 @@ requires transitive org.objectweb.asm.tree.analysis; requires transitive org.objectweb.asm.util; requires java.logging; - + // // Modules we require for compilation but don't necessarily need at runtime // @@ -48,16 +47,16 @@ requires static transitive cpw.mods.modlauncher; requires static cpw.mods.securejarhandler; requires static transitive org.apache.logging.log4j; - + // // Automatic modules we depend on, using static to avoid the forward compatibility mess // requires static jopt.simple; - + // Guava, by file name and official module requires static com.google.common; requires static guava; - + // Gson, by file name and official module requires static com.google.gson; requires static gson; @@ -111,10 +110,10 @@ exports org.spongepowered.tools.obfuscation.mcp; exports org.spongepowered.tools.obfuscation.mirror; exports org.spongepowered.tools.obfuscation.service; - + opens org.spongepowered.asm.mixin.transformer to com.google.gson, gson; - + // // Service wiring // @@ -137,7 +136,7 @@ uses cpw.mods.modlauncher.serviceapi.ILaunchPluginService; provides cpw.mods.modlauncher.serviceapi.ILaunchPluginService with org.spongepowered.asm.launch.MixinLaunchPlugin; - + uses javax.annotation.processing.Processor; provides javax.annotation.processing.Processor with org.spongepowered.tools.obfuscation.MixinObfuscationProcessorInjection,