Skip to content

Commit e2fdfbf

Browse files
Merge pull request #126 from alexarchambault/using-directives
Accept dependencies via using directives too
2 parents c36de8a + ac077fd commit e2fdfbf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+964
-158
lines changed

.scalafmt.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ fileOverride {
2222
}
2323
project.excludeFilters = [
2424
".metals"
25-
"examples/scala-3" # Scala 3 scripts not supported yet
25+
"examples" # Scala 3 scripts and using directives not supported yet
2626
"out"
2727
]

build.sc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ object `generate-reference-doc` extends SbtModule {
8888
def moduleDeps = Seq(
8989
cli
9090
)
91+
def repositories = super.repositories ++ Seq(
92+
coursier.Repositories.sonatype("snapshots")
93+
)
9194
def ivyDeps = Agg(
9295
Deps.caseApp,
9396
Deps.munit
@@ -115,6 +118,9 @@ class Build(val crossScalaVersion: String)
115118
`test-runner`(),
116119
`tasty-lib`()
117120
)
121+
def repositories = super.repositories ++ Seq(
122+
coursier.Repositories.sonatype("snapshots")
123+
)
118124
def ivyDeps = super.ivyDeps() ++ Agg(
119125
Deps.asm,
120126
Deps.bloopConfig,
@@ -229,6 +235,9 @@ trait Cli extends SbtModule with CliLaunchers with ScalaCliPublishModule with Fo
229235
build(Scala.defaultInternal),
230236
`test-runner`(Scala.defaultInternal)
231237
)
238+
def repositories = super.repositories ++ Seq(
239+
coursier.Repositories.sonatype("snapshots")
240+
)
232241
def ivyDeps = super.ivyDeps() ++ Agg(
233242
Deps.caseApp,
234243
Deps.coursierLauncher,

examples/cross-build/Hello.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// using scala "2.12.14", "2.13.6", "3.0.1"
2+
3+
object Hello {
4+
def main(args: Array[String]): Unit =
5+
println("Hello")
6+
}

examples/utest/MyTests.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import $ivy.`com.lihaoyi::utest::0.7.10`, utest._
1+
// using "com.lihaoyi::utest::0.7.10"
2+
3+
import utest._
24

35
object MyTests extends TestSuite {
46
val tests = Tests {

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

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -106,38 +106,51 @@ object Build {
106106
)
107107
}
108108

109-
def build(
109+
private def build(
110110
inputs: Inputs,
111111
options: BuildOptions,
112112
logger: Logger,
113113
buildClient: BloopBuildClient,
114-
bloopServer: bloop.BloopServer
115-
): Build = {
114+
bloopServer: bloop.BloopServer,
115+
crossBuilds: Boolean
116+
): (Build, Seq[Build]) = {
116117

117-
val sources = Sources.forInputs(
118+
val crossSources = CrossSources.forInputs(
118119
inputs,
119120
Sources.defaultPreprocessors(options.scriptOptions.codeWrapper.getOrElse(CustomCodeWrapper))
120121
)
121122

123+
val sources = crossSources.sources(options)
124+
122125
val options0 = options.orElse(sources.buildOptions)
123126
val inputs0 = updateInputs(inputs, options)
124127

125128
val generatedSources = sources.generateSources(inputs0.generatedSrcRoot)
126129

127-
buildClient.setProjectParams(options0.projectParams)
128-
build(
129-
inputs0,
130-
sources,
131-
inputs0.generatedSrcRoot,
132-
generatedSources,
133-
options0,
134-
logger,
135-
buildClient,
136-
bloopServer
137-
)
130+
def doBuild(buildOptions: BuildOptions) = {
131+
buildClient.setProjectParams(buildOptions.projectParams)
132+
build(
133+
inputs0,
134+
sources,
135+
inputs0.generatedSrcRoot,
136+
generatedSources,
137+
buildOptions,
138+
logger,
139+
buildClient,
140+
bloopServer
141+
)
142+
}
143+
144+
val mainBuild = doBuild(options0)
145+
146+
val extraBuilds =
147+
if (crossBuilds) options0.crossOptions.map(opt => doBuild(opt))
148+
else Nil
149+
150+
(mainBuild, extraBuilds)
138151
}
139152

140-
def build(
153+
private def build(
141154
inputs: Inputs,
142155
sources: Sources,
143156
generatedSrcRoot0: os.Path,
@@ -186,8 +199,9 @@ object Build {
186199
options: BuildOptions,
187200
threads: BuildThreads,
188201
bloopConfig: BloopRifleConfig,
189-
logger: Logger
190-
): Build = {
202+
logger: Logger,
203+
crossBuilds: Boolean
204+
): (Build, Seq[Build]) = {
191205

192206
val buildClient = BloopBuildClient.create(
193207
logger,
@@ -209,7 +223,8 @@ object Build {
209223
options,
210224
logger,
211225
buildClient,
212-
bloopServer
226+
bloopServer,
227+
crossBuilds = crossBuilds
213228
)
214229
}
215230
}
@@ -218,17 +233,19 @@ object Build {
218233
inputs: Inputs,
219234
options: BuildOptions,
220235
bloopConfig: BloopRifleConfig,
221-
logger: Logger
222-
): Build =
223-
build(inputs, options, BuildThreads.create(), bloopConfig, logger)
236+
logger: Logger,
237+
crossBuilds: Boolean
238+
): (Build, Seq[Build]) =
239+
build(inputs, options, BuildThreads.create(), bloopConfig, logger, crossBuilds = crossBuilds)
224240

225241
def watch(
226242
inputs: Inputs,
227243
options: BuildOptions,
228244
bloopConfig: BloopRifleConfig,
229245
logger: Logger,
246+
crossBuilds: Boolean,
230247
postAction: () => Unit = () => ()
231-
)(action: Build => Unit): Watcher = {
248+
)(action: (Build, Seq[Build]) => Unit): Watcher = {
232249

233250
val buildClient = BloopBuildClient.create(
234251
logger,
@@ -249,8 +266,15 @@ object Build {
249266

250267
def run() = {
251268
try {
252-
val build0 = build(inputs, options, logger, buildClient, bloopServer)
253-
action(build0)
269+
val (build0, crossBuilds0) = build(
270+
inputs,
271+
options,
272+
logger,
273+
buildClient,
274+
bloopServer,
275+
crossBuilds = crossBuilds
276+
)
277+
action(build0, crossBuilds0)
254278
}
255279
catch {
256280
case NonFatal(e) =>
@@ -606,7 +630,14 @@ object Build {
606630
runJmh = build.options.jmhOptions.runJmh.map(_ => false)
607631
)
608632
)
609-
val jmhBuild = Build.build(jmhInputs, updatedOptions, logger, buildClient, bloopServer)
633+
val (jmhBuild, _) = Build.build(
634+
jmhInputs,
635+
updatedOptions,
636+
logger,
637+
buildClient,
638+
bloopServer,
639+
crossBuilds = false
640+
)
610641
Some(jmhBuild)
611642
}
612643
else None
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package scala.build
2+
3+
import scala.build.internal.CodeWrapper
4+
import scala.build.options.{BuildOptions, BuildRequirements, HasBuildRequirements}
5+
import scala.build.preprocessing._
6+
7+
final case class CrossSources(
8+
paths: Seq[HasBuildRequirements[(os.Path, os.RelPath)]],
9+
inMemory: Seq[HasBuildRequirements[(Either[String, os.Path], os.RelPath, String, Int)]],
10+
mainClass: Option[String],
11+
resourceDirs: Seq[os.Path],
12+
buildOptions: Seq[HasBuildRequirements[(Either[String, os.Path], BuildOptions)]]
13+
) {
14+
15+
def sources(baseOptions: BuildOptions): Sources = {
16+
17+
val sharedOptions = buildOptions
18+
.filter(_.requirements.isEmpty)
19+
.map(_.value._2)
20+
.foldLeft(baseOptions)(_ orElse _)
21+
22+
val retainedScalaVersion = sharedOptions.scalaParams.scalaVersion
23+
24+
val buildOptionsWithScalaVersion = buildOptions
25+
.flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)
26+
.filter(_.requirements.isEmpty)
27+
.map(_.value._2)
28+
.foldLeft(sharedOptions)(_ orElse _)
29+
30+
val platform =
31+
if (buildOptionsWithScalaVersion.scalaJsOptions.enable)
32+
BuildRequirements.Platform.JS
33+
else if (buildOptionsWithScalaVersion.scalaNativeOptions.enable)
34+
BuildRequirements.Platform.Native
35+
else
36+
BuildRequirements.Platform.JVM
37+
38+
// FIXME Not 100% sure the way we compute the intermediate and final BuildOptions
39+
// is consistent (we successively filter out / retain options to compute a scala
40+
// version and platform, which might not be the version and platform of the final
41+
// BuildOptions).
42+
43+
Sources(
44+
paths
45+
.flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)
46+
.flatMap(_.withPlatform(platform).toSeq)
47+
.map(_.value),
48+
inMemory
49+
.flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)
50+
.flatMap(_.withPlatform(platform).toSeq)
51+
.map(_.value),
52+
mainClass,
53+
resourceDirs,
54+
buildOptions
55+
.flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)
56+
.flatMap(_.withPlatform(platform).toSeq)
57+
.map(_.value._2)
58+
.foldLeft(BuildOptions() /* not baseOptions */ )(_ orElse _)
59+
)
60+
}
61+
62+
}
63+
64+
object CrossSources {
65+
66+
def forInputs(
67+
inputs: Inputs,
68+
preprocessors: Seq[Preprocessor]
69+
): CrossSources = {
70+
71+
val preprocessedSources = inputs.flattened().flatMap { elem =>
72+
preprocessors.iterator.flatMap(p => p.preprocess(elem).iterator).toStream.headOption
73+
.getOrElse(Nil) // FIXME Warn about unprocessed stuff?
74+
}
75+
76+
val buildOptions = preprocessedSources.flatMap {
77+
case d: PreprocessedSource.OnDisk =>
78+
d.options.toSeq.map { opt =>
79+
HasBuildRequirements(
80+
d.requirements.getOrElse(BuildRequirements()),
81+
(Right(d.path), opt)
82+
)
83+
}
84+
case m: PreprocessedSource.InMemory =>
85+
m.options.toSeq.map { opt =>
86+
HasBuildRequirements(
87+
m.requirements.getOrElse(BuildRequirements()),
88+
(m.reportingPath, opt)
89+
)
90+
}
91+
case n: PreprocessedSource.NoSourceCode =>
92+
val elem = HasBuildRequirements(
93+
n.requirements.getOrElse(BuildRequirements()),
94+
(Right(n.path), n.options.getOrElse(BuildOptions()))
95+
)
96+
Seq(elem)
97+
case _ =>
98+
Nil
99+
}
100+
101+
val mainClassOpt = inputs.mainClassElement
102+
.collect {
103+
case elem: Inputs.SingleElement =>
104+
preprocessors.iterator
105+
.flatMap(p => p.preprocess(elem).iterator)
106+
.toStream.headOption
107+
.getOrElse(Nil)
108+
.flatMap(_.mainClassOpt.toSeq)
109+
.headOption
110+
}
111+
.flatten
112+
113+
val paths = preprocessedSources.collect {
114+
case d: PreprocessedSource.OnDisk =>
115+
HasBuildRequirements(
116+
d.requirements.getOrElse(BuildRequirements()),
117+
(d.path, d.path.relativeTo(inputs.workspace))
118+
)
119+
}
120+
val inMemory = preprocessedSources.collect {
121+
case m: PreprocessedSource.InMemory =>
122+
HasBuildRequirements(
123+
m.requirements.getOrElse(BuildRequirements()),
124+
(m.reportingPath, m.relPath, m.code, m.ignoreLen)
125+
)
126+
}
127+
128+
val resourceDirs = inputs.elements.collect {
129+
case r: Inputs.ResourceDirectory =>
130+
r.path
131+
}
132+
133+
CrossSources(paths, inMemory, mainClassOpt, resourceDirs, buildOptions)
134+
}
135+
}

modules/build/src/main/scala/scala/build/Sources.scala

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -42,47 +42,4 @@ object Sources {
4242
ConfigPreprocessor,
4343
ScalaPreprocessor
4444
)
45-
46-
def forInputs(
47-
inputs: Inputs,
48-
preprocessors: Seq[Preprocessor]
49-
): Sources = {
50-
51-
val preprocessedSources = inputs.flattened().flatMap { elem =>
52-
preprocessors.iterator.flatMap(p => p.preprocess(elem).iterator).toStream.headOption
53-
.getOrElse(Nil) // FIXME Warn about unprocessed stuff?
54-
}
55-
56-
val buildOptions = preprocessedSources
57-
.flatMap(_.options.toSeq)
58-
.foldLeft(BuildOptions())(_.orElse(_))
59-
60-
val mainClassOpt = inputs.mainClassElement
61-
.collect {
62-
case elem: Inputs.SingleElement =>
63-
preprocessors.iterator
64-
.flatMap(p => p.preprocess(elem).iterator)
65-
.toStream.headOption
66-
.getOrElse(Nil)
67-
.flatMap(_.mainClassOpt.toSeq)
68-
.headOption
69-
}
70-
.flatten
71-
72-
val paths = preprocessedSources.collect {
73-
case d: PreprocessedSource.OnDisk =>
74-
(d.path, d.path.relativeTo(inputs.workspace))
75-
}
76-
val inMemory = preprocessedSources.collect {
77-
case m: PreprocessedSource.InMemory =>
78-
(m.reportingPath, m.relPath, m.code, m.ignoreLen)
79-
}
80-
81-
val resourceDirs = inputs.elements.collect {
82-
case r: Inputs.ResourceDirectory =>
83-
r.path
84-
}
85-
86-
Sources(paths, inMemory, mainClassOpt, resourceDirs, buildOptions)
87-
}
8845
}

0 commit comments

Comments
 (0)