diff --git a/src/it/projects/copy-using-symlink/invoker.properties b/src/it/projects/copy-using-symlink/invoker.properties new file mode 100644 index 0000000000..6592030fb5 --- /dev/null +++ b/src/it/projects/copy-using-symlink/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 process-sources -Dmdep.link=true diff --git a/src/it/projects/copy-using-symlink/pom.xml b/src/it/projects/copy-using-symlink/pom.xml new file mode 100644 index 0000000000..d683399b4d --- /dev/null +++ b/src/it/projects/copy-using-symlink/pom.xml @@ -0,0 +1,64 @@ + + + + + 4.0.0 + + org.apache.maven.its.dependency + test + 1.0-SNAPSHOT + + Test + + Test dependency:copy -Dmdep.link=true + + + + UTF-8 + + + + + + maven-dependency-plugin + @project.version@ + + + test + + copy + + + + + org.apache.maven + maven-model + 2.0.6 + + + + + + + + + diff --git a/src/it/projects/copy-using-symlink/verify.bsh b/src/it/projects/copy-using-symlink/verify.bsh new file mode 100644 index 0000000000..0cd89dcd9f --- /dev/null +++ b/src/it/projects/copy-using-symlink/verify.bsh @@ -0,0 +1,38 @@ +/* + * 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.nio.file.*; + +Path libDir = basedir.toPath().resolve( "target/dependency" ); + +String[] expectedFiles = { + "maven-model-2.0.6.jar", +}; + +for ( String expectedFile : expectedFiles ) +{ + Path path = libDir.resolve( expectedFile ); + System.out.println( "Checking for existence of link " + path ); + if ( !Files.isSymbolicLink( path ) ) + { + throw new Exception( "Missing symlink " + path ); + } +} + +return true; diff --git a/src/main/java/org/apache/maven/plugins/dependency/AbstractDependencyMojo.java b/src/main/java/org/apache/maven/plugins/dependency/AbstractDependencyMojo.java index 07df9f631a..ec79e953aa 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/AbstractDependencyMojo.java +++ b/src/main/java/org/apache/maven/plugins/dependency/AbstractDependencyMojo.java @@ -44,6 +44,7 @@ import org.codehaus.plexus.components.io.filemappers.FileMapper; import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector; import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.NioFiles; import org.codehaus.plexus.util.ReflectionUtils; import org.codehaus.plexus.util.StringUtils; @@ -196,6 +197,38 @@ protected void copyFile( File artifact, File destFile ) } } + /** + * Does the actual link of the file and logging. + * + * @param artifact represents the file to link to. + * @param destFile file name of destination link. + * @throws MojoExecutionException with a message if an error occurs. + */ + protected void linkFile( File artifact, File destFile ) + throws MojoExecutionException + { + try + { + getLog().info( "Linking " + + destFile + " to " + + ( this.outputAbsoluteArtifactFilename ? artifact.getAbsolutePath() : artifact.getName() ) ); + + if ( artifact.isDirectory() ) + { + // usual case is a future jar packaging, but there are special cases: classifier and other packaging + throw new MojoExecutionException( "Artifact has not been packaged yet. When used on reactor artifact, " + + "copy should be executed after packaging: see MDEP-187." ); + } + + // TODO Replace with FileUtils.linkFile(artifact, destFile); once https://github.com/codehaus-plexus/plexus-utils/pull/82 is merged + NioFiles.createSymbolicLink( destFile, artifact ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error linking " + destFile + " to artifact " + artifact, e ); + } + } + /** * @param artifact {@link Artifact} * @param location The location. diff --git a/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/CopyMojo.java b/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/CopyMojo.java index a2f2d359d9..32065f16d1 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/CopyMojo.java +++ b/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/CopyMojo.java @@ -40,6 +40,13 @@ public class CopyMojo extends AbstractFromConfigurationMojo { + /** + * Link instead of copy + + * @since 3.1.3 + */ + @Parameter( property = "mdep.link", defaultValue = "false" ) + private boolean link = false; /** * Strip artifact version during copy @@ -132,7 +139,14 @@ protected void copyArtifact( ArtifactItem artifactItem ) { File destFile = new File( artifactItem.getOutputDirectory(), artifactItem.getDestFileName() ); - copyFile( artifactItem.getArtifact().getFile(), destFile ); + if ( this.isLink() ) + { + linkFile( artifactItem.getArtifact().getFile(), destFile ); + } + else + { + copyFile( artifactItem.getArtifact().getFile(), destFile ); + } } @Override @@ -145,6 +159,22 @@ protected ArtifactItemFilter getMarkedArtifactFilter( ArtifactItem item ) return destinationNameOverrideFilter; } + /** + * @return Returns whether to link instead of copy + */ + public boolean isLink() + { + return this.link; + } + + /** + * @param link Whether to link instead of copy. + */ + public void setLink( boolean link ) + { + this.link = link; + } + /** * @return Returns the stripVersion. */ diff --git a/src/test/java/org/apache/maven/plugins/dependency/fromConfiguration/TestCopyMojo.java b/src/test/java/org/apache/maven/plugins/dependency/fromConfiguration/TestCopyMojo.java index 52fa4e9c23..6dd2884234 100644 --- a/src/test/java/org/apache/maven/plugins/dependency/fromConfiguration/TestCopyMojo.java +++ b/src/test/java/org/apache/maven/plugins/dependency/fromConfiguration/TestCopyMojo.java @@ -19,8 +19,13 @@ * under the License. */ +import static org.junit.Assume.assumeTrue; + import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.FileSystemException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -34,13 +39,19 @@ import org.apache.maven.plugins.dependency.AbstractDependencyMojoTestCase; import org.apache.maven.plugins.dependency.utils.DependencyUtil; import org.apache.maven.project.MavenProject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; +@RunWith(BlockJUnit4ClassRunner.class) public class TestCopyMojo extends AbstractDependencyMojoTestCase { private CopyMojo mojo; - protected void setUp() + @Before + public void setUp() throws Exception { super.setUp( "copy", false, false ); @@ -82,6 +93,7 @@ public void testSetArtifactWithoutPackaging() assertNull( item.getClassifier() ); } + @Test public void testSetArtifactWithoutClassifier() throws Exception { @@ -94,6 +106,7 @@ public void testSetArtifactWithoutClassifier() assertNull( item.getClassifier() ); } + @Test public void testSetArtifact() throws Exception { @@ -106,6 +119,7 @@ public void testSetArtifact() assertEquals( "e", item.getClassifier() ); } + @Test public void testGetArtifactItems() throws Exception { @@ -144,6 +158,42 @@ public void assertFileExists( ArtifactItem item, boolean exist ) assertEquals( exist, file.exists() ); } + private static final boolean supportsSymbolicLinks = supportsSymbolicLinks(); + + private static boolean supportsSymbolicLinks( ) + { + try { + Path target = Files.createTempFile( null, null ); + Path link = Files.createTempFile( null, null ); + Files.delete( link ); + try { + Files.createSymbolicLink( link, target ); + } catch ( FileSystemException e ) { + return false; + } + Files.delete( link ); + Files.delete( target ); + return true; + } catch ( IOException e ) { + throw new RuntimeException( e ); + } + } + + public void assertFilesAreLinks( Collection items, boolean areLinks ) + { + for ( ArtifactItem item : items ) + { + assertFileIsLink( item, areLinks ); + } + } + + public void assertFileIsLink( ArtifactItem item, boolean isLink ) + { + Path path = item.getOutputDirectory().toPath().resolve( item.getDestFileName() ); + assertEquals( isLink, Files.isSymbolicLink( path ) ); + } + + @Test public void testMojoDefaults() { CopyMojo themojo = new CopyMojo(); @@ -153,6 +203,7 @@ public void testMojoDefaults() assertFalse( themojo.isStripClassifier() ); } + @Test public void testCopyFile() throws Exception { @@ -165,6 +216,7 @@ public void testCopyFile() assertFilesExist( list, true ); } + @Test public void testCopyFileWithBaseVersion() throws Exception { @@ -184,6 +236,7 @@ public void testCopyFileWithBaseVersion() assertFilesExist( list, true ); } + @Test public void testSkip() throws Exception { @@ -202,6 +255,7 @@ public void testSkip() } + @Test public void testCopyFileNoOverwrite() throws Exception { @@ -219,6 +273,7 @@ public void testCopyFileNoOverwrite() assertFilesExist( list, true ); } + @Test public void testCopyToLocation() throws Exception { @@ -233,6 +288,24 @@ public void testCopyToLocation() assertFilesExist( list, true ); } + @Test + public void testLink() + throws Exception + { + assumeTrue("supports symbolic links", supportsSymbolicLinks); + + List list = stubFactory.getArtifactItems( stubFactory.getClassifiedArtifacts() ); + + mojo.setArtifactItems( createArtifactItemArtifacts( list ) ); + mojo.setLink( true ); + + mojo.execute(); + + assertFilesExist( list, true ); + assertFilesAreLinks( list, true ); + } + + @Test public void testCopyStripVersionSetInMojo() throws Exception { @@ -250,6 +323,7 @@ public void testCopyStripVersionSetInMojo() assertFilesExist( list, true ); } + @Test public void testCopyStripClassifierSetInMojo() throws Exception { @@ -268,6 +342,7 @@ public void testCopyStripClassifierSetInMojo() assertFilesExist( list, true ); } + @Test public void testNonClassifierStrip() throws Exception { @@ -280,6 +355,7 @@ public void testNonClassifierStrip() assertFilesExist( list, true ); } + @Test public void testNonClassifierNoStrip() throws Exception { @@ -292,6 +368,7 @@ public void testNonClassifierNoStrip() assertFilesExist( list, true ); } + @Test public void testMissingVersionNotFound() throws Exception { @@ -340,6 +417,7 @@ public List getDependencyList( ArtifactItem item ) return list; } + @Test public void testMissingVersionFromDependencies() throws Exception { @@ -362,6 +440,7 @@ public void testMissingVersionFromDependencies() assertEquals( "2.0-SNAPSHOT", item.getVersion() ); } + @Test public void testMissingVersionFromDependenciesLooseMatch() throws Exception { @@ -393,6 +472,7 @@ public void testMissingVersionFromDependenciesLooseMatch() assertEquals( "2.1", item.getVersion() ); } + @Test public void testMissingVersionFromDependenciesWithClassifier() throws Exception { @@ -438,6 +518,7 @@ public List getDependencyMgtList( ArtifactItem item ) return list; } + @Test public void testMissingVersionFromDependencyMgt() throws Exception { @@ -471,6 +552,7 @@ public void testMissingVersionFromDependencyMgt() assertEquals( "3.0-SNAPSHOT", item.getVersion() ); } + @Test public void testMissingVersionFromDependencyMgtLooseMatch() throws Exception { @@ -511,6 +593,7 @@ public void testMissingVersionFromDependencyMgtLooseMatch() assertEquals( "3.1", item.getVersion() ); } + @Test public void testMissingVersionFromDependencyMgtWithClassifier() throws Exception { @@ -544,12 +627,14 @@ public void testMissingVersionFromDependencyMgtWithClassifier() assertEquals( "3.1", item.getVersion() ); } + @Test public void testArtifactNotFound() throws Exception { dotestArtifactExceptions( false, true ); } + @Test public void testArtifactResolutionException() throws Exception { @@ -582,6 +667,7 @@ public void dotestArtifactExceptions( boolean are, boolean anfe ) } } + @Test public void testNoArtifactItems() { try @@ -596,6 +682,7 @@ public void testNoArtifactItems() } + @Test public void testCopyDontOverWriteReleases() throws Exception { @@ -627,6 +714,7 @@ public void testCopyDontOverWriteReleases() assertEquals( time, copiedFile.lastModified() ); } + @Test public void testCopyDontOverWriteSnapshots() throws Exception { @@ -658,6 +746,7 @@ public void testCopyDontOverWriteSnapshots() assertEquals( time, copiedFile.lastModified() ); } + @Test public void testCopyOverWriteReleases() throws Exception { @@ -687,6 +776,7 @@ public void testCopyOverWriteReleases() assertEquals( 1000L, timeCopyNow ); } + @Test public void testCopyOverWriteSnapshot() throws Exception { @@ -717,6 +807,7 @@ public void testCopyOverWriteSnapshot() assertEquals( 1000L, timeCopyNow ); } + @Test public void testCopyOverWriteIfNewer() throws Exception { @@ -746,6 +837,7 @@ public void testCopyOverWriteIfNewer() assertTrue( time < copiedFile.lastModified() ); } + @Test public void testCopyFileWithOverideLocalRepo() throws Exception {