Skip to content

Commit 8904970

Browse files
committed
Change option structure to make main a required option
Linktime property parsing was also adjusted to parse values as strings. Unrecognized options are now being allowed, with a warning message. Test were adjusted to accomodate changes.
1 parent 6d23dbf commit 8904970

File tree

9 files changed

+60
-57
lines changed

9 files changed

+60
-57
lines changed

cli/src/main/scala/scala/scalanative/cli/ScalaNativeCli.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,27 @@ import scala.scalanative.cli.options.BuildInfo
1111

1212
object ScalaNativeCli extends CaseApp[CliOptions] {
1313

14+
override def ignoreUnrecognized: Boolean = true
15+
1416
def run(options: CliOptions, args: RemainingArgs) = {
1517
if (options.misc.version) {
1618
println(BuildInfo.nativeVersion)
19+
} else if (options.config.main == None) {
20+
println("Required option not specified: --main")
21+
exit(1)
1722
} else {
18-
val positionalArgs = args.all
19-
val buildOptionsMaybe = ConfigConverter.convert(options, positionalArgs)
23+
val ignoredArgs = args.all.filter(_.startsWith("--"))
24+
ignoredArgs.foreach { arg =>
25+
println(s"Unrecognised argument: ${arg}")
26+
}
27+
val main = options.config.main.get
28+
val classpath = args.all.filter(!_.startsWith("--"))
29+
val buildOptionsMaybe = ConfigConverter.convert(options, main, classpath)
2030

2131
buildOptionsMaybe match {
2232
case Left(thrown) =>
2333
System.err.println(thrown.getMessage())
34+
exit(1)
2435
case Right(buildOptions) =>
2536
Scope { implicit scope =>
2637
Build.build(buildOptions.config, buildOptions.outpath)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import caseapp._
44

55
@AppName("ScalaNativeCli")
66
@ProgName("scala-native-cli")
7-
@ArgsName("main-class] [nir-sources")
7+
@ArgsName("classpath")
88
case class CliOptions(
99
@Recurse
1010
config: ConfigOptions,

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ package scala.scalanative.cli.options
33
import caseapp._
44

55
case class ConfigOptions(
6+
@Group("Config")
7+
@HelpMessage("Required main class.")
8+
@ValueDescription("<main>")
9+
main: Option[String] = None,
610
@Group("Config")
711
@ExtraName("o")
812
@HelpMessage(
9-
"Required path of the resulting output binary. [./scala-native-out]"
13+
"Path of the resulting output binary. [./scala-native-out]"
1014
)
1115
@ValueDescription("<output-path>")
1216
outpath: String = "scala-native-out",

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,16 @@ object ConfigConverter {
1818

1919
def convert(
2020
options: CliOptions,
21-
positionalArgs: Seq[String]
21+
main: String,
22+
classpath: Seq[String]
2223
): Either[Throwable, BuildOptions] = {
23-
if (positionalArgs.size < 2) {
24+
if (classpath.size == 0) {
2425
Left(
2526
new IllegalArgumentException(
26-
"Not enough positional arguments. Main and at least one source file need to be specified."
27+
"Classpath not specified. Pass classpath files as positional arguments."
2728
)
2829
)
2930
} else {
30-
val main = positionalArgs.head
31-
val classpath = positionalArgs.tail
3231
generateConfig(options, main, classpath).flatMap(config =>
3332
Try(Paths.get(options.config.outpath)).toEither.map(outpath =>
3433
BuildOptions(config, outpath)

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

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ import scala.scalanative.cli.options.MiscOptions
1616
class ConfigConverterTest extends AnyFlatSpec {
1717
val dummyLoggerOptions = LoggerOptions()
1818
val dummyNativeConfigOptions = NativeConfigOptions()
19-
val dummyConfigOptions = ConfigOptions()
19+
val dummyConfigOptions = ConfigOptions(main = Some("Main$"))
2020
val dummyMiscOptions = MiscOptions()
2121

2222
val dummyArguments =
23-
Seq("Main$", "A.nir", "B.nir")
23+
Seq("A.jar", "B.jar")
24+
val dummyMain = "Main$"
2425

2526
val dummyCliOptions = CliOptions(
2627
config = dummyConfigOptions,
@@ -30,18 +31,15 @@ class ConfigConverterTest extends AnyFlatSpec {
3031
)
3132

3233
"ArgParser" should "parse default options" in {
33-
val config = ConfigConverter.convert(dummyCliOptions, dummyArguments)
34+
val config =
35+
ConfigConverter.convert(dummyCliOptions, dummyMain, dummyArguments)
3436
assert(config.isRight)
3537
}
3638

3739
it should "report incomplete arguments" in {
3840
val noArgs = Seq()
39-
val noArgsResult = ConfigConverter.convert(dummyCliOptions, noArgs)
40-
assert(noArgsResult.isLeft)
41-
assert(noArgsResult.left.get.isInstanceOf[IllegalArgumentException])
42-
43-
val mainOnly = Seq("Main$")
44-
val mainOnlyResult = ConfigConverter.convert(dummyCliOptions, mainOnly)
41+
val mainOnlyResult =
42+
ConfigConverter.convert(dummyCliOptions, dummyMain, noArgs)
4543
assert(mainOnlyResult.isLeft)
4644
assert(mainOnlyResult.left.get.isInstanceOf[IllegalArgumentException])
4745
}
@@ -59,7 +57,7 @@ class ConfigConverterTest extends AnyFlatSpec {
5957
)
6058

6159
val config =
62-
ConfigConverter.convert(dummyCliOptions, Seq("$Main") ++ classPathStrings)
60+
ConfigConverter.convert(dummyCliOptions, dummyMain, classPathStrings)
6361

6462
assert(config != None)
6563
assert(config.right.get.config.classPath.sameElements(expected))
@@ -76,7 +74,11 @@ class ConfigConverterTest extends AnyFlatSpec {
7674
dummyMiscOptions
7775
)
7876
val config =
79-
ConfigConverter.convert(options, dummyArguments).right.get.config
77+
ConfigConverter
78+
.convert(options, dummyMain, dummyArguments)
79+
.right
80+
.get
81+
.config
8082
assert(config.compilerConfig.gc == expectedGC)
8183
}
8284
gcAssertion("immix", GC.immix)
@@ -96,7 +98,11 @@ class ConfigConverterTest extends AnyFlatSpec {
9698
dummyMiscOptions
9799
)
98100
val config =
99-
ConfigConverter.convert(options, dummyArguments).right.get.config
101+
ConfigConverter
102+
.convert(options, dummyMain, dummyArguments)
103+
.right
104+
.get
105+
.config
100106
assert(config.compilerConfig.mode == expectedMode)
101107
}
102108
modeAssertion("debug", Mode.debug)
@@ -115,7 +121,11 @@ class ConfigConverterTest extends AnyFlatSpec {
115121
dummyMiscOptions
116122
)
117123
val config =
118-
ConfigConverter.convert(options, dummyArguments).right.get.config
124+
ConfigConverter
125+
.convert(options, dummyMain, dummyArguments)
126+
.right
127+
.get
128+
.config
119129
assert(config.compilerConfig.lto == expectedLto)
120130
}
121131
ltoAssertion("none", LTO.none)
@@ -141,7 +151,7 @@ class ConfigConverterTest extends AnyFlatSpec {
141151
)
142152

143153
val nativeConfig =
144-
ConfigConverter.convert(options, dummyArguments).right.get
154+
ConfigConverter.convert(options, dummyMain, dummyArguments).right.get
145155

146156
assert(nativeConfig.config.compilerConfig.clang == expectedClangPath)
147157
assert(nativeConfig.config.compilerConfig.clangPP == expectedClangPPPath)
@@ -160,13 +170,13 @@ class ConfigConverterTest extends AnyFlatSpec {
160170
dummyMiscOptions
161171
)
162172
val default = ConfigConverter
163-
.convert(dummyCliOptions, dummyArguments)
173+
.convert(dummyCliOptions, dummyMain, dummyArguments)
164174
.right
165175
.get
166176
.config
167177
.compilerConfig
168178
val nonDefault = ConfigConverter
169-
.convert(options, dummyArguments)
179+
.convert(options, dummyMain, dummyArguments)
170180
.right
171181
.get
172182
.config

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ case class NativeConfigOptions(
5353
@ValueDescription("<path-to-clang>")
5454
clang: Option[String] = None,
5555
@Group("Native Config")
56+
@Name("clang++")
5657
@HelpMessage(
5758
"Path to the `clang++` executable. Internally discovered if not specified."
5859
)

cli/version_newer/src/main/scala/scala/scalanative/cli/utils/LinktimePropertyParser.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,8 @@ object LinktimePropertyParser {
2525
) {
2626
Left(getLtpPatternException(inputPattern))
2727
} else {
28-
val (key, booleanString) = (matcher.group(1), matcher.group(2))
29-
30-
Try(booleanString.toBoolean).toEither match {
31-
case Right(value) => Right((key, value))
32-
case Left(value) => Left(value)
33-
}
28+
val (key, value) = (matcher.group(1), matcher.group(2))
29+
Right((key, value))
3430
}
3531
}
3632

cli/version_newer/src/test/scala/scala/scalanative/cli/LinktimePropertyParserTest.scala

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class LinktimePropertyParserTest extends AnyFlatSpec {
88
"LinktimePropertyParser" should "handle boolean value types correctly" in {
99
val input = List("isTesting=true", "isNotTesting=False")
1010
val expected =
11-
Map[String, Any]("isTesting" -> true, "isNotTesting" -> false)
11+
Map[String, Any]("isTesting" -> "true", "isNotTesting" -> "False")
1212
val obtained = LinktimePropertyParser.toMap(input)
1313
assert(obtained.right.get == expected)
1414
}
@@ -21,24 +21,6 @@ class LinktimePropertyParserTest extends AnyFlatSpec {
2121
val noKey = List("=true")
2222
val obtainedNoKey = LinktimePropertyParser.toMap(noKey)
2323
assert(obtainedNoKey.isLeft)
24-
25-
val noBoolean = List("key=")
26-
val obtainedNoBoolean = LinktimePropertyParser.toMap(noBoolean)
27-
assert(obtainedNoBoolean.isLeft)
28-
}
29-
30-
it should "return error on non boolean values" in {
31-
val withString = List("key=string")
32-
val obtainedWithString = LinktimePropertyParser.toMap(withString)
33-
assert(obtainedWithString.isLeft)
34-
35-
val withNull = List("key=null")
36-
val obtainedWithNull = LinktimePropertyParser.toMap(withNull)
37-
assert(obtainedWithNull.isLeft)
38-
39-
val withFloat = List("key=1.1")
40-
val obtainedWithFloat = LinktimePropertyParser.toMap(withFloat)
41-
assert(obtainedWithFloat.isLeft)
4224
}
4325

4426
}

scala-native-cli-integration-test-runner/src/test/run/integration-test/test

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@
44
> runCli --version
55

66
# -- Link and check if outpath exists
7-
> runCli --outpath target/out1 -v -v Main$
7+
> runCli --outpath target/out1 -v -v --main Main$
88
$ exists target/out1
99

1010
# -- Fail to link without main specified
1111
> runCli --outpath target/out2 -v -v
1212
-$ exists target/out2
1313

1414
# -- Fail to link with an incorrect option
15-
> runCli --outpath target/out3 -v -v --gc fast Main$
15+
> runCli --outpath target/out3 -v -v --gc fast --main Main$
1616
-$ exists target/out3
1717

18-
# -- Fail to link with an unspecified option
19-
> runCli --outpath target/out4 -v -v --unspecified Main$
20-
-$ exists target/out4
18+
# -- Link even with an unspecified option
19+
> runCli --outpath target/out4 -v -v --unspecified --main Main$
20+
$ exists target/out4
2121

2222
# -- Do not write nir files if not specified
23-
> runCli --outpath target/out5 -v -v Main$
23+
> runCli --outpath target/out5 -v -v --main Main$
2424
-$ exists optimized.hnir
2525
$ exists target/out5
2626

2727
# -- Write nir files to workdir if specified
2828
$ mkdir native-dir
29-
> runCli --outpath target/out6 -v -v --workdir native-dir --dump Main$
29+
> runCli --outpath target/out6 -v -v --workdir native-dir --dump --main Main$
3030
$ exists native-dir/optimized.hnir
3131
$ exists target/out6

0 commit comments

Comments
 (0)