Skip to content

Commit 3612b84

Browse files
authored
Merge pull request #9 from tindzk/feat/compiler-deps
Add support for compiler plug-ins
2 parents da8eec2 + 93fd260 commit 3612b84

18 files changed

+274
-76
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.bloop/
2+
.idea/
23
target/
34
/.idea/
45
/build/
5-
/test/
6+
/test/meta-module/

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ scalaVersion = "" # If unset, the project-wide default will be used
223223
root = "" # Module root path for IntelliJ
224224
sources = [] # List of source directories and files
225225
scalaDeps = [] # Module-specific Scala dependencies
226+
compilerDeps = [] # Compiler plug-ins (specified in the same format as scalaDeps)
226227
moduleDeps = [] # Module dependencies
227228
mainClass = "" # Optional entry point; needed for running/packaging module
228229
targets = [] # Platform targets
@@ -444,6 +445,20 @@ Setting `moduleDeps` to `core`, gives `webapp` and `server` access to its compil
444445

445446
`bloop compile webapp` triggers the compilation of `core-js` whereas `bloop compile server` would compile `core-jvm`.
446447

448+
### Compiler plug-ins
449+
A module can add Scala plug-ins with `compilerDeps`. The setting behaves like `scalaDeps`, but also adds the `-Xplugin` parameter to the Scala compiler when the module is compiled. A minimal example looks as follows:
450+
451+
```toml
452+
[module.macros.js]
453+
compilerDeps = [
454+
["org.scalamacros", "paradise", "2.1.1", "full"]
455+
]
456+
```
457+
458+
Note that plug-ins are inherited by all dependent modules such that `compilerDeps` only needs to be defined on the base module.
459+
460+
For a complete cross-compiled Macro Paradise example, please refer to [this project](test/example-paradise/).
461+
447462
### Project dependencies
448463
External builds can be imported into the scope by using the `import` setting at the root level. It can point to a build file, or its parent directory in which case it will attempt to load `<path>/build.toml`. From the imported file, Seed will only make its modules accessible. Other project-level settings are being ignored. Multiple projects can be imported as `import` is a list.
449464

src/main/scala/seed/artefact/ArtefactResolution.scala

+25-17
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ object ArtefactResolution {
4141
val scalaJsVersion = build.project.scalaJsVersion.get
4242

4343
Set(
44-
Artefact.ScalaJsCompiler,
4544
Artefact.ScalaJsLibrary
4645
).map(artefact =>
4746
javaDepFromArtefact(artefact, scalaJsVersion, JavaScript,
@@ -54,7 +53,6 @@ object ArtefactResolution {
5453
val scalaNativeVersion = build.project.scalaNativeVersion.get
5554

5655
Set(
57-
Artefact.ScalaNativePlugin,
5856
Artefact.ScalaNativeJavalib,
5957
Artefact.ScalaNativeScalalib,
6058
Artefact.ScalaNativeNativelib,
@@ -105,24 +103,34 @@ object ArtefactResolution {
105103
}
106104

107105
def compilerDeps(build: Build, module: Module): List[Set[JavaDep]] = {
108-
def f(build: Build, module: Module): Set[JavaDep] = {
106+
def f(build: Build, module: Module, platform: Platform): Set[JavaDep] = {
109107
import build.project.scalaOrganisation
110-
val scalaVersion = BuildConfig.scalaVersion(build.project, List(module))
111-
112-
Set(
113-
Artefact.scalaCompiler(scalaOrganisation),
114-
Artefact.scalaLibrary(scalaOrganisation),
115-
Artefact.scalaReflect(scalaOrganisation)
116-
).map(artefact =>
117-
javaDepFromArtefact(artefact, scalaVersion, JVM, scalaVersion,
118-
scalaVersion))
108+
val platformVer = BuildConfig.platformVersion(build, module, platform)
109+
val compilerVer = BuildConfig.scalaVersion(build.project, List(module))
110+
111+
val scalaDeps = Set(
112+
Artefact.scalaCompiler(scalaOrganisation) -> compilerVer,
113+
Artefact.scalaLibrary(scalaOrganisation) -> compilerVer,
114+
Artefact.scalaReflect(scalaOrganisation) -> compilerVer
115+
) ++ (
116+
if (platform == Platform.Native)
117+
Set(Artefact.ScalaNativePlugin -> platformVer)
118+
else if (platform == Platform.JavaScript)
119+
Set(Artefact.ScalaJsCompiler -> platformVer)
120+
else Set()
121+
)
122+
123+
scalaDeps.map { case (artefact, version) =>
124+
javaDepFromArtefact(artefact, version, platform, platformVer,
125+
compilerVer)
126+
} ++ module.compilerDeps.map(dep =>
127+
javaDepFromScalaDep(dep, platform, platformVer, compilerVer))
119128
}
120129

121-
List(
122-
f(build, module),
123-
module.jvm.toSet.flatMap(module => f(build, module)),
124-
module.js.toSet.flatMap(module => f(build, module)),
125-
module.native.toSet.flatMap(module => f(build, module))
130+
module.targets.map(target =>
131+
f(build,
132+
BuildConfig.platformModule(module, target).getOrElse(module),
133+
target)
126134
).filter(_.nonEmpty)
127135
}
128136

src/main/scala/seed/config/BuildConfig.scala

+16-14
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ import seed.Log
1010
import seed.config.util.TomlUtils
1111

1212
object BuildConfig {
13-
def fixPath(projectPath: Path, path: Path): Path =
14-
if (path.toString.startsWith("/")) path
15-
else projectPath.resolve(path).normalize()
13+
import TomlUtils.parseBuildToml
1614

1715
def load(path: Path): (Path, Build) = {
1816
if (!Files.exists(path)) {
@@ -39,7 +37,7 @@ object BuildConfig {
3937
}
4038

4139
val parsed = TomlUtils.parseFile(
42-
projectFile, parseToml(projectPath), "build file")
40+
projectFile, parseBuildToml(projectPath), "build file")
4341

4442
(projectPath.normalize(), processBuild(parsed, { path =>
4543
val (_, build) = load(path)
@@ -51,16 +49,6 @@ object BuildConfig {
5149
}))
5250
}
5351

54-
def parseToml(projectPath: Path)(content: String) = {
55-
import toml._
56-
import toml.Codecs._
57-
import seed.config.util.TomlUtils.Codecs._
58-
59-
implicit val pCodec = pathCodec(fixPath(projectPath, _))
60-
61-
Toml.parseAs[Build](content)
62-
}
63-
6452
def processBuild(build: Build, parse: Path => Build): Build = {
6553
val parsed = build match { case p =>
6654
p.copy(module = p.module.mapValues { module =>
@@ -160,6 +148,13 @@ object BuildConfig {
160148
.flatMap(_.scalaVersion)
161149
.getOrElse(project.scalaVersion)
162150

151+
def platformVersion(build: Build, module: Module, platform: Platform): String =
152+
platform match {
153+
case JVM => BuildConfig.scalaVersion(build.project, List(module))
154+
case JavaScript => build.project.scalaJsVersion.get
155+
case Native => build.project.scalaNativeVersion.get
156+
}
157+
163158
def isCrossBuild(module: Module): Boolean = module.targets.toSet.size > 1
164159

165160
def targetName(build: Build, name: String, platform: Platform): String =
@@ -197,6 +192,13 @@ object BuildConfig {
197192
jvmModuleDeps(module).flatMap(m =>
198193
List(m) ++ collectJvmModuleDeps(build, build.module(m)))
199194

195+
def collectModuleDeps(build: Build, module: Module, platform: Platform): List[String] =
196+
platform match {
197+
case JVM => collectJvmModuleDeps(build, module)
198+
case JavaScript => collectJsModuleDeps(build, module)
199+
case Native => collectNativeModuleDeps(build, module)
200+
}
201+
200202
def collectJsClassPath(buildPath: Path,
201203
build: Build,
202204
module: Module): List[Path] =

src/main/scala/seed/config/util/TomlUtils.scala

+15-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import org.apache.commons.io.FileUtils
66
import seed.Log
77
import seed.cli.util.Ansi
88
import seed.model.Build.VersionTag
9-
import seed.model.Platform
9+
import seed.model.{Build, Platform}
1010
import toml.{Codec, Value}
1111

1212
object TomlUtils {
@@ -63,4 +63,18 @@ object TomlUtils {
6363
case Right(c) => c
6464
}
6565
}
66+
67+
def fixPath(projectPath: Path, path: Path): Path =
68+
if (path.toString.startsWith("/")) path
69+
else projectPath.resolve(path).normalize()
70+
71+
def parseBuildToml(projectPath: Path)(content: String): Either[Codec.Error, Build] = {
72+
import toml._
73+
import toml.Codecs._
74+
import seed.config.util.TomlUtils.Codecs._
75+
76+
implicit val pCodec = pathCodec(fixPath(projectPath, _))
77+
78+
Toml.parseAs[Build](content)
79+
}
6680
}

src/main/scala/seed/generation/Bloop.scala

+14-23
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ object Bloop {
2020
def writeBloop(projectPath: Path,
2121
name: String,
2222
bloopPath: Path,
23-
buildPath: Path,
2423
dependencies: List[String],
2524
classesDir: Path,
2625
classPath: List[Path],
@@ -35,7 +34,7 @@ object Bloop {
3534
directory = projectPath.toAbsolutePath,
3635
sources = sources.map(_.toAbsolutePath),
3736
dependencies = dependencies,
38-
classpath = scalaCompiler.fold(List[Path]())(_.fullClassPath.sorted),
37+
classpath = scalaCompiler.fold(List[Path]())(_.fullClassPath.map(_.toAbsolutePath).sorted),
3938
out = classesDir.toAbsolutePath,
4039
classesDir = classesDir.toAbsolutePath,
4140
`scala` = scalaCompiler.map(scalaCompiler =>
@@ -105,15 +104,12 @@ object Bloop {
105104
val scalaVersion = BuildConfig.scalaVersion(project,
106105
List(js, parentModule.js.getOrElse(Module()), parentModule))
107106

108-
val scalaJsArtefacts = Set(
109-
Artefact.ScalaJsCompiler, Artefact.ScalaJsLibrary
110-
).map(a =>
111-
a -> Coursier.artefactPath(resolution, a, JavaScript,
107+
def resolveLibrary(artefact: Artefact): Path =
108+
Coursier.artefactPath(resolution, artefact, JavaScript,
112109
scalaJsVersion.get, scalaVersion, scalaJsVersion.get).get
113-
).toMap
114110

115-
val scalaJsPluginOption =
116-
"-Xplugin:" + scalaJsArtefacts(Artefact.ScalaJsCompiler)
111+
val plugIns = util.ScalaCompiler.compilerPlugIns(build,
112+
parentModule, compilerResolution, JavaScript)
117113

118114
val resolvedDeps = Coursier.localArtefacts(
119115
resolution,
@@ -127,8 +123,7 @@ object Bloop {
127123
val classPath = resolvedDeps.map(_.libraryJar) ++
128124
(if (test) List(buildPath.resolve(name))
129125
else List()
130-
) ++ parentClassPaths ++
131-
List(scalaJsArtefacts(Artefact.ScalaJsLibrary))
126+
) ++ parentClassPaths ++ List(resolveLibrary(Artefact.ScalaJsLibrary))
132127

133128
val scalaCompiler = ArtefactResolution.resolveScalaCompiler(
134129
compilerResolution, scalaOrganisation, scalaVersion, classPath)
@@ -137,13 +132,12 @@ object Bloop {
137132
projectPath = projectPath,
138133
name = bloopName,
139134
bloopPath = bloopPath,
140-
buildPath = buildPath,
141135
dependencies = dependencies,
142136
classesDir = classesDir,
143137
classPath = classPath,
144138
sources = sources ++ js.sources,
145139
scalaCompiler = Some(scalaCompiler),
146-
scalaOptions = scalaOptions :+ scalaJsPluginOption,
140+
scalaOptions = scalaOptions ++ plugIns,
147141
testFrameworks = if (test) testFrameworks else List(),
148142
platform = Some(Config.Platform.Js(
149143
Config.JsConfig(
@@ -212,7 +206,6 @@ object Bloop {
212206
List(native, parentModule.native.getOrElse(Module()), parentModule))
213207

214208
val scalaNativeArtefacts = Set(
215-
Artefact.ScalaNativePlugin,
216209
Artefact.ScalaNativeJavalib,
217210
Artefact.ScalaNativeScalalib,
218211
Artefact.ScalaNativeNativelib,
@@ -222,8 +215,8 @@ object Bloop {
222215
scalaNativeVersion.get, scalaVersion, scalaNativeVersion.get).get
223216
).toMap
224217

225-
val scalaJsPluginOption =
226-
"-Xplugin:" + scalaNativeArtefacts(Artefact.ScalaNativePlugin)
218+
val plugIns = util.ScalaCompiler.compilerPlugIns(build,
219+
parentModule, compilerResolution, Native)
227220

228221
val resolvedDeps =
229222
Coursier.localArtefacts(resolution,
@@ -253,13 +246,12 @@ object Bloop {
253246
projectPath = projectPath,
254247
name = bloopName,
255248
bloopPath = bloopPath,
256-
buildPath = buildPath,
257249
dependencies = dependencies,
258250
classesDir = classesDir,
259251
classPath = classPath,
260252
sources = sources ++ native.sources,
261253
scalaCompiler = Some(scalaCompiler),
262-
scalaOptions = scalaOptions :+ scalaJsPluginOption,
254+
scalaOptions = scalaOptions ++ plugIns,
263255
testFrameworks = if (test) testFrameworks else List(),
264256
platform = Some(Config.Platform.Native(Config.NativeConfig(
265257
version = scalaNativeVersion.get,
@@ -315,10 +307,12 @@ object Bloop {
315307
val scalaDeps = (parentModule.scalaDeps ++ jvm.scalaDeps).map(dep =>
316308
ArtefactResolution.javaDepFromScalaDep(dep, JVM, scalaVersion,
317309
scalaVersion))
318-
319310
val resolvedDeps = Coursier.localArtefacts(resolution,
320311
(javaDeps ++ scalaDeps).toSet)
321312

313+
val plugIns = util.ScalaCompiler.compilerPlugIns(build,
314+
parentModule, compilerResolution, JVM)
315+
322316
val dependencies = if (test) List(name)
323317
else (moduleDeps ++ jvm.moduleDeps).map(name => BuildConfig.targetName(build, name, JVM))
324318
val classesDir = buildPath.resolve(bloopName)
@@ -335,14 +329,13 @@ object Bloop {
335329
projectPath = projectPath,
336330
name = bloopName,
337331
bloopPath = bloopPath,
338-
buildPath = buildPath,
339332
dependencies = dependencies,
340333
classesDir = classesDir,
341334
classPath = classPath,
342335
sources = sources ++ jvm.sources,
343336
resources = jvm.resources,
344337
scalaCompiler = Some(scalaCompiler),
345-
scalaOptions = scalaOptions,
338+
scalaOptions = scalaOptions ++ plugIns,
346339
testFrameworks = if (test) testFrameworks else List(),
347340
platform = Some(Config.Platform.Jvm(
348341
Config.JvmConfig(None, List()),
@@ -434,7 +427,6 @@ object Bloop {
434427
projectPath = projectPath,
435428
name = name + "-test",
436429
bloopPath = bloopPath,
437-
buildPath = buildPath,
438430
dependencies = targets.map(t => name + "-" + t.id + "-test"),
439431
classesDir = buildPath,
440432
classPath = List(),
@@ -450,7 +442,6 @@ object Bloop {
450442
projectPath = projectPath,
451443
name = name,
452444
bloopPath = bloopPath,
453-
buildPath = buildPath,
454445
dependencies = module.targets.map(t => name + "-" + t.id),
455446
classesDir = buildPath,
456447
classPath = List(),

src/main/scala/seed/generation/Idea.scala

+14-2
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,21 @@ object Idea {
119119
}
120120

121121
def createCompilerSettings(build: Build,
122+
compilerResolution: List[Coursier.ResolutionResult],
122123
ideaPath: Path,
123124
modules: List[String]): Unit = {
124-
val xml = IdeaFile.createScalaCompiler(build.project.scalaOptions, modules)
125+
// Group all modules by additional settings; create compiler configuration
126+
// for each unique set of parameters
127+
val modulePlugIns = modules.filter(build.module.contains).map(m =>
128+
m -> util.ScalaCompiler.compilerPlugIns(
129+
build, build.module(m), compilerResolution, JVM))
130+
val compilerSettings =
131+
modulePlugIns.groupBy(_._2).mapValues(_.map(_._1)).toList.map {
132+
case (settings, modules) =>
133+
(build.project.scalaOptions ++ settings, modules)
134+
}
135+
136+
val xml = IdeaFile.createScalaCompiler(compilerSettings)
125137
FileUtils.write(ideaPath.resolve("scala_compiler.xml").toFile, xml, "UTF-8")
126138
}
127139

@@ -389,7 +401,7 @@ object Idea {
389401
resolution, name, module)
390402
}
391403

392-
createCompilerSettings(build, ideaPath, modules)
404+
createCompilerSettings(build, compilerResolution, ideaPath, modules)
393405
writeModules(projectPath, ideaPath, modulesPath, modules)
394406

395407
Log.info("IDEA project has been created")

0 commit comments

Comments
 (0)