/*- * Copyright (c) 2016 Red Hat, Inc. * * 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. */ package org.fedoraproject.xmvn.mojo; import java.io.File; import java.io.IOException; import java.lang.ProcessBuilder.Redirect; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Named; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; 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.project.MavenProject; import org.apache.maven.repository.RepositorySystem; import org.codehaus.plexus.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Mikolaj Izdebski */ @Mojo( name = "javadoc", aggregator = true, requiresDependencyResolution = ResolutionScope.COMPILE ) @Named public class JavadocMojo extends AbstractMojo { private final Logger logger = LoggerFactory.getLogger( JavadocMojo.class ); @Inject private RepositorySystem repositorySystem; @Parameter( defaultValue = "${reactorProjects}", readonly = true, required = true ) private List<MavenProject> reactorProjects; @Parameter( defaultValue = "${localRepository}", readonly = true, required = true ) private ArtifactRepository localRepository; @Parameter( defaultValue = "${project.build.sourceEncoding}" ) private String encoding; @Parameter( defaultValue = "${project.reporting.outputEncoding}" ) private String docencoding; @Parameter( defaultValue = "${project.build.directory}", required = true ) private File buildDirectory; private static String quoted( Object obj ) { String arg = obj.toString(); arg = StringUtils.replace( arg, "\\", "\\\\" ); arg = StringUtils.replace( arg, "'", "\\'" ); return "'" + arg + "'"; } private static void findJavaSources( Collection<Path> javaFiles, Path dir ) throws IOException { List<Path> paths = new ArrayList<>(); try (DirectoryStream<Path> stream = Files.newDirectoryStream( dir )) { for ( Path path : stream ) { paths.add( path ); } } for ( Path path : paths ) { if ( Files.isDirectory( path ) ) { findJavaSources( javaFiles, path ); } else if ( path.toString().endsWith( ".java" ) ) { javaFiles.add( path ); } } } private Path getOutputDir() { return buildDirectory.toPath().resolve( "apidocs" ); } private List<Path> getClasspath() throws MojoExecutionException { List<Artifact> reactorArtifacts = reactorProjects.stream() // .map( project -> project.getArtifact() ) // .collect( Collectors.toList() ); List<Path> classpath = new ArrayList<>(); classpath.addAll( reactorProjects.stream() // .map( project -> project.getBuild().getOutputDirectory() ) // .filter( StringUtils::isNotEmpty ) // .map( dir -> Paths.get( dir ) ) // .filter( path -> Files.isDirectory( path ) ) // .collect( Collectors.toSet() ) ); for ( MavenProject project : reactorProjects ) { Set<Artifact> dependencyArtifacts = project.getDependencies().stream() // .filter( dep -> Artifact.SCOPE_COMPILE.equals( dep.getScope() ) // || Artifact.SCOPE_PROVIDED.equals( dep.getScope() ) // || Artifact.SCOPE_SYSTEM.equals( dep.getScope() ) ) // .filter( dep -> !dep.isOptional() ) // .map( dep -> repositorySystem.createArtifactWithClassifier( dep.getGroupId(), // dep.getArtifactId(), // dep.getVersion(), // dep.getType(), // dep.getClassifier() ) ) // .filter( artifact -> !reactorArtifacts.contains( artifact ) ) // .collect( Collectors.toSet() ); if ( dependencyArtifacts.isEmpty() ) continue; ArtifactResolutionRequest request = new ArtifactResolutionRequest(); request.setArtifact( project.getArtifact() ); request.setResolveRoot( false ); request.setResolveTransitively( true ); request.setArtifactDependencies( dependencyArtifacts ); request.setManagedVersionMap( project.getManagedVersionMap() ); request.setLocalRepository( localRepository ); request.setRemoteRepositories( project.getRemoteArtifactRepositories() ); ArtifactResolutionResult result = repositorySystem.resolve( request ); if ( result.isSuccess() ) { classpath.addAll( result.getArtifacts().stream() // .map( artifact -> artifact.getFile().toPath() ) // .collect( Collectors.toList() ) ); } } return classpath; } @Override public void execute() throws MojoExecutionException, MojoFailureException { try { if ( StringUtils.isEmpty( encoding ) ) encoding = "UTF-8"; if ( StringUtils.isEmpty( docencoding ) ) docencoding = "UTF-8"; Set<Path> sourcePaths = Stream.concat( reactorProjects.stream(), // reactorProjects.stream().map( p -> p.getExecutionProject() ) ) // .filter( project -> project != null ) // .filter( project -> !project.getPackaging().equals( "pom" ) ) // .filter( project -> project.getArtifact().getArtifactHandler().getLanguage().equals( "java" ) ) // .filter( project -> project.getCompileSourceRoots() != null ) // .map( project -> project.getCompileSourceRoots().stream() // .filter( compileRoot -> compileRoot != null ) // .map( compileRoot -> Paths.get( compileRoot ) ) // .map( sourcePath -> sourcePath.isAbsolute() ? sourcePath : project.getBasedir().toPath().resolve( sourcePath ).toAbsolutePath() ) // .filter( sourcePath -> Files.isDirectory( sourcePath ) ) ) // .flatMap( x -> x ) // .collect( Collectors.toSet() ); Set<Path> files = new LinkedHashSet<>(); for ( Path sourcePath : sourcePaths ) findJavaSources( files, sourcePath ); if ( files.isEmpty() ) { logger.info( "Skipping Javadoc generation: no Java sources found" ); return; } Path outputDir = getOutputDir(); if ( !Files.isDirectory( outputDir ) ) Files.createDirectories( outputDir ); outputDir = outputDir.toRealPath(); List<String> opts = new ArrayList<>(); opts.add( "-private" ); opts.add( "-use" ); opts.add( "-version" ); opts.add( "-Xdoclint:none" ); opts.add( "-classpath" ); opts.add( quoted( StringUtils.join( getClasspath().iterator(), ":" ) ) ); opts.add( "-encoding" ); opts.add( quoted( encoding ) ); opts.add( "-sourcepath" ); opts.add( quoted( StringUtils.join( sourcePaths.iterator(), ":" ) ) ); opts.add( "-charset" ); opts.add( quoted( docencoding ) ); opts.add( "-d" ); opts.add( quoted( outputDir ) ); opts.add( "-docencoding" ); opts.add( quoted( docencoding ) ); opts.add( "-doctitle" ); opts.add( quoted( "Javadoc for package XXX" ) ); for ( Path file : files ) opts.add( quoted( file ) ); Files.write( outputDir.resolve( "args" ), opts, StandardOpenOption.CREATE ); Path javadocExecutable = Paths.get( System.getenv( "JAVA_HOME" ) ) // .resolve( "bin" ) // .resolve( "javadoc" ) // .toRealPath(); ProcessBuilder pb = new ProcessBuilder( javadocExecutable.toString(), "@args" ); pb.directory( outputDir.toFile() ); pb.redirectOutput( Redirect.INHERIT ); pb.redirectError( Redirect.INHERIT ); Process process = pb.start(); int exitCode = process.waitFor(); if ( exitCode != 0 ) { throw new MojoExecutionException( "Javadoc failed with exit code " + exitCode ); } } catch ( IOException | InterruptedException e ) { throw new MojoExecutionException( "Unable to execute javadoc command: " + e.getMessage(), e ); } } }