Skip to content

Commit 8887045

Browse files
MaciejG604Gedochao
andauthored
Make object wrapper extend App (#2556)
* Add AppCodeWrapper * Extract main class name from the wrapping process and add it to WrapperParams information * Update ScriptWrapperTests, test complex class name * Update stack trace tests * Update build tests * Update the names of main classes from scripts in tests * Improve class script wrapper, allow the object with special characters to be called with backticked name * Remove test for App wrapper since it will never succeed, scripts wrapped with App can't call scripts from 'main.sc' files * Add warning about 'main.sc' used, update tests * Bring back ObjectCodeWrapper * Update comment Co-authored-by: Piotr Chabelski <[email protected]> --------- Co-authored-by: Piotr Chabelski <[email protected]>
1 parent a8b7c66 commit 8887045

29 files changed

+527
-198
lines changed

modules/build/src/main/scala/scala/build/Build.scala

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,11 @@ object Build {
110110
logger: Logger
111111
): Either[Seq[String], String] = {
112112
val scriptInferredMainClasses =
113-
sources.inMemory.map(im => im.originalPath.map(_._1))
114-
.flatMap {
115-
case Right(originalRelPath) if originalRelPath.toString.endsWith(".sc") =>
116-
Some {
117-
originalRelPath
118-
.toString
119-
.replace(".", "_")
120-
.replace("/", ".")
121-
}
122-
case Left(VirtualScriptNameRegex(name)) => Some(s"${name}_sc")
123-
case _ => None
124-
}
113+
sources.inMemory.collect {
114+
case Sources.InMemory(_, _, _, Some(wrapperParams)) =>
115+
wrapperParams.mainClass
116+
}
117+
125118
val filteredMainClasses =
126119
mainClasses.filter(!scriptInferredMainClasses.contains(_))
127120
if (filteredMainClasses.length == 1) {
@@ -279,10 +272,12 @@ object Build {
279272

280273
val scopedSources = value(crossSources.scopedSources(baseOptions))
281274

282-
val mainSources = value(scopedSources.sources(Scope.Main, baseOptions, allInputs.workspace))
275+
val mainSources =
276+
value(scopedSources.sources(Scope.Main, baseOptions, allInputs.workspace, logger))
283277
val mainOptions = mainSources.buildOptions
284278

285-
val testSources = value(scopedSources.sources(Scope.Test, baseOptions, allInputs.workspace))
279+
val testSources =
280+
value(scopedSources.sources(Scope.Test, baseOptions, allInputs.workspace, logger))
286281
val testOptions = testSources.buildOptions
287282

288283
val inputs0 = updateInputs(

modules/build/src/main/scala/scala/build/CrossSources.scala

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import scala.util.chaining.*
4747
final case class CrossSources(
4848
paths: Seq[WithBuildRequirements[(os.Path, os.RelPath)]],
4949
inMemory: Seq[WithBuildRequirements[Sources.InMemory]],
50-
defaultMainClass: Option[String],
50+
defaultMainElemPath: Option[os.Path],
5151
resourceDirs: Seq[WithBuildRequirements[os.Path]],
5252
buildOptions: Seq[WithBuildRequirements[BuildOptions]],
5353
unwrappedScripts: Seq[WithBuildRequirements[Sources.UnwrappedScript]]
@@ -130,7 +130,7 @@ final case class CrossSources(
130130
ScopedSources(
131131
crossSources0.paths.map(_.scopedValue(defaultScope)),
132132
crossSources0.inMemory.map(_.scopedValue(defaultScope)),
133-
defaultMainClass,
133+
defaultMainElemPath,
134134
crossSources0.resourceDirs.map(_.scopedValue(defaultScope)),
135135
crossSources0.buildOptions.map(_.scopedValue(defaultScope)),
136136
crossSources0.unwrappedScripts.map(_.scopedValue(defaultScope))
@@ -273,12 +273,9 @@ object CrossSources {
273273
)
274274
}).flatten
275275

276-
val defaultMainClassOpt: Option[String] = for {
277-
mainClassPath <- allInputs.defaultMainClassElement
278-
.map(s => ScopePath.fromPath(s.path).subPath)
279-
processedMainClass <- preprocessedSources.find(_.scopePath.subPath == mainClassPath)
280-
mainClass <- processedMainClass.mainClassOpt
281-
} yield mainClass
276+
val defaultMainElemPath = for {
277+
defaultMainElem <- allInputs.defaultMainClassElement
278+
} yield defaultMainElem.path
282279

283280
val pathsWithDirectivePositions
284281
: Seq[(WithBuildRequirements[(os.Path, os.RelPath)], Option[Position.File])] =
@@ -369,7 +366,7 @@ object CrossSources {
369366
CrossSources(
370367
paths,
371368
inMemory,
372-
defaultMainClassOpt,
369+
defaultMainElemPath,
373370
resourceDirs,
374371
buildOptions,
375372
unwrappedScripts

modules/build/src/main/scala/scala/build/ScopedSources.scala

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import java.nio.charset.StandardCharsets
55
import scala.build.EitherCps.{either, value}
66
import scala.build.errors.BuildException
77
import scala.build.info.{BuildInfo, ScopedBuildInfo}
8+
import scala.build.internal.AppCodeWrapper
9+
import scala.build.internal.util.WarningMessages
810
import scala.build.options.{BuildOptions, HasScope, Scope}
911
import scala.build.preprocessing.ScriptPreprocessor
1012

@@ -28,7 +30,7 @@ import scala.build.preprocessing.ScriptPreprocessor
2830
final case class ScopedSources(
2931
paths: Seq[HasScope[(os.Path, os.RelPath)]],
3032
inMemory: Seq[HasScope[Sources.InMemory]],
31-
defaultMainClass: Option[String],
33+
defaultMainElemPath: Option[os.Path],
3234
resourceDirs: Seq[HasScope[os.Path]],
3335
buildOptions: Seq[HasScope[BuildOptions]],
3436
unwrappedScripts: Seq[HasScope[Sources.UnwrappedScript]]
@@ -55,7 +57,8 @@ final case class ScopedSources(
5557
def sources(
5658
scope: Scope,
5759
baseOptions: BuildOptions,
58-
workspace: os.Path
60+
workspace: os.Path,
61+
logger: Logger
5962
): Either[BuildException, Sources] = either {
6063
val combinedOptions = combinedBuildOptions(scope, baseOptions)
6164

@@ -65,6 +68,21 @@ final case class ScopedSources(
6568
.flatMap(_.valueFor(scope).toSeq)
6669
.map(_.wrap(codeWrapper))
6770

71+
codeWrapper match {
72+
case _: AppCodeWrapper.type if wrappedScripts.size > 1 =>
73+
wrappedScripts.find(_.originalPath.exists(_._1.toString == "main.sc"))
74+
.foreach(_ => logger.diagnostic(WarningMessages.mainScriptNameClashesWithAppWrapper))
75+
case _ => ()
76+
}
77+
78+
val defaultMainClass = defaultMainElemPath.flatMap { mainElemPath =>
79+
wrappedScripts.collectFirst {
80+
case Sources.InMemory(Right((_, path)), _, _, Some(wrapperParams))
81+
if mainElemPath == path =>
82+
wrapperParams.mainClass
83+
}
84+
}
85+
6886
val needsBuildInfo = combinedOptions.sourceGeneratorOptions.useBuildInfo.getOrElse(false)
6987

7088
val maybeBuildInfoSource = if (needsBuildInfo && scope == Scope.Main)

modules/build/src/main/scala/scala/build/bsp/BspImpl.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,12 @@ final class BspImpl(
123123
pprint.err.log(scopedSources)
124124

125125
val sourcesMain = value {
126-
scopedSources.sources(Scope.Main, sharedOptions, allInputs.workspace)
126+
scopedSources.sources(Scope.Main, sharedOptions, allInputs.workspace, persistentLogger)
127127
.left.map((_, Scope.Main))
128128
}
129129

130130
val sourcesTest = value {
131-
scopedSources.sources(Scope.Test, sharedOptions, allInputs.workspace)
131+
scopedSources.sources(Scope.Test, sharedOptions, allInputs.workspace, persistentLogger)
132132
.left.map((_, Scope.Test))
133133
}
134134

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package scala.build.internal
2+
3+
case object AppCodeWrapper extends CodeWrapper {
4+
override def mainClassObject(className: Name) = className
5+
6+
def apply(
7+
code: String,
8+
pkgName: Seq[Name],
9+
indexedWrapperName: Name,
10+
extraCode: String,
11+
scriptPath: String
12+
) = {
13+
val wrapperObjectName = indexedWrapperName.backticked
14+
15+
val packageDirective =
16+
if (pkgName.isEmpty) "" else s"package ${AmmUtil.encodeScalaSourcePath(pkgName)}" + "\n"
17+
val top = AmmUtil.normalizeNewlines(
18+
s"""$packageDirective
19+
|
20+
|object $wrapperObjectName extends App {
21+
|val scriptPath = \"\"\"$scriptPath\"\"\"
22+
|""".stripMargin
23+
)
24+
val bottom = AmmUtil.normalizeNewlines(
25+
s"""
26+
|$extraCode
27+
|}
28+
|""".stripMargin
29+
)
30+
31+
(top, bottom)
32+
}
33+
}

modules/build/src/main/scala/scala/build/internal/ClassCodeWrapper.scala

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ package scala.build.internal
66
* Scala 3 feature 'export'<br> Incompatible with native JS members - the wrapper is a class
77
*/
88
case object ClassCodeWrapper extends CodeWrapper {
9+
10+
override def mainClassObject(className: Name): Name =
11+
Name(className.raw ++ "_sc")
912
def apply(
1013
code: String,
1114
pkgName: Seq[Name],
1215
indexedWrapperName: Name,
1316
extraCode: String,
1417
scriptPath: String
1518
) = {
16-
val name = CodeWrapper.mainClassObject(indexedWrapperName).backticked
19+
val name = mainClassObject(indexedWrapperName).backticked
1720
val wrapperClassName = Name(indexedWrapperName.raw ++ "$_").backticked
1821
val mainObjectCode =
1922
AmmUtil.normalizeNewlines(s"""|object $name {
@@ -34,29 +37,27 @@ case object ClassCodeWrapper extends CodeWrapper {
3437
| }
3538
|}
3639
|
37-
|export $name.script as ${indexedWrapperName.backticked}
40+
|export $name.script as `${indexedWrapperName.raw}`
3841
|""".stripMargin)
3942

4043
val packageDirective =
4144
if (pkgName.isEmpty) "" else s"package ${AmmUtil.encodeScalaSourcePath(pkgName)}" + "\n"
4245

43-
// indentation is important in the generated code, so we don't want scalafmt to touch that
44-
// format: off
45-
val top = AmmUtil.normalizeNewlines(s"""
46-
$packageDirective
47-
48-
49-
final class $wrapperClassName {
50-
def args = $name.args$$
51-
def scriptPath = \"\"\"$scriptPath\"\"\"
52-
""")
53-
val bottom = AmmUtil.normalizeNewlines(s"""
54-
$extraCode
55-
}
56-
57-
$mainObjectCode
58-
""")
59-
// format: on
46+
val top = AmmUtil.normalizeNewlines(
47+
s"""$packageDirective
48+
|
49+
|final class $wrapperClassName {
50+
|def args = $name.args$$
51+
|def scriptPath = \"\"\"$scriptPath\"\"\"
52+
|""".stripMargin
53+
)
54+
val bottom = AmmUtil.normalizeNewlines(
55+
s"""$extraCode
56+
|}
57+
|
58+
|$mainObjectCode
59+
|""".stripMargin
60+
)
6061

6162
(top, bottom)
6263
}

modules/build/src/main/scala/scala/build/internal/ObjectCodeWrapper.scala

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ package scala.build.internal
55
* threads from script
66
*/
77
case object ObjectCodeWrapper extends CodeWrapper {
8+
9+
override def mainClassObject(className: Name): Name =
10+
Name(className.raw ++ "_sc")
811
def apply(
912
code: String,
1013
pkgName: Seq[Name],
1114
indexedWrapperName: Name,
1215
extraCode: String,
1316
scriptPath: String
1417
) = {
15-
val name = CodeWrapper.mainClassObject(indexedWrapperName).backticked
18+
val name = mainClassObject(indexedWrapperName).backticked
1619
val aliasedWrapperName = name + "$$alias"
1720
val funHashCodeMethod =
1821
if (name == "main_sc")
@@ -46,23 +49,22 @@ case object ObjectCodeWrapper extends CodeWrapper {
4649
|}""".stripMargin
4750
else ""
4851

49-
// indentation is important in the generated code, so we don't want scalafmt to touch that
50-
// format: off
51-
val top = AmmUtil.normalizeNewlines(s"""
52-
$packageDirective
53-
52+
val top = AmmUtil.normalizeNewlines(
53+
s"""$packageDirective
54+
|
55+
|object ${indexedWrapperName.backticked} {
56+
|def args = $name.args$$
57+
|def scriptPath = \"\"\"$scriptPath\"\"\"
58+
|""".stripMargin
59+
)
5460

55-
object ${indexedWrapperName.backticked} {
56-
def args = $name.args$$
57-
def scriptPath = \"\"\"$scriptPath\"\"\"
58-
""")
59-
val bottom = AmmUtil.normalizeNewlines(s"""
60-
$extraCode
61-
}
62-
$aliasObject
63-
$mainObjectCode
64-
""")
65-
// format: on
61+
val bottom = AmmUtil.normalizeNewlines(
62+
s"""$extraCode
63+
|}
64+
|$aliasObject
65+
|$mainObjectCode
66+
|""".stripMargin
67+
)
6668

6769
(top, bottom)
6870
}

modules/build/src/main/scala/scala/build/internal/util/WarningMessages.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,7 @@ object WarningMessages {
120120
|$recommendedMsg
121121
|""".stripMargin
122122
}
123+
124+
val mainScriptNameClashesWithAppWrapper =
125+
"Script file named 'main.sc' detected, keep in mind that accessing it from other scripts is impossible due to a clash of `main` symbols"
123126
}

modules/build/src/main/scala/scala/build/preprocessing/PreprocessedSource.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ sealed abstract class PreprocessedSource extends Product with Serializable {
1313
def scopePath: ScopePath
1414
def directivesPositions: Option[Position.File]
1515
def distinctPathOrSource: String = this match {
16-
case PreprocessedSource.OnDisk(p, _, _, _, _, _, _) => p.toString
17-
case PreprocessedSource.InMemory(op, rp, _, _, _, _, _, _, _, _, _) => s"$op; $rp"
18-
case PreprocessedSource.UnwrappedScript(p, _, _, _, _, _, _, _, _, _) => p.toString
19-
case PreprocessedSource.NoSourceCode(_, _, _, _, p) => p.toString
16+
case p: PreprocessedSource.OnDisk => p.path.toString
17+
case p: PreprocessedSource.InMemory => s"${p.originalPath}; ${p.relPath}"
18+
case p: PreprocessedSource.UnwrappedScript => p.originalPath.toString
19+
case p: PreprocessedSource.NoSourceCode => p.path.toString
2020
}
2121
}
2222

@@ -58,11 +58,12 @@ object PreprocessedSource {
5858
optionsWithTargetRequirements: List[WithBuildRequirements[BuildOptions]],
5959
requirements: Option[BuildRequirements],
6060
scopedRequirements: Seq[Scoped[BuildRequirements]],
61-
mainClassOpt: Option[String],
6261
scopePath: ScopePath,
6362
directivesPositions: Option[Position.File],
6463
wrapScriptFun: CodeWrapper => (String, WrapperParams)
65-
) extends PreprocessedSource
64+
) extends PreprocessedSource {
65+
override def mainClassOpt: Option[String] = None
66+
}
6667

6768
final case class NoSourceCode(
6869
options: Option[BuildOptions],

0 commit comments

Comments
 (0)