
com.google.cloud.tools.opensource.classpath.LinkageCheckerArguments Maven / Gradle / Ivy
/*
* Copyright 2018 Google LLC.
*
* 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 com.google.cloud.tools.opensource.classpath;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.cloud.tools.opensource.dependencies.RepositoryUtility;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.eclipse.aether.RepositoryException;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
/**
* Option for {@link LinkageChecker}. To construct an input class path, the checker requires
* exactly one of the following types of input:
*
*
* - {@code bom}: a Maven BOM specified by its Maven coordinates
*
- {@code artifacts}: list of Maven artifacts specified by their Maven coordinates
*
- {@code jarFiles}: list of jar files in the filesystem
*
*
* @see
* Linkage Checker: Input
*/
final class LinkageCheckerArguments {
private static final Options options = configureOptions();
private static final HelpFormatter helpFormatter = new HelpFormatter();
private final CommandLine commandLine;
private ImmutableList cachedInputClasspath;
private ImmutableList cachedArtifacts;
private final ImmutableList extraMavenRepositoryUrls;
private final boolean addMavenCentral;
private LinkageCheckerArguments(CommandLine commandLine) {
this.commandLine = checkNotNull(commandLine);
this.extraMavenRepositoryUrls =
commandLine.hasOption("m")
? ImmutableList.copyOf(commandLine.getOptionValues("m"))
: ImmutableList.of();
// this may throw IllegalArgumentException upon validating the syntax
extraMavenRepositoryUrls.forEach(RepositoryUtility::mavenRepositoryFromUrl);
this.addMavenCentral = !commandLine.hasOption("nm");
}
static LinkageCheckerArguments readCommandLine(String... arguments) throws ParseException {
// TODO is this reentrant? Can we reuse it?
// https://issues.apache.org/jira/browse/CLI-291
CommandLineParser parser = new DefaultParser();
try {
return new LinkageCheckerArguments(parser.parse(options, arguments));
} catch (IllegalArgumentException ex) {
throw new ParseException("Invalid URL syntax in Maven repository URL");
} catch (ParseException ex) {
helpFormatter.printHelp("LinkageChecker", options);
throw ex;
}
}
private static Options configureOptions() {
Options options = new Options();
OptionGroup inputGroup = new OptionGroup();
inputGroup.setRequired(true);
Option bomOption =
Option.builder("b")
.longOpt("bom")
.hasArg()
.desc("BOM to generate a class path, specified by its Maven coordinates")
.build();
inputGroup.addOption(bomOption);
Option artifactOption =
Option.builder("a")
.longOpt("artifacts")
.hasArgs()
.valueSeparator(',')
.desc(
"Maven coordinates for Maven artifacts (separated by ',') to generate a class path")
.build();
inputGroup.addOption(artifactOption);
Option jarOption =
Option.builder("j")
.longOpt("jars")
.hasArgs()
.valueSeparator(',')
.desc("Jar files (separated by ',') to generate a class path")
.build();
inputGroup.addOption(jarOption);
Option repositoryOption =
Option.builder("m")
.longOpt("maven-repositories")
.hasArgs()
.valueSeparator(',')
.desc(
"Maven repository URLs to search for dependencies. "
+ "The repositories are added to a repository list in order before "
+ "the default Maven Central (http://repo1.maven.org/maven2/).")
.build();
options.addOption(repositoryOption);
Option noMavenCentralOption =
Option.builder("nm")
.longOpt("no-maven-central")
.hasArg(false)
.desc(
"Do not search Maven Central in addition to the repositories specified by -m. "
+ "Useful when Maven Central is inaccessible.")
.build();
options.addOption(noMavenCentralOption);
options.addOptionGroup(inputGroup);
return options;
}
/** Returns a list of artifacts specified in the option of BOM or coordinates list. */
ImmutableList getArtifacts() throws RepositoryException {
if (cachedArtifacts != null) {
return cachedArtifacts;
}
if (commandLine.hasOption("b")) {
String bomCoordinates = commandLine.getOptionValue("b");
DefaultArtifact bomArtifact = new DefaultArtifact(bomCoordinates);
return cachedArtifacts = ImmutableList.copyOf(RepositoryUtility.readBom(bomArtifact));
} else if (commandLine.hasOption("a")) {
// option 'a'
String[] mavenCoordinatesOption = commandLine.getOptionValues("a");
return cachedArtifacts =
Arrays.stream(mavenCoordinatesOption)
.map(DefaultArtifact::new)
.collect(toImmutableList());
} else {
throw new IllegalArgumentException(
"The arguments must have option 'a' or 'b' to list Maven artifacts");
}
}
/**
* Returns a list of absolute paths to jar files for the option. The list includes dependencies if
* BOM or Maven coordinates are specified in the option.
*/
ImmutableList getInputClasspath() throws RepositoryException {
if (cachedInputClasspath != null) {
// Avoid unnecessary dependency resolution, which Cloud OSS BOM takes around 4 seconds
return cachedInputClasspath;
}
if (commandLine.hasOption("b") || commandLine.hasOption("a")) {
List artifacts = getArtifacts();
cachedInputClasspath = ClassPathBuilder.artifactsToClasspath(artifacts);
} else {
// b, a, or j is specified in OptionGroup
String[] jarFiles = commandLine.getOptionValues("j");
cachedInputClasspath =
Arrays.stream(jarFiles)
.map(name -> Paths.get(name).toAbsolutePath())
.collect(toImmutableList());
}
return cachedInputClasspath;
}
/** Returns a set of jar files that hold entry point classes. */
ImmutableSet getEntryPointJars() throws RepositoryException {
if (commandLine.hasOption("a") || commandLine.hasOption('b')) {
// For an artifact list (or a BOM), the first elements in inputClasspath are the artifacts
// specified the list, followed by their dependencies.
int artifactCount = getArtifacts().size();
// For Maven artifact list (or a BOM), entry point classes are ones in the list
return ImmutableSet.copyOf(getInputClasspath().subList(0, artifactCount));
} else {
// For list of jar files, entry point classes are all classes in the files
return ImmutableSet.copyOf(getInputClasspath());
}
}
ImmutableList getExtraMavenRepositoryUrls() {
return extraMavenRepositoryUrls;
}
boolean getAddMavenCentral() {
return addMavenCentral;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy