Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync with Scala Native 0.5.0 #39

Merged
merged 1 commit into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
val ScalaNativeVersion = "0.5.0-SNAPSHOT"
val ScalaNativeVersion = "0.5.0"
// Update during release procedure to provide access to staged, but not published artifacts
val StagingRepoIds = Nil
val StagingRepoIds = 1147 to 1149
val StagingRepoNames = StagingRepoIds.map(id => s"orgscala-native-$id").toSeq

val crossScalaVersions212 = (14 to 19).map("2.12." + _)
Expand Down
19 changes: 10 additions & 9 deletions cli/src/main/scala/scala/scalanative/cli/ScalaNativeLd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,19 @@ object ScalaNativeLd {
System.err.println(thrown.getMessage())
sys.exit(1)
case Right(buildOptions) =>
val outpath = Paths.get(options.config.outpath)
val build = Scope { implicit scope =>
Build
.build(buildOptions.config)
.map(
Files.copy(
_,
outpath,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES
)
)
.map { outputFile =>
options.config.outpath.map(Paths.get(_)).foreach { outpath =>
Files.copy(
outputFile,
outpath,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES
)
}
}
}
Await.result(build, Duration.Inf)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import scopt.OptionParser

case class ConfigOptions(
main: Option[String] = None,
outpath: String = "scala-native-out",
outpath: Option[String] = None,
workdir: String = "."
)

Expand All @@ -21,8 +21,8 @@ object ConfigOptions {
.opt[String]('o', "outpath")
.valueName("<output-path>")
.optional()
.action((x, c) => c.copy(config = c.config.copy(outpath = x)))
.text("Path of the resulting output binary. [./scala-native-out]")
.action((x, c) => c.copy(config = c.config.copy(outpath = Some(x))))
.text("Path of the resulting output binary.")
parser
.opt[String]("workdir")
.valueName("<path-to-directory>")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ case class NativeConfigOptions(
linkStubs: Boolean = false,
check: Boolean = false,
checkFatalWarnings: Boolean = false,
checkFeatures: Option[Boolean] = None,
dump: Boolean = false,
noOptimize: Boolean = false,
embedResources: Boolean = false,
resourceIncludePatterns: List[String] = Nil,
resourceExcludePatterns: List[String] = Nil,
multithreading: Option[Boolean] = None,
incrementalCompilation: Boolean = false,
baseName: Option[String] = None,
Expand All @@ -24,7 +27,9 @@ case class NativeConfigOptions(
compileOption: List[String] = List.empty,
targetTriple: Option[String] = None,
clang: Option[String] = None,
clangPP: Option[String] = None
clangPP: Option[String] = None,
serviceProviders: List[(String, String)] = Nil,
sanitizer: Option[String] = None
)

object NativeConfigOptions {
Expand Down Expand Up @@ -81,6 +86,15 @@ object NativeConfigOptions {
c.copy(nativeConfig = c.nativeConfig.copy(checkFatalWarnings = true))
)
.text("Shall linker NIR check treat warnings as errors? [false]")
parser
.opt[Boolean]("check-features")
.optional()
.action((x, c) =>
c.copy(nativeConfig = c.nativeConfig.copy(checkFeatures = Some(x)))
)
.text(
"Shall build fail if it detects usage of unsupported feature on given platform? [true]"
)
parser
.opt[Unit]("dump")
.optional()
Expand All @@ -93,6 +107,34 @@ object NativeConfigOptions {
c.copy(nativeConfig = c.nativeConfig.copy(noOptimize = true))
)
.text("Should the resulting NIR code be not optimized? [false]")
parser
.opt[String]("embed-resources-include")
.optional()
.unbounded()
.action((x, c) =>
c.copy(nativeConfig =
c.nativeConfig.copy(resourceIncludePatterns =
x :: c.nativeConfig.resourceIncludePatterns
)
)
)
.text(
"Add glob pattern for resource files that should be embeded in the binary [**]"
)
parser
.opt[String]("embed-resources-exclude")
.optional()
.unbounded()
.action((x, c) =>
c.copy(nativeConfig =
c.nativeConfig.copy(resourceExcludePatterns =
x :: c.nativeConfig.resourceExcludePatterns
)
)
)
.text(
"Add glob pattern for resource files that should not be embeded in the binary"
)
parser
.opt[Unit]("embed-resources")
.optional()
Expand Down Expand Up @@ -137,6 +179,7 @@ object NativeConfigOptions {
.opt[String]("ltp")
.valueName("<keystring=value>")
.optional()
.unbounded()
.action((x, c) =>
c.copy(nativeConfig =
c.nativeConfig.copy(ltp = c.nativeConfig.ltp :+ x)
Expand All @@ -145,6 +188,29 @@ object NativeConfigOptions {
.text(
"User defined properties resolved at link-time. Multiple can be defined. Example: \"isCli=true\""
)
parser
.opt[String]("service-providers")
.valueName("<serviceName:implementationName>")
.optional()
.unbounded()
.action((x, c) =>
x.split(':') match {
case Array(serviceName, serviceImplementation) =>
c.copy(nativeConfig =
c.nativeConfig.copy(serviceProviders =
c.nativeConfig.serviceProviders :+ (serviceName, serviceImplementation)
)
)
case _ =>
parser.reportWarning(
"Invalid format for 'service-providers', expected <serviceName:implementationName>"
)
c
}
)
.text(
"Explicitly enabled service providers that should be avaiable in the executable"
)
parser
.opt[String]("linking-option")
.valueName("<passed-option>")
Expand Down Expand Up @@ -196,5 +262,22 @@ object NativeConfigOptions {
.text(
"Path to the `clang++` executable. Internally discovered if not specified."
)
parser
.opt[String]("sanitizer")
.optional()
.action { (x, c) =>
val supportedSanitizers = Seq("thread", "address", "undefined")
if (supportedSanitizers.contains(x))
c.copy(nativeConfig = c.nativeConfig.copy(sanitizer = Some(x)))
else {
parser.reportWarning(
s"Unsupported sanitizer type '$x', allowed values: ${supportedSanitizers.mkString(", ")}"
)
c
}
}
.text(
"Path to the `clang++` executable. Internally discovered if not specified."
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import scopt.OptionParser
import scala.scalanative.build

case class SemanticsConfigOptions(
finalFields: Option[JVMMemoryModelCompliance] = None
finalFields: Option[JVMMemoryModelCompliance] = None,
strictExternCalls: Option[Boolean] = None
)

sealed abstract class JVMMemoryModelCompliance {
Expand Down Expand Up @@ -40,6 +41,15 @@ object SemanticsConfigOptions {
.valueName("<final-fields-semantics> (none, relaxed, or stricts)")
.optional()
.action((x, c) => update(c)(_.copy(finalFields = Some(x))))
.text("Maximal number of allowed nested inlines.")
.text(
"JVM memory model compliance for final, either 'none' (no special handling), 'relaxed' (default, synchronization of annotated fiedls), `strict` (synchronization of all final fields)"
)
parser
.opt[Boolean]("strict-extern-call-semantics")
.optional()
.action((x, c) => update(c)(_.copy(strictExternCalls = Some(x))))
.text(
"Should runtime notify GC when entering unmanaged (foreign, native) code. When enabled every extern call would notify GC, otherwise (default) only annotated blocking methods notify GC."
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.io.File

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

object ConfigConverter {
Expand All @@ -32,7 +32,7 @@ object ConfigConverter {
)
} else {
generateConfig(options, main, classpath).flatMap(config =>
Try(Paths.get(options.config.outpath)).toEither.map(outpath =>
Try(options.config.outpath.map(Paths.get(_))).toEither.map(outpath =>
BuildOptions(config, outpath)
)
)
Expand All @@ -54,24 +54,27 @@ object ConfigConverter {
options.nativeConfig.baseName match {
case Some(name) => Right(name)
case _ =>
Paths
.get(
options.config.outpath
.replace('/', File.separatorChar)
options.config.outpath
.map(
_.replace('/', File.separatorChar)
.replace('\\', File.separatorChar)
)
.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}'"
)
)
}
.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())
Expand All @@ -80,16 +83,32 @@ object ConfigConverter {
)
ltp <- LinktimePropertyParser.parseAll(options.nativeConfig.ltp)
baseName <- resolveBaseName
} yield NativeConfig.empty
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)
Expand All @@ -105,8 +124,22 @@ object ConfigConverter {
.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 = {
Expand All @@ -124,7 +157,9 @@ object ConfigConverter {
val c0 = SemanticsConfig.default
val c1 =
options.finalFields.map(_.convert).foldLeft(c0)(_.withFinalFields(_))
c1
val c2 =
options.strictExternCalls.foldLeft(c1)(_.withStrictExternCallSemantics(_))
c2
}

private def generateSourceLevelDebuggingConfig(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ class ConfigConverterTest extends AnyFlatSpec {
) {
val options = LinkerOptions(
classpath = dummyArguments.toList,
config = dummyConfigOptions.copy(outpath = path),
config = dummyConfigOptions.copy(outpath = Some(path)),
nativeConfig = NativeConfigOptions(baseName = None)
)
val resolved = ConfigConverter
Expand All @@ -263,7 +263,7 @@ class ConfigConverterTest extends AnyFlatSpec {
it should "prioritize baseName over outpath" in {
val options = LinkerOptions(
classpath = dummyArguments.toList,
config = dummyConfigOptions.copy(outpath = "my-bin.exe"),
config = dummyConfigOptions.copy(outpath = Some("my-bin.exe")),
nativeConfig = NativeConfigOptions(baseName = Some("my-app"))
)
val resolved = ConfigConverter
Expand Down
Loading