Skip to content

Commit

Permalink
Move potemkin generator to buildSrc and switch to groovy
Browse files Browse the repository at this point in the history
  • Loading branch information
Mumfrey committed Dec 1, 2021
1 parent b47627f commit a40e4bc
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 167 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ dependency-reduced-pom.xml
# Compiled #
############
bin
/build
/build-logic/build
build
dist
out
run
Expand Down
1 change: 0 additions & 1 deletion build-logic/settings.gradle

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

27 changes: 17 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ buildscript {
}
}

plugins {
id 'java'
id 'synthetic-modules'
}

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

// Apply plugin
Expand All @@ -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'
Expand Down Expand Up @@ -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'
}
Expand Down Expand Up @@ -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
}
Expand Down
13 changes: 4 additions & 9 deletions build-logic/build.gradle → buildSrc/build.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
plugins {
id 'java-gradle-plugin'
}

java {
sourceCompatibility 'VERSION_1_8'
targetCompatibility 'VERSION_1_8'
id 'groovy-gradle-plugin'
}

repositories {
Expand All @@ -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'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* This file is part of Mixin, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* 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<String> 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())
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* This file is part of Mixin, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* 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<GeneratePotemkinModule> 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)))
})
}

}
Loading

0 comments on commit a40e4bc

Please sign in to comment.