This is an automated email from the ASF dual-hosted git repository. slachiewicz pushed a commit to branch MDEP-648 in repository https://gitbox.apache.org/repos/asf/maven-dependency-plugin.git
commit 2220ad90dc89cdb19d95d9cf5abb1ebf851935f6 Author: Pim Moerenhout <[email protected]> AuthorDate: Tue May 5 23:49:04 2020 +0200 MDEP-648: Add location of listed repository. --- pom.xml | 9 + .../invoker.properties | 37 +-- src/it/projects/list-repositories-verbose/pom.xml | 47 +++ .../list-repositories-verbose/verify.groovy | 26 ++ .../projects/list-repositories/invoker.properties | 1 + src/it/projects/list-repositories/verify.groovy | 26 ++ .../dependency/resolvers/ListRepositoriesMojo.java | 327 ++++++++++++++++++++- src/site/apt/usage.apt.vm | 11 + 8 files changed, 457 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 4687c93..929c38a 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,9 @@ under the License. <contributor> <name>Maarten Mulders</name> </contributor> + <contributor> + <name>Pim Moerenhout</name> + </contributor> </contributors> <properties> @@ -340,6 +343,12 @@ under the License. </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-http-lightweight</artifactId> + <version>3.4.0</version> + <scope>provided</scope> + </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> diff --git a/src/it/projects/list-repositories/invoker.properties b/src/it/projects/list-repositories-verbose/invoker.properties similarity index 97% copy from src/it/projects/list-repositories/invoker.properties copy to src/it/projects/list-repositories-verbose/invoker.properties index 6973bb1..48fda22 100644 --- a/src/it/projects/list-repositories/invoker.properties +++ b/src/it/projects/list-repositories-verbose/invoker.properties @@ -1,18 +1,19 @@ -# 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 = ${project.groupId}:${project.artifactId}:${project.version}:list-repositories +# 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 = ${project.groupId}:${project.artifactId}:${project.version}:list-repositories +invoker.debug = true \ No newline at end of file diff --git a/src/it/projects/list-repositories-verbose/pom.xml b/src/it/projects/list-repositories-verbose/pom.xml new file mode 100644 index 0000000..9082485 --- /dev/null +++ b/src/it/projects/list-repositories-verbose/pom.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + --> + +<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>org.apache.maven.its.dependency</groupId> + <artifactId>test</artifactId> + <version>1.0-SNAPSHOT</version> + + <name>Test</name> + <description> + Test dependency:list-repositories + </description> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-project</artifactId> + <version>2.0.6</version> + </dependency> + </dependencies> + +</project> diff --git a/src/it/projects/list-repositories-verbose/verify.groovy b/src/it/projects/list-repositories-verbose/verify.groovy new file mode 100644 index 0000000..68bdb6a --- /dev/null +++ b/src/it/projects/list-repositories-verbose/verify.groovy @@ -0,0 +1,26 @@ +/* + * 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. + */ + +File file = new File( basedir, "build.log" ); +assert file.exists(); + +String buildLog = file.getText( "UTF-8" ); +assert buildLog.contains( 'location: Maven settings (user/global)' ); + +return true; diff --git a/src/it/projects/list-repositories/invoker.properties b/src/it/projects/list-repositories/invoker.properties index 6973bb1..e79fcff 100644 --- a/src/it/projects/list-repositories/invoker.properties +++ b/src/it/projects/list-repositories/invoker.properties @@ -16,3 +16,4 @@ # under the License. invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:list-repositories +invoker.debug = false \ No newline at end of file diff --git a/src/it/projects/list-repositories/verify.groovy b/src/it/projects/list-repositories/verify.groovy new file mode 100644 index 0000000..cb10938 --- /dev/null +++ b/src/it/projects/list-repositories/verify.groovy @@ -0,0 +1,26 @@ +/* + * 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. + */ + +File file = new File( basedir, "build.log" ); +assert file.exists(); + +String buildLog = file.getText( "UTF-8" ); +assert !buildLog.contains( 'location:' ); + +return true; diff --git a/src/main/java/org/apache/maven/plugins/dependency/resolvers/ListRepositoriesMojo.java b/src/main/java/org/apache/maven/plugins/dependency/resolvers/ListRepositoriesMojo.java index deed8d3..f2fb18f 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/resolvers/ListRepositoriesMojo.java +++ b/src/main/java/org/apache/maven/plugins/dependency/resolvers/ListRepositoriesMojo.java @@ -19,15 +19,36 @@ package org.apache.maven.plugins.dependency.resolvers; * under the License. */ +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.model.Model; +import org.apache.maven.model.Repository; +import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.plugins.dependency.AbstractDependencyMojo; -import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult; -import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollector; -import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollectorException; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Settings; +import org.apache.maven.shared.transfer.artifact.ArtifactCoordinate; +import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver; +import org.apache.maven.shared.transfer.collection.CollectResult; +import org.apache.maven.shared.transfer.collection.DependencyCollectionException; +import org.apache.maven.shared.transfer.collection.DependencyCollector; +import org.apache.maven.shared.transfer.graph.DependencyNode; +import org.apache.maven.shared.transfer.graph.DependencyVisitor; /** * Goal that resolves all project dependencies and then lists the repositories used by the build and by the transitive @@ -41,12 +62,46 @@ public class ListRepositoriesMojo extends AbstractDependencyMojo { /** + * Maven Project Builder component. + */ + @Component + private ProjectBuilder projectBuilder; + + /** * Dependency collector, needed to resolve dependencies. */ @Component( role = DependencyCollector.class ) private DependencyCollector dependencyCollector; /** + * Component used to resolve artifacts and download their files from remote repositories. + */ + @Component + private ArtifactResolver artifactResolver; + + /** + * The system settings for Maven. This is the instance resulting from + * merging global and user-level settings files. + */ + @Parameter( defaultValue = "${settings}", readonly = true, required = true ) + private Settings settings; + + /** + * Remote repositories used for the project. + */ + @Parameter( defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true ) + private List<ArtifactRepository> remoteRepositories; + + /** + * Sets whether the plugin runs in verbose mode. As of plugin version 2.3, the default value is derived from Maven's + * global debug flag (compare command line switch <code>-X</code>). <br/> + * + * @since 3.1.2 + */ + @Parameter( property = "verbose" ) + private boolean verbose; + + /** * Displays a list of the repositories used by this build. * * @throws MojoExecutionException with a message if an error occurs. @@ -55,22 +110,276 @@ public class ListRepositoriesMojo protected void doExecute() throws MojoExecutionException { + + for ( ArtifactRepository artifactRepository : remoteRepositories ) + { + verbose( "Maven remote repositories: " + repositoryAsString( artifactRepository ) ); + } + + final Set<ArtifactRepository> repositories = new HashSet<>(); + final Set<Artifact> artifacts = new HashSet<>(); + + DependencyVisitor visitor = new DependencyVisitor() + { + @Override + public boolean visitEnter( DependencyNode dependencyNode ) + { + repositories.addAll( dependencyNode.getRemoteRepositories() ); + artifacts.add( dependencyNode.getArtifact() ); + return true; + } + + @Override + public boolean visitLeave( DependencyNode dependencyNode ) + { + return true; + } + }; + try { - CollectorResult collectResult = - dependencyCollector.collectDependencies( session.getProjectBuildingRequest(), getProject().getModel() ); + ProjectBuildingRequest projectBuildingRequest = session.getProjectBuildingRequest(); + + CollectResult collectResult = + dependencyCollector.collectDependencies( projectBuildingRequest, getProject().getModel() ); + + for ( Exception e : collectResult.getExceptions() ) + { + throw new MojoExecutionException( "Collect dependencies failed", e ); + } + + collectResult.getRoot().accept( visitor ); + + verbose( "Artifacts used by the build of " + collectResult.getRoot().getArtifact() + ":" ); + for ( Artifact artifact : artifacts ) + { + verbose( " " + artifact.toString() ); + } this.getLog().info( "Repositories used by this build:" ); + for ( ArtifactRepository repo : repositories ) + { + if ( isVerbose() ) + { + Set<String> locations = new HashSet<String>(); + for ( Mirror mirror : settings.getMirrors() ) + { + if ( mirror.getId().equals( repo.getId() ) + && ( mirror.getUrl().equals( repo.getUrl() ) ) ) + { + locations.add( "Maven settings (user/global)" ); + } + } + + Artifact projectArtifact = getProject().getArtifact(); + MavenProject project = getMavenProject( ArtifactUtils.key( projectArtifact ) ); + traversePom( repo, projectArtifact, project, locations ); + + for ( Artifact artifact : artifacts ) + { + MavenProject artifactProject = getMavenProject( ArtifactUtils.key( artifact ) ); + traversePom( repo, artifact, artifactProject, locations ); + } + writeRepository( repo, locations ); + } + else + { + this.getLog().info( repo.toString() ); + } + } + } + catch ( DependencyCollectionException e ) + { + throw new MojoExecutionException( "Unable to collect", e ); + } + } + + private void writeRepository( ArtifactRepository artifactRepository, Set<String> locations ) + { + StringBuilder sb = new StringBuilder( 256 ); + sb.append( artifactRepository.toString() ); + for ( String location : locations ) + { + sb.append( " location: " ).append( location ).append( System.lineSeparator() ); + } + this.getLog().info( sb.toString() ); + } + + /** + * Parses the given String into GAV artifact coordinate information, adding the given type. + * + * @param artifactString should respect the format <code>groupId:artifactId[:version]</code> + * @param type The extension for the artifact, must not be <code>null</code>. + * @return the <code>Artifact</code> object for the <code>artifactString</code> parameter. + * @throws MojoExecutionException if the <code>artifactString</code> doesn't respect the format. + */ + private ArtifactCoordinate getArtifactCoordinate( String artifactString, String type ) + throws MojoExecutionException + { + if ( org.codehaus.plexus.util.StringUtils.isEmpty( artifactString ) ) + { + throw new IllegalArgumentException( "artifact parameter could not be empty" ); + } + + String groupId; // required + String artifactId; // required + String version; // optional + + String[] artifactParts = artifactString.split( ":" ); + switch ( artifactParts.length ) + { + case 2: + groupId = artifactParts[0]; + artifactId = artifactParts[1]; + version = Artifact.LATEST_VERSION; + break; + case 3: + groupId = artifactParts[0]; + artifactId = artifactParts[1]; + version = artifactParts[2]; + break; + default: + throw new MojoExecutionException( "The artifact parameter '" + artifactString + + "' should be conform to: " + "'groupId:artifactId[:version]'." ); + } + return getArtifactCoordinate( groupId, artifactId, version, type ); + } + + private ArtifactCoordinate getArtifactCoordinate( String groupId, String artifactId, String version, String type ) + { + DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate(); + coordinate.setGroupId( groupId ); + coordinate.setArtifactId( artifactId ); + coordinate.setVersion( version ); + coordinate.setExtension( type ); + return coordinate; + } + + /** + * Retrieves the Maven Project associated with the given artifact String, in the form of + * <code>groupId:artifactId[:version]</code>. This resolves the POM artifact at those coordinates and then builds + * the Maven project from it. + * + * @param artifactString Coordinates of the Maven project to get. + * @return New Maven project. + * @throws MojoExecutionException If there was an error while getting the Maven project. + */ + private MavenProject getMavenProject( String artifactString ) + throws MojoExecutionException + { + ArtifactCoordinate coordinate = getArtifactCoordinate( artifactString, "pom" ); + try + { + ProjectBuildingRequest pbr = new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() ); + pbr.setRemoteRepositories( remoteRepositories ); + pbr.setProject( null ); + pbr.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL ); + pbr.setResolveDependencies( false ); + Artifact artifact = artifactResolver.resolveArtifact( pbr, coordinate ).getArtifact(); + return projectBuilder.build( artifact.getFile(), pbr ).getProject(); + } + catch ( Exception e ) + { + throw new MojoExecutionException( "Unable to get the POM for the artifact '" + artifactString + + "'. Verify the artifact parameter.", e ); + } + } + + private void traversePom( ArtifactRepository artifactRepository, + Artifact artifact, MavenProject mavenProject, Set<String> locations ) + throws MojoExecutionException + { + getLog().debug( "Looking for locations of repository " + repositoryAsString( artifactRepository ) + + " for " + artifact ); + if ( mavenProject != null ) + { + for ( Repository repository : mavenProject.getOriginalModel().getRepositories() ) + { + getLog().debug( "Found repository: " + repositoryAsString( repository ) + + " @ " + artifact + ":" + mavenProject.getOriginalModel().getPomFile() ); + if ( isRepositoryEqual( repository, artifactRepository ) ) + { + locations.add( mavenProject.getModel().getPomFile().toString() ); + } + } + + traverseParentPom( artifactRepository, mavenProject, locations ); + } + else + { + throw new MojoExecutionException( "No POM for the artifact '" + artifact + "'" ); + } + return; + } - for ( ArtifactRepository repo : collectResult.getRemoteRepositories() ) + private void traverseParentPom( ArtifactRepository artifactRepository, + MavenProject mavenProject, Set<String> locations ) + throws MojoExecutionException + { + MavenProject parent = mavenProject.getParent(); + if ( parent != null ) + { + Model originalModel = parent.getOriginalModel(); + if ( originalModel.getRepositories().size() != 0 + || originalModel.getPluginRepositories().size() != 0 ) { - this.getLog().info( repo.toString() ); + String artifactKey = + ArtifactUtils.key( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() ); + MavenProject parentPom = getMavenProject( artifactKey ); + + for ( Repository repository : originalModel.getRepositories() ) + { + getLog().debug( "Found parent repository " + repositoryAsString( repository ) + + " @ " + parentPom.getArtifact() + ":" + parentPom.getFile() ); + if ( isRepositoryEqual( repository, artifactRepository ) ) + { + locations.add( parentPom.getFile().toString() ); + } + } } + traverseParentPom( artifactRepository, parent, locations ); } - catch ( DependencyCollectorException e ) + return; + } + + private String repositoryAsString( Repository repository ) + { + StringBuilder sb = new StringBuilder( 32 ); + sb.append( repository.getId() ); + sb.append( " (" ); + sb.append( repository.getUrl() ); + sb.append( ")" ); + return sb.toString(); + } + + private String repositoryAsString( ArtifactRepository repository ) + { + StringBuilder sb = new StringBuilder( 32 ); + sb.append( repository.getId() ); + sb.append( " (" ); + sb.append( repository.getUrl() ); + sb.append( ")" ); + return sb.toString(); + } + + private boolean isVerbose() + { + return ( verbose || getLog().isDebugEnabled() ); + } + + private void verbose( String message ) + { + if ( isVerbose() ) { - throw new MojoExecutionException( "Unable to resolve artifacts", e ); + getLog().info( message ); } } + private boolean isRepositoryEqual( Repository repository, ArtifactRepository artifactRepository ) + { + // TODO: Use org.apache.maven.RepositoryUtils in Maven or check also snapshots, etc. + return repository.getId().equals( artifactRepository.getId() ) + && repository.getUrl().equals( artifactRepository.getUrl() ); + } + } diff --git a/src/site/apt/usage.apt.vm b/src/site/apt/usage.apt.vm index a67381c..9f912dc 100644 --- a/src/site/apt/usage.apt.vm +++ b/src/site/apt/usage.apt.vm @@ -685,6 +685,17 @@ mvn dependency:build-classpath -Dmdep.outputFile=cp.txt This goal is used to list all the repositories that this build depends upon. It will show repositories defined in your settings, poms and declared in transitive dependency poms. + This goal can be executed from the command line: + ++-----+ +mvn dependency:list-repositories ++-----+ + + Optionally, in verbose or debug mode it will display the location of the listed repository: + ++-----+ +mvn dependency:list-repositories -Dverbose ++-----+ * <<<dependency:get>>>
