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

scala.scalanative.cli.utils.ConfigConverter.scala Maven / Gradle / Ivy

The newest version!
package scala.scalanative.cli.utils

import scala.scalanative.build._
import java.nio.file.Paths
import java.nio.file.Path
import scala.util.Try
import scala.scalanative.cli.options._
import java.io.File

case class BuildOptions(
    config: Config,
    outpath: Option[Path]
)

object ConfigConverter {
  def convert(
      options: LinkerOptions,
      main: String,
      classpath: Seq[String]
  ): Either[Throwable, BuildOptions] = convert(options, Some(main), classpath)

  def convert(
      options: LinkerOptions,
      main: Option[String],
      classpath: Seq[String]
  ): Either[Throwable, BuildOptions] = {
    if (classpath.isEmpty) {
      Left(
        new IllegalArgumentException(
          "Classpath not specified. Pass classpath files as positional arguments."
        )
      )
    } else {
      generateConfig(options, main, classpath).flatMap(config =>
        Try(options.config.outpath.map(Paths.get(_))).toEither.map(outpath =>
          BuildOptions(config, outpath)
        )
      )
    }
  }

  private def generateNativeConfig(
      options: LinkerOptions
  ): Either[Throwable, NativeConfig] = {

    def toPathOrDiscover(
        optPath: Option[String]
    )(discover: => Path): Either[Throwable, Path] =
      Try {
        optPath.map(Paths.get(_)).getOrElse(discover)
      }.toEither

    def resolveBaseName: Either[Throwable, String] =
      options.nativeConfig.baseName match {
        case Some(name) => Right(name)
        case _ =>
          options.config.outpath
            .map(
              _.replace('/', File.separatorChar)
                .replace('\\', File.separatorChar)
            )
            .fold[Either[Throwable, String]](Right("scala-native")) {
              Paths
                .get(_)
                .getFileName()
                .toString()
                .split('.')
                .headOption match {
                case Some(name) => Right(name)
                case None =>
                  Left(
                    new IllegalArgumentException(
                      s"Invalid output path, failed to resolve base name of output file for path '${options.config.outpath}'"
                    )
                  )
              }
            }
      }
    for {
      clang <- toPathOrDiscover(options.nativeConfig.clang)(Discover.clang())
      clangPP <- toPathOrDiscover(options.nativeConfig.clangPP)(
        Discover.clangpp()
      )
      ltp <- LinktimePropertyParser.parseAll(options.nativeConfig.ltp)
      baseName <- resolveBaseName
      default = NativeConfig.empty
    } yield default
      .withMode(options.nativeConfig.mode)
      .withLTO(options.nativeConfig.lto)
      .withGC(options.nativeConfig.gc)
      .withLinkStubs(options.nativeConfig.linkStubs)
      .withCheck(options.nativeConfig.check)
      .withCheckFatalWarnings(options.nativeConfig.checkFatalWarnings)
      .withCheckFeatures(
        options.nativeConfig.checkFeatures.getOrElse(default.checkFeatures)
      )
      .withDump(options.nativeConfig.dump)
      .withOptimize(!options.nativeConfig.noOptimize)
      .withEmbedResources(options.nativeConfig.embedResources)
      .withResourceIncludePatterns(
        options.nativeConfig.resourceIncludePatterns match {
          case Nil      => default.resourceIncludePatterns
          case patterns => patterns
        }
      )
      .withResourceExcludePatterns(
        options.nativeConfig.resourceExcludePatterns match {
          case Nil      => default.resourceExcludePatterns
          case patterns => patterns
        }
      )
      .withTargetTriple(options.nativeConfig.targetTriple)
      .withClang(clang)
      .withClangPP(clangPP)
      .withCompileOptions(options.nativeConfig.compileOption)
      .withLinkingOptions(options.nativeConfig.linkingOption)
      .withLinktimeProperties(ltp)
      .withBuildTarget(options.nativeConfig.buildTarget)
      .withIncrementalCompilation(options.nativeConfig.incrementalCompilation)
      .withOptimizerConfig(generateOptimizerConfig(options.optimizerConifg))
      .withBaseName(baseName)
      .withMultithreading(options.nativeConfig.multithreading)
      .withSemanticsConfig(generateSemanticsConfig(options.semanticsConfig))
      .withSourceLevelDebuggingConfig(
        generateSourceLevelDebuggingConfig(options.sourceLevelDebuggingConfig)
      )
      .withServiceProviders(
        options.nativeConfig.serviceProviders.groupBy(_._1).map {
          case (key, values) => (key, values.map(_._2))
        }
      )
      .withSanitizer(
        options.nativeConfig.sanitizer.flatMap(sanitizerFromString)
      )
  }

  private def sanitizerFromString(v: String): Option[Sanitizer] = Seq(
    Sanitizer.ThreadSanitizer,
    Sanitizer.AddressSanitizer,
    Sanitizer.UndefinedBehaviourSanitizer
  ).find(_.name == v)

  private def generateOptimizerConfig(
      options: OptimizerConfigOptions
  ): OptimizerConfig = {
    val c0 = OptimizerConfig.empty
    val c1 = options.maxInlineDepth.foldLeft(c0)(_.withMaxInlineDepth(_))
    val c2 = options.maxCallerSize.foldLeft(c1)(_.withMaxCallerSize(_))
    val c3 = options.maxCalleeSize.foldLeft(c2)(_.withMaxCalleeSize(_))
    val c4 = options.maxInlineSize.foldLeft(c3)(_.withSmallFunctionSize(_))
    c4
  }

  private def generateSemanticsConfig(
      options: SemanticsConfigOptions
  ): SemanticsConfig = {
    val c0 = SemanticsConfig.default
    val c1 =
      options.finalFields.map(_.convert).foldLeft(c0)(_.withFinalFields(_))
    val c2 =
      options.strictExternCalls.foldLeft(c1)(_.withStrictExternCallSemantics(_))
    c2
  }

  private def generateSourceLevelDebuggingConfig(
      options: SourceLevelDebuggingConfigOptions
  ): SourceLevelDebuggingConfig = {
    val c0 = SourceLevelDebuggingConfig.disabled.withCustomSourceRoots(
      options.customSourceRoots
    )
    val c1 = options.enabled.foldLeft(c0)(_.enabled(_))
    val c2 = options.genFunctionSourcePositions.foldLeft(c1)(
      _.generateFunctionSourcePositions(_)
    )
    val c3 = options.genLocalVariables.foldLeft(c2)(_.generateLocalVariables(_))
    c3
  }

  private def generateConfig(
      options: LinkerOptions,
      main: Option[String],
      classPath: Seq[String]
  ): Either[Throwable, Config] = {
    for {
      nativeConfig <- generateNativeConfig(options)
      classPath <- Try(parseClassPath(classPath)).toEither
    } yield Config.empty
      .withBaseDir(Paths.get(options.config.workdir).toAbsolutePath())
      .withCompilerConfig(nativeConfig)
      .withClassPath(classPath)
      .withLogger(new FilteredLogger(options.verbose))
      .withMainClass(main)

  }

  private def parseClassPath(classPath: Seq[String]): Seq[Path] =
    classPath.map(Paths.get(_))
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy