Skip to content

Commit

Permalink
Add 'argumentFileDirectory' to make reproducible builds easier
Browse files Browse the repository at this point in the history
After in the previous commit the plugin itself was improved to produce
reproducible builds, this commit is about plugin users: By using a
combination of 'argumentFileDirectory' and 'argumentFileName', it is now
easy to generate argument files (builddef.lst) outside the
target/classes and target/test-classes directories, thus making AspectJ
Maven builds reproducible.

Fixes a problem which also exists in the MojoHaus plugin, see
mojohaus/aspectj-maven-plugin#52.
  • Loading branch information
kriegaex committed Mar 28, 2022
1 parent a323cd2 commit b4c9a8c
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 49 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ version has the following improvements compared to MojoHaus:
compiler option. Robert Scholte [suggested a better way](https://github.com/mojohaus/aspectj-maven-plugin/pull/100#discussion_r646632402)
to implement JMS support in AspectJ Maven, similar to how Maven Compiler does it, but I am not confident I can
implement it correctly. He did not volunteer to do it either, so for now this new option is better than nothing.
* Since 1.13.2, both the plugin builds themselves (including sources and javadocs) and the artifacts produced by the
plugin are [reproducible](https://reproducible-builds.org/), see also the
[Maven mini guide](https://maven.apache.org/guides/mini/guide-reproducible-builds.html). For more information, read
the descriptions for options `argumentFileDirectory` and `argumentFileName`. Mojohaus version 1.14.0 does not
produce reproducible AspectJ artifacts, only the plugin itself has a partially reproducible build (JAR only, not
javadoc).
* The documentation for this plugin is somewhat better than the MojoHaus version, if you look at the
[Maven site](https://dev-aspectj.github.io/aspectj-maven-plugin/), e.g. the additional UML diagram for the
[multi-module example](https://dev-aspectj.github.io/aspectj-maven-plugin/multimodule/multimodule_strategy.html)
Expand Down
1 change: 1 addition & 0 deletions src/it/changeArgumentFileLocation/invoker.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
invoker.goals = clean test-compile
60 changes: 60 additions & 0 deletions src/it/changeArgumentFileLocation/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dev.aspectj.aspectj-maven.it</groupId>
<artifactId>changeArgumentFileLocation</artifactId>
<version>0.0.1-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>@aspectjVersion@</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>dev.aspectj</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>@project.version@</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<argumentFileDirectory>${project.build.directory}/aspectj-build</argumentFileDirectory>
</configuration>
<executions>
<execution>
<id>aj-compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>aj-test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<argumentFileName>builddef-test.lst</argumentFileName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class Application {
public static void main(String[] args) {
System.out.println("Dummy class to trigger AspectJ Maven 'compile' goal");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class DummyTest {
public static void main(String[] args) {
System.out.println("Dummy class to trigger AspectJ Maven 'test-compile' goal");
}
}
15 changes: 15 additions & 0 deletions src/it/changeArgumentFileLocation/verify.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
notExistingFiles = [
"classes/builddef.lst",
"test-classes/builddef.lst",
"classes/builddef-test.lst",
"test-classes/builddef-test.lst"
]
existingFiles = [
"aspectj-build/builddef.lst",
"aspectj-build/builddef-test.lst"
]

for (file in notExistingFiles)
assert !new File("$basedir/target/$file").exists()
for (file in existingFiles)
assert new File("$basedir/target/$file").exists()
33 changes: 21 additions & 12 deletions src/main/java/org/codehaus/mojo/aspectj/AbstractAjcCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -406,12 +406,16 @@ public abstract class AbstractAjcCompiler extends AbstractAjcMojo {

/**
* The filename holding AJC build arguments.
* The file will be placed in the project build output directory, and will contain all the arguments passed to
* the AJC compiler in the last run, and also all the files included in the AJC build.
* <p>
* Sample content shown below to illustrate typical content within the builddef.lst file:
* By default, the file will be placed in the respective build output directory, i.e. in the same directory where
* your application or test classes reside, and will contain all the arguments passed to the AJC compiler in the
* last run, and also all the files included in the AJC build. The file is also used to determine if recompilation
* is necessary in subsequent builds. The {@code argumentFileDirectory} can be changed, which is helpful for
* <a href="https://maven.apache.org/guides/mini/guide-reproducible-builds.html">reproducible builds</a>.
* <p>
* A typical <i>builddef.lst</i> file might look like this:
* <pre><code>
* -1.6
* -1.8
* -encoding
* UTF-8
* -classpath
Expand Down Expand Up @@ -494,6 +498,13 @@ public abstract class AbstractAjcCompiler extends AbstractAjcMojo {
*/
protected abstract File getOutputDirectory();

/**
* The directory where argument files (builddef.lst) go.
*
* @return the argument file directory
*/
protected abstract File getArgumentFileDirectory();

/**
* The directory for sources generated by annotation processing.
*
Expand Down Expand Up @@ -597,10 +608,12 @@ public void execute() throws MojoExecutionException {
}
try {
getLog().debug(
"Compiling and weaving " + resolvedIncludes.size() + " sources to " + getOutputDirectory());
AjcHelper.writeBuildConfigToFile(ajcOptions, argumentFileName, getOutputDirectory());
"Compiling and weaving " + resolvedIncludes.size() + " sources to " + getOutputDirectory()
);
AjcHelper.writeBuildConfigToFile(ajcOptions, argumentFileName, getArgumentFileDirectory());
getLog().debug(
"Arguments file written : " + new File(getOutputDirectory(), argumentFileName).getAbsolutePath());
"Arguments file written : " + new File(getArgumentFileDirectory(), argumentFileName).getAbsolutePath()
);
} catch (IOException e) {
throw new MojoExecutionException("Could not write arguments file to the target area", e);
}
Expand Down Expand Up @@ -813,7 +826,7 @@ private void addModulesArgument(final String argument, final List<String> argume
*/
protected boolean isBuildNeeded()
throws MojoExecutionException {
File outDir = getOutputDirectory();
File outDir = getArgumentFileDirectory();
return hasNoPreviousBuild(outDir) || hasArgumentsChanged(outDir) ||
hasSourcesChanged(outDir) || hasNonWeavedClassesChanged(outDir);

Expand Down Expand Up @@ -1046,8 +1059,4 @@ public void setXjoinpoints(String xjoinpoints) {
public void setWarn(String warn) {
this.warn = warn;
}

public void setArgumentFileName(String argumentFileName) {
this.argumentFileName = argumentFileName;
}
}
41 changes: 30 additions & 11 deletions src/main/java/org/codehaus/mojo/aspectj/AjcCompileMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,49 +37,68 @@

/**
* Weaves all main classes.
*
*
* AspectJ Compiler Plugin.
* @author <a href="mailto:kaare.nilsen@gmail.com">Kaare Nilsen</a>
*/
@Mojo( name="compile", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true )
public class AjcCompileMojo
extends AbstractAjcCompiler
{

/**
* The directory for compiled classes.
*
*/
@Parameter( readonly = true, required = true, defaultValue = "${project.build.outputDirectory}" )
private File outputDirectory;

/**
* The directory for the argument file, see {@link #argumentFileName}.
* <p>
* For <a href="https://maven.apache.org/guides/mini/guide-reproducible-builds.html">reproducible builds</a>, please
* change the directory to be outside the {@link #outputDirectory}, because argument files contain path names, which
* can vary between build systems. So you do not want an argument file to end up in your build artifact (e.g. JAR
* file) for a reproducible build.
* <p>
* Caveat: If you configure a common directory, e.g. <code>${project.build.directory}/aj-build</code>, in the plugin
* configuration, please make sure to choose different {@link #argumentFileName}s for {@code compile} vs
* {@code test-compile} goal execution configurations to avoid one file overwriting the other.
*/
@Parameter( defaultValue = "${project.build.outputDirectory}" )
private File argumentFileDirectory;

/**
* The directory for sources generated by annotation processing.
*/
@Parameter( defaultValue = "${project.build.directory}/generated-sources/aspectj-maven-plugin" )
private File generatedSourcesDirectory;

/**
* <p>
* Set the java source folders to use, specifying the includes and excludes.
* Set the java source folders to use, specifying the includes and excludes.
* </p>
* <p>
* If you don't specify this parameter, all java sources of the current project fill be used.
* If you don't specify this parameter, all java sources of the current project will be used.
* If you specify this parameter as an empty tag (i.e. &lt;sources/&gt;), all source folders will be ignored.
* Otherwise specify the source folder(s) to use.
* </p>
*
*
* @since 1.4
* @see DirectoryScanner
*/
@Parameter
private Scanner[] sources;
protected File getOutputDirectory()
{

@Override
protected File getOutputDirectory() {
return outputDirectory;
}

@Override
protected File getArgumentFileDirectory() {
return argumentFileDirectory;
}

@Override
protected File getGeneratedSourcesDirectory() {
return generatedSourcesDirectory;
Expand All @@ -101,7 +120,7 @@ protected List<String> getSourceDirectories()
{
return project.getCompileSourceRoots();
}

protected Scanner[] getJavaSources()
{
return sources;
Expand Down
13 changes: 6 additions & 7 deletions src/main/java/org/codehaus/mojo/aspectj/AjcHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,13 @@ public static void writeBuildConfigToFile( List<String> arguments, String fileNa
File argFile = new File( outputDir, fileName );
argFile.getParentFile().mkdirs();
argFile.createNewFile();
BufferedWriter writer = new BufferedWriter( new FileWriter( argFile ) );
for ( String argument : arguments )
{
writer.write( argument );
writer.newLine();
try (BufferedWriter writer = new BufferedWriter( new FileWriter( argFile ) )) {
for (String argument : arguments) {
writer.write(argument);
writer.newLine();
}
writer.flush();
}
writer.flush();
writer.close();
}

/**
Expand Down
35 changes: 27 additions & 8 deletions src/main/java/org/codehaus/mojo/aspectj/AjcTestCompileMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

/**
* Weaves all test classes.
*
*
* AspectJ Compiler Plugin.
*
* @author <a href="mailto:kaare.nilsen@gmail.com">Kaare Nilsen</a>
Expand All @@ -65,14 +65,29 @@ public class AjcTestCompileMojo
*/
@Parameter( defaultValue = "true" )
protected boolean weaveWithAspectsInMainSourceFolder = true;

/**
* The directory where compiled test classes go.
*
*/
@Parameter( readonly = true, required = true, defaultValue = "${project.build.testOutputDirectory}" )
private File outputDirectory;

/**
* The directory for the argument file, see {@link #argumentFileName}.
* <p>
* For <a href="https://maven.apache.org/guides/mini/guide-reproducible-builds.html">reproducible builds</a>, please
* change the directory to be outside the {@link #outputDirectory}, because argument files contain path names, which
* can vary between build systems. So you do not want an argument file to end up in your build artifact (e.g. JAR
* file) for a reproducible build.
* <p>
* Caveat: If you configure a common directory, e.g. <code>${project.build.directory}/aj-build</code>, in the plugin
* configuration, please make sure to choose different {@link #argumentFileName}s for {@code compile} vs
* {@code test-compile} goal execution configurations to avoid one file overwriting the other.
*/
@Parameter( defaultValue = "${project.build.testOutputDirectory}" )
private File argumentFileDirectory;

/**
* The directory for sources generated by annotation processing.
*/
Expand All @@ -82,13 +97,13 @@ public class AjcTestCompileMojo
/**
* <p>
* Set the java test source folders to use, specifying the includes and excludes.
* </p>
* </p>
* <p>
* If you don't specify this parameter, all java test sources of the current project fill be used.
* If you don't specify this parameter, all java test sources of the current project will be used.
* If you specify this parameter as an empty tag (i.e. &lt;testSources/&gt;), all test source folders will be ignored.
* Otherwise specify the test source folder(s) to use.
* <p>
*
*
* @since 1.4
* @see DirectoryScanner
*/
Expand All @@ -114,13 +129,17 @@ protected List<String> getClasspathDirectories()
outputDirectories.add( project.getBuild().getOutputDirectory() );
return outputDirectories;
}

@Override
protected File getOutputDirectory()
{
protected File getOutputDirectory() {
return outputDirectory;
}

@Override
protected File getArgumentFileDirectory() {
return argumentFileDirectory;
}

@Override
public File getGeneratedSourcesDirectory() {
return generatedTestSourcesDirectory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void testModificationSet()
{
ajcMojo.aspectDirectory = "src/main/aspect";
final String[] includes = new String[]{"org/codehaus/mojo/aspectj/OldStyleAspect.aj"};
ajcMojo.setArgumentFileName("builddef.lst");
ajcMojo.argumentFileName = "builddef.lst";
FileUtils.fileDelete(project.getBuild().getDirectory() + ajcMojo.argumentFileName);

ajcMojo.includes= new String[]{"org/codehaus/mojo/aspectj/OldStyleAspect.aj"};
Expand All @@ -68,12 +68,7 @@ public void testModificationSet()
ajcMojo.includes = includes;
ajcMojo.execute();
}
catch ( CompilationFailedException cfe )
{
// we're only testing modifications, don't care if it won't compile
}
catch ( UnsupportedClassVersionError ucve )
{
catch ( CompilationFailedException | UnsupportedClassVersionError cfe ) {
// we're only testing modifications, don't care if it won't compile
}

Expand Down
Loading

0 comments on commit b4c9a8c

Please sign in to comment.