Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version-injection-plugin
Gradle plugin used by Hibernate to inject project version into compiled classes

To use you'd simply apply the plugin (after making sure it is added to your buildscript classpath):
```
```groovy
buildscript {
...
dependencies {
Expand All @@ -16,9 +16,12 @@ apply plugin: 'version-injection'
```

Then, you'd configure it. Configuration mainly involves naming the Class member to inject the project version into:
```
```groovy
versionInjection {
into( 'com.acme.Version', 'getVersionString' )
version.set('version')
targetMembers.set(List.of(
member('com.acme.Version', 'getVersionString')
))
}
```

Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.TaskProvider;

/**
* Mainly used to apply the InjectionAction to the main compileJava task
Expand All @@ -39,15 +40,23 @@ public class InjectionPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getPluginManager().apply( "java" );

// Allow user to configure
final InjectionSpec injectionSpec = new InjectionSpec( project );
project.getExtensions().add( EXTENSION_NAME, injectionSpec );
final InjectionSpec injectionSpec = project.getExtensions().create( EXTENSION_NAME, InjectionSpec.class );

// The action to run after compilation
final InjectionAction injectionAction = new InjectionAction( injectionSpec );
final Task compileJava = project.getTasks().getByName( "compileJava" );
compileJava.getInputs().property( "version-injection-targets", injectionSpec.getTargetMembers() );
compileJava.doLast( injectionAction );
final TaskProvider<InjectionTask> injectionTaskProvider = project.getTasks().register(
InjectionTask.TASK_NAME, InjectionTask.class, task -> {
JavaPluginExtension javaExtension = (JavaPluginExtension) project.getExtensions().getByName( "java" );

task.getVersion().set( injectionSpec.getVersion() );
task.getInjectionTargets().set( injectionSpec.getTargetMembers() );

task.getClasspath().setFrom(
javaExtension.getSourceSets().getByName( "main" ).getOutput().getClassesDirs()
);

task.dependsOn( project.getTasks().named( "compileJava" ) );
}
);
project.getTasks().named( "classes" ).configure( classes -> classes.dependsOn( injectionTaskProvider ) );
}

}
28 changes: 4 additions & 24 deletions src/main/java/org/hibernate/build/gradle/inject/InjectionSpec.java
Original file line number Diff line number Diff line change
@@ -1,35 +1,15 @@
package org.hibernate.build.gradle.inject;

import javax.inject.Inject;

import org.gradle.api.Project;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;

/**
* @author Steve Ebersole
*/
public class InjectionSpec {
private final ListProperty<TargetMember> targetMembers;

@Inject
public InjectionSpec(ObjectFactory objects) {
targetMembers = objects.listProperty( TargetMember.class );
}

public InjectionSpec(Project project) {
this( project.getObjects() );
}
public abstract class InjectionSpec {

public ListProperty<TargetMember> getTargetMembers() {
return targetMembers;
}
public abstract Property<String> getVersion();

public void into(String className, String member) {
into( new TargetMember( className, member ) );
}
public abstract ListProperty<TargetMember> getTargetMembers();

private void into(TargetMember targetMember) {
targetMembers.add( targetMember );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
import java.net.URLClassLoader;
import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
Expand All @@ -43,64 +46,62 @@
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstantAttribute;
import javassist.bytecode.FieldInfo;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.TaskAction;

/**
* Responsible for coordinating the actual injection of project version. Runs as a Gradle Action (doLast typically
* to the main CompileJava task)
* Responsible for coordinating the actual injection of project version.
*
* @author Steve Ebersole
*/
public class InjectionAction implements Action<Task> {
private static final Logger log = LoggerFactory.getLogger( InjectionAction.class );
public abstract class InjectionTask extends DefaultTask {
static final String TASK_NAME = "injectVersion";
private static final Logger log = LoggerFactory.getLogger( InjectionTask.class );

private LoaderClassPath loaderClassPath;
private ClassPool classPool;

private final InjectionSpec injectionSpec;
@InputFiles
abstract ConfigurableFileCollection getClasspath();

public InjectionAction(InjectionSpec injectionSpec) {
this.injectionSpec = injectionSpec;
}
@Input
public abstract Property<String> getVersion();

@Input
public abstract ListProperty<TargetMember> getInjectionTargets();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, I've tried adding an output, as:

	@OutputDirectory
	public FileCollection getOutputFiles() {
		return getClasspath();
	}

^ my thinking was since we update classes, it somewhat makes sense ...

but I had build issues on the ORM side when I tried building an entire project, rather than just a single module where the injection plugin is actually applied. Some of those modules that didn't need the plugin were complaining...

maybe it's because of that "global" declaration here:

https://github.com/hibernate/hibernate-orm/blob/0080e17b43515f4a6e12218dd16dc879659e64a0/build.gradle#L22

🤔


@Override
public void execute(Task task) {
final ClassLoader runtimeScopeClassLoader = buildRuntimeScopeClassLoader( task.getProject() );
@TaskAction
public void execute() {
final ClassLoader runtimeScopeClassLoader = buildRuntimeScopeClassLoader();
loaderClassPath = new LoaderClassPath( runtimeScopeClassLoader );
classPool = new ClassPool( true );
classPool.appendClassPath( loaderClassPath );

performInjections( task.getProject() );
performInjections();
}

private ClassLoader buildRuntimeScopeClassLoader(Project project) {
private ClassLoader buildRuntimeScopeClassLoader() {
final ArrayList<URL> classPathUrls = new ArrayList<>();
final SourceSet mainSourceSet = project
.getExtensions()
.getByType( SourceSetContainer.class )
.getByName( SourceSet.MAIN_SOURCE_SET_NAME );
for ( File file : mainSourceSet.getRuntimeClasspath() ) {
getClasspath().forEach( file -> {
try {
classPathUrls.add( file.toURI().toURL() );
}
catch (MalformedURLException e) {
throw new InjectionException( "Could not determine artifact URL [" + file.getPath() + "]", e );
}
}
} );
return new URLClassLoader( classPathUrls.toArray( URL[]::new ), getClass().getClassLoader() );
}

private void performInjections(Project project) {
final String projectVersion = project.getVersion().toString();
private void performInjections() {
final String projectVersion = getVersion().get();

for ( TargetMember targetMember : injectionSpec.getTargetMembers().get() ) {
for ( TargetMember targetMember : getInjectionTargets().get() ) {
resolveInjectionTarget( targetMember ).inject( projectVersion );
}
}
Expand All @@ -113,7 +114,7 @@ private InjectionTarget resolveInjectionTarget(TargetMember targetMember) {
CtField field = ctClass.getField( targetMember.getMemberName() );
return new FieldInjectionTarget( targetMember, ctClass, field );
}
catch( NotFoundException ignore ) {
catch (NotFoundException ignore) {
}

// see if it is a method...
Expand All @@ -133,7 +134,7 @@ private InjectionTarget resolveInjectionTarget(TargetMember targetMember) {
// finally throw an exception
throw new InjectionException( "Unknown member [" + targetMember.getQualifiedName() + "]" );
}
catch ( Throwable e ) {
catch (Throwable e) {
throw new InjectionException( "Unable to resolve class [" + targetMember.getClassName() + "]", e );
}
}
Expand All @@ -146,14 +147,13 @@ private interface InjectionTarget {
* Inject the given value per this target's strategy.
*
* @param value The value to inject.
*
* @throws org.hibernate.build.gradle.inject.InjectionException Indicates a problem performing the injection.
*/
void inject(String value);
}

private abstract class BaseInjectionTarget implements InjectionTarget {
@SuppressWarnings( {"UnusedDeclaration"})
@SuppressWarnings({ "UnusedDeclaration" })
private final TargetMember targetMember;
private final CtClass ctClass;
private final File classFileLocation;
Expand All @@ -164,7 +164,7 @@ protected BaseInjectionTarget(TargetMember targetMember, CtClass ctClass) {
try {
classFileLocation = new File( loaderClassPath.find( targetMember.getClassName() ).toURI() );
}
catch ( Throwable e ) {
catch (Throwable e) {
throw new InjectionException( "Unable to resolve class file path", e );
}
}
Expand Down Expand Up @@ -205,7 +205,7 @@ private class FieldInjectionTarget extends BaseInjectionTarget {
private FieldInjectionTarget(TargetMember targetMember, CtClass ctClass, CtField ctField) {
super( targetMember, ctClass );
this.ctField = ctField;
if ( ! Modifier.isStatic( ctField.getModifiers() ) ) {
if ( !Modifier.isStatic( ctField.getModifiers() ) ) {
throw new InjectionException( "Field is not static [" + targetMember.getQualifiedName() + "]" );
}
}
Expand Down Expand Up @@ -237,7 +237,7 @@ protected void doInjection(String value) {
try {
ctMethod.setBody( "{return \"" + value + "\";}" );
}
catch ( Throwable t ) {
catch (Throwable t) {
throw new InjectionException( "Unable to replace method body", t );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@ public class TargetMember implements Serializable {
private final String className;
private final String memberName;

public TargetMember(String className, String memberName) {
private TargetMember(String className, String memberName) {
this.className = className;
this.memberName = memberName;
}

public static TargetMember member(String className, String memberName) {
return new TargetMember( className, memberName );
}

public String getClassName() {
return className;
}
Expand Down