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

scala.scalanative.build.Config.scala Maven / Gradle / Ivy

There is a newer version: 0.5.5
Show newest version
package scala.scalanative
package build

import java.nio.file.{Files, Path, Paths}

/** An object describing how to configure the Scala Native toolchain. */
sealed trait Config {

  private val testSuffix = "-test"

  /** Base Directory for native work products. */
  def baseDir: Path

  /** Indicates whether this is a test config or not. */
  def testConfig: Boolean

  /** Directory to emit intermediate compilation results. Calculated based on
   *  [[#baseDir]] / `native` or `native-test` if a test project. The build
   *  creates directories if they do not exist.
   */
  def workDir: Path

  /** Name of the project module from the build system. Must be unique amongst
   *  modules in the larger project.
   *
   *  @return
   *    moduleName
   */
  def moduleName: String

  /** Base name for executable or library, typically the project/module name
   *  from the build tool [[#moduleName]] or can be overridden by the user with
   *  [[NativeConfig#baseName]]. This must be unique over all module names and
   *  other `baseName`s in the project. Delegated method to
   *  [[NativeConfig#baseName]]
   */
  def baseName: String

  /** This is the name of the executable or library. Calculated based on a
   *  prefix for libraries `lib` for UNIX like OSes, [[#baseName]], `-test` if
   *  [[#testConfig]] is `true`, and the executable or library suffix depending
   *  on platform and library type.
   */
  def artifactName: String

  /** Final Path to the output file, executable or library. Calculated based on
   *  [[#baseDir]] `/` [[#artifactName]].
   */
  def artifactPath: Path

  /** Build path to support multiple main applications.
   *
   *  For libraries it is the same as the [[#artifactPath]] and for applications
   *  it resolves to [[#workDir]] `/` [[#artifactName]] and after the build it
   *  is copied to [[#artifactPath]].
   */
  def buildPath: Path

  /** Entry point for linking. */
  def mainClass: Option[String]

  /** Sequence of all NIR locations. */
  def classPath: Seq[Path]

  /** Sequence of all Scala sources locations used when mapping binary symbols
   *  with original sources.
   */
  def sourcesClassPath: Seq[Path]

  /** The logger used by the toolchain. */
  def logger: Logger

  /** The [[NativeConfig]] that is used by the developer to control settings. */
  def compilerConfig: NativeConfig

  /** Create a new config with given base directory. */
  def withBaseDir(value: Path): Config

  /** Create a new config with test (true) or normal config (false). */
  def withTestConfig(value: Boolean): Config

  /** Create a new config with the module name - required. */
  def withModuleName(value: String): Config

  /** Create new config with a fully qualified (with package) main class name as
   *  an [[Option]]. Only applicable if [[NativeConfig#buildTarget]] is a
   *  [[BuildTarget#application]].
   *
   *  @param value
   *    fully qualified main class name as an [[Option]], default [[None]]
   *  @return
   *    this config object
   */
  def withMainClass(value: Option[String]): Config

  /** Create a new config with given nir paths. */
  def withClassPath(value: Seq[Path]): Config

  /** Create a new config with given Scala sources paths. */
  def withSourcesClassPath(value: Seq[Path]): Config

  /** Create a new config with the given logger. */
  def withLogger(value: Logger): Config

  /** Create a [[Config]] with a new [[NativeConfig]]. */
  def withCompilerConfig(value: NativeConfig): Config

  /** Create a [[Config]] with a function which takes and returns a
   *  [[NativeConfig]].
   */
  def withCompilerConfig(fn: NativeConfig => NativeConfig): Config

  // delegated methods

  /** The garbage collector to use. */
  def gc: GC = compilerConfig.gc

  /** Compilation mode. */
  def mode: Mode = compilerConfig.mode

  /** The path to the `clang` executable. */
  def clang: Path = compilerConfig.clang

  /** The path to the `clang++` executable. */
  def clangPP: Path = compilerConfig.clangPP

  /** The options passed to LLVM's linker. */
  def linkingOptions: Seq[String] = compilerConfig.linkingOptions

  /** The compilation options passed to LLVM. */
  def compileOptions: Seq[String] = compilerConfig.compileOptions

  /** Should stubs be linked? */
  def linkStubs: Boolean = compilerConfig.linkStubs

  /** The LTO mode to use used during a release build. */
  def LTO: LTO = compilerConfig.lto

  /** Shall linker check that NIR is well-formed after every phase? */
  def check: Boolean = compilerConfig.check

  /** Shall linker dump intermediate NIR after every phase? */
  def dump: Boolean = compilerConfig.dump

  // helpers

  protected def nameSuffix = if (testConfig) testSuffix else ""

  private[scalanative] lazy val targetsWindows: Boolean = {
    compilerConfig.targetTriple.fold(Platform.isWindows) { customTriple =>
      customTriple.contains("win32") ||
      customTriple.contains("windows")
    }
  }

  private[scalanative] lazy val targetsMac: Boolean =
    compilerConfig.targetTriple.fold(Platform.isMac) { customTriple =>
      Seq("mac", "apple", "darwin").exists(customTriple.contains(_))
    }

  private[scalanative] lazy val targetsMsys: Boolean = {
    compilerConfig.targetTriple.fold(Platform.isMsys) { customTriple =>
      customTriple.contains("windows-msys")
    }
  }
  private[scalanative] lazy val targetsCygwin: Boolean = {
    compilerConfig.targetTriple.fold(Platform.isCygwin) { customTriple =>
      customTriple.contains("windows-cygnus")
    }
  }

  private[scalanative] lazy val targetsLinux: Boolean =
    compilerConfig.targetTriple.fold(Platform.isLinux) { customTriple =>
      Seq("linux").exists(customTriple.contains(_))
    }

  private[scalanative] lazy val targetsOpenBSD: Boolean =
    compilerConfig.targetTriple.fold(Platform.isOpenBSD) { customTriple =>
      Seq("openbsd").exists(customTriple.contains(_))
    }

  private[scalanative] lazy val targetsNetBSD: Boolean =
    compilerConfig.targetTriple.fold(Platform.isNetBSD) { customTriple =>
      Seq("netbsd").exists(customTriple.contains(_))
    }

  // see https://no-color.org/
  private[scalanative] lazy val noColor: Boolean = sys.env.contains("NO_COLOR")

  private[scalanative] lazy val useTrapBasedGCYieldPoints =
    compilerConfig.gc match {
      case GC.Immix | GC.Commix | GC.Experimental =>
        sys.env
          .get("SCALANATIVE_GC_TRAP_BASED_YIELDPOINTS")
          .map(_ == "1")
          .getOrElse(compilerConfig.mode.isInstanceOf[Mode.Release])
      case _ => false
    }
}

/** Factory to create [[#empty]] [[Config]] objects */
object Config {

  /** Default empty config object where all of the fields are left blank or the
   *  default value.
   */
  def empty: Config =
    Impl(
      baseDir = Paths.get(""),
      testConfig = false,
      moduleName = "",
      mainClass = None,
      classPath = Seq.empty,
      sourcesClassPath = Seq.empty,
      compilerConfig = NativeConfig.empty
    )(Logger.default)

  private final case class Impl(
      baseDir: Path,
      testConfig: Boolean,
      moduleName: String,
      mainClass: Option[String],
      classPath: Seq[Path],
      sourcesClassPath: Seq[Path],
      compilerConfig: NativeConfig
  )(implicit
      val logger: Logger // Exclude logger from hashCode calculation https://stackoverflow.com/questions/10373715/scala-ignore-case-class-field-for-equals-hascode
  ) extends Config {

    def withBaseDir(value: Path): Config =
      copy(baseDir = value)

    def withTestConfig(value: Boolean): Config =
      copy(testConfig = value)

    def withModuleName(value: String): Config =
      copy(moduleName = value)

    def withMainClass(value: Option[String]): Config =
      copy(mainClass = value)

    def withClassPath(value: Seq[Path]): Config =
      copy(classPath = value)

    def withSourcesClassPath(value: Seq[Path]): Config =
      copy(sourcesClassPath = value)

    override def withCompilerConfig(value: NativeConfig): Config =
      copy(compilerConfig = value)

    override def withCompilerConfig(fn: NativeConfig => NativeConfig): Config =
      copy(compilerConfig = fn(compilerConfig))

    override def withLogger(value: Logger): Config =
      copy()(value)

    override lazy val workDir: Path =
      baseDir.resolve(s"native$nameSuffix")

    override lazy val baseName: String =
      compilerConfig.baseName match {
        case bn if bn.nonEmpty => bn
        case _                 => moduleName
      }

    override lazy val artifactName: String =
      artifactName(baseName)

    override lazy val artifactPath: Path =
      baseDir.resolve(artifactName)

    override lazy val buildPath: Path =
      compilerConfig.buildTarget match {
        case BuildTarget.Application =>
          workDir.resolve(artifactName(mainClass.get))
        case _: BuildTarget.Library =>
          baseDir.resolve(artifactName)
      }

    private def artifactName(name: String) = {
      val ext = compilerConfig.buildTarget match {
        case BuildTarget.Application =>
          if (targetsWindows) ".exe" else ""
        case BuildTarget.LibraryDynamic =>
          if (targetsWindows) ".dll"
          else if (targetsMac) ".dylib"
          else ".so"
        case BuildTarget.LibraryStatic =>
          if (targetsWindows) ".lib"
          else ".a"
      }
      val namePrefix = compilerConfig.buildTarget match {
        case BuildTarget.Application => ""
        case _: BuildTarget.Library  => if (targetsWindows) "" else "lib"
      }
      s"$namePrefix${name}$nameSuffix$ext"
    }

    override def toString: String = {
      def formatClassPath(cp: Seq[Path]) =
        cp.mkString("List(", "\n".padTo(22, ' '), ")")

      s"""Config(
        | - baseDir:          $baseDir
        | - testConfig:       $testConfig
        | - workDir:          $workDir
        | - moduleName:       $moduleName
        | - baseName:         $baseName
        | - artifactName:     $artifactName
        | - artifactPath:     $artifactPath
        | - buildPath:        $buildPath
        | - mainClass:        $mainClass
        | - classPath:        ${formatClassPath(classPath)}
        | - sourcesClasspath: ${formatClassPath(sourcesClassPath)}
        | - compilerConfig:   $compilerConfig
        |)""".stripMargin
    }

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy