diff --git a/src/main/scala/seed/Cli.scala b/src/main/scala/seed/Cli.scala index 25eac35..4d44827 100644 --- a/src/main/scala/seed/Cli.scala +++ b/src/main/scala/seed/Cli.scala @@ -270,12 +270,12 @@ ${underlined("Usage:")} seed [--build=] [--config=] import command._ val config = SeedConfig.load(configPath) val log = Log(config) - val BuildConfig.Result(build, projectPath, _) = + val BuildConfig.Result(projectPath, modules) = BuildConfig.load(buildPath, log).getOrElse(sys.exit(1)) cli.Package.ui( config, projectPath, - build, + modules, module, output, libs, @@ -287,9 +287,16 @@ ${underlined("Usage:")} seed [--build=] [--config=] ) => val config = SeedConfig.load(configPath) val log = Log(config) - val BuildConfig.Result(build, projectPath, _) = + val BuildConfig.Result(projectPath, modules) = BuildConfig.load(buildPath, log).getOrElse(sys.exit(1)) - cli.Generate.ui(config, projectPath, projectPath, build, command, log) + cli.Generate.ui( + config, + projectPath, + projectPath, + modules, + command, + log + ) case Success(Config(configPath, _, command: Command.Server)) => val config = SeedConfig.load(configPath) val log = Log(config) diff --git a/src/main/scala/seed/artefact/ArtefactResolution.scala b/src/main/scala/seed/artefact/ArtefactResolution.scala index d6b4fe9..7ba0424 100644 --- a/src/main/scala/seed/artefact/ArtefactResolution.scala +++ b/src/main/scala/seed/artefact/ArtefactResolution.scala @@ -5,11 +5,12 @@ import java.nio.file.Path import seed.Cli.PackageConfig import MavenCentral.{CompilerVersion, PlatformVersion} import seed.cli.util.Ansi -import seed.model.Build.{Dep, JavaDep, Module, ScalaDep} +import seed.model.Build.{Dep, JavaDep, Module, Resolvers, ScalaDep} import seed.model.Platform.{JVM, JavaScript, Native} -import seed.model.{Artefact, Build, Platform, Resolution} +import seed.model.{Artefact, Platform, Resolution} import seed.Log import seed.config.BuildConfig +import seed.config.BuildConfig.Build object ArtefactResolution { def javaDepFromScalaDep( @@ -52,9 +53,9 @@ object ArtefactResolution { version ) - def jsPlatformDeps(build: Build, module: Module): Set[JavaDep] = { - val scalaVersion = BuildConfig.scalaVersion(build.project, List(module)) - val scalaJsVersion = build.project.scalaJsVersion.get + def jsPlatformDeps(module: Module): Set[JavaDep] = { + val scalaVersion = module.scalaVersion.get + val scalaJsVersion = module.scalaJsVersion.get Set( Artefact.ScalaJsLibrary @@ -70,9 +71,9 @@ object ArtefactResolution { ) } - def nativePlatformDeps(build: Build, modules: List[Module]): Set[JavaDep] = { - val scalaVersion = BuildConfig.scalaVersion(build.project, modules) - val scalaNativeVersion = build.project.scalaNativeVersion.get + def nativePlatformDeps(module: Module): Set[JavaDep] = { + val scalaVersion = module.scalaVersion.get + val scalaNativeVersion = module.scalaNativeVersion.get Set( Artefact.ScalaNativeJavalib, @@ -91,9 +92,9 @@ object ArtefactResolution { ) } - def nativeLibraryDep(build: Build, modules: List[Module]): JavaDep = { - val scalaVersion = BuildConfig.scalaVersion(build.project, modules) - val scalaNativeVersion = build.project.scalaNativeVersion.get + def nativeLibraryDep(module: Module): JavaDep = { + val scalaVersion = module.scalaVersion.get + val scalaNativeVersion = module.scalaNativeVersion.get javaDepFromArtefact( Artefact.ScalaNativeNativelib, @@ -104,67 +105,67 @@ object ArtefactResolution { ) } - def jvmArtefacts(stack: List[Module]): Set[(Platform, Dep)] = - stack.flatMap(_.scalaDeps).map(dep => JVM -> dep).toSet ++ - stack.flatMap(_.javaDeps).map(dep => JVM -> dep).toSet - - def jvmDeps(build: Build, stack: List[Module]): Set[JavaDep] = { - val scalaVersion = BuildConfig.scalaVersion(build.project, stack) - - stack - .flatMap(_.scalaDeps) - .map(dep => javaDepFromScalaDep(dep, JVM, scalaVersion, scalaVersion)) + def jvmArtefacts(module: Module): Set[(Platform, Dep)] = + (module.scalaDeps ++ module.javaDeps).map(dep => JVM -> dep).toSet + + def jvmDeps(module: Module): Set[JavaDep] = + module.scalaDeps + .map( + dep => + javaDepFromScalaDep( + dep, + JVM, + module.scalaVersion.get, + module.scalaVersion.get + ) + ) .toSet ++ - stack.flatMap(_.javaDeps).toSet - } - - def jsArtefacts(stack: List[Module]): Set[(Platform, Dep)] = - stack.flatMap(_.scalaDeps).map(dep => JavaScript -> dep).toSet - - def jsDeps(build: Build, stack: List[Module]): Set[JavaDep] = - build.project.scalaJsVersion match { - case None => Set() - case Some(scalaJsVersion) => - val scalaVersion = BuildConfig.scalaVersion(build.project, stack) - stack - .flatMap(_.scalaDeps) - .map( - dep => - javaDepFromScalaDep(dep, JavaScript, scalaJsVersion, scalaVersion) + module.javaDeps.toSet + + def jsArtefacts(module: Module): Set[(Platform, Dep)] = + module.scalaDeps.map(dep => JavaScript -> dep).toSet + + def jsDeps(module: Module): Set[JavaDep] = + module.scalaDeps + .map( + dep => + javaDepFromScalaDep( + dep, + JavaScript, + module.scalaJsVersion.get, + module.scalaVersion.get ) - .toSet - } - - def nativeArtefacts(stack: List[Module]): Set[(Platform, Dep)] = - stack.flatMap(_.scalaDeps).map(dep => Native -> dep).toSet - - def nativeDeps(build: Build, stack: List[Module]): Set[JavaDep] = - build.project.scalaNativeVersion match { - case None => Set() - case Some(scalaNativeVersion) => - val scalaVersion = BuildConfig.scalaVersion(build.project, stack) - stack - .flatMap(_.scalaDeps) - .map( - dep => - javaDepFromScalaDep(dep, Native, scalaNativeVersion, scalaVersion) + ) + .toSet + + def nativeArtefacts(module: Module): Set[(Platform, Dep)] = + module.scalaDeps.map(dep => Native -> dep).toSet + + def nativeDeps(module: Module): Set[JavaDep] = + module.scalaDeps + .map( + dep => + javaDepFromScalaDep( + dep, + Native, + module.scalaNativeVersion.get, + module.scalaVersion.get ) - .toSet - } + ) + .toSet - def compilerDeps(build: Build, module: Module): List[Set[JavaDep]] = { - def f(build: Build, module: Module, platform: Platform): Set[JavaDep] = { - import build.project.scalaOrganisation - val platformModule = BuildConfig.platformModule(module, platform) + def compilerDeps(module: Module): List[Set[JavaDep]] = { + def f(module: Module, platform: Platform): Set[JavaDep] = { + val platformModule = BuildConfig.platformModule(module, platform).get - val platformVer = BuildConfig.platformVersion(build, module, platform) - val compilerVer = - BuildConfig.scalaVersion(build.project, platformModule.toList :+ module) + val platformVer = BuildConfig.platformVersion(module, platform) + val compilerVer = platformModule.scalaVersion.get + val organisation = platformModule.scalaOrganisation.get val scalaDeps = Set( - Artefact.scalaCompiler(scalaOrganisation) -> compilerVer, - Artefact.scalaLibrary(scalaOrganisation) -> compilerVer, - Artefact.scalaReflect(scalaOrganisation) -> compilerVer + Artefact.scalaCompiler(organisation) -> compilerVer, + Artefact.scalaLibrary(organisation) -> compilerVer, + Artefact.scalaReflect(organisation) -> compilerVer ) ++ ( if (platform == Platform.Native) Set(Artefact.ScalaNativePlugin -> platformVer) @@ -173,10 +174,6 @@ object ArtefactResolution { else Set() ) - val dependencies = mergeDeps[ScalaDep]( - module.compilerDeps ++ platformModule.toList.flatMap(_.compilerDeps) - ) - scalaDeps.map { case (artefact, version) => javaDepFromArtefact( @@ -186,110 +183,51 @@ object ArtefactResolution { platformVer, compilerVer ) - } ++ dependencies.map( + } ++ platformModule.compilerDeps.map( dep => javaDepFromScalaDep(dep, platform, platformVer, compilerVer) ) } - module.targets.map(target => f(build, module, target)).filter(_.nonEmpty) + module.targets.map(target => f(module, target)).filter(_.nonEmpty) } def allCompilerDeps(build: Build): List[Set[JavaDep]] = - build.module.values.toList.flatMap(compilerDeps(build, _)).distinct + build.values.toList.flatMap(m => compilerDeps(m.module)).distinct def platformDeps(build: Build, module: Module): Set[JavaDep] = module.targets.toSet[Platform].flatMap { target => - if (target == JavaScript) - jsPlatformDeps(build, module.js.getOrElse(Module())) - else if (target == Native) - nativePlatformDeps(build, module.native.toList) + if (target == JavaScript) jsPlatformDeps(module.js.get) + else if (target == Native) nativePlatformDeps(module.native.get) else Set[JavaDep]() } - def libraryDeps( - build: Build, - module: Module, - platforms: Set[Platform], - parent: Module = Module() - ): Set[JavaDep] = { - val targets = if (module.targets.isEmpty) parent.targets else module.targets - targets.toSet[Platform].intersect(platforms).flatMap { target => - // Shared libraries - if (target == JVM) - jvmDeps( - build, - module.jvm.toList ++ parent.jvm.toList ++ List(module, parent) - ) - else if (target == JavaScript) - jsDeps( - build, - module.js.toList ++ parent.js.toList ++ List(module, parent) - ) - else - nativeDeps( - build, - module.native.toList ++ parent.native.toList ++ List(module, parent) - ) - } ++ - (if (!platforms.contains(JVM)) Set() - else - module.jvm.toSet.flatMap( - jvm => - jvmDeps(build, List(jvm, parent.jvm.getOrElse(Module()), module)) - )) ++ + def libraryDeps(module: Module, platforms: Set[Platform]): Set[JavaDep] = + (if (!platforms.contains(JVM)) Set() + else module.jvm.toSet.flatMap(jvmDeps)) ++ (if (!platforms.contains(JavaScript)) Set() - else - module.js.toSet.flatMap( - js => jsDeps(build, List(js, parent.js.getOrElse(Module()), module)) - )) ++ + else module.js.toSet.flatMap(jsDeps)) ++ (if (!platforms.contains(Native)) Set() - else - module.native.toSet.flatMap( - native => - nativeDeps( - build, - List(native, parent.native.getOrElse(Module()), module) - ) - )) ++ - module.test.toSet.flatMap(libraryDeps(build, _, platforms, module)) - } + else module.native.toSet.flatMap(nativeDeps)) ++ + module.test.toSet.flatMap(libraryDeps(_, platforms)) - def libraryArtefacts( - build: Build, - module: Module, - parent: Module = Module() - ): Set[(Platform, Dep)] = - module.targets.toSet[Platform].flatMap { target => - if (target == JVM) jvmArtefacts(List(module, parent)) - else if (target == JavaScript) jsArtefacts(List(module, parent)) - else nativeArtefacts(List(module, parent)) - } ++ - module.jvm.toSet.flatMap( - jvm => jvmArtefacts(List(jvm, parent.jvm.getOrElse(Module()), module)) - ) ++ - module.js.toSet.flatMap( - js => jsArtefacts(List(js, parent.js.getOrElse(Module()), module)) - ) ++ - module.native.toSet.flatMap( - native => - nativeArtefacts( - List(native, parent.native.getOrElse(Module()), module) - ) - ) ++ - module.test.toSet.flatMap(libraryArtefacts(build, _, module)) + def libraryArtefacts(module: Module): Set[(Platform, Dep)] = + module.jvm.toSet.flatMap(jvmArtefacts) ++ + module.js.toSet.flatMap(jsArtefacts) ++ + module.native.toSet.flatMap(nativeArtefacts) ++ + module.test.toSet.flatMap(libraryArtefacts) def allPlatformDeps(build: Build): Set[JavaDep] = - build.module.values.toSet.flatMap(platformDeps(build, _)) + build.values.toSet.flatMap(m => platformDeps(build, m.module)) def allLibraryDeps( build: Build, platforms: Set[Platform] = Set(JVM, JavaScript, Native) ): Set[JavaDep] = - build.module.values.toSet.flatMap(libraryDeps(build, _, platforms)) + build.values.toSet.flatMap(m => libraryDeps(m.module, platforms)) def allLibraryArtefacts(build: Build): Map[Platform, Set[Dep]] = - build.module.values.toSet - .flatMap(libraryArtefacts(build, _)) + build.values.toSet + .flatMap(m => libraryArtefacts(m.module)) .groupBy(_._1) .mapValues(_.map(_._2)) @@ -340,6 +278,7 @@ object ArtefactResolution { def resolution( seedConfig: seed.model.Config, + resolvers: Resolvers, build: Build, packageConfig: PackageConfig, optionalArtefacts: Boolean, @@ -356,15 +295,15 @@ object ArtefactResolution { log.info("Configured resolvers:") log.detail("- " + Ansi.italic(resolvedIvyPath.toString) + " (Ivy)") log.detail("- " + Ansi.italic(resolvedCachePath.toString) + " (Coursier)") - build.resolvers.ivy + resolvers.ivy .foreach(ivy => log.detail("- " + Ansi.italic(ivy.url) + " (Ivy)")) - build.resolvers.maven + resolvers.maven .foreach(maven => log.detail("- " + Ansi.italic(maven) + " (Maven)")) def resolve(deps: Set[JavaDep]) = Coursier.resolveAndDownload( deps, - build.resolvers, + resolvers, resolvedIvyPath, resolvedCachePath, optionalArtefacts, diff --git a/src/main/scala/seed/cli/Build.scala b/src/main/scala/seed/cli/Build.scala index d23ed6d..c424ad3 100644 --- a/src/main/scala/seed/cli/Build.scala +++ b/src/main/scala/seed/cli/Build.scala @@ -6,9 +6,9 @@ import java.nio.file.Path import seed.Log import seed.cli.util.{Ansi, RTS, WsClient} import seed.config.BuildConfig -import seed.model import seed.model.Config import seed.Cli.Command +import seed.config.BuildConfig.Build import zio._ object Build { @@ -61,20 +61,17 @@ object Build { watch: Boolean, tmpfs: Boolean, log: Log, - onStdOut: model.Build => String => Unit + onStdOut: Build => String => Unit ): Either[List[String], UIO[Unit]] = BuildConfig.load(buildPath, log) match { case None => Left(List()) - case Some( - BuildConfig.Result(build, buildProjectPath, moduleProjectPaths) - ) => + case Some(BuildConfig.Result(buildProjectPath, build)) => val parsedModules = modules.map(util.Target.parseModuleString(build)) util.Validation.unpack(parsedModules).right.map { allModules => val processes = BuildTarget.buildTargets( build, allModules, projectPath.getOrElse(buildProjectPath), - moduleProjectPaths, watch, tmpfs, log diff --git a/src/main/scala/seed/cli/BuildTarget.scala b/src/main/scala/seed/cli/BuildTarget.scala index 89ae809..bc3e2d6 100644 --- a/src/main/scala/seed/cli/BuildTarget.scala +++ b/src/main/scala/seed/cli/BuildTarget.scala @@ -5,18 +5,16 @@ import java.nio.file.{Files, Path} import seed.Log import seed.cli.util.Ansi import seed.config.BuildConfig +import seed.config.BuildConfig.Build import seed.generation.util.PathUtil -import seed.model import seed.process.ProcessHelper - import zio._ object BuildTarget { def buildTargets( - build: model.Build, + build: Build, modules: List[util.Target.Parsed], projectPath: Path, - moduleProjectPaths: Map[String, Path], watch: Boolean, tmpfs: Boolean, log: Log @@ -38,7 +36,7 @@ object BuildTarget { case util.Target.Parsed(module, None) => module.name +: BuildConfig.collectModuleDeps(build, module.module) } - .flatMap(m => build.module(m).target.keys.toList.map(m -> _)) + .flatMap(m => build(m).module.target.keys.toList.map(m -> _)) .distinct if (targets.nonEmpty) @@ -57,8 +55,8 @@ object BuildTarget { case (m, t) => val customLog = log.prefix(Ansi.bold(s"[${format(m, t)}]: ")) - val modulePath = moduleProjectPaths(m) - val target = build.module(m).target(t) + val modulePath = build(m).path + val target = build(m).module.target(t) target.`class` match { case Some(c) => diff --git a/src/main/scala/seed/cli/Generate.scala b/src/main/scala/seed/cli/Generate.scala index c428167..b6d5847 100644 --- a/src/main/scala/seed/cli/Generate.scala +++ b/src/main/scala/seed/cli/Generate.scala @@ -6,13 +6,14 @@ import seed.{Log, model} import seed.Cli.Command import seed.generation.{Bloop, Idea} import seed.artefact.ArtefactResolution +import seed.config.BuildConfig.Build object Generate { def ui( seedConfig: model.Config, projectPath: Path, outputPath: Path, - build: model.Build, + build: Build, command: Command.Generate, log: Log ): Unit = { @@ -31,6 +32,7 @@ object Generate { val (_, platformResolution, compilerResolution) = ArtefactResolution.resolution( seedConfig, + ???, // TODO resolvers build, command.packageConfig, optionalArtefacts, diff --git a/src/main/scala/seed/cli/Link.scala b/src/main/scala/seed/cli/Link.scala index 61f15be..762c626 100644 --- a/src/main/scala/seed/cli/Link.scala +++ b/src/main/scala/seed/cli/Link.scala @@ -9,6 +9,7 @@ import seed.config.BuildConfig import seed.model import seed.model.Config import seed.Cli.Command +import seed.config.BuildConfig.Build import zio._ object Link { @@ -62,18 +63,17 @@ object Link { watch: Boolean, tmpfs: Boolean, log: Log, - onStdOut: model.Build => String => Unit + onStdOut: Build => String => Unit ): Either[List[String], UIO[Unit]] = BuildConfig.load(buildPath, log) match { case None => Left(List()) - case Some(BuildConfig.Result(build, projectPath, moduleProjectPaths)) => + case Some(BuildConfig.Result(projectPath, build)) => val parsedModules = modules.map(util.Target.parseModuleString(build)) util.Validation.unpack(parsedModules).right.map { allModules => val processes = BuildTarget.buildTargets( build, allModules, projectPath, - moduleProjectPaths, watch, tmpfs, log diff --git a/src/main/scala/seed/cli/Package.scala b/src/main/scala/seed/cli/Package.scala index 4f4b37f..c5900bb 100644 --- a/src/main/scala/seed/cli/Package.scala +++ b/src/main/scala/seed/cli/Package.scala @@ -6,10 +6,10 @@ import scala.collection.JavaConverters._ import seed.artefact.{ArtefactResolution, Coursier} import seed.cli.util.Ansi import seed.config.BuildConfig +import seed.config.BuildConfig.Build import seed.generation.util.PathUtil -import seed.model import seed.model.Build.{JavaDep, Module} -import seed.model.{Build, Config} +import seed.model.Config import seed.model.Platform.JVM import seed.{Cli, Log} @@ -17,7 +17,7 @@ object Package { def ui( seedConfig: Config, projectPath: Path, - build: model.Build, + build: Build, module: String, output: Option[Path], libs: Boolean, @@ -30,16 +30,16 @@ object Package { val platform = JVM val outputPath = output.getOrElse(buildPath.resolve("dist")) - build.module.get(module) match { + build.get(module) match { case None => log.error(s"Module ${Ansi.italic(module)} does not exist") case Some(resolvedModule) => val paths = ( List(module) ++ BuildConfig.collectJvmModuleDeps( build, - resolvedModule + resolvedModule.module ) ).map { name => - if (BuildConfig.isCrossBuild(build.module(name))) + if (BuildConfig.isCrossBuild(build(name).module)) bloopBuildPath.resolve(name + "-" + platform.id) else bloopBuildPath.resolve(name) @@ -53,8 +53,9 @@ object Package { else if (paths.isEmpty) log.error(s"No build paths were found") else { - val files = collectFiles(paths) - val jvmModule = resolvedModule.jvm.getOrElse(resolvedModule) + val files = collectFiles(paths) + val jvmModule = + resolvedModule.module.jvm.getOrElse(resolvedModule.module) val classPath = if (!libs) List() @@ -110,17 +111,18 @@ object Package { outputPath: Path, log: Log ): List[String] = { - val scalaVersion = BuildConfig.scalaVersion(build.project, List(jvmModule)) + val scalaVersion = jvmModule.scalaVersion.get val scalaLibraryDep = - JavaDep(build.project.scalaOrganisation, "scala-library", scalaVersion) + JavaDep(jvmModule.scalaOrganisation.get, "scala-library", scalaVersion) val scalaReflectDep = - JavaDep(build.project.scalaOrganisation, "scala-reflect", scalaVersion) + JavaDep(jvmModule.scalaOrganisation.get, "scala-reflect", scalaVersion) val platformDeps = Set(scalaLibraryDep, scalaReflectDep) val libraryDeps = ArtefactResolution.allLibraryDeps(build, Set(JVM)) val (resolvedDepPath, libraryResolution, platformResolution) = ArtefactResolution.resolution( seedConfig, + ???, // TODO resolvers build, packageConfig, optionalArtefacts = false, diff --git a/src/main/scala/seed/cli/Server.scala b/src/main/scala/seed/cli/Server.scala index 4a81fd7..e163120 100644 --- a/src/main/scala/seed/cli/Server.scala +++ b/src/main/scala/seed/cli/Server.scala @@ -8,8 +8,8 @@ import io.circe.{Decoder, DecodingFailure, Encoder, Json} import org.java_websocket.WebSocket import seed.Log import seed.cli.util.{BloopCli, RTS, WsServer} -import seed.model import seed.Cli.Command +import seed.config.BuildConfig.Build import seed.model.Config import scala.collection.JavaConverters._ @@ -87,7 +87,7 @@ object Server { def onStdOut( wsServer: WsServer, wsClient: WebSocket, - build: model.Build, + build: Build, serverLog: Log )(message: String): Unit = { wsClient.send(message) diff --git a/src/main/scala/seed/cli/Update.scala b/src/main/scala/seed/cli/Update.scala index 8bb3854..343b2ef 100644 --- a/src/main/scala/seed/cli/Update.scala +++ b/src/main/scala/seed/cli/Update.scala @@ -64,7 +64,7 @@ object Update { } def ui(path: Path, stable: Boolean, log: Log): Unit = { - val BuildConfig.Result(build, projectPath, _) = BuildConfig + val BuildConfig.Result(projectPath, build) = BuildConfig .load(path, log) .getOrElse(sys.exit(1)) @@ -72,7 +72,7 @@ object Update { val (compilerVersions, platformVersions, libraryArtefacts) = new Scaffold(log).checkVersions( - build.project.scalaOrganisation, + ???, // TODO build.project.scalaOrganisation, BuildConfig.buildTargets(build), buildArtefacts.mapValues(_.map(Artefact.fromDep)), stable @@ -83,12 +83,11 @@ object Update { BuildConfig.buildTargets(build).toList.sorted(Platform.Ordering).foreach { platform => val oldCompilerVersion = - build.module.values - .flatMap(BuildConfig.platformModule(_, platform)) + build.values + .flatMap(m => BuildConfig.platformModule(m.module, platform)) .view .flatMap(_.scalaVersion) - .headOption - .getOrElse(build.project.scalaVersion) + .head val newCompilerVersion = compilerVersions.get(platform) compareVersion( @@ -99,7 +98,7 @@ object Update { ) if (platform == JavaScript) { - val oldPlatformVersion = build.project.scalaJsVersion.get + val oldPlatformVersion = ??? // TODO build.project.scalaJsVersion.get val newPlatformVersion = platformVersions.get(platform) compareVersion( @@ -109,7 +108,7 @@ object Update { log ) } else if (platform == Native) { - val oldPlatformVersion = build.project.scalaNativeVersion.get + val oldPlatformVersion = ??? // TODO build.project.scalaNativeVersion.get val newPlatformVersion = platformVersions.get(platform) compareVersion( diff --git a/src/main/scala/seed/cli/util/BloopCli.scala b/src/main/scala/seed/cli/util/BloopCli.scala index c2ddb5b..20071ef 100644 --- a/src/main/scala/seed/cli/util/BloopCli.scala +++ b/src/main/scala/seed/cli/util/BloopCli.scala @@ -3,10 +3,8 @@ package seed.cli.util import java.nio.file.Path import seed.Log -import seed.model.Build +import seed.config.BuildConfig.Build import seed.process.ProcessHelper - -import seed.model import seed.model.BuildEvent import seed.model.Platform.{JVM, JavaScript, Native} import seed.model.Platform @@ -21,7 +19,7 @@ object BloopCli { def skipOutput(output: String): Boolean = output.contains("\u001b[H\u001b[2J") def parseBloopModule( - build: model.Build, + build: Build, bloopName: String ): (String, Platform) = if (bloopName.endsWith("-js")) @@ -32,13 +30,13 @@ object BloopCli { (bloopName.dropRight("-native".length), Native) else { require( - build.module(bloopName).targets.length == 1, + build(bloopName).module.targets.length == 1, "Only one target expected" ) - (bloopName, build.module(bloopName).targets.head) + (bloopName, build(bloopName).module.targets.head) } - def parseStdOut(build: model.Build)(message: String): Option[BuildEvent] = { + def parseStdOut(build: Build)(message: String): Option[BuildEvent] = { val parts = message.split(" ") if (parts(0) == "Compiling") { val (module, platform) = parseBloopModule(build, parts(1)) diff --git a/src/main/scala/seed/cli/util/Target.scala b/src/main/scala/seed/cli/util/Target.scala index ed0d310..9f6be4d 100644 --- a/src/main/scala/seed/cli/util/Target.scala +++ b/src/main/scala/seed/cli/util/Target.scala @@ -1,5 +1,6 @@ package seed.cli.util +import seed.config.BuildConfig.Build import seed.model.Build.Module import seed.model.{Build, Platform} @@ -17,11 +18,11 @@ object Target { else { def invalidName(name: String) = Left(s"Invalid module name: ${Ansi - .italic(name)}. Valid names: ${build.module.keys.mkString(", ")}") + .italic(name)}. Valid names: ${build.keys.mkString(", ")}") if (!module.contains(":")) { - if (!build.module.contains(module)) invalidName(module) - else Right(Parsed(ModuleRef(module, build.module(module)), None)) + if (!build.contains(module)) invalidName(module) + else Right(Parsed(ModuleRef(module, build(module).module), None)) } else { val parts = module.split(":") if (parts.length != 2) { @@ -29,24 +30,20 @@ object Target { } else { val (name, target) = (parts(0), parts(1)) - if (!build.module.contains(name)) invalidName(name) + if (!build.contains(name)) invalidName(name) else { - build - .module(name) - .targets + build(name).module.targets .find(_.id == target) .map(Left(_)) .orElse( - build - .module(name) - .target + build(name).module.target .get(target) .map(tgt => Right(TargetRef(target, tgt))) ) match { case None => Left(s"Invalid build target ${Ansi.italic(target)} provided") case Some(tgt) => - Right(Parsed(ModuleRef(name, build.module(name)), Some(tgt))) + Right(Parsed(ModuleRef(name, build(name).module), Some(tgt))) } } } diff --git a/src/main/scala/seed/config/BuildConfig.scala b/src/main/scala/seed/config/BuildConfig.scala index d619dbd..e82c0c4 100644 --- a/src/main/scala/seed/config/BuildConfig.scala +++ b/src/main/scala/seed/config/BuildConfig.scala @@ -1,31 +1,29 @@ package seed.config -import java.nio.file.{Files, Path, Paths} +import java.nio.file.{Files, Path} import seed.cli.util.{Ansi, ColourScheme} -import seed.model.Build.{JavaDep, Module, Project, ScalaDep} +import seed.model.Build.{JavaDep, Module, ScalaDep} import seed.model.Platform.{JVM, JavaScript, Native} -import seed.model.{Build, Platform} +import seed.model.{Build, Organisation, Platform, TomlBuild} import seed.Log +import seed.artefact.ArtefactResolution import seed.config.util.TomlUtils -import scala.collection.mutable - object BuildConfig { import TomlUtils.parseBuildToml - case class Result( - build: Build, - projectPath: Path, - moduleProjectPaths: Map[String, Path] - ) + case class ModuleConfig(module: Module, path: Path) + type Build = Map[String, ModuleConfig] + + case class Result(projectPath: Path, build: Build) def load(path: Path, log: Log): Option[Result] = loadInternal(path, log).filter( result => - result.build.module.toList.forall { + result.build.toList.forall { case (name, module) => - checkModule(result.build, name, module, log) + checkModule(result, name, module.module, log) } ) @@ -63,114 +61,100 @@ object BuildConfig { log ) .map { parsed => - val (build, moduleProjectPaths) = processBuild( + val modules = processBuild( parsed, - projectPath, { path => - loadInternal(path, log).map { - case Result(build, projectPath, moduleProjectPaths) => - parsed.module.keySet - .intersect(build.module.keySet) - .foreach( - name => - log.error( - s"Module name ${Ansi.italic(name)} is not unique" - ) - ) - (build, moduleProjectPaths) - } - } + projectPath, + path => loadInternal(path, log), + log ) - - Result(build, projectPath.normalize(), moduleProjectPaths) + Result(projectPath.normalize(), modules) } } } def processBuild( - build: Build, + build: TomlBuild, projectPath: Path, - parse: Path => Option[(Build, Map[String, Path])] - ): (Build, Map[String, Path]) = { - val parsed = build.copy( - module = build.module.mapValues { module => - val parentTargets = moduleTargets(module, module.targets) - module.copy( - targets = parentTargets, - test = module.test.map { module => - module.copy( - targets = moduleTargets( - module, - if (module.targets.isEmpty) parentTargets - else module.targets - ) - ) - } - ) - } - ) - - val imported = parsed.`import`.flatMap(parse(_)) + parse: Path => Option[Result], + log: Log + ): Build = { + val modules = + build.module.mapValues(inheritSettings(build.project.toModule)) + val imported = build.`import`.flatMap(parse(_)) + + modules.keySet + .intersect(imported.flatMap(_.build).map(_._1).toSet) + .foreach( + name => log.error(s"Module name ${Ansi.italic(name)} is not unique") + ) - val parsedModules = - parsed.module.mapValues(inheritCompilerDeps(parsed.project)) + (imported.flatMap(_.build) ++ modules.mapValues( + ModuleConfig(_, projectPath) + )).toMap + } - val importedModules = imported.foldLeft(Map.empty[String, Module]) { - case (acc, (importedBuild, importedPaths)) => - acc ++ importedBuild.module.mapValues( - inheritCompilerDeps(importedBuild.project) - ) + def inheritSettings(parent: Module)(module: Module): Module = { + def merge(m: Module): Module = { + val parentTargets = moduleTargets(module, module.targets) + + m.copy( + targets = parentTargets, + scalaVersion = m.scalaVersion.orElse(parent.scalaVersion), + scalaJsVersion = m.scalaJsVersion.orElse(parent.scalaJsVersion), + scalaNativeVersion = + m.scalaNativeVersion.orElse(parent.scalaNativeVersion), + scalaOptions = parent.scalaOptions ++ m.scalaOptions, + scalaOrganisation = m.scalaOrganisation + .orElse(parent.scalaOrganisation) + .orElse(Some(Organisation.Lightbend.packageName)), + compilerDeps = + ArtefactResolution.mergeDeps(parent.compilerDeps ++ m.compilerDeps), + testFrameworks = (parent.testFrameworks ++ m.testFrameworks).distinct, + mainClass = m.mainClass.orElse(parent.mainClass), + sources = parent.sources ++ m.sources, + moduleDeps = (parent.moduleDeps ++ m.moduleDeps).distinct, + + scalaDeps = + ArtefactResolution.mergeDeps(parent.scalaDeps ++ m.scalaDeps), + javaDeps = ArtefactResolution.mergeDeps(parent.javaDeps ++ m.javaDeps), + test = module.test.map { module => + // TODO inherit from base module? + module.copy( + targets = moduleTargets( + module, + if (module.targets.isEmpty) parentTargets else module.targets + ) + ) + } + ) } - val combinedBuild = parsed.copy( - project = parsed.project.copy( - testFrameworks = (parsed.project.testFrameworks ++ imported.flatMap( - _._1.project.testFrameworks - )).distinct - ), - module = parsedModules ++ importedModules + val mergedModule = merge(module) + + mergedModule.copy( + jvm = module.jvm + .map(inheritSettings(mergedModule)) + .orElse( + if (!module.targets.contains(Platform.JVM)) None + else Some(merge(Module())) + ), + js = module.js + .map(inheritSettings(mergedModule)) + .orElse( + if (!module.targets.contains(Platform.JavaScript)) None + else Some(merge(Module())) + ), + native = module.native + .map(inheritSettings(mergedModule)) + .orElse( + if (!module.targets.contains(Platform.Native)) None + else Some(merge(Module())) + ) ) - - val moduleProjectPaths = - imported.flatMap(_._2).toMap ++ - parsed.module.keys.map(m => m -> projectPath).toMap - - (combinedBuild, moduleProjectPaths) } - def inheritCompilerDeps(project: Project)(module: Module): Module = - module.copy( - compilerDeps = (project.compilerDeps ++ module.compilerDeps).distinct, - jvm = module.jvm.map(inheritCompilerDeps(project)), - js = module.js.map(inheritCompilerDeps(project)), - native = module.native.map(inheritCompilerDeps(project)) - ) - - def moduleTargets( - module: Build.Module, - otherTargets: List[Platform] - ): List[Platform] = - ( - otherTargets ++ - (if (module.jvm.nonEmpty) List(JVM) else List()) ++ - (if (module.js.nonEmpty) List(JavaScript) else List()) ++ - (if (module.native.nonEmpty) List(Native) else List()) - ).distinct - - def platformModule( - module: Build.Module, - platform: Platform - ): Option[Build.Module] = - platform match { - case JVM => module.jvm - case JavaScript => module.js - case Native => module.native - } - - def buildTargets(build: Build): Set[Platform] = - build.module.flatMap { case (_, module) => module.targets }.toSet - def checkModule( - build: Build, + result: Result, name: String, module: Build.Module, log: Log @@ -181,12 +165,12 @@ object BuildConfig { } val invalidModuleDeps = - module.moduleDeps.filter(!build.module.isDefinedAt(_)) + module.moduleDeps.filter(!result.build.isDefinedAt(_)) val invalidTargetModules = module.target.toList .flatMap(_._2.`class`) .map(_.module.module) - .filter(!build.module.isDefinedAt(_)) + .filter(!result.build.isDefinedAt(_)) val invalidTargetModules2 = module.target.keys .filter(id => Platform.All.keys.exists(_.id == id)) @@ -205,11 +189,15 @@ object BuildConfig { error(s"Source paths must be set on root or JVM module $moduleName") else if (module.sources.isEmpty && module.native.exists(_.sources.isEmpty)) error(s"Source paths must be set on root or native module $moduleName") - else if (module.targets.contains(JavaScript) && build.project.scalaJsVersion.isEmpty) + else if (module.targets.contains(JavaScript) && !module.js.exists( + _.scalaJsVersion.isDefined + )) error( s"Module $moduleName has JavaScript target, but Scala.js version not set" ) - else if (module.targets.contains(Native) && build.project.scalaNativeVersion.isEmpty) + else if (module.targets.contains(Native) && !module.native.exists( + _.scalaNativeVersion.isDefined + )) error( s"Module $moduleName has native target, but Scala Native version not set" ) @@ -256,60 +244,64 @@ object BuildConfig { else true } - def scalaVersion(project: Project, stack: List[Module]): String = - stack - .find(_.scalaVersion.isDefined) - .flatMap(_.scalaVersion) - .getOrElse(project.scalaVersion) - - def platformVersion( - build: Build, - module: Module, - platform: Platform - ): String = + def platformVersion(module: Module, platform: Platform): String = platform match { - case JVM => BuildConfig.scalaVersion(build.project, List(module)) - case JavaScript => build.project.scalaJsVersion.get - case Native => build.project.scalaNativeVersion.get + case JVM => module.scalaVersion.get + case JavaScript => module.scalaJsVersion.get + case Native => module.scalaNativeVersion.get } def isCrossBuild(module: Module): Boolean = module.targets.toSet.size > 1 - def hasTarget(build: Build, name: String, platform: Platform): Boolean = - build.module(name).targets.contains(platform) + def hasTarget(modules: Build, name: String, platform: Platform): Boolean = + modules(name).module.targets.contains(platform) def targetName(build: Build, name: String, platform: Platform): String = - if (!isCrossBuild(build.module(name))) name else name + "-" + platform.id + if (!isCrossBuild(build(name).module)) name else name + "-" + platform.id def buildTargets(build: Build, module: String): List[String] = { - val m = build.module(module) + val m = build(module).module val p = m.targets p.map(p => targetName(build, module, p)) } def linkTargets(build: Build, module: String): List[String] = { - val m = build.module(module) + val m = build(module).module val p = m.targets.diff(List(JVM)) p.map(p => targetName(build, module, p)) } + def moduleTargets( + module: Build.Module, + otherTargets: List[Platform] + ): List[Platform] = + ( + otherTargets ++ + (if (module.jvm.nonEmpty) List(JVM) else List()) ++ + (if (module.js.nonEmpty) List(JavaScript) else List()) ++ + (if (module.native.nonEmpty) List(Native) else List()) + ).distinct + + def buildTargets(build: Build): Set[Platform] = + build.flatMap { case (_, module) => module.module.targets }.toSet + def platformModule( - build: Build, - name: String, + module: Build.Module, platform: Platform - ): Option[Module] = - if (platform == JavaScript) build.module(name).js - else if (platform == JVM) build.module(name).jvm - else if (platform == Native) build.module(name).native - else throw new IllegalArgumentException() + ): Option[Build.Module] = + platform match { + case JVM => module.jvm + case JavaScript => module.js + case Native => module.native + } def targetNames( - build: Build, + modules: Build, name: String, platform: Platform ): List[String] = - if (!isCrossBuild(build.module(name))) List(name) - else if (platformModule(build, name, platform).isEmpty) List(name) + if (!isCrossBuild(modules(name).module)) List(name) + else if (platformModule(modules(name).module, platform).isEmpty) List(name) else List(name, name + "-" + platform.id) def jsModuleDeps(module: Module): List[String] = @@ -321,19 +313,19 @@ object BuildConfig { def jvmModuleDeps(module: Module): List[String] = module.moduleDeps ++ module.jvm.map(_.moduleDeps).getOrElse(List()) - def collectJsModuleDeps(build: Build, module: Module): List[String] = + def collectJsModuleDeps(modules: Build, module: Module): List[String] = jsModuleDeps(module).flatMap( - m => List(m) ++ collectJsModuleDeps(build, build.module(m)) + m => List(m) ++ collectJsModuleDeps(modules, modules(m).module) ) def collectNativeModuleDeps(build: Build, module: Module): List[String] = nativeModuleDeps(module).flatMap( - m => List(m) ++ collectNativeModuleDeps(build, build.module(m)) + m => List(m) ++ collectNativeModuleDeps(build, build(m).module) ) def collectJvmModuleDeps(build: Build, module: Module): List[String] = jvmModuleDeps(module).flatMap( - m => List(m) ++ collectJvmModuleDeps(build, build.module(m)) + m => List(m) ++ collectJvmModuleDeps(build, build(m).module) ) def collectModuleDeps( @@ -365,7 +357,7 @@ object BuildConfig { .resolve(targetName(build, name, JavaScript)) +: collectJsClassPath( buildPath, build, - build.module(name) + build(name).module ) ) @@ -382,7 +374,7 @@ object BuildConfig { .resolve(targetName(build, name, Native)) +: collectNativeClassPath( buildPath, build, - build.module(name) + build(name).module ) ) @@ -395,33 +387,33 @@ object BuildConfig { .filter(name => hasTarget(build, name, Platform.JVM)) .flatMap( name => - buildPath - .resolve(targetName(build, name, JVM)) +: collectJvmClassPath( - buildPath, - build, - build.module(name) - ) + buildPath.resolve(targetName(build, name, JVM)) +: + collectJvmClassPath( + buildPath, + build, + build(name).module + ) ) def collectJsDeps(build: Build, module: Module): List[ScalaDep] = - module.scalaDeps ++ module.js.map(_.scalaDeps).getOrElse(List()) ++ - jsModuleDeps(module).flatMap(m => collectJsDeps(build, build.module(m))) + module.js.map(_.scalaDeps).getOrElse(List()) ++ + jsModuleDeps(module).flatMap(m => collectJsDeps(build, build(m).module)) def collectNativeDeps(build: Build, module: Module): List[ScalaDep] = - module.scalaDeps ++ module.native.map(_.scalaDeps).getOrElse(List()) ++ + module.native.map(_.scalaDeps).getOrElse(List()) ++ nativeModuleDeps(module).flatMap( - m => collectNativeDeps(build, build.module(m)) + m => collectNativeDeps(build, build(m).module) ) def collectJvmScalaDeps(build: Build, module: Module): List[ScalaDep] = - module.scalaDeps ++ module.jvm.map(_.scalaDeps).getOrElse(List()) ++ + module.jvm.map(_.scalaDeps).getOrElse(List()) ++ jvmModuleDeps(module).flatMap( - m => collectJvmScalaDeps(build, build.module(m)) + m => collectJvmScalaDeps(build, build(m).module) ) def collectJvmJavaDeps(build: Build, module: Module): List[JavaDep] = - module.javaDeps ++ module.jvm.map(_.javaDeps).getOrElse(List()) ++ + module.jvm.map(_.javaDeps).getOrElse(List()) ++ jvmModuleDeps(module).flatMap( - m => collectJvmJavaDeps(build, build.module(m)) + m => collectJvmJavaDeps(build, build(m).module) ) } diff --git a/src/main/scala/seed/config/util/TomlUtils.scala b/src/main/scala/seed/config/util/TomlUtils.scala index 1fd8c00..f0cb17f 100644 --- a/src/main/scala/seed/config/util/TomlUtils.scala +++ b/src/main/scala/seed/config/util/TomlUtils.scala @@ -6,7 +6,7 @@ import org.apache.commons.io.FileUtils import seed.{Log, LogLevel} import seed.cli.util.Ansi import seed.model.Build.{PlatformModule, VersionTag} -import seed.model.{Build, Platform} +import seed.model.{Platform, TomlBuild} import toml.{Codec, Value} import scala.util.Try @@ -134,13 +134,13 @@ object TomlUtils { def parseBuildToml( projectPath: Path - )(content: String): Either[Codec.Error, Build] = { + )(content: String): Either[Codec.Error, TomlBuild] = { import toml._ import toml.Codecs._ import seed.config.util.TomlUtils.Codecs._ implicit val pCodec = pathCodec(fixPath(projectPath, _)) - Toml.parseAs[Build](content) + Toml.parseAs[TomlBuild](content) } } diff --git a/src/main/scala/seed/generation/Bloop.scala b/src/main/scala/seed/generation/Bloop.scala index af9a234..7938fa0 100644 --- a/src/main/scala/seed/generation/Bloop.scala +++ b/src/main/scala/seed/generation/Bloop.scala @@ -3,6 +3,7 @@ package seed.generation import java.nio.file.{Files, Path, Paths} import seed.config.BuildConfig.{ + Build, collectJsClassPath, collectJsDeps, collectJvmClassPath, @@ -13,9 +14,9 @@ import seed.config.BuildConfig.{ } import seed.artefact.{ArtefactResolution, Coursier} import seed.cli.util.Ansi -import seed.model.Build.{Module, Project} +import seed.model.Build.Module import seed.model.Platform.{JVM, JavaScript, Native} -import seed.model.{Build, Resolution} +import seed.model.Resolution import seed.Log import seed.config.BuildConfig import seed.generation.util.PathUtil @@ -138,68 +139,50 @@ object Bloop { bloopPath: Path, buildPath: Path, jsOutputPath: Option[Path], - parentModule: Module, - parentClassPaths: List[Path], jsModule: Option[Module], - project: Project, + parentClassPaths: List[Path], resolution: Coursier.ResolutionResult, compilerResolution: List[Coursier.ResolutionResult], - jsdom: Boolean, - emitSourceMaps: Boolean, test: Boolean, optionalArtefacts: Boolean, log: Log - ): Unit = { - import parentModule.{moduleDeps, scalaDeps, sources, targets} - import project.{ - scalaJsVersion, - scalaOptions, - scalaOrganisation, - testFrameworks - } - - val mainClass = jsModule - .flatMap(_.mainClass) - .orElse(parentModule.mainClass) - + ): Unit = jsModule - .orElse(if (!targets.contains(JavaScript)) None else Some(Module())) .foreach { js => + val jsdom = js.jsdom + val emitSourceMaps = js.emitSourceMaps + val mainClass = js.mainClass + val bloopName = if (!test) name else name + "-test" log.info(s"Writing JavaScript module ${Ansi.italic(bloopName)}...") - val scalaVersion = BuildConfig.scalaVersion( - project, - List(js, parentModule.js.getOrElse(Module()), parentModule) - ) - val plugIns = util.ScalaCompiler.compilerPlugIns( build, - parentModule, + js, compilerResolution, JavaScript, - scalaVersion + js.scalaVersion.get ) val resolvedDeps = Coursier.localArtefacts( resolution, - (scalaDeps ++ js.scalaDeps) + collectJsDeps(build, js) .map( dep => ArtefactResolution.javaDepFromScalaDep( dep, JavaScript, - scalaJsVersion.get, - scalaVersion + js.scalaJsVersion.get, + js.scalaVersion.get ) ) - .toSet ++ ArtefactResolution.jsPlatformDeps(build, js), + .toSet ++ ArtefactResolution.jsPlatformDeps(js), optionalArtefacts ) val dependencies = if (test) List(name) else - (moduleDeps ++ js.moduleDeps) + js.moduleDeps .filter(name => BuildConfig.hasTarget(build, name, JavaScript)) .map(name => BuildConfig.targetName(build, name, JavaScript)) @@ -210,8 +193,8 @@ object Bloop { val scalaCompiler = ArtefactResolution.resolveScalaCompiler( compilerResolution, - scalaOrganisation, - scalaVersion, + js.scalaOrganisation.get, + js.scalaVersion.get, resolvedDeps, classPath, optionalArtefacts @@ -223,14 +206,14 @@ object Bloop { bloopPath = bloopPath, dependencies = dependencies, classesDir = classesDir, - sources = sources ++ js.sources, + sources = js.sources, scalaCompiler = Some(scalaCompiler), - scalaOptions = scalaOptions ++ plugIns, - testFrameworks = if (test) testFrameworks else List(), + scalaOptions = js.scalaOptions ++ plugIns, + testFrameworks = if (test) js.testFrameworks else List(), platform = Some( Config.Platform.Js( Config.JsConfig( - version = majorMinorVersion(scalaJsVersion.get), + version = majorMinorVersion(js.scalaJsVersion.get), mode = Config.LinkerMode.Debug, kind = Config.ModuleKindJS.NoModule, emitSourceMaps = emitSourceMaps, @@ -244,7 +227,6 @@ object Bloop { ) ) } - } def writeNativeModule( build: Build, @@ -253,91 +235,54 @@ object Bloop { bloopPath: Path, buildPath: Path, outputPathBinary: Option[Path], - parentModule: Module, - parentClassPaths: List[Path], nativeModule: Option[Module], - project: Project, + parentClassPaths: List[Path], resolution: Coursier.ResolutionResult, compilerResolution: List[Coursier.ResolutionResult], test: Boolean, optionalArtefacts: Boolean, log: Log - ): Unit = { - import parentModule.{moduleDeps, scalaDeps, sources, targets} - import project.{ - scalaNativeVersion, - scalaOptions, - scalaOrganisation, - testFrameworks - } - - val mainClass = nativeModule - .flatMap(_.mainClass) - .orElse(parentModule.mainClass) - - val gc = nativeModule - .flatMap(_.gc) - .orElse(parentModule.gc) - .getOrElse("immix") - val targetTriple = nativeModule - .flatMap(_.targetTriple) - .orElse(parentModule.targetTriple) - .getOrElse("") - val clang = nativeModule - .flatMap(_.clang) - .orElse(parentModule.clang) - .getOrElse(Paths.get("/usr/bin/clang")) - val clangpp = nativeModule - .flatMap(_.clangpp) - .orElse(parentModule.clangpp) - .getOrElse(Paths.get("/usr/bin/clang++")) - val linkStubs = nativeModule.exists(_.linkStubs) || parentModule.linkStubs - val linkerOptions = nativeModule - .flatMap(_.linkerOptions) - .orElse(parentModule.linkerOptions) - .getOrElse(List()) - val compilerOptions = nativeModule - .flatMap(_.compilerOptions) - .orElse(parentModule.compilerOptions) - .getOrElse(List()) - + ): Unit = nativeModule - .orElse(if (!targets.contains(Native)) None else Some(Module())) .foreach { native => + val mainClass = native.mainClass + val gc = native.gc.getOrElse("immix") + val targetTriple = native.targetTriple.getOrElse("") + val clang = native.clang.getOrElse(Paths.get("/usr/bin/clang")) + val clangpp = native.clangpp.getOrElse(Paths.get("/usr/bin/clang++")) + val linkStubs = native.linkStubs + val linkerOptions = native.linkerOptions.getOrElse(List()) + val compilerOptions = native.compilerOptions.getOrElse(List()) + val bloopName = if (!test) name else name + "-test" log.info(s"Writing native module ${Ansi.italic(bloopName)}...") - val modules = - List(native, parentModule.native.getOrElse(Module()), parentModule) - - val scalaVersion = BuildConfig.scalaVersion(project, modules) - val plugIns = util.ScalaCompiler.compilerPlugIns( build, - parentModule, + native, compilerResolution, Native, - scalaVersion + native.scalaVersion.get ) val resolvedDeps = Coursier.localArtefacts( resolution, - (scalaDeps ++ native.scalaDeps) + collectNativeDeps(build, native) .map( dep => ArtefactResolution.javaDepFromScalaDep( dep, Native, - scalaNativeVersion.get, - scalaVersion + native.scalaNativeVersion.get, + native.scalaVersion.get ) ) - .toSet ++ ArtefactResolution.nativePlatformDeps(build, modules), + .toSet ++ ArtefactResolution.nativePlatformDeps(native), optionalArtefacts ) - val nativeLibDep = ArtefactResolution.nativeLibraryDep(build, modules) + val nativeLibDep = ArtefactResolution.nativeLibraryDep(native) val scalaNativelib = resolvedDeps .find(_.javaDep == nativeLibDep) .map(_.libraryJar) @@ -346,7 +291,7 @@ object Bloop { val dependencies = if (test) List(name) else - (moduleDeps ++ native.moduleDeps) + native.moduleDeps .filter(name => BuildConfig.hasTarget(build, name, Native)) .map(name => BuildConfig.targetName(build, name, Native)) @@ -357,8 +302,8 @@ object Bloop { val scalaCompiler = ArtefactResolution.resolveScalaCompiler( compilerResolution, - scalaOrganisation, - scalaVersion, + native.scalaOrganisation.get, + native.scalaVersion.get, resolvedDeps, classPath, optionalArtefacts @@ -370,14 +315,14 @@ object Bloop { bloopPath = bloopPath, dependencies = dependencies, classesDir = classesDir, - sources = sources ++ native.sources, + sources = native.sources, scalaCompiler = Some(scalaCompiler), - scalaOptions = scalaOptions ++ plugIns, - testFrameworks = if (test) testFrameworks else List(), + scalaOptions = native.scalaOptions ++ plugIns, + testFrameworks = if (test) native.testFrameworks else List(), platform = Some( Config.Platform.Native( Config.NativeConfig( - version = scalaNativeVersion.get, + version = native.scalaNativeVersion.get, mode = Config.LinkerMode.Debug, gc = gc, targetTriple = targetTriple, @@ -397,7 +342,6 @@ object Bloop { ) ) } - } def writeJvmModule( build: Build, @@ -405,40 +349,27 @@ object Bloop { projectPath: Path, bloopPath: Path, buildPath: Path, - parentModule: Module, - parentClassPaths: List[Path], jvmModule: Option[Module], - project: Project, + parentClassPaths: List[Path], resolution: Coursier.ResolutionResult, compilerResolution: List[Coursier.ResolutionResult], test: Boolean, optionalArtefacts: Boolean, log: Log - ): Unit = { - import parentModule.{moduleDeps, sources, targets} - import project.{scalaOptions, scalaOrganisation, testFrameworks} - - val mainClass = jvmModule - .flatMap(_.mainClass) - .orElse(parentModule.mainClass) - + ): Unit = jvmModule - .orElse(if (!targets.contains(JVM)) None else Some(Module())) .foreach { jvm => val bloopName = if (!test) name else name + "-test" log.info(s"Writing JVM module ${Ansi.italic(bloopName)}...") - val scalaVersion = BuildConfig.scalaVersion( - project, - List(jvm, parentModule.jvm.getOrElse(Module()), parentModule) - ) - - val javaDeps = parentModule.javaDeps ++ jvm.javaDeps - val scalaDeps = (parentModule.scalaDeps ++ jvm.scalaDeps).map( + val scalaVersion = jvm.scalaVersion.get + val javaDeps = collectJvmJavaDeps(build, jvm) + val scalaDeps = collectJvmScalaDeps(build, jvm).map( dep => ArtefactResolution .javaDepFromScalaDep(dep, JVM, scalaVersion, scalaVersion) ) + val resolvedDeps = Coursier.localArtefacts( resolution, (javaDeps ++ scalaDeps).toSet, @@ -447,7 +378,7 @@ object Bloop { val plugIns = util.ScalaCompiler.compilerPlugIns( build, - parentModule, + jvm, compilerResolution, JVM, scalaVersion @@ -456,7 +387,7 @@ object Bloop { val dependencies = if (test) List(name) else - (moduleDeps ++ jvm.moduleDeps) + jvm.moduleDeps .filter(name => BuildConfig.hasTarget(build, name, JVM)) .map(name => BuildConfig.targetName(build, name, JVM)) @@ -467,7 +398,7 @@ object Bloop { val scalaCompiler = ArtefactResolution.resolveScalaCompiler( compilerResolution, - scalaOrganisation, + jvm.scalaOrganisation.get, scalaVersion, resolvedDeps, classPath, @@ -480,18 +411,17 @@ object Bloop { bloopPath = bloopPath, dependencies = dependencies, classesDir = classesDir, - sources = sources ++ jvm.sources, + sources = jvm.sources, resources = jvm.resources, scalaCompiler = Some(scalaCompiler), - scalaOptions = scalaOptions ++ plugIns, - testFrameworks = if (test) testFrameworks else List(), + scalaOptions = jvm.scalaOptions ++ plugIns, + testFrameworks = if (test) jvm.testFrameworks else List(), platform = Some( Config.Platform - .Jvm(Config.JvmConfig(None, List()), mainClass = mainClass) + .Jvm(Config.JvmConfig(None, List()), mainClass = jvm.mainClass) ) ) } - } def moduleOutputPath( buildPath: Path, @@ -521,17 +451,11 @@ object Bloop { ): Unit = { val isCrossBuild = module.targets.toSet.size > 1 - val jsOutputPath = module.js - .orElse( - if (!module.targets.contains(JavaScript)) None - else Some(Module()) - ) - .map(js => moduleOutputPath(buildPath, js, name + ".js")) - - val nativeOutputPath = - module.native - .orElse(if (!module.targets.contains(Native)) None else Some(Module())) - .map(native => moduleOutputPath(buildPath, native, name + ".run")) + val jsOutputPath = + module.js.map(js => moduleOutputPath(buildPath, js, name + ".js")) + val nativeOutputPath = module.native.map( + native => moduleOutputPath(buildPath, native, name + ".run") + ) jsOutputPath.foreach { path => if (!Files.exists(path.getParent)) Files.createDirectories(path.getParent) @@ -548,14 +472,10 @@ object Bloop { bloopPath, bloopBuildPath, jsOutputPath, - module.copy(scalaDeps = collectJsDeps(build, module)), - collectJsClassPath(bloopBuildPath, build, module), module.js, - build.project, + collectJsClassPath(bloopBuildPath, build, module), resolution, compilerResolution, - jsdom = module.js.exists(_.jsdom), - emitSourceMaps = module.js.exists(_.emitSourceMaps), test = false, optionalArtefacts, log @@ -566,13 +486,8 @@ object Bloop { projectPath, bloopPath, bloopBuildPath, - module.copy( - scalaDeps = collectJvmScalaDeps(build, module), - javaDeps = collectJvmJavaDeps(build, module) - ), - collectJvmClassPath(bloopBuildPath, build, module), module.jvm, - build.project, + collectJvmClassPath(bloopBuildPath, build, module), resolution, compilerResolution, test = false, @@ -586,10 +501,8 @@ object Bloop { bloopPath, bloopBuildPath, nativeOutputPath, - module.copy(scalaDeps = collectNativeDeps(build, module)), - collectJvmClassPath(bloopBuildPath, build, module), module.native, - build.project, + collectJvmClassPath(bloopBuildPath, build, module), resolution, compilerResolution, test = false, @@ -598,10 +511,6 @@ object Bloop { ) module.test.foreach { test => - val targets = if (test.targets.nonEmpty) test.targets else module.targets - val jsdom = test.js.exists(_.jsdom) - val emitSourceMaps = test.js.exists(_.emitSourceMaps) - writeJsModule( build, if (!isCrossBuild) name else name + "-js", @@ -609,18 +518,18 @@ object Bloop { bloopPath, bloopBuildPath, None, - module.copy( - sources = test.sources, - scalaDeps = collectJsDeps(build, module) ++ test.scalaDeps, - targets = targets + // TODO Write helper function for inheriting from base module; must use mergeDeps + test.js.map( + js => + js.copy( + sources = module.js.get.sources ++ js.sources, + scalaDeps = + (collectJsDeps(build, module.js.get) ++ js.scalaDeps).distinct + ) ), collectJsClassPath(bloopBuildPath, build, module), - test.js, - build.project, resolution, compilerResolution, - jsdom, - emitSourceMaps, test = true, optionalArtefacts, log @@ -633,14 +542,15 @@ object Bloop { bloopPath, bloopBuildPath, None, - module.copy( - sources = test.sources, - scalaDeps = collectNativeDeps(build, module) ++ test.scalaDeps, - targets = targets + test.native.map( + native => + native.copy( + sources = module.native.get.sources ++ native.sources, + scalaDeps = + (collectNativeDeps(build, module.native.get) ++ native.scalaDeps).distinct + ) ), collectNativeClassPath(bloopBuildPath, build, module), - test.native, - build.project, resolution, compilerResolution, test = true, @@ -654,15 +564,17 @@ object Bloop { projectPath, bloopPath, bloopBuildPath, - module.copy( - sources = test.sources, - scalaDeps = collectJvmScalaDeps(build, module) ++ test.scalaDeps, - javaDeps = collectJvmJavaDeps(build, module) ++ test.javaDeps, - targets = targets + test.jvm.map( + jvm => + jvm.copy( + sources = module.sources ++ jvm.sources, + scalaDeps = + (collectJvmScalaDeps(build, module.jvm.get) ++ jvm.scalaDeps).distinct, + javaDeps = + (collectJvmJavaDeps(build, module.jvm.get) ++ jvm.javaDeps).distinct + ) ), collectJvmClassPath(bloopBuildPath, build, module), - test.jvm, - build.project, resolution, compilerResolution, test = true, @@ -675,7 +587,7 @@ object Bloop { projectPath = projectPath, name = name + "-test", bloopPath = bloopPath, - dependencies = targets.map(t => name + "-" + t.id + "-test"), + dependencies = test.targets.map(t => name + "-" + t.id + "-test"), classesDir = bloopBuildPath, sources = List(), scalaCompiler = None, @@ -725,7 +637,7 @@ object Bloop { .asScala .foreach(Files.delete) - build.module.foreach { + build.foreach { case (name, module) => log.info(s"Building module ${Ansi.italic(name)}...") buildModule( @@ -737,7 +649,7 @@ object Bloop { resolution, compilerResolution, name, - module, + module.module, optionalArtefacts, log ) diff --git a/src/main/scala/seed/generation/Idea.scala b/src/main/scala/seed/generation/Idea.scala index 05578d4..36cde1d 100644 --- a/src/main/scala/seed/generation/Idea.scala +++ b/src/main/scala/seed/generation/Idea.scala @@ -5,6 +5,7 @@ import java.nio.file.{Files, Path} import scala.collection.JavaConverters._ import org.apache.commons.io.FileUtils import seed.config.BuildConfig.{ + Build, collectJsDeps, collectJsModuleDeps, collectJvmJavaDeps, @@ -59,7 +60,6 @@ object Idea { } def createModule( - build: Build, root: Path, name: String, sources: List[Path], @@ -71,6 +71,7 @@ object Idea { buildPath: Path, modulesPath: Path, librariesPath: Path, + scalaOrganisation: String, scalaVersion: String ): Unit = { val filteredResolvedDeps = @@ -89,7 +90,7 @@ object Idea { ) ) - val scalaDep = build.project.scalaOrganisation + "-" + scalaVersion + val scalaDep = scalaOrganisation + "-" + scalaVersion val classPathOut = buildPath.resolve(name).resolve("main") val testClassPathOut = buildPath.resolve(name).resolve("test") @@ -123,46 +124,49 @@ object Idea { } def createCompilerLibraries( - build: Build, + modules: Build, resolution: List[Coursier.ResolutionResult], librariesPath: Path ): Unit = { - val scalaVersions = (build.module.values.toList.flatMap( - module => - module.jvm.flatMap(_.scalaVersion).toList ++ - module.js.flatMap(_.scalaVersion).toList ++ - module.native.flatMap(_.scalaVersion).toList ++ - module.scalaVersion.toList - ) ++ List(build.project.scalaVersion)).distinct - - scalaVersions.foreach { scalaVersion => - val scalaCompiler = ArtefactResolution.resolveScalaCompiler( - resolution, - build.project.scalaOrganisation, - scalaVersion, - List(), - List(), - optionalArtefacts = false + val scalaVersions = modules.values.toList + .map(_.module) + .flatMap( + module => + (module.jvm.toList ++ module.js.toList ++ module.native.toList) + .map(s => s.scalaOrganisation.get -> s.scalaVersion.get) ) + .distinct - val xml = IdeaFile.createLibrary( - IdeaFile.Library( - name = build.project.scalaOrganisation + "-" + scalaVersion, - compilerInfo = Some( - IdeaFile.CompilerInfo( - scalaVersion, - scalaCompiler.compilerJars.map(_.toString) - ) - ), - classes = scalaCompiler.libraries.map(_.libraryJar.toString), - javaDoc = List(), - sources = List() + scalaVersions.foreach { + case (scalaOrganisation, scalaVersion) => + val scalaCompiler = ArtefactResolution.resolveScalaCompiler( + resolution, + scalaOrganisation, + scalaVersion, + List(), + List(), + optionalArtefacts = false + ) + + val xml = IdeaFile.createLibrary( + IdeaFile.Library( + name = scalaOrganisation + "-" + scalaVersion, + compilerInfo = Some( + IdeaFile.CompilerInfo( + scalaVersion, + scalaCompiler.compilerJars.map(_.toString) + ) + ), + classes = scalaCompiler.libraries.map(_.libraryJar.toString), + javaDoc = List(), + sources = List() + ) ) - ) - val fileName = ideaName(build.project.scalaOrganisation) + "_" + - ideaName(scalaVersion) + ".xml" - FileUtils.write(librariesPath.resolve(fileName).toFile, xml, "UTF-8") + val fileName = ideaName(scalaOrganisation) + "_" + ideaName( + scalaVersion + ) + ".xml" + FileUtils.write(librariesPath.resolve(fileName).toFile, xml, "UTF-8") } } @@ -174,16 +178,14 @@ object Idea { ): Unit = { // Group all modules by additional settings; create compiler configuration // for each unique set of parameters - val modulePlugIns = modules.filter(build.module.contains).map { m => - val module = build.module(m) - val scalaVersion = BuildConfig - .scalaVersion(build.project, module.jvm.toList ++ List(module)) + val modulePlugIns = modules.filter(build.contains).map { m => + val module = build(m).module m -> util.ScalaCompiler.compilerPlugIns( build, - build.module(m), + build(m).module, compilerResolution, JVM, - scalaVersion + module.jvm.getOrElse(module).scalaVersion.get ) } val compilerSettings = @@ -191,13 +193,13 @@ object Idea { case (settings, modules) => val allModules = modules.flatMap { module => val targets = - BuildConfig.moduleTargets(build.module(module), List()) + BuildConfig.moduleTargets(build(module).module, List()) val all = module +: targets .map(target => BuildConfig.targetName(build, module, target)) all.distinct } - (build.project.scalaOptions ++ settings, allModules) + (settings, allModules) } val xml = IdeaFile.createScalaCompiler(compilerSettings) @@ -212,12 +214,12 @@ object Idea { * project will use JVM. */ def buildModule( + build: Build, projectPath: Path, buildPath: Path, ideaPath: Path, modulesPath: Path, librariesPath: Path, - build: Build, compilerResolution: List[Coursier.ResolutionResult], resolution: Coursier.ResolutionResult, name: String, @@ -241,11 +243,7 @@ object Idea { ) List() } else { - val scalaVersion = - BuildConfig.scalaVersion(build.project, List(jsModule, module)) - createModule( - build = build, root = jsModule.root.get, name = moduleName, sources = jsSources, @@ -258,8 +256,8 @@ object Idea { ArtefactResolution.javaDepFromScalaDep( dep, JavaScript, - build.project.scalaJsVersion.get, - scalaVersion + jsModule.scalaJsVersion.get, + jsModule.scalaVersion.get ) ) .toSet, @@ -275,8 +273,8 @@ object Idea { ArtefactResolution.javaDepFromScalaDep( dep, JavaScript, - build.project.scalaJsVersion.get, - scalaVersion + test.scalaJsVersion.get, + test.scalaVersion.get ) ) .toSet, @@ -292,7 +290,8 @@ object Idea { buildPath = buildPath, modulesPath = modulesPath, librariesPath = librariesPath, - scalaVersion = scalaVersion + scalaOrganisation = jsModule.scalaOrganisation.get, + scalaVersion = jsModule.scalaVersion.get ) List(moduleName) @@ -314,13 +313,7 @@ object Idea { ) List() } else { - val scalaVersion = BuildConfig.scalaVersion( - build.project, - List(jvmModule, module) - ) - createModule( - build = build, root = jvmModule.root.get, name = moduleName, sources = jvmSources, @@ -334,8 +327,8 @@ object Idea { ArtefactResolution.javaDepFromScalaDep( dep, JVM, - scalaVersion, - scalaVersion + jvmModule.scalaVersion.get, + jvmModule.scalaVersion.get ) ) .toSet, @@ -352,8 +345,8 @@ object Idea { ArtefactResolution.javaDepFromScalaDep( dep, JVM, - scalaVersion, - scalaVersion + test.scalaVersion.get, + test.scalaVersion.get ) ) .toSet, @@ -369,7 +362,8 @@ object Idea { buildPath = buildPath, modulesPath = modulesPath, librariesPath = librariesPath, - scalaVersion = scalaVersion + scalaOrganisation = jvmModule.scalaOrganisation.get, + scalaVersion = jvmModule.scalaVersion.get ) List(moduleName) @@ -392,25 +386,21 @@ object Idea { ) List() } else { - val scalaVersion = - BuildConfig.scalaVersion(build.project, List(nativeModule, module)) - createModule( - build = build, root = nativeModule.root.get, name = moduleName, sources = nativeSources, tests = nativeTests, resolvedDeps = Coursier.localArtefacts( resolution, - collectNativeDeps(build, module) + collectNativeDeps(null, module) .map( dep => ArtefactResolution.javaDepFromScalaDep( dep, Native, - build.project.scalaNativeVersion.get, - scalaVersion + nativeModule.scalaNativeVersion.get, + nativeModule.scalaVersion.get ) ) .toSet, @@ -420,14 +410,14 @@ object Idea { test => Coursier.localArtefacts( resolution, - collectNativeDeps(build, test) + collectNativeDeps(null, test) .map( dep => ArtefactResolution.javaDepFromScalaDep( dep, Native, - build.project.scalaNativeVersion.get, - scalaVersion + nativeModule.scalaNativeVersion.get, + nativeModule.scalaVersion.get ) ) .toSet, @@ -443,7 +433,8 @@ object Idea { buildPath = buildPath, modulesPath = modulesPath, librariesPath = librariesPath, - scalaVersion = scalaVersion + scalaOrganisation = nativeModule.scalaOrganisation.get, + scalaVersion = nativeModule.scalaVersion.get ) List(moduleName) @@ -463,26 +454,22 @@ object Idea { ) List() } else { - val scalaVersion = - BuildConfig.scalaVersion(build.project, List(module)) - createModule( - build = build, root = module.root.get, name = name, sources = sharedSources, tests = sharedTests, resolvedDeps = Coursier.localArtefacts( resolution, - collectJvmJavaDeps(build, module).toSet ++ - collectJvmScalaDeps(build, module) + collectJvmJavaDeps(null, module).toSet ++ + collectJvmScalaDeps(null, module) .map( dep => ArtefactResolution.javaDepFromScalaDep( dep, JVM, - scalaVersion, - scalaVersion + module.scalaVersion.get, + module.scalaVersion.get ) ) .toSet, @@ -499,8 +486,8 @@ object Idea { ArtefactResolution.javaDepFromScalaDep( dep, JVM, - scalaVersion, - scalaVersion + module.scalaVersion.get, + module.scalaVersion.get ) ) .toSet, @@ -515,7 +502,8 @@ object Idea { buildPath = buildPath, modulesPath = modulesPath, librariesPath = librariesPath, - scalaVersion = scalaVersion + scalaOrganisation = module.scalaOrganisation.get, + scalaVersion = module.scalaVersion.get ) List(name) @@ -534,12 +522,9 @@ object Idea { ) List() } else { - val scalaVersion = - BuildConfig.scalaVersion(build.project, List(module)) val moduleName = name + "-" + targetName createModule( - build = build, root = target.root.get, name = moduleName, sources = List(), @@ -551,7 +536,8 @@ object Idea { buildPath = buildPath, modulesPath = modulesPath, librariesPath = librariesPath, - scalaVersion = scalaVersion + scalaOrganisation = module.scalaOrganisation.get, + scalaVersion = module.scalaVersion.get ) List(moduleName) @@ -583,7 +569,7 @@ object Idea { def build( projectPath: Path, outputPath: Path, - build: Build, + modules: Build, resolution: Coursier.ResolutionResult, compilerResolution: List[Coursier.ResolutionResult], tmpfs: Boolean, @@ -616,32 +602,32 @@ object Idea { .asScala .foreach(Files.delete) - createCompilerLibraries(build, compilerResolution, librariesPath) + createCompilerLibraries(modules, compilerResolution, librariesPath) FileUtils.write( ideaPath.resolve("misc.xml").toFile, IdeaFile.createJdk(jdkVersion = "1.8"), "UTF-8" ) - val modules = build.module.toList.flatMap { + val ideaModules = modules.toList.flatMap { case (name, module) => buildModule( + modules, projectPath, ideaBuildPath, ideaPath, modulesPath, librariesPath, - build, compilerResolution, resolution, name, - module, + module.module, log ) } - createCompilerSettings(build, compilerResolution, ideaPath, modules) - writeModules(projectPath, ideaPath, modulesPath, modules) + createCompilerSettings(modules, compilerResolution, ideaPath, ideaModules) + writeModules(projectPath, ideaPath, modulesPath, ideaModules) log.info("IDEA project has been created") } diff --git a/src/main/scala/seed/generation/util/ScalaCompiler.scala b/src/main/scala/seed/generation/util/ScalaCompiler.scala index e47579d..4d8f389 100644 --- a/src/main/scala/seed/generation/util/ScalaCompiler.scala +++ b/src/main/scala/seed/generation/util/ScalaCompiler.scala @@ -5,6 +5,7 @@ import java.nio.file.Path import seed.artefact.Coursier import seed.config.BuildConfig import seed.artefact.ArtefactResolution +import seed.config.BuildConfig.Build import seed.model.Build.Module import seed.model.Platform.{JavaScript, Native} import seed.model.{Artefact, Build, Platform} @@ -43,9 +44,9 @@ object ScalaCompiler { ): List[String] = { import ArtefactResolution.mergeDeps - val platformVer = BuildConfig.platformVersion(build, module, platform) + val platformVer = BuildConfig.platformVersion(module, platform) val moduleDeps = BuildConfig.collectModuleDeps(build, module, platform) - val modules = moduleDeps.map(build.module) :+ module + val modules = moduleDeps.map(build(_).module) :+ module val artefacts = (if (platform == JavaScript) List(Artefact.ScalaJsCompiler -> platformVer) else if (platform == Native) diff --git a/src/main/scala/seed/model/Build.scala b/src/main/scala/seed/model/Build.scala index 8c1dc3d..57182df 100644 --- a/src/main/scala/seed/model/Build.scala +++ b/src/main/scala/seed/model/Build.scala @@ -4,9 +4,10 @@ import java.nio.file.Path import seed.artefact.MavenCentral -case class Build( +case class TomlBuild( `import`: List[Path] = List(), project: Build.Project, + // TODO currently unused; write test case resolvers: Build.Resolvers = Build.Resolvers(), module: Map[String, Build.Module] ) @@ -58,17 +59,35 @@ object Build { ) case class Project( - scalaVersion: String, + scalaVersion: Option[String], scalaJsVersion: Option[String] = None, scalaNativeVersion: Option[String] = None, scalaOptions: List[String] = List(), - scalaOrganisation: String = Organisation.Lightbend.packageName, + scalaOrganisation: Option[String] = None, testFrameworks: List[String] = List(), compilerDeps: List[ScalaDep] = List() - ) + ) { + def toModule = + Module( + scalaVersion = scalaVersion, + scalaJsVersion = scalaJsVersion, + scalaNativeVersion = scalaNativeVersion, + scalaOptions = scalaOptions, + scalaOrganisation = scalaOrganisation, + testFrameworks = testFrameworks, + compilerDeps = compilerDeps + ) + } + // TODO Instead of using this `case class` directly, create a polymorphic + // version for different platform types case class Module( scalaVersion: Option[String] = None, + scalaJsVersion: Option[String] = None, + scalaNativeVersion: Option[String] = None, + scalaOptions: List[String] = List(), + scalaOrganisation: Option[String] = None, + testFrameworks: List[String] = List(), root: Option[Path] = None, sources: List[Path] = List(), resources: List[Path] = List(), @@ -81,10 +100,10 @@ object Build { // If this was a Path, it would include the directory // relative to the build file. output: Option[String] = None, - // JavaScript + // --- JavaScript jsdom: Boolean = false, emitSourceMaps: Boolean = true, - // Native + // --- Native gc: Option[String] = None, targetTriple: Option[String] = None, clang: Option[Path] = None, @@ -92,6 +111,7 @@ object Build { linkerOptions: Option[List[String]] = None, compilerOptions: Option[List[String]] = None, linkStubs: Boolean = false, + // --- test: Option[Module] = None, js: Option[Module] = None, jvm: Option[Module] = None, diff --git a/src/test/scala/seed/artefact/ArtefactResolutionSpec.scala b/src/test/scala/seed/artefact/ArtefactResolutionSpec.scala index b9bc762..4a8c1c4 100644 --- a/src/test/scala/seed/artefact/ArtefactResolutionSpec.scala +++ b/src/test/scala/seed/artefact/ArtefactResolutionSpec.scala @@ -3,10 +3,10 @@ package seed.artefact import java.nio.file.Paths import minitest.SimpleTestSuite -import seed.model.Build.{JavaDep, Module, Project, ScalaDep, VersionTag} +import seed.config.BuildConfig.ModuleConfig +import seed.model.Build.{JavaDep, Module, ScalaDep, VersionTag} import seed.model.Platform.JavaScript import seed.model.Platform.JVM -import seed.model.Build object ArtefactResolutionSpec extends SimpleTestSuite { test("dependencyFromScalaDep() with Scala.js dependency") { @@ -37,21 +37,22 @@ object ArtefactResolutionSpec extends SimpleTestSuite { } test("Extract platform dependencies of test module in libraryDeps()") { - val build = - Build( - project = Project("2.12.8", scalaJsVersion = Some("0.6.26")), - module = Map( - "a" -> Module( - targets = List(JVM, JavaScript), - test = Some( - Module( - sources = List(Paths.get("a/test")), - scalaDeps = List(ScalaDep("io.monix", "minitest", "2.3.2")) - ) + val build = Map( + "a" -> ModuleConfig( + Module( + scalaVersion = Some("2.12.8"), + scalaJsVersion = Some("0.6.26"), + targets = List(JVM, JavaScript), + test = Some( + Module( + sources = List(Paths.get("a/test")), + scalaDeps = List(ScalaDep("io.monix", "minitest", "2.3.2")) ) ) - ) + ), + Paths.get(".") ) + ) val libraryDeps = ArtefactResolution.allLibraryDeps(build) assertEquals( @@ -64,12 +65,11 @@ object ArtefactResolutionSpec extends SimpleTestSuite { } test("jvmDeps()") { - val build = Build(project = Project("2.12.8"), module = Map()) val module = Module( scalaDeps = List(ScalaDep("io.monix", "minitest", "2.3.2")), javaDeps = List(JavaDep("net.java.dev.jna", "jna", "4.5.1")) ) - val deps = ArtefactResolution.jvmDeps(build, List(module)) + val deps = ArtefactResolution.jvmDeps(module) assertEquals( deps, Set( @@ -80,11 +80,9 @@ object ArtefactResolutionSpec extends SimpleTestSuite { } test("Inherit compiler dependencies") { - val build = Build( - project = Project("2.12.8", scalaJsVersion = Some("0.6.26")), - module = Map() - ) val module = Module( + scalaVersion = Some("2.12.8"), + scalaJsVersion = Some("0.6.26"), targets = List(JVM, JavaScript), compilerDeps = List(ScalaDep("org.scalamacros", "paradise", "2.1.1", VersionTag.Full)), @@ -102,7 +100,7 @@ object ArtefactResolutionSpec extends SimpleTestSuite { ) ) ) - val deps = ArtefactResolution.compilerDeps(build, module) + val deps = ArtefactResolution.compilerDeps(module) assertEquals( deps, @@ -126,11 +124,9 @@ object ArtefactResolutionSpec extends SimpleTestSuite { } test("Compiler dependency with overridden version in platform module") { - val build = Build( - project = Project("2.12.8", scalaJsVersion = Some("0.6.26")), - module = Map() - ) val module = Module( + scalaVersion = Some("2.12.8"), + scalaJsVersion = Some("0.6.26"), targets = List(JVM, JavaScript), compilerDeps = List(ScalaDep("org.scalamacros", "paradise", "2.1.0", VersionTag.Full)), @@ -142,7 +138,7 @@ object ArtefactResolutionSpec extends SimpleTestSuite { ) ) ) - val deps = ArtefactResolution.compilerDeps(build, module) + val deps = ArtefactResolution.compilerDeps(module) assertEquals( deps, diff --git a/src/test/scala/seed/cli/util/TargetSpec.scala b/src/test/scala/seed/cli/util/TargetSpec.scala index 5a9b9d6..c5fce03 100644 --- a/src/test/scala/seed/cli/util/TargetSpec.scala +++ b/src/test/scala/seed/cli/util/TargetSpec.scala @@ -1,28 +1,27 @@ package seed.cli.util +import java.nio.file.Paths + import minitest.SimpleTestSuite +import seed.config.BuildConfig.ModuleConfig import seed.model.{Build, Platform} import seed.model.Build.Module object TargetSpec extends SimpleTestSuite { test("Parse module string") { assertEquals( - Target.parseModuleString( - Build(project = Build.Project(""), module = Map()) - )(""), + Target.parseModuleString(Map())(""), Left("Module name cannot be empty") ) assertEquals( - Target.parseModuleString( - Build(project = Build.Project(""), module = Map()) - )("test"), + Target.parseModuleString(Map())("test"), Left(s"Invalid module name: ${Ansi.italic("test")}. Valid names: ") ) assertEquals( Target.parseModuleString( - Build(project = Build.Project(""), module = Map("test" -> Module())) + Map("test" -> ModuleConfig(Module(), Paths.get("."))) )("test:jvm"), Left(s"Invalid build target ${Ansi.italic("jvm")} provided") ) @@ -30,9 +29,11 @@ object TargetSpec extends SimpleTestSuite { assertEquals( Target .parseModuleString( - Build( - project = Build.Project(""), - module = Map("test" -> Module(targets = List(Platform.JVM))) + Map( + "test" -> ModuleConfig( + Module(targets = List(Platform.JVM)), + Paths.get(".") + ) ) )("test:jvm") .isRight, @@ -41,7 +42,7 @@ object TargetSpec extends SimpleTestSuite { assertEquals( Target.parseModuleString( - Build(project = Build.Project(""), module = Map("test" -> Module())) + Map("test" -> ModuleConfig(Module(), Paths.get("."))) )("test:custom"), Left(s"Invalid build target ${Ansi.italic("custom")} provided") ) @@ -49,10 +50,11 @@ object TargetSpec extends SimpleTestSuite { assertEquals( Target .parseModuleString( - Build( - project = Build.Project(""), - module = - Map("test" -> Module(target = Map("custom" -> Build.Target()))) + Map( + "test" -> ModuleConfig( + Module(target = Map("custom" -> Build.Target())), + Paths.get(".") + ) ) )("test:custom") .isRight, diff --git a/src/test/scala/seed/config/BuildConfigSpec.scala b/src/test/scala/seed/config/BuildConfigSpec.scala index 76a1f45..e2d3b6a 100644 --- a/src/test/scala/seed/config/BuildConfigSpec.scala +++ b/src/test/scala/seed/config/BuildConfigSpec.scala @@ -12,8 +12,8 @@ import seed.generation.util.BuildUtil import seed.model.Build import seed.model.Build.{Project, ScalaDep, VersionTag} import seed.model.Platform.{JVM, JavaScript} - import BuildUtil.tempPath +import seed.config.BuildConfig.{ModuleConfig, Result} object BuildConfigSpec extends SimpleTestSuite { test("Resolve absolute project path") { @@ -29,10 +29,10 @@ object BuildConfigSpec extends SimpleTestSuite { "UTF-8" ) - val BuildConfig.Result(_, projectPath, moduleProjectPaths) = + val BuildConfig.Result(projectPath, modules) = BuildConfig.load(tempPath.resolve("a.toml"), Log.urgent).get assertEquals(projectPath, tempPath) - assertEquals(moduleProjectPaths, Map("example" -> tempPath)) + assertEquals(modules, Map("example" -> tempPath)) } test("Resolve relative project path") { @@ -48,10 +48,10 @@ object BuildConfigSpec extends SimpleTestSuite { "UTF-8" ) - val BuildConfig.Result(_, projectPath, moduleProjectPaths) = + val BuildConfig.Result(projectPath, modules) = BuildConfig.load(Paths.get("test/a.toml"), Log.urgent).get assertEquals(projectPath, Paths.get("test")) - assertEquals(moduleProjectPaths, Map("example" -> Paths.get("test"))) + assertEquals(modules, Map("example" -> Paths.get("test"))) Files.delete(Paths.get("test/a.toml")) } @@ -88,10 +88,10 @@ object BuildConfigSpec extends SimpleTestSuite { "UTF-8" ) - val BuildConfig.Result(_, projectPath, moduleProjectPaths) = + val BuildConfig.Result(projectPath, modules) = BuildConfig.load(tempPath.resolve("seed-root"), Log.urgent).get assertEquals( - moduleProjectPaths, + modules, Map( "root" -> tempPath.resolve("seed-root"), "child" -> tempPath.resolve("seed-root").resolve("child") @@ -123,20 +123,15 @@ object BuildConfigSpec extends SimpleTestSuite { """.stripMargin val buildRaw = TomlUtils.parseBuildToml(Paths.get("."))(toml) - val (build, _) = BuildConfig.processBuild( + val build = BuildConfig.processBuild( buildRaw.right.get, Paths.get("."), - _ => - Some( - ( - Build(project = Project(scalaVersion = "2.12.8"), module = Map()), - Map() - ) - ) + _ => Some(Result(Paths.get("."), Map())), + Log.urgent ) assertEquals( - build.module("example").test.get.targets, + build("example").module.test.get.targets, List(JavaScript, JVM) ) } @@ -154,23 +149,20 @@ object BuildConfigSpec extends SimpleTestSuite { """.stripMargin val buildRaw = TomlUtils.parseBuildToml(Paths.get("."))(toml) - val (build, _) = BuildConfig.processBuild( + val build = BuildConfig.processBuild( buildRaw.right.get, Paths.get("."), - _ => - Some( - Build(project = Project(scalaVersion = "2.12.8"), module = Map()), - Map() - ) + _ => Some(Result(Paths.get("."), Map())), + Log.urgent ) assertEquals( - build.module("example").jvm.get.scalaDeps, + build("example").module.jvm.get.scalaDeps, List(ScalaDep("org.scalameta", "interactive", "4.1.0", VersionTag.Full)) ) } - test("Copy compilerDeps from project definitions to modules") { + test("Copy settings from project definitions to modules") { val fooToml = """ |import = ["bar"] | @@ -182,6 +174,7 @@ object BuildConfigSpec extends SimpleTestSuite { | |[module.foo] |sources = ["foo-jvm/src"] + | |[module.foo.js] |sources = ["foo-js/src"] |compilerDeps = [ @@ -201,23 +194,32 @@ object BuildConfigSpec extends SimpleTestSuite { """.stripMargin val buildRaw = TomlUtils.parseBuildToml(Paths.get("."))(fooToml) - val (build, _) = BuildConfig.processBuild( + val build = BuildConfig.processBuild( buildRaw.right.get, Paths.get("."), _ => TomlUtils .parseBuildToml(Paths.get("."))(barToml) .toOption - .map(build => build -> Map.empty) + .map( + x => + Result( + Paths.get("."), + x.module.mapValues(m => ModuleConfig(m, Paths.get("."))) + ) + ), + Log.urgent ) + assertEquals(build("foo").module.scalaVersion, Some("2.12.8")) + assertEquals( - build.module("foo").compilerDeps, + build("foo").module.compilerDeps, List(ScalaDep("foo", "foo", "1.0", VersionTag.Full)) ) assertEquals( - build.module("foo").js.get.compilerDeps, + build("foo").module.js.get.compilerDeps, List( ScalaDep("foo", "foo", "1.0", VersionTag.Full), ScalaDep("foo-js", "foo-js", "1.0", VersionTag.Full) @@ -225,8 +227,49 @@ object BuildConfigSpec extends SimpleTestSuite { ) assertEquals( - build.module("bar").compilerDeps, + build("bar").module.compilerDeps, List(ScalaDep("bar", "bar", "1.0", VersionTag.Full)) ) } + + test("Inheritance of settings") { + val fooToml = """ + |[project] + |scalaVersion = "2.12.8" + |testFrameworks = ["a.b"] + | + |[module.foo] + |scalaVersion = "2.11.11" + |sources = ["foo-jvm/src"] + | + |[module.foo.js] + |sources = ["foo-js/src"] + |testFrameworks = ["c.d"] + |compilerDeps = [ + | ["foo-js", "foo-js", "1.0", "full"] + |] + | + |[module.bar] + |testFrameworks = ["a.b"] + |sources = ["foo-jvm/src"] + """.stripMargin + + val buildRaw = TomlUtils.parseBuildToml(Paths.get("."))(fooToml) + val build = BuildConfig.processBuild( + buildRaw.right.get, + Paths.get("."), + _ => None, + Log.urgent + ) + + assertEquals(build("foo").module.scalaVersion, Some("2.11.11")) + assertEquals(build("foo").module.js.get.scalaVersion, Some("2.11.11")) + assertEquals(build("bar").module.scalaVersion, Some("2.12.8")) + + assertEquals(build("foo").module.testFrameworks, List("a.b")) + assertEquals(build("foo").module.js.get.testFrameworks, List("a.b", "c.d")) + } + + // TODO Test case for inheritance of settings across files + // TODO Test case for inheritance of Scala options } diff --git a/src/test/scala/seed/generation/BloopIntegrationSpec.scala b/src/test/scala/seed/generation/BloopIntegrationSpec.scala index e4d8bb0..9a85ea7 100644 --- a/src/test/scala/seed/generation/BloopIntegrationSpec.scala +++ b/src/test/scala/seed/generation/BloopIntegrationSpec.scala @@ -58,7 +58,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] { testAsync( "Build project with compiler plug-in defined on cross-platform module" ) { _ => - val BuildConfig.Result(build, projectPath, _) = + val BuildConfig.Result(projectPath, build) = BuildConfig.load(Paths.get("test/example-paradise"), Log.urgent).get val buildPath = tempPath.resolve("example-paradise") Files.createDirectory(buildPath) @@ -81,7 +81,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] { testAsync("Build project with compiler plug-in defined on platform modules") { _ => - val BuildConfig.Result(build, projectPath, _) = BuildConfig + val BuildConfig.Result(projectPath, build) = BuildConfig .load(Paths.get("test/example-paradise-platform"), Log.urgent) .get val buildPath = tempPath.resolve("example-paradise-platform") @@ -104,7 +104,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] { } testAsync("Link JavaScript modules with custom target path") { _ => - val BuildConfig.Result(build, projectPath, _) = + val BuildConfig.Result(projectPath, build) = BuildConfig.load(Paths.get("test/submodule-output-path"), Log.urgent).get val buildPath = tempPath.resolve("submodule-output-path") Files.createDirectory(buildPath) @@ -140,7 +140,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] { testAsync("Build project with overridden compiler plug-in version") { _ => val projectPath = Paths.get("test/example-paradise-versions") - val BuildConfig.Result(build, _, _) = + val BuildConfig.Result(_, build) = BuildConfig.load(projectPath, Log.urgent).get val buildPath = tempPath.resolve("example-paradise-versions") Files.createDirectory(buildPath) @@ -221,7 +221,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] { } testAsync("Build modules with different Scala versions") { _ => - val BuildConfig.Result(build, projectPath, _) = BuildConfig + val BuildConfig.Result(projectPath, build) = BuildConfig .load(Paths.get("test/multiple-scala-versions"), Log.urgent) .get val buildPath = tempPath.resolve("multiple-scala-versions-bloop") @@ -255,7 +255,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] { ): Future[Unit] = { val path = Paths.get(s"test/$name") - val BuildConfig.Result(build, projectPath, _) = + val BuildConfig.Result(projectPath, build) = BuildConfig.load(path, Log.urgent).get val buildPath = tempPath.resolve(name) Files.createDirectory(buildPath) diff --git a/src/test/scala/seed/generation/IdeaSpec.scala b/src/test/scala/seed/generation/IdeaSpec.scala index 0feef77..0a3180a 100644 --- a/src/test/scala/seed/generation/IdeaSpec.scala +++ b/src/test/scala/seed/generation/IdeaSpec.scala @@ -8,11 +8,11 @@ import seed.Cli.{Command, PackageConfig} import seed.{Log, cli} import seed.artefact.ArtefactResolution import seed.config.BuildConfig +import seed.config.BuildConfig.ModuleConfig import seed.generation.util.PathUtil -import seed.model.Build.{Module, Project} +import seed.model.Build.{Module, Project, Resolvers} import seed.model.Platform.JVM import seed.model.{Build, Config} - import seed.generation.util.BuildUtil.tempPath object IdeaSpec extends SimpleTestSuite { @@ -53,39 +53,37 @@ object IdeaSpec extends SimpleTestSuite { test("Generate modules") { val build = - Build( - project = Project("2.12.8"), - module = Map( - "a" -> Module( - targets = List(JVM), - jvm = Some( - Module( - root = Some(Paths.get("a")), - sources = List(Paths.get("a/src")) - ) - ), - target = Map("assets" -> Build.Target()) - ), - "b" -> Module( - targets = List(JVM), - jvm = Some( - Module( - root = Some(Paths.get("b")), - sources = List(Paths.get("b/src")) - ) - ), - target = Map("assets" -> Build.Target(Some(Paths.get("b/assets")))) + Map( + "a" -> Module( + scalaVersion = Some("2.12.8"), + targets = List(JVM), + jvm = Some( + Module( + root = Some(Paths.get("a")), + sources = List(Paths.get("a/src")) + ) ), - "c" -> Module( - // Module that only has test sources - targets = List(JVM), - jvm = Some(Module(root = Some(Paths.get("c")))), - test = Some( - Module(jvm = Some(Module(sources = List(Paths.get("c/test"))))) + target = Map("assets" -> Build.Target()) + ), + "b" -> Module( + targets = List(JVM), + jvm = Some( + Module( + root = Some(Paths.get("b")), + sources = List(Paths.get("b/src")) ) + ), + target = Map("assets" -> Build.Target(Some(Paths.get("b/assets")))) + ), + "c" -> Module( + // Module that only has test sources + targets = List(JVM), + jvm = Some(Module(root = Some(Paths.get("c")))), + test = Some( + Module(jvm = Some(Module(sources = List(Paths.get("c/test"))))) ) ) - ) + ).mapValues(m => ModuleConfig(m, Paths.get("."))) val projectPath = Paths.get(".") val outputPath = Paths.get("/tmp") @@ -94,6 +92,7 @@ object IdeaSpec extends SimpleTestSuite { val (_, platformResolution, compilerResolution) = ArtefactResolution.resolution( seed.model.Config(), + Resolvers(), build, packageConfig, optionalArtefacts = false, @@ -122,7 +121,7 @@ object IdeaSpec extends SimpleTestSuite { } test("Generate project with custom compiler options") { - val BuildConfig.Result(build, projectPath, _) = + val BuildConfig.Result(projectPath, build) = BuildConfig.load(Paths.get("test/compiler-options"), Log.urgent).get val packageConfig = PackageConfig( tmpfs = false, @@ -164,7 +163,7 @@ object IdeaSpec extends SimpleTestSuite { } test("Generate project with different Scala versions") { - val BuildConfig.Result(build, projectPath, _) = BuildConfig + val BuildConfig.Result(projectPath, build) = BuildConfig .load(Paths.get("test/multiple-scala-versions"), Log.urgent) .get val packageConfig = PackageConfig( diff --git a/src/test/scala/seed/generation/PackageSpec.scala b/src/test/scala/seed/generation/PackageSpec.scala index d33aade..e0c3fa5 100644 --- a/src/test/scala/seed/generation/PackageSpec.scala +++ b/src/test/scala/seed/generation/PackageSpec.scala @@ -22,7 +22,7 @@ object PackageSpec extends TestSuite[Unit] { testAsync("Package modules with same package") { _ => val path = Paths.get("test/package-modules") - val BuildConfig.Result(build, projectPath, _) = + val BuildConfig.Result(projectPath, build) = BuildConfig.load(path, Log.urgent).get val outputPath = tempPath.resolve("package-modules") Files.createDirectory(outputPath) diff --git a/src/test/scala/seed/generation/util/ProjectGeneration.scala b/src/test/scala/seed/generation/util/ProjectGeneration.scala index 035b53a..06b6686 100644 --- a/src/test/scala/seed/generation/util/ProjectGeneration.scala +++ b/src/test/scala/seed/generation/util/ProjectGeneration.scala @@ -1,12 +1,13 @@ package seed.generation.util -import java.nio.file.{Files, Path} +import java.nio.file.{Files, Path, Paths} import org.apache.commons.io.FileUtils import seed.Log import seed.artefact.{ArtefactResolution, Coursier} +import seed.config.BuildConfig.{Build, ModuleConfig} import seed.generation.Bloop -import seed.model.Build.JavaDep +import seed.model.Build.{JavaDep, Resolvers} import seed.model.{Build, Platform} object ProjectGeneration { @@ -28,7 +29,7 @@ object ProjectGeneration { val resolution = Coursier.resolveAndDownload( platformDeps ++ libraryDeps, - build.resolvers, + Resolvers(), resolvedIvyPath, resolvedCachePath, optionalArtefacts = false, @@ -40,7 +41,7 @@ object ProjectGeneration { d => Coursier.resolveAndDownload( d, - build.resolvers, + Resolvers(), resolvedIvyPath, resolvedCachePath, optionalArtefacts = false, @@ -49,7 +50,7 @@ object ProjectGeneration { ) ) - build.module.foreach { + build.foreach { case (id, module) => Bloop.buildModule( projectPath, @@ -60,7 +61,7 @@ object ProjectGeneration { resolution, compilerResolution, id, - module, + module.module, optionalArtefacts = false, Log.urgent ) @@ -68,19 +69,24 @@ object ProjectGeneration { } def generateJavaDepBloopProject(projectPath: Path): Unit = { - val build = Build( - project = Build.Project("2.12.8"), - module = Map( - "base" -> Build.Module( + val build = Map( + "base" -> ModuleConfig( + Build.Module( + scalaVersion = Some("2.12.8"), targets = List(Platform.JVM), javaDeps = List(JavaDep("org.postgresql", "postgresql", "42.2.5")) ), - "example" -> Build.Module( + Paths.get(".") + ), + "example" -> ModuleConfig( + Build.Module( + scalaVersion = Some("2.12.8"), moduleDeps = List("base"), targets = List(Platform.JVM), jvm = Some(Build.Module()), test = Some(Build.Module(jvm = Some(Build.Module()))) - ) + ), + Paths.get(".") ) ) @@ -92,13 +98,15 @@ object ProjectGeneration { val sourcePath = projectPath.resolve("src") Files.createDirectories(sourcePath) - val build = Build( - project = Build.Project("2.12.8", scalaJsVersion = Some("0.6.26")), - module = Map( - "example" -> Build.Module( + val build = Map( + "example" -> ModuleConfig( + Build.Module( + scalaVersion = Some("2.12.8"), + scalaJsVersion = Some("0.6.26"), sources = List(sourcePath), targets = List(Platform.JVM, Platform.JavaScript) - ) + ), + Paths.get(".") ) )