Merge pull request #101 from rbygrave/master

ENH: Support explicit merging of tiles execution configuration
This commit is contained in:
Mark Derricutt
2019-12-02 10:08:46 +13:00
committed by GitHub
6 changed files with 399 additions and 9 deletions

View File

@@ -40,6 +40,7 @@ import org.apache.maven.model.DistributionManagement
import org.apache.maven.model.Model
import org.apache.maven.model.Parent
import org.apache.maven.model.Plugin
import org.apache.maven.model.PluginExecution
import org.apache.maven.model.PluginManagement
import org.apache.maven.model.Repository
import org.apache.maven.model.building.DefaultModelBuilder
@@ -68,6 +69,7 @@ import org.apache.maven.shared.filtering.MavenResourcesFiltering
import org.codehaus.plexus.component.annotations.Component
import org.codehaus.plexus.component.annotations.Requirement
import org.codehaus.plexus.logging.Logger
import org.codehaus.plexus.util.xml.Xpp3Dom
import org.codehaus.plexus.util.xml.pull.XmlPullParserException
import org.eclipse.aether.impl.VersionRangeResolver
import org.eclipse.aether.resolution.VersionRangeRequest
@@ -108,7 +110,7 @@ public class TilesMavenLifecycleParticipant extends AbstractMavenLifecyclePartic
@Requirement
ProjectBuilder projectBuilder
@Requirement
ModelBuilder modelBuilder
@@ -154,6 +156,8 @@ public class TilesMavenLifecycleParticipant extends AbstractMavenLifecyclePartic
Map<String, ArtifactModel> processedTiles = [:]
List<String> tileDiscoveryOrder = []
Map<String, Artifact> unprocessedTiles = [:]
Map<String,TileModel> tilesByExecution = [:];
String applyBeforeParent;
/**
@@ -359,9 +363,10 @@ public class TilesMavenLifecycleParticipant extends AbstractMavenLifecyclePartic
*/
protected void orchestrateMerge(MavenSession mavenSession, MavenProject project) throws MavenExecutionException {
// Clear collected tiles from previous project in reactor
processedTiles.clear();
tileDiscoveryOrder.clear();
unprocessedTiles.clear();
processedTiles.clear()
tileDiscoveryOrder.clear()
unprocessedTiles.clear()
tilesByExecution.clear()
// collect the first set of tiles
parseConfiguration(project.model, project.file)
@@ -467,7 +472,7 @@ public class TilesMavenLifecycleParticipant extends AbstractMavenLifecyclePartic
try {
ModelBuildingResult interimBuild = modelBuilder.build(request)
// this will revert the tile dependencies inserted by TilesProjectBuilder, which is fine since by now they
// this will revert the tile dependencies inserted by TilesProjectBuilder, which is fine since by now they
// served their purpose of correctly ordering projects, so we can now do without them
ModelBuildingResult finalModel = modelBuilder.build(request, interimBuild)
if (!tilesInjected && applyBeforeParent) {
@@ -687,6 +692,8 @@ public class TilesMavenLifecycleParticipant extends AbstractMavenLifecyclePartic
}
protected void loadAllDiscoveredTiles(MavenSession mavenSession, MavenProject project) throws MavenExecutionException {
List<TileModel> mergeSourceTiles = []
while (unprocessedTiles.size() > 0) {
String unresolvedTile = unprocessedTiles.keySet().iterator().next()
@@ -696,14 +703,102 @@ public class TilesMavenLifecycleParticipant extends AbstractMavenLifecyclePartic
// ensure we have resolved the tile (it could come from a non-tile model)
if (tileModel) {
processedTiles.put(artifactName(resolvedTile), new ArtifactModel(resolvedTile, tileModel))
parseForExtendedSyntax(tileModel, resolvedTile.getFile())
if (hasProperty(tileModel, 'tile-merge-source')) {
// hold and merge into target later
mergeSourceTiles.add(tileModel)
} else {
if (hasProperty(tileModel, 'tile-merge-target')) {
registerTargetTile(tileModel)
}
String tileName = artifactName(resolvedTile)
processedTiles.put(tileName, new ArtifactModel(resolvedTile, tileModel))
parseForExtendedSyntax(tileModel, resolvedTile.getFile())
}
}
}
// merge all the source tiles last
for (TileModel mergeTile : mergeSourceTiles) {
mergeTileIntoTarget(mergeTile)
}
ensureAllTilesDiscoveredAreAccountedFor()
}
private static boolean hasProperty(TileModel tileModel, String propertyKey) {
// remove these properties, we don't want them in the merged result
return 'true' == tileModel.model?.properties?.remove(propertyKey)
}
private List<Plugin> registerTargetTile(TileModel targetTile) {
return mergeTile(targetTile, false)
}
private List<Plugin> mergeTileIntoTarget(TileModel fragmentTile) {
return mergeTile(fragmentTile, true)
}
private List<Plugin> mergeTile(TileModel tileModel, boolean mergeIntoTarget) {
tileModel.model?.build?.plugins?.each { plugin ->
plugin.executions.each { execution ->
String eid = "$plugin.groupId:$plugin.artifactId:$execution.id"
if (!mergeIntoTarget) {
tilesByExecution.put(eid, tileModel)
} else {
String fragmentId = "$tileModel.model.groupId:$tileModel.model.artifactId"
TileModel targetTile = tilesByExecution.get(eid)
if (targetTile) {
String targetId = "$targetTile.model.groupId:$targetTile.model.artifactId"
logger.info("Merged tile $fragmentId into $targetId plugin:$eid")
mergeProperties(targetTile, tileModel)
mergeExecutionConfiguration(targetTile, execution, eid)
} else {
String missingTileId = tileModel.model?.properties?.getProperty('tile-merge-expected-target')
if (missingTileId) {
throw new MavenExecutionException("Please add missing tile $missingTileId. This is required for tile $fragmentId, plugin:$eid", (Throwable)null)
} else {
throw new MavenExecutionException("Error with tile $fragmentId - Missing target tile required with plugin:$eid. Please check the documentation for this tile.", (Throwable)null)
}
}
}
}
}
}
/**
* Merge the properties from the mergeTile into targetTile.
*/
private static void mergeProperties(TileModel targetTile, TileModel mergeTile) {
if (mergeTile.model.properties) {
targetTile.model.properties.putAll(mergeTile.model.properties)
}
}
/**
* Merge the execution configuration from mergeExecution into the target tile.
*/
private void mergeExecutionConfiguration(TileModel targetTile, PluginExecution mergeExecution, String eid) {
targetTile.model?.build?.plugins?.each { plugin ->
plugin.executions.each { execution ->
String targetEid = "$plugin.groupId:$plugin.artifactId:$execution.id"
if (targetEid.equals(eid)) {
Xpp3Dom configuration = (Xpp3Dom)execution.configuration
String appendElementName = configuration.getAttribute('tiles-append')
if (appendElementName) {
Xpp3Dom target = configuration.getChild(appendElementName)
Xpp3Dom source = ((Xpp3Dom)mergeExecution.configuration).getChild(appendElementName)
// append from source into target
Xpp3Dom.mergeXpp3Dom(target, source, false)
logger.debug("merged execution configuration - $eid")
}
}
}
}
}
/**
* removes all invalid tiles from the discovery order
*/
@@ -787,8 +882,7 @@ public class TilesMavenLifecycleParticipant extends AbstractMavenLifecyclePartic
void resolveVersionRange(MavenProject project, Artifact tileArtifact) {
def versionRangeRequest = new VersionRangeRequest(RepositoryUtils.toArtifact(tileArtifact),
RepositoryUtils.toRepos(project?.remoteArtifactRepositories),
null)
RepositoryUtils.toRepos(project?.remoteArtifactRepositories), null)
def versionRangeResult = versionRangeResolver.resolveVersionRange(mavenSession?.repositorySession, versionRangeRequest)

View File

@@ -30,6 +30,7 @@ import org.apache.maven.model.Build
import org.apache.maven.model.Model
import org.apache.maven.model.Parent
import org.apache.maven.model.Plugin
import org.apache.maven.model.PluginExecution
import org.apache.maven.model.building.ModelBuildingRequest
import org.apache.maven.model.io.xpp3.MavenXpp3Reader
import org.apache.maven.project.MavenProject
@@ -37,6 +38,7 @@ import org.apache.maven.shared.filtering.DefaultMavenFileFilter
import org.apache.maven.shared.filtering.DefaultMavenReaderFilter
import org.apache.maven.shared.filtering.DefaultMavenResourcesFiltering
import org.codehaus.plexus.logging.Logger
import org.codehaus.plexus.util.xml.Xpp3Dom
import org.codehaus.plexus.util.xml.Xpp3DomBuilder
import org.eclipse.aether.impl.VersionRangeResolver
import org.junit.AfterClass
@@ -48,6 +50,8 @@ import org.sonatype.plexus.build.incremental.DefaultBuildContext
import static groovy.test.GroovyAssert.shouldFail
import static io.repaint.maven.tiles.Constants.TILEPLUGIN_ARTIFACT
import static io.repaint.maven.tiles.Constants.TILEPLUGIN_GROUP
import static io.repaint.maven.tiles.GavUtil.artifactName
import static org.junit.Assert.assertEquals
import static org.mockito.Mockito.mock
import static org.mockito.Mockito.when
@@ -149,6 +153,69 @@ public class TilesMavenLifecycleParticipantTest {
}
}
@Test
void testTileMerge() {
Model model = new Model()
model.setGroupId("io.repaint.tiles")
model.setArtifactId("test-merge-tile")
model.setVersion("1.1-SNAPSHOT")
model.build = new Build()
model.build.directory = "target/test-merge-tile"
MavenProject project = new MavenProject(model)
project.setFile(new File("src/test/resources/test-merge-tile/pom.xml"))
MavenExecutionRequest req = mock(MavenExecutionRequest.class)
when(req.getUserProperties()).thenReturn(new Properties())
when(req.getSystemProperties()).thenReturn(new Properties())
MavenSession session = new MavenSession(null, req, mock(MavenExecutionResult.class), Arrays.asList(project))
addUnprocessedTile('test-merge-tile/kapt-tile.xml', 'kapt-tile')
addUnprocessedTile('test-merge-tile/kapt-dinject-tile.xml', 'kapt-dinject-tile')
addUnprocessedTile('test-merge-tile/kapt-javalin-tile.xml', 'kapt-javalin-tile')
// act
participant.loadAllDiscoveredTiles(session, project)
Model tileModel = participant.processedTiles['io.repaint.tiles:kapt-tile'].tileModel.model
PluginExecution pluginExecution = tileModel.build.plugins[0].executions[0]
assert pluginExecution.id == 'kapt'
// assert properties have been merged
assert tileModel.properties['dinject-generator.version'] == '1.8'
assert tileModel.properties['kotlin.version'] == '1.3.31'
String expectedAnnotationProcessorPaths = '''
<?xml version="1.0" encoding="UTF-8"?>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>io.dinject</groupId>
<artifactId>javalin-generator</artifactId>
<version>1.6</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>io.dinject</groupId>
<artifactId>dinject-generator</artifactId>
<version>${dinject-generator.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
'''.trim()
// assert the annotationProcessorPaths have been appended
Xpp3Dom paths = ((Xpp3Dom)pluginExecution.configuration).getChild('annotationProcessorPaths')
assertEquals(paths.toString().trim(), expectedAnnotationProcessorPaths)
}
def addUnprocessedTile(String testResourceName, String tileName) {
Artifact kaptTile = participant.turnPropertyIntoUnprocessedTile("io.repaint.tiles:$tileName:1.1", null)
kaptTile.file = new File("src/test/resources/$testResourceName")
participant.unprocessedTiles.put(artifactName(kaptTile), kaptTile)
}
@Test
public void testFiltering() {
final def context = new DefaultBuildContext()

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project>
<description>
Add KAPT annotation processor to generate DInject Dependency injection (as java source code).
</description>
<properties>
<dinject-generator.version>1.8</dinject-generator.version>
<tile-merge-source>true</tile-merge-source>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>kapt</id>
<configuration tiles-keep-id="true">
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>io.dinject</groupId>
<artifactId>dinject-generator</artifactId>
<version>${dinject-generator.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project>
<description>
Add KAPT annotation processor to generate javalin controllers (as java source code).
</description>
<properties>
<tile-merge-source>true</tile-merge-source>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>kapt</id>
<configuration tiles-keep-id="true">
<annotationProcessorPaths>
<annotationProcessorPath>
<!-- Generate web route adapters for Javalin Controllers -->
<groupId>io.dinject</groupId>
<artifactId>javalin-generator</artifactId>
<version>1.6</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<project>
<description>
Kotlin compiler with KAPT annotation processing support.
</description>
<properties>
<tile-merge-target>true</tile-merge-target>
<java.version>1.8</java.version>
<kotlin.version>1.3.31</kotlin.version>
<kotlin.apiVersion>1.3</kotlin.apiVersion>
<kotlin.jvmTarget>1.8</kotlin.jvmTarget>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<apiVersion>${kotlin.apiVersion}</apiVersion>
<jvmTarget>${kotlin.jvmTarget}</jvmTarget>
</configuration>
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
<configuration tiles-keep-id="true" tiles-append="annotationProcessorPaths">
<sourceDirs>
<sourceDir>src/main/kotlin</sourceDir>
<sourceDir>src/main/java</sourceDir>
</sourceDirs>
<annotationProcessorPaths>
<!-- annotation processors added here -->
</annotationProcessorPaths>
</configuration>
</execution>
<execution>
<configuration tiles-keep-id="true"/>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<configuration tiles-keep-id="true"/>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
<executions>
<!-- Replacing default-compile as it is treated specially by maven -->
<execution>
<id>default-compile</id>
<phase>none</phase>
<configuration tiles-keep-id="true"/>
</execution>
<!-- Replacing default-testCompile as it is treated specially by maven -->
<execution>
<id>default-testCompile</id>
<phase>none</phase>
<configuration tiles-keep-id="true"/>
</execution>
<execution>
<configuration tiles-keep-id="true"/>
<id>java-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<configuration tiles-keep-id="true"/>
<id>java-test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>io.repaint.tiles</groupId>
<artifactId>test-tile-merge</artifactId>
<version>1.1-SNAPSHOT</version>
<packaging>tile</packaging>
<build>
<plugins>
<plugin>
<groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId>
<version>1.1-SNAPSHOT</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>