All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.cloud.tools.opensource.classpath.LinkageCheckerMain Maven / Gradle / Ivy

There is a newer version: 1.5.13
Show newest version
/*
 * Copyright 2019 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.collect.ImmutableSet.toImmutableSet;

import com.google.cloud.tools.opensource.dependencies.ArtifactProblem;
import com.google.cloud.tools.opensource.dependencies.DependencyGraphBuilder;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.Path;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.TransformerException;
import org.apache.commons.cli.ParseException;
import org.eclipse.aether.RepositoryException;
import org.eclipse.aether.artifact.Artifact;

/**
 * A tool to find linkage errors in a class path.
 */
class LinkageCheckerMain {

  /**
   * Forms a classpath from Maven coordinates or a list of jar files and reports linkage errors in
   * that classpath.
   *
   * @throws IOException when there is a problem reading a jar file
   * @throws RepositoryException when there is a problem finding an artifact
   *     in the Maven repository system
   */
  public static void main(String[] arguments)
      throws IOException, RepositoryException, TransformerException, XMLStreamException,
          LinkageCheckResultException {

    try {
      LinkageCheckerArguments linkageCheckerArguments =
          LinkageCheckerArguments.readCommandLine(arguments);
      
      if (linkageCheckerArguments.needsHelp() || arguments.length == 0) {
        linkageCheckerArguments.printHelp();
      }

      if (linkageCheckerArguments.hasInput()) {
        // artifacts is not empty if a BOM or Maven coordinates are specified in the argument.
        // If JAR files are specified, it's empty.
        ImmutableList artifacts = linkageCheckerArguments.getArtifacts();

        Problems problems =
            artifacts.isEmpty()
                ? checkJarFiles(linkageCheckerArguments)
                : checkArtifacts(linkageCheckerArguments);

        Path outputExclusionFile = linkageCheckerArguments.getOutputExclusionFile();
        if (!problems.linkageProblems.isEmpty()) {
          // TODO really uncertain about this check. Whether to write an exclusion file is
          // a separate issue from whether to print the linkage problems.
          if (outputExclusionFile == null) {
            problems.print();
            // Throwing an exception is more test-friendly than System.exit(1). The latter
            // abruptly stops test execution.
            throw new LinkageCheckResultException(problems.linkageProblems.size());
          } else {
            problems.writeExclusionFile(outputExclusionFile);
          }
        }
      }
    } catch (ParseException ex) {
      System.err.println(ex.getMessage());
    }
  }
  
  // output from a check
  private static final class Problems { 
    
    private final ImmutableList artifactProblems;
    private final ImmutableSet linkageProblems;
    private final ClassPathResult classPathResult;

    private Problems(
        ImmutableSet linkageProblems,
        ImmutableList artifactProblems,
        ClassPathResult classPathResult) {
      this.artifactProblems = artifactProblems;
      this.linkageProblems = linkageProblems;
      this.classPathResult = classPathResult;
    }

    private Problems(ImmutableSet linkageProblems) {
      this.linkageProblems = linkageProblems;
      this.artifactProblems = ImmutableList.of();
      this.classPathResult = null;
    }
    
    void print() {
      System.out.println(LinkageProblem.formatLinkageProblems(
          linkageProblems, classPathResult));
      if (!artifactProblems.isEmpty()) {
        System.out.println("\n");
        System.out.println(ArtifactProblem.formatProblems(artifactProblems));
      }
      System.out.println(
          "For the details of the linkage errors, see "
              + "https://github.com/GoogleCloudPlatform/cloud-opensource-java/wiki/Linkage-Checker-Messages");
    }
    
    void writeExclusionFile(Path path) throws IOException, XMLStreamException, TransformerException {
      ExclusionFiles.write(path, linkageProblems);
      System.out.println("Wrote the linkage errors as exclusion file: " + path);
    }

  }

  private static Problems checkJarFiles(
      LinkageCheckerArguments linkageCheckerArguments)
      throws IOException, TransformerException, XMLStreamException {

    ImmutableList inputClassPath = linkageCheckerArguments.getJarFiles();
    ImmutableSet entryPoints = ImmutableSet.copyOf(inputClassPath);
    LinkageChecker linkageChecker =
        LinkageChecker.create(
            inputClassPath, entryPoints, linkageCheckerArguments.getInputExclusionFile());

    ImmutableSet linkageProblems =
        findLinkageProblems(linkageChecker, linkageCheckerArguments.getReportOnlyReachable());

    return new Problems(linkageProblems); 
  }

  private static Problems checkArtifacts(
      LinkageCheckerArguments linkageCheckerArguments)
      throws IOException, RepositoryException, TransformerException, XMLStreamException {
    
    ImmutableList artifacts = linkageCheckerArguments.getArtifacts();

    // When a BOM or Maven artifacts are passed as arguments, resolve the dependencies.
    DependencyGraphBuilder dependencyGraphBuilder =
        new DependencyGraphBuilder(linkageCheckerArguments.getMavenRepositoryUrls());
    ClassPathBuilder classPathBuilder = new ClassPathBuilder(dependencyGraphBuilder);
    ClassPathResult classPathResult = classPathBuilder.resolve(artifacts, false);
    ImmutableList inputClassPath = classPathResult.getClassPath();
    ImmutableList artifactProblems =
        ImmutableList.copyOf(classPathResult.getArtifactProblems());
    ImmutableSet entryPoints =
        ImmutableSet.copyOf(inputClassPath.subList(0, artifacts.size()));

    LinkageChecker linkageChecker =
        LinkageChecker.create(
            inputClassPath, entryPoints, linkageCheckerArguments.getInputExclusionFile());
    ImmutableSet linkageProblems =
        findLinkageProblems(linkageChecker,
            linkageCheckerArguments.getReportOnlyReachable());
    
    LinkageProblemCauseAnnotator.annotate(classPathBuilder, classPathResult, linkageProblems);
    
    return new Problems(linkageProblems, artifactProblems, classPathResult); 
  }

  private static ImmutableSet findLinkageProblems(LinkageChecker linkageChecker,
      boolean reportOnlyReachable)
      throws IOException, TransformerException, XMLStreamException {

    ImmutableSet linkageProblems = linkageChecker.findLinkageProblems();

    if (reportOnlyReachable) {
      ClassReferenceGraph graph = linkageChecker.getClassReferenceGraph();
      linkageProblems =
          linkageProblems.stream()
              .filter(
                  (LinkageProblem problem) ->
                      graph.isReachable(problem.getSourceClass().getBinaryName()))
              .collect(toImmutableSet());
    }

    return linkageProblems;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy