diff --git a/src/it/dependency-filesets-resources-test/invoker.properties b/src/it/dependency-filesets-resources-test/invoker.properties new file mode 100644 index 0000000..f7d698e --- /dev/null +++ b/src/it/dependency-filesets-resources-test/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +invoker.goals = clean generate-resources diff --git a/src/it/dependency-filesets-resources-test/pom.xml b/src/it/dependency-filesets-resources-test/pom.xml new file mode 100644 index 0000000..8c47b62 --- /dev/null +++ b/src/it/dependency-filesets-resources-test/pom.xml @@ -0,0 +1,123 @@ + + + + + + 4.0.0 + + org.apache.maven.plugins.antrun + dependency-filesets-resources-test + 1.0-SNAPSHOT + Test for the dependency resources reference of the dependencyfilesets task + Test for the dependency resources reference of the dependencyfilesets task + + + + + org.apache.maven.plugins + maven-antrun-plugin + @pom.version@ + + + antrun + generate-resources + + run + + + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-clean-plugin + @version.maven-clean-plugin@ + + + org.apache.maven.plugins + maven-site-plugin + @version.maven-site-plugin@ + + + + org.apache.maven.plugins + maven-resources-plugin + @version.maven-resources-plugin@ + + + org.apache.maven.plugins + maven-compiler-plugin + @version.maven-compiler-plugin@ + + + org.apache.maven.plugins + maven-surefire-plugin + @version.maven-surefire@ + + + org.apache.maven.plugins + maven-jar-plugin + @version.maven-jar-plugin@ + + + org.apache.maven.plugins + maven-install-plugin + @version.maven-install-plugin@ + + + org.apache.maven.plugins + maven-deploy-plugin + @version.maven-deploy-plugin@ + + + + + + + + org.apache.ant + ant + @version.ant@ + + + org.junit.jupiter + junit-jupiter-engine + @versions.junit5@ + + + + + diff --git a/src/it/dependency-filesets-resources-test/src/main/java/Test.java b/src/it/dependency-filesets-resources-test/src/main/java/Test.java new file mode 100644 index 0000000..7b5b646 --- /dev/null +++ b/src/it/dependency-filesets-resources-test/src/main/java/Test.java @@ -0,0 +1,29 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +public class Test +{ + + public static void main( String args[] ) + { + System.out.println( "done" ); + } + +} diff --git a/src/it/dependency-filesets-resources-test/verify.bsh b/src/it/dependency-filesets-resources-test/verify.bsh new file mode 100644 index 0000000..6d2a05f --- /dev/null +++ b/src/it/dependency-filesets-resources-test/verify.bsh @@ -0,0 +1,50 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import org.codehaus.plexus.util.*; + +try +{ + File depsDir = new File( basedir, "target/dependencies" ); + File antJar = new File( depsDir, "ant-" + scriptVarVersionAnt + ".jar" ); + File junitJar = new File( depsDir, "junit-jupiter-engine-" + scriptVarVersionJunit + ".jar" ); + + if ( ! antJar.exists() || antJar.isDirectory() ) + { + System.out.println( "Ant jar file does not exist: " + antJar ); + return false; + } + if ( ! junitJar.exists() || junitJar.isDirectory() ) + { + System.out.println( "Junit jar file does not exist: " + junitJar ); + return false; + } +} +catch( Throwable t ) +{ + t.printStackTrace(); + return false; +} + +return true; diff --git a/src/main/java/org/apache/maven/ant/tasks/DependencyFilesetsTask.java b/src/main/java/org/apache/maven/ant/tasks/DependencyFilesetsTask.java index 818f506..ef168c5 100644 --- a/src/main/java/org/apache/maven/ant/tasks/DependencyFilesetsTask.java +++ b/src/main/java/org/apache/maven/ant/tasks/DependencyFilesetsTask.java @@ -33,6 +33,7 @@ import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.resources.Union; /** * Ant task which create a fileset for each dependency in a Maven project, and a @@ -72,6 +73,9 @@ public void execute() { dependenciesFileSet.createExclude().setName("**"); } + Union dependenciesResourcesFileSet = new Union(); + dependenciesResourcesFileSet.setProject(getProject()); + for (Artifact artifact : depArtifacts) { String relativeArtifactPath = localRepository.pathOf(artifact); dependenciesFileSet.createInclude().setName(relativeArtifactPath); @@ -81,10 +85,12 @@ public void execute() { FileSet singleArtifactFileSet = new FileSet(); singleArtifactFileSet.setProject(getProject()); singleArtifactFileSet.setFile(artifact.getFile()); + dependenciesResourcesFileSet.add(singleArtifactFileSet); getProject().addReference(fileSetName, singleArtifactFileSet); } getProject().addReference((getPrefix() + getProjectDependenciesId()), dependenciesFileSet); + getProject().addReference((getPrefix() + getProjectDependenciesResourcesId()), dependenciesResourcesFileSet); } /** @@ -162,6 +168,21 @@ public void setProjectDependenciesId(String projectDependenciesId) { this.configuration.setProjectDependenciesId(projectDependenciesId); } + /** + * @return RefId for the resource collection containing all project dependencies - default + * maven.project.dependencies.resources + */ + public String getProjectDependenciesResourcesId() { + return this.configuration.getProjectDependenciesResourcesId(); + } + + /** + * @param projectDependenciesResourcesId RefId for the resource collection containing all project dependencies + */ + public void setProjectDependenciesResourcesId(String projectDependenciesResourcesId) { + this.configuration.setProjectDependenciesResourcesId(projectDependenciesResourcesId); + } + /** * Filter a set of artifacts using the scopes and type filters. * diff --git a/src/main/mdo/antrun.mdo b/src/main/mdo/antrun.mdo index 4eb29c9..8e1b3a3 100644 --- a/src/main/mdo/antrun.mdo +++ b/src/main/mdo/antrun.mdo @@ -88,6 +88,13 @@ under the License. maven.project.dependencies RefId for the fileset containing all project dependencies + + projectDependenciesResourcesId + String + maven.project.dependencies.resources + RefId for the resource collection containing all project dependencies, resolved from the actual + artifact files + scopes String diff --git a/src/test/java/org/apache/maven/ant/tasks/DependencyFilesetsTaskAggregateTest.java b/src/test/java/org/apache/maven/ant/tasks/DependencyFilesetsTaskAggregateTest.java new file mode 100644 index 0000000..44640d8 --- /dev/null +++ b/src/test/java/org/apache/maven/ant/tasks/DependencyFilesetsTaskAggregateTest.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.maven.ant.tasks; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.plugins.antrun.AntRunMojo; +import org.apache.maven.project.MavenProject; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Resource; +import org.apache.tools.ant.types.ResourceCollection; +import org.apache.tools.ant.types.resources.FileResource; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test class for the aggregate dependency references of {@link DependencyFilesetsTask}. + */ +class DependencyFilesetsTaskAggregateTest { + + private static final String LOCAL_REPOSITORY_REFID = "maven.local.repository"; + + private static final String LEGACY_DEPENDENCIES_REFID = "maven.project.dependencies"; + + private static final String DEPENDENCIES_RESOURCES_REFID = "maven.project.dependencies.resources"; + + @TempDir + Path folder; + + /** + * The {@code maven.project.dependencies.resources} resource collection must be assembled from the actual resolved + * artifact files, so that artifacts located outside the local repository (e.g. reactor inter-module dependencies) + * are not silently omitted. + * + * @throws IOException In case of problems + */ + @Test + void resourcesReferenceIncludesArtifactsOutsideLocalRepository() throws IOException { + Artifact localArtifact = newArtifact("org.example", "artX"); + Artifact reactorArtifact = newArtifact("com.example", "artY"); + DependencyFilesetsTask task = executeTask(localArtifact, reactorArtifact); + + task.execute(); + + ResourceCollection resources = + (ResourceCollection) task.getProject().getReference(DEPENDENCIES_RESOURCES_REFID); + Set files = filesOf(resources); + assertThat(files, hasItem(localArtifact.getFile())); + assertThat(files, hasItem(reactorArtifact.getFile())); + } + + /** + * The legacy {@code maven.project.dependencies} reference is preserved as a fileset rooted at the local + * repository, so it only contains artifacts physically present there. + * + * @throws IOException In case of problems + */ + @Test + void legacyFilesetReferenceRemainsRootedAtLocalRepository() throws IOException { + Artifact localArtifact = newArtifact("org.example", "artX"); + Artifact reactorArtifact = newArtifact("com.example", "artY"); + DependencyFilesetsTask task = executeTask(localArtifact, reactorArtifact); + + task.execute(); + + Object reference = task.getProject().getReference(LEGACY_DEPENDENCIES_REFID); + assertTrue(reference instanceof FileSet); + Set files = filesOf((ResourceCollection) reference); + assertThat(files, hasItem(localArtifact.getFile())); + assertThat(files, not(hasItem(reactorArtifact.getFile()))); + } + + private DependencyFilesetsTask executeTask(Artifact localArtifact, Artifact reactorArtifact) throws IOException { + Path localRepositoryDir = Files.createDirectories(folder.resolve("local-repository")); + Path reactorDirectory = Files.createDirectories(folder.resolve("reactor-module")); + + File localArtifactFile = + localRepositoryDir.resolve(artifactPath(localArtifact)).toFile(); + Files.createDirectories(localArtifactFile.getParentFile().toPath()); + Files.write(localArtifactFile.toPath(), new byte[0]); + localArtifact.setFile(localArtifactFile); + + File reactorArtifactFile = reactorDirectory.resolve("artY-1.0.jar").toFile(); + Files.write(reactorArtifactFile.toPath(), new byte[0]); + reactorArtifact.setFile(reactorArtifactFile); + + MavenProject mavenProject = new MavenProject(); + Set artifacts = new HashSet<>(); + artifacts.add(localArtifact); + artifacts.add(reactorArtifact); + mavenProject.setArtifacts(artifacts); + + Project antProject = new Project(); + antProject.addReference(AntRunMojo.DEFAULT_MAVEN_PROJECT_REFID, mavenProject); + antProject.addReference(LOCAL_REPOSITORY_REFID, newRepositoryStub(localRepositoryDir)); + + DependencyFilesetsTask task = new DependencyFilesetsTask(); + task.setProject(antProject); + return task; + } + + private Set filesOf(ResourceCollection resources) { + Set files = new HashSet<>(); + for (Resource resource : resources) { + files.add(((FileResource) resource).getFile()); + } + return files; + } + + private Artifact newArtifact(String groupId, String artifactId) throws IOException { + Artifact artifact = new DefaultArtifact( + groupId, artifactId, "1.0", Artifact.SCOPE_COMPILE, "jar", null, new DefaultArtifactHandler("jar")); + artifact.setFile(Files.createTempFile(folder, artifactId, ".jar").toFile()); + return artifact; + } + + private ArtifactRepository newRepositoryStub(Path basedir) { + InvocationHandler handler = (proxy, method, args) -> { + switch (method.getName()) { + case "pathOf": + return artifactPath((Artifact) args[0]); + case "getBasedir": + return basedir.toString(); + default: + return null; + } + }; + return (ArtifactRepository) + Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {ArtifactRepository.class}, handler); + } + + private static String artifactPath(Artifact artifact) { + return artifact.getGroupId() + "/" + artifact.getArtifactId() + "/" + artifact.getVersion() + "/" + + artifact.getArtifactId() + "-" + artifact.getVersion() + "." + artifact.getType(); + } +}