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

io.joern.x2cpg.utils.dependency.MavenDependencies.scala Maven / Gradle / Ivy

There is a newer version: 4.0.131
Show newest version
package io.joern.x2cpg.utils.dependency

import io.joern.x2cpg.utils.ExternalCommand
import org.slf4j.LoggerFactory

import java.nio.file.Path
import scala.util.{Failure, Success}

object MavenDependencies {
  private val logger = LoggerFactory.getLogger(getClass)

  private val MavenCliOpts = "MAVEN_CLI_OPTS"
  // we can't use -Dmdep.outputFile because that keeps overwriting its own output for each sub-project it's running for
  // also separate this from fetchCommandWithOpts to log a version that clearly separates options we provide from
  // options specified by the user via the MAVEN_CLI_OPTS environment variable, while also making it clear that this
  // environment variable is being considered.
  private val fetchCommand =
    s"mvn $$$MavenCliOpts --fail-never -B dependency:build-classpath -DincludeScope=compile -Dorg.slf4j.simpleLogger.defaultLogLevel=info -Dorg.slf4j.simpleLogger.logFile=System.out"

  private val fetchCommandWithOpts = {
    // These options suppress output, so if they're provided we won't get any results.
    // "-q" and "--quiet" are the only ones that would realistically be used.
    val optionsToStrip = Set("-h", "--help", "-q", "--quiet", "-v", "--version")

    val mavenOpts         = Option(System.getenv(MavenCliOpts)).getOrElse("")
    val mavenOptsStripped = mavenOpts.split(raw"\s").filterNot(optionsToStrip.contains).mkString(" ")
    fetchCommand.replace(s"$$$MavenCliOpts", mavenOptsStripped)
  }

  private def logErrors(output: String): Unit = {

    logger.warn(
      s"Retrieval of compile class path via maven return with error.\n" +
        "The compile class path may be missing or partial.\n" +
        "Results will suffer from poor type information.\n" +
        "To fix this issue, please ensure that the below command can be executed successfully from the project root directory:\n" +
        fetchCommand + "\n\n",
      output
    )
  }

  private[dependency] def get(projectDir: Path): Option[collection.Seq[String]] = {
    val lines = ExternalCommand.run(fetchCommandWithOpts, projectDir.toString) match {
      case Success(lines) =>
        if (lines.contains("[INFO] Build failures were ignored.")) {
          logErrors(lines.mkString(System.lineSeparator()))
        }

        lines
      case Failure(exception) =>
        logErrors(exception.getMessage)
        // exception message is the program output - and we still want to look for potential partial results
        exception.getMessage.linesIterator.toSeq
    }

    var classPathNext = false
    val deps = lines
      .flatMap { line =>
        val isClassPathNow = classPathNext
        classPathNext = line.endsWith("Dependencies classpath:")

        if (isClassPathNow) line.split(':') else Array.empty[String]
      }
      .distinct
      .toList

    logger.info("got {} Maven dependencies", deps.size)
    Some(deps)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy