Skip to content

Commit d192be8

Browse files
Sync with Scala Native 0.5.0 (#39)
1 parent ac22c27 commit d192be8

File tree

7 files changed

+168
-39
lines changed

7 files changed

+168
-39
lines changed

build.sbt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
val ScalaNativeVersion = "0.5.0-SNAPSHOT"
1+
val ScalaNativeVersion = "0.5.0"
22
// Update during release procedure to provide access to staged, but not published artifacts
3-
val StagingRepoIds = Nil
3+
val StagingRepoIds = 1147 to 1149
44
val StagingRepoNames = StagingRepoIds.map(id => s"orgscala-native-$id").toSeq
55

66
val crossScalaVersions212 = (14 to 19).map("2.12." + _)

cli/src/main/scala/scala/scalanative/cli/ScalaNativeLd.scala

+10-9
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,19 @@ object ScalaNativeLd {
7575
System.err.println(thrown.getMessage())
7676
sys.exit(1)
7777
case Right(buildOptions) =>
78-
val outpath = Paths.get(options.config.outpath)
7978
val build = Scope { implicit scope =>
8079
Build
8180
.build(buildOptions.config)
82-
.map(
83-
Files.copy(
84-
_,
85-
outpath,
86-
StandardCopyOption.REPLACE_EXISTING,
87-
StandardCopyOption.COPY_ATTRIBUTES
88-
)
89-
)
81+
.map { outputFile =>
82+
options.config.outpath.map(Paths.get(_)).foreach { outpath =>
83+
Files.copy(
84+
outputFile,
85+
outpath,
86+
StandardCopyOption.REPLACE_EXISTING,
87+
StandardCopyOption.COPY_ATTRIBUTES
88+
)
89+
}
90+
}
9091
}
9192
Await.result(build, Duration.Inf)
9293

cli/src/main/scala/scala/scalanative/cli/options/ConfigOptions.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import scopt.OptionParser
44

55
case class ConfigOptions(
66
main: Option[String] = None,
7-
outpath: String = "scala-native-out",
7+
outpath: Option[String] = None,
88
workdir: String = "."
99
)
1010

@@ -21,8 +21,8 @@ object ConfigOptions {
2121
.opt[String]('o', "outpath")
2222
.valueName("<output-path>")
2323
.optional()
24-
.action((x, c) => c.copy(config = c.config.copy(outpath = x)))
25-
.text("Path of the resulting output binary. [./scala-native-out]")
24+
.action((x, c) => c.copy(config = c.config.copy(outpath = Some(x))))
25+
.text("Path of the resulting output binary.")
2626
parser
2727
.opt[String]("workdir")
2828
.valueName("<path-to-directory>")

cli/src/main/scala/scala/scalanative/cli/options/NativeConfigOptions.scala

+84-1
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ case class NativeConfigOptions(
1313
linkStubs: Boolean = false,
1414
check: Boolean = false,
1515
checkFatalWarnings: Boolean = false,
16+
checkFeatures: Option[Boolean] = None,
1617
dump: Boolean = false,
1718
noOptimize: Boolean = false,
1819
embedResources: Boolean = false,
20+
resourceIncludePatterns: List[String] = Nil,
21+
resourceExcludePatterns: List[String] = Nil,
1922
multithreading: Option[Boolean] = None,
2023
incrementalCompilation: Boolean = false,
2124
baseName: Option[String] = None,
@@ -24,7 +27,9 @@ case class NativeConfigOptions(
2427
compileOption: List[String] = List.empty,
2528
targetTriple: Option[String] = None,
2629
clang: Option[String] = None,
27-
clangPP: Option[String] = None
30+
clangPP: Option[String] = None,
31+
serviceProviders: List[(String, String)] = Nil,
32+
sanitizer: Option[String] = None
2833
)
2934

3035
object NativeConfigOptions {
@@ -81,6 +86,15 @@ object NativeConfigOptions {
8186
c.copy(nativeConfig = c.nativeConfig.copy(checkFatalWarnings = true))
8287
)
8388
.text("Shall linker NIR check treat warnings as errors? [false]")
89+
parser
90+
.opt[Boolean]("check-features")
91+
.optional()
92+
.action((x, c) =>
93+
c.copy(nativeConfig = c.nativeConfig.copy(checkFeatures = Some(x)))
94+
)
95+
.text(
96+
"Shall build fail if it detects usage of unsupported feature on given platform? [true]"
97+
)
8498
parser
8599
.opt[Unit]("dump")
86100
.optional()
@@ -93,6 +107,34 @@ object NativeConfigOptions {
93107
c.copy(nativeConfig = c.nativeConfig.copy(noOptimize = true))
94108
)
95109
.text("Should the resulting NIR code be not optimized? [false]")
110+
parser
111+
.opt[String]("embed-resources-include")
112+
.optional()
113+
.unbounded()
114+
.action((x, c) =>
115+
c.copy(nativeConfig =
116+
c.nativeConfig.copy(resourceIncludePatterns =
117+
x :: c.nativeConfig.resourceIncludePatterns
118+
)
119+
)
120+
)
121+
.text(
122+
"Add glob pattern for resource files that should be embeded in the binary [**]"
123+
)
124+
parser
125+
.opt[String]("embed-resources-exclude")
126+
.optional()
127+
.unbounded()
128+
.action((x, c) =>
129+
c.copy(nativeConfig =
130+
c.nativeConfig.copy(resourceExcludePatterns =
131+
x :: c.nativeConfig.resourceExcludePatterns
132+
)
133+
)
134+
)
135+
.text(
136+
"Add glob pattern for resource files that should not be embeded in the binary"
137+
)
96138
parser
97139
.opt[Unit]("embed-resources")
98140
.optional()
@@ -137,6 +179,7 @@ object NativeConfigOptions {
137179
.opt[String]("ltp")
138180
.valueName("<keystring=value>")
139181
.optional()
182+
.unbounded()
140183
.action((x, c) =>
141184
c.copy(nativeConfig =
142185
c.nativeConfig.copy(ltp = c.nativeConfig.ltp :+ x)
@@ -145,6 +188,29 @@ object NativeConfigOptions {
145188
.text(
146189
"User defined properties resolved at link-time. Multiple can be defined. Example: \"isCli=true\""
147190
)
191+
parser
192+
.opt[String]("service-providers")
193+
.valueName("<serviceName:implementationName>")
194+
.optional()
195+
.unbounded()
196+
.action((x, c) =>
197+
x.split(':') match {
198+
case Array(serviceName, serviceImplementation) =>
199+
c.copy(nativeConfig =
200+
c.nativeConfig.copy(serviceProviders =
201+
c.nativeConfig.serviceProviders :+ (serviceName, serviceImplementation)
202+
)
203+
)
204+
case _ =>
205+
parser.reportWarning(
206+
"Invalid format for 'service-providers', expected <serviceName:implementationName>"
207+
)
208+
c
209+
}
210+
)
211+
.text(
212+
"Explicitly enabled service providers that should be avaiable in the executable"
213+
)
148214
parser
149215
.opt[String]("linking-option")
150216
.valueName("<passed-option>")
@@ -196,5 +262,22 @@ object NativeConfigOptions {
196262
.text(
197263
"Path to the `clang++` executable. Internally discovered if not specified."
198264
)
265+
parser
266+
.opt[String]("sanitizer")
267+
.optional()
268+
.action { (x, c) =>
269+
val supportedSanitizers = Seq("thread", "address", "undefined")
270+
if (supportedSanitizers.contains(x))
271+
c.copy(nativeConfig = c.nativeConfig.copy(sanitizer = Some(x)))
272+
else {
273+
parser.reportWarning(
274+
s"Unsupported sanitizer type '$x', allowed values: ${supportedSanitizers.mkString(", ")}"
275+
)
276+
c
277+
}
278+
}
279+
.text(
280+
"Path to the `clang++` executable. Internally discovered if not specified."
281+
)
199282
}
200283
}

cli/src/main/scala/scala/scalanative/cli/options/SemanticsConfigOptions.scala

+12-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import scopt.OptionParser
44
import scala.scalanative.build
55

66
case class SemanticsConfigOptions(
7-
finalFields: Option[JVMMemoryModelCompliance] = None
7+
finalFields: Option[JVMMemoryModelCompliance] = None,
8+
strictExternCalls: Option[Boolean] = None
89
)
910

1011
sealed abstract class JVMMemoryModelCompliance {
@@ -40,6 +41,15 @@ object SemanticsConfigOptions {
4041
.valueName("<final-fields-semantics> (none, relaxed, or stricts)")
4142
.optional()
4243
.action((x, c) => update(c)(_.copy(finalFields = Some(x))))
43-
.text("Maximal number of allowed nested inlines.")
44+
.text(
45+
"JVM memory model compliance for final, either 'none' (no special handling), 'relaxed' (default, synchronization of annotated fiedls), `strict` (synchronization of all final fields)"
46+
)
47+
parser
48+
.opt[Boolean]("strict-extern-call-semantics")
49+
.optional()
50+
.action((x, c) => update(c)(_.copy(strictExternCalls = Some(x))))
51+
.text(
52+
"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."
53+
)
4454
}
4555
}

cli/src/main/scala/scala/scalanative/cli/utils/ConfigConverter.scala

+55-20
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import java.io.File
99

1010
case class BuildOptions(
1111
config: Config,
12-
outpath: Path
12+
outpath: Option[Path]
1313
)
1414

1515
object ConfigConverter {
@@ -32,7 +32,7 @@ object ConfigConverter {
3232
)
3333
} else {
3434
generateConfig(options, main, classpath).flatMap(config =>
35-
Try(Paths.get(options.config.outpath)).toEither.map(outpath =>
35+
Try(options.config.outpath.map(Paths.get(_))).toEither.map(outpath =>
3636
BuildOptions(config, outpath)
3737
)
3838
)
@@ -54,24 +54,27 @@ object ConfigConverter {
5454
options.nativeConfig.baseName match {
5555
case Some(name) => Right(name)
5656
case _ =>
57-
Paths
58-
.get(
59-
options.config.outpath
60-
.replace('/', File.separatorChar)
57+
options.config.outpath
58+
.map(
59+
_.replace('/', File.separatorChar)
6160
.replace('\\', File.separatorChar)
6261
)
63-
.getFileName()
64-
.toString()
65-
.split('.')
66-
.headOption match {
67-
case Some(name) => Right(name)
68-
case None =>
69-
Left(
70-
new IllegalArgumentException(
71-
s"Invalid output path, failed to resolve base name of output file for path '${options.config.outpath}'"
72-
)
73-
)
74-
}
62+
.fold[Either[Throwable, String]](Right("scala-native")) {
63+
Paths
64+
.get(_)
65+
.getFileName()
66+
.toString()
67+
.split('.')
68+
.headOption match {
69+
case Some(name) => Right(name)
70+
case None =>
71+
Left(
72+
new IllegalArgumentException(
73+
s"Invalid output path, failed to resolve base name of output file for path '${options.config.outpath}'"
74+
)
75+
)
76+
}
77+
}
7578
}
7679
for {
7780
clang <- toPathOrDiscover(options.nativeConfig.clang)(Discover.clang())
@@ -80,16 +83,32 @@ object ConfigConverter {
8083
)
8184
ltp <- LinktimePropertyParser.parseAll(options.nativeConfig.ltp)
8285
baseName <- resolveBaseName
83-
} yield NativeConfig.empty
86+
default = NativeConfig.empty
87+
} yield default
8488
.withMode(options.nativeConfig.mode)
8589
.withLTO(options.nativeConfig.lto)
8690
.withGC(options.nativeConfig.gc)
8791
.withLinkStubs(options.nativeConfig.linkStubs)
8892
.withCheck(options.nativeConfig.check)
8993
.withCheckFatalWarnings(options.nativeConfig.checkFatalWarnings)
94+
.withCheckFeatures(
95+
options.nativeConfig.checkFeatures.getOrElse(default.checkFeatures)
96+
)
9097
.withDump(options.nativeConfig.dump)
9198
.withOptimize(!options.nativeConfig.noOptimize)
9299
.withEmbedResources(options.nativeConfig.embedResources)
100+
.withResourceIncludePatterns(
101+
options.nativeConfig.resourceIncludePatterns match {
102+
case Nil => default.resourceIncludePatterns
103+
case patterns => patterns
104+
}
105+
)
106+
.withResourceExcludePatterns(
107+
options.nativeConfig.resourceExcludePatterns match {
108+
case Nil => default.resourceExcludePatterns
109+
case patterns => patterns
110+
}
111+
)
93112
.withTargetTriple(options.nativeConfig.targetTriple)
94113
.withClang(clang)
95114
.withClangPP(clangPP)
@@ -105,8 +124,22 @@ object ConfigConverter {
105124
.withSourceLevelDebuggingConfig(
106125
generateSourceLevelDebuggingConfig(options.sourceLevelDebuggingConfig)
107126
)
127+
.withServiceProviders(
128+
options.nativeConfig.serviceProviders.groupBy(_._1).map {
129+
case (key, values) => (key, values.map(_._2))
130+
}
131+
)
132+
.withSanitizer(
133+
options.nativeConfig.sanitizer.flatMap(sanitizerFromString)
134+
)
108135
}
109136

137+
private def sanitizerFromString(v: String): Option[Sanitizer] = Seq(
138+
Sanitizer.ThreadSanitizer,
139+
Sanitizer.AddressSanitizer,
140+
Sanitizer.UndefinedBehaviourSanitizer
141+
).find(_.name == v)
142+
110143
private def generateOptimizerConfig(
111144
options: OptimizerConfigOptions
112145
): OptimizerConfig = {
@@ -124,7 +157,9 @@ object ConfigConverter {
124157
val c0 = SemanticsConfig.default
125158
val c1 =
126159
options.finalFields.map(_.convert).foldLeft(c0)(_.withFinalFields(_))
127-
c1
160+
val c2 =
161+
options.strictExternCalls.foldLeft(c1)(_.withStrictExternCallSemantics(_))
162+
c2
128163
}
129164

130165
private def generateSourceLevelDebuggingConfig(

cli/src/test/scala/scala/scalanative/cli/utils/ConfigConverterTest.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class ConfigConverterTest extends AnyFlatSpec {
250250
) {
251251
val options = LinkerOptions(
252252
classpath = dummyArguments.toList,
253-
config = dummyConfigOptions.copy(outpath = path),
253+
config = dummyConfigOptions.copy(outpath = Some(path)),
254254
nativeConfig = NativeConfigOptions(baseName = None)
255255
)
256256
val resolved = ConfigConverter
@@ -263,7 +263,7 @@ class ConfigConverterTest extends AnyFlatSpec {
263263
it should "prioritize baseName over outpath" in {
264264
val options = LinkerOptions(
265265
classpath = dummyArguments.toList,
266-
config = dummyConfigOptions.copy(outpath = "my-bin.exe"),
266+
config = dummyConfigOptions.copy(outpath = Some("my-bin.exe")),
267267
nativeConfig = NativeConfigOptions(baseName = Some("my-app"))
268268
)
269269
val resolved = ConfigConverter

0 commit comments

Comments
 (0)