From 07767714ab90d881980953af6cc4ca3b196cd958 Mon Sep 17 00:00:00 2001 From: Tim Nieradzik Date: Sun, 14 Jul 2019 20:27:47 +0300 Subject: [PATCH] Refactor build path logic Currently, if tmpfs is disabled, `BuildTarget` uses an output path that is different from the generated Bloop project. Introduce `PathUtil` to unify the build path logic across the entire code base and write test cases. Also, change the path of the linked Bloop artefacts from `build/bloop/` to `build/`. When packaging modules, place `dist/` under the same build path. Further changes: - Idea: Move `normalisePath()` to `PathUtil` - `Cli`: Improve help --- src/main/scala/seed/Cli.scala | 10 ++-- src/main/scala/seed/cli/BuildTarget.scala | 8 ++- src/main/scala/seed/cli/Package.scala | 14 +++--- src/main/scala/seed/config/BuildConfig.scala | 7 --- src/main/scala/seed/generation/Bloop.scala | 49 +++++++++---------- src/main/scala/seed/generation/Idea.scala | 34 +++---------- .../scala/seed/generation/util/PathUtil.scala | 34 +++++++++++++ src/test/scala/seed/generation/IdeaSpec.scala | 7 +-- .../seed/generation/util/PathUtilSpec.scala | 18 +++++++ .../generation/util/ProjectGeneration.scala | 5 +- 10 files changed, 105 insertions(+), 81 deletions(-) create mode 100644 src/main/scala/seed/generation/util/PathUtil.scala create mode 100644 src/test/scala/seed/generation/util/PathUtilSpec.scala diff --git a/src/main/scala/seed/Cli.scala b/src/main/scala/seed/Cli.scala index 64779d5..cb6c94e 100644 --- a/src/main/scala/seed/Cli.scala +++ b/src/main/scala/seed/Cli.scala @@ -156,8 +156,8 @@ ${underlined("Usage:")} seed [--build=] [--config=] ${italic("link")} Link module(s) ${italic("buildEvents")} Subscribe to build events on Seed server ${italic("update")} Check library dependencies for updates - ${italic("package")} Create JAR package for given module and its dependent modules - Also sets the main class from the configuration file + ${italic("package")} Create JAR package for given module and its dependencies + Also sets the main class from the build file Specify --libs to copy all library dependencies for distribution ${bold("Parameters:")} @@ -176,7 +176,7 @@ ${underlined("Usage:")} seed [--build=] [--config=] ${bold("Command:")} ${underlined("build")} [--connect[=${webSocketDefaultConnection.format}]] [--watch] [--tmpfs] ${italic("--connect")} Run build command on remote Seed server ${italic("--watch")} Build upon source changes (cannot be combined with ${Ansi.italic("--connect")}) - ${italic("")} One or multiple space-separated modules. The syntax of a module is: ${italic("")} or ${italic(":")} + ${italic("")} One or multiple space-separated modules. The syntax of a module is: ${italic("")} or ${italic(":")} ${italic("Examples:")} - app Compile all available platforms of module ${Ansi.italic("app")} - app:js Only compile JavaScript platform of module ${Ansi.italic("app")} @@ -205,8 +205,8 @@ ${underlined("Usage:")} seed [--build=] [--config=] ${bold("Command:")} ${underlined("package")} [--tmpfs] [--libs] [--output=] ${italic("--tmpfs")} Read build directory in tmpfs - ${italic("--libs")} Copy libraries and reference them in the JAR's class path. - ${italic("--output")} Output path (default: ${Ansi.italic("dist/")}) + ${italic("--libs")} Copy libraries and reference them in the JAR's class path + ${italic("--output")} Output path (default: ${Ansi.italic("build/dist/")}) ${italic("")} Module to package""") } diff --git a/src/main/scala/seed/cli/BuildTarget.scala b/src/main/scala/seed/cli/BuildTarget.scala index 9c41194..f4caf8e 100644 --- a/src/main/scala/seed/cli/BuildTarget.scala +++ b/src/main/scala/seed/cli/BuildTarget.scala @@ -1,15 +1,15 @@ package seed.cli -import java.nio.file.{Path, Paths} +import java.nio.file.Path import seed.Log import seed.config.BuildConfig +import seed.generation.util.PathUtil import seed.model import seed.process.ProcessHelper import scala.concurrent.{Await, Future} import scala.concurrent.duration.Duration - import scala.concurrent.ExecutionContext.Implicits.global object BuildTarget { @@ -46,9 +46,7 @@ object BuildTarget { val allTargets = (targets ++ inheritedTargets).distinct - val buildPath = - if (tmpfs) BuildConfig.tmpfsPath(projectPath, log).resolve("bloop") - else Paths.get("build") + val buildPath = PathUtil.buildPath(projectPath, tmpfs, log) log.info(s"Build path: $buildPath") diff --git a/src/main/scala/seed/cli/Package.scala b/src/main/scala/seed/cli/Package.scala index 522c0cf..4ca3093 100644 --- a/src/main/scala/seed/cli/Package.scala +++ b/src/main/scala/seed/cli/Package.scala @@ -1,13 +1,12 @@ package seed.cli -import java.nio.file.{Files, Path, Paths} +import java.nio.file.{Files, Path} import scala.collection.JavaConverters._ - import seed.artefact.{ArtefactResolution, Coursier} import seed.cli.util.Ansi import seed.config.BuildConfig -import seed.generation.Bloop +import seed.generation.util.PathUtil import seed.model import seed.model.Build.JavaDep import seed.model.Config @@ -24,9 +23,10 @@ object Package { packageConfig: Cli.PackageConfig ): Unit = { val tmpfs = packageConfig.tmpfs || seedConfig.build.tmpfs - val buildPath = Bloop.getBuildPath(projectPath, tmpfs) + val buildPath = PathUtil.buildPath(projectPath, tmpfs) + val bloopBuildPath = buildPath.resolve("bloop") val platform = JVM - val outputPath = output.getOrElse(Paths.get("dist")) + val outputPath = output.getOrElse(buildPath.resolve("dist")) build.module.get(module) match { case None => Log.error(s"Module ${Ansi.italic(module)} does not exist") @@ -35,9 +35,9 @@ object Package { List(module) ++ BuildConfig.collectJvmModuleDeps(build, resolvedModule) ).map { name => if (BuildConfig.isCrossBuild(build.module(name))) - buildPath.resolve(name + "-" + platform.id) + bloopBuildPath.resolve(name + "-" + platform.id) else - buildPath.resolve(name) + bloopBuildPath.resolve(name) } val notFound = paths.find(p => !Files.exists(p)) diff --git a/src/main/scala/seed/config/BuildConfig.scala b/src/main/scala/seed/config/BuildConfig.scala index 9f0efca..593b835 100644 --- a/src/main/scala/seed/config/BuildConfig.scala +++ b/src/main/scala/seed/config/BuildConfig.scala @@ -290,11 +290,4 @@ object BuildConfig { def collectJvmJavaDeps(build: Build, module: Module): List[JavaDep] = module.javaDeps ++ module.jvm.map(_.javaDeps).getOrElse(List()) ++ jvmModuleDeps(module).flatMap(m => collectJvmJavaDeps(build, build.module(m))) - - def tmpfsPath(projectPath: Path, log: Log = Log): Path = { - val name = projectPath.toAbsolutePath.getFileName.toString - log.info("Build path set to tmpfs") - log.warn(s"Please ensure that there is no other project with the name ${Ansi.italic(name)} that also compiles to tmpfs") - Paths.get("/tmp").resolve("build-" + name) - } } diff --git a/src/main/scala/seed/generation/Bloop.scala b/src/main/scala/seed/generation/Bloop.scala index d388d97..2626501 100644 --- a/src/main/scala/seed/generation/Bloop.scala +++ b/src/main/scala/seed/generation/Bloop.scala @@ -2,14 +2,15 @@ package seed.generation import java.nio.file.{Files, Path, Paths} -import seed.config.BuildConfig.{collectJsClassPath, collectJsDeps, collectJvmClassPath, collectJvmScalaDeps, collectJvmJavaDeps, collectNativeClassPath, collectNativeDeps} -import seed.artefact.{Coursier, ArtefactResolution} +import seed.config.BuildConfig.{collectJsClassPath, collectJsDeps, collectJvmClassPath, collectJvmJavaDeps, collectJvmScalaDeps, collectNativeClassPath, collectNativeDeps} +import seed.artefact.{ArtefactResolution, Coursier} import seed.cli.util.Ansi import seed.model.Build.{Module, Project} import seed.model.Platform.{JVM, JavaScript, Native} import seed.model.{Artefact, Build, Resolution} import seed.Log import seed.config.BuildConfig +import seed.generation.util.PathUtil object Bloop { import bloop.config.Config @@ -366,6 +367,7 @@ object Bloop { def buildModule(projectPath: Path, bloopPath: Path, buildPath: Path, + bloopBuildPath: Path, build: Build, resolution: Coursier.ResolutionResult, compilerResolution: List[Coursier.ResolutionResult], @@ -379,25 +381,25 @@ object Bloop { moduleOutputPath(buildPath, module.native, name + ".run") writeJsModule(build, if (!isCrossBuild) name else name + "-js", - projectPath, bloopPath, buildPath, Some(jsOutputPath), + projectPath, bloopPath, bloopBuildPath, Some(jsOutputPath), module.copy(scalaDeps = collectJsDeps(build, module)), - collectJsClassPath(buildPath, build, module), + collectJsClassPath(bloopBuildPath, build, module), module.js, build.project, resolution, compilerResolution, jsdom = module.js.exists(_.jsdom), emitSourceMaps = module.js.exists(_.emitSourceMaps), test = false) writeJvmModule(build, if (!isCrossBuild) name else name + "-jvm", - projectPath, bloopPath, buildPath, + projectPath, bloopPath, bloopBuildPath, module.copy( scalaDeps = collectJvmScalaDeps(build, module), javaDeps = collectJvmJavaDeps(build, module) ), - collectJvmClassPath(buildPath, build, module), + collectJvmClassPath(bloopBuildPath, build, module), module.jvm, build.project, resolution, compilerResolution, test = false) writeNativeModule(build, if (!isCrossBuild) name else name + "-native", - projectPath, bloopPath, buildPath, Some(nativeOutputPath), + projectPath, bloopPath, bloopBuildPath, Some(nativeOutputPath), module.copy(scalaDeps = collectNativeDeps(build, module)), - collectJvmClassPath(buildPath, build, module), + collectJvmClassPath(bloopBuildPath, build, module), module.native, build.project, resolution, compilerResolution, test = false) module.test.foreach { test => @@ -406,35 +408,35 @@ object Bloop { val emitSourceMaps = test.js.exists(_.emitSourceMaps) writeJsModule(build, if (!isCrossBuild) name else name + "-js", - projectPath, bloopPath, buildPath, None, + projectPath, bloopPath, bloopBuildPath, None, module.copy( sources = test.sources, scalaDeps = collectJsDeps(build, module) ++ test.scalaDeps, targets = targets ), - collectJsClassPath(buildPath, build, module), + collectJsClassPath(bloopBuildPath, build, module), test.js, build.project, resolution, compilerResolution, jsdom, emitSourceMaps, test = true) writeNativeModule(build, if (!isCrossBuild) name else name + "-native", - projectPath, bloopPath, buildPath, None, + projectPath, bloopPath, bloopBuildPath, None, module.copy( sources = test.sources, scalaDeps = collectNativeDeps(build, module) ++ test.scalaDeps, targets = targets ), - collectNativeClassPath(buildPath, build, module), + collectNativeClassPath(bloopBuildPath, build, module), test.native, build.project, resolution, compilerResolution, test = true) writeJvmModule(build, if (!isCrossBuild) name else name + "-jvm", - projectPath, bloopPath, buildPath, + projectPath, bloopPath, bloopBuildPath, module.copy( sources = test.sources, scalaDeps = collectJvmScalaDeps(build, module) ++ test.scalaDeps, javaDeps = collectJvmJavaDeps(build, module) ++ test.javaDeps, targets = targets ), - collectJvmClassPath(buildPath, build, module), + collectJvmClassPath(bloopBuildPath, build, module), test.jvm, build.project, resolution, compilerResolution, test = true) if (isCrossBuild) @@ -443,7 +445,7 @@ object Bloop { name = name + "-test", bloopPath = bloopPath, dependencies = targets.map(t => name + "-" + t.id + "-test"), - classesDir = buildPath, + classesDir = bloopBuildPath, classPath = List(), sources = List(), scalaCompiler = None, @@ -458,7 +460,7 @@ object Bloop { name = name, bloopPath = bloopPath, dependencies = module.targets.map(t => name + "-" + t.id), - classesDir = buildPath, + classesDir = bloopBuildPath, classPath = List(), sources = List(), scalaCompiler = None, @@ -467,26 +469,19 @@ object Bloop { platform = None) } - def getBuildPath(projectPath: Path, tmpfs: Boolean): Path = { - val baseBuildPath = - if (tmpfs) BuildConfig.tmpfsPath(projectPath) - else projectPath.resolve("build") - - baseBuildPath.resolve("bloop") - } - def build(projectPath: Path, build: Build, resolution: Coursier.ResolutionResult, compilerResolution: List[Coursier.ResolutionResult], tmpfs: Boolean): Unit = { val bloopPath = projectPath.resolve(".bloop") - val buildPath = getBuildPath(projectPath, tmpfs) + val buildPath = PathUtil.buildPath(projectPath, tmpfs) + val bloopBuildPath = buildPath.resolve("bloop") Log.info(s"Build path: ${Ansi.italic(buildPath.toString)}") if (!Files.exists(bloopPath)) Files.createDirectory(bloopPath) - if (!Files.exists(buildPath)) Files.createDirectories(buildPath) + if (!Files.exists(bloopBuildPath)) Files.createDirectories(bloopBuildPath) import scala.collection.JavaConverters._ Files.newDirectoryStream(bloopPath, "*.json").iterator().asScala @@ -494,7 +489,7 @@ object Bloop { build.module.foreach { case (name, module) => Log.info(s"Building module ${Ansi.italic(name)}...") - buildModule(projectPath, bloopPath, buildPath, build, + buildModule(projectPath, bloopPath, buildPath, bloopBuildPath, build, resolution, compilerResolution, name, module) } diff --git a/src/main/scala/seed/generation/Idea.scala b/src/main/scala/seed/generation/Idea.scala index 2367a52..5f016b8 100644 --- a/src/main/scala/seed/generation/Idea.scala +++ b/src/main/scala/seed/generation/Idea.scala @@ -1,18 +1,19 @@ package seed.generation -import java.nio.file.{Files, Path, Paths} +import java.nio.file.{Files, Path} import scala.collection.JavaConverters._ import org.apache.commons.io.FileUtils import seed.config.BuildConfig.{collectJsDeps, collectJsModuleDeps, collectJvmJavaDeps, collectJvmModuleDeps, collectJvmScalaDeps, collectNativeDeps, collectNativeModuleDeps} -import seed.artefact.{Coursier, ArtefactResolution} +import seed.artefact.{ArtefactResolution, Coursier} import seed.cli.util.Ansi -import seed.generation.util.IdeaFile +import seed.generation.util.{IdeaFile, PathUtil} import seed.model.{Build, Resolution} import seed.model.Build.Module import seed.model.Platform.{JVM, JavaScript, Native} import seed.Log import seed.config.BuildConfig +import seed.generation.util.PathUtil.normalisePath object Idea { val ModuleDir = "$MODULE_DIR$" @@ -42,22 +43,6 @@ object Idea { .toFile, xml, "UTF-8") } - def normalisePath(pathVariable: String, root: Path)(path: Path): String = { - val canonicalRoot = root.toFile.getCanonicalPath - val canonicalPath = path.toFile.getCanonicalPath - - val rootElems = canonicalRoot.split("/").toList - val pathElems = canonicalPath.split("/").toList - val common = pathElems.zip(rootElems).takeWhile { case (a, b) => a == b } - - if (common.length == 1) canonicalPath - else { - val levels = rootElems.length - common.length - val relativePath = (0 until levels).map(_ => "../").mkString - pathVariable + "/" + relativePath + pathElems.drop(common.length).mkString("/") - } - } - def createModule(build : Build, root : Path, name : String, @@ -430,13 +415,10 @@ object Idea { resolution: Coursier.ResolutionResult, compilerResolution: List[Coursier.ResolutionResult], tmpfs: Boolean): Unit = { - val baseBuildPath = - if (tmpfs) BuildConfig.tmpfsPath(projectPath) - else Paths.get("build") - - val buildPath = baseBuildPath.resolve("idea") + val buildPath = PathUtil.buildPath(projectPath, tmpfs) + val ideaBuildPath = buildPath.resolve("idea") - Log.info(s"Build path: ${Ansi.italic(buildPath.toString)}") + Log.info(s"Build path: ${Ansi.italic(ideaBuildPath.toString)}") val ideaPath = outputPath.resolve(".idea") val modulesPath = ideaPath.resolve("modules") @@ -460,7 +442,7 @@ object Idea { val modules = build.module.toList.flatMap { case (name, module) => buildModule( - projectPath, buildPath, ideaPath, modulesPath, librariesPath, build, + projectPath, ideaBuildPath, ideaPath, modulesPath, librariesPath, build, compilerResolution, resolution, name, module) } diff --git a/src/main/scala/seed/generation/util/PathUtil.scala b/src/main/scala/seed/generation/util/PathUtil.scala new file mode 100644 index 0000000..2ee7c2c --- /dev/null +++ b/src/main/scala/seed/generation/util/PathUtil.scala @@ -0,0 +1,34 @@ +package seed.generation.util + +import java.nio.file.{Path, Paths} + +import seed.Log +import seed.cli.util.Ansi + +object PathUtil { + def tmpfsPath(projectPath: Path, log: Log = Log): Path = { + val name = projectPath.toAbsolutePath.getFileName.toString + log.info("Build path set to tmpfs") + log.warn(s"Please ensure that there is no other project with the name ${Ansi.italic(name)} that also compiles to tmpfs") + Paths.get("/tmp").resolve("build-" + name) + } + + def buildPath(projectPath: Path, tmpfs: Boolean, log: Log = Log): Path = + if (tmpfs) tmpfsPath(projectPath) else projectPath.resolve("build") + + def normalisePath(pathVariable: String, root: Path)(path: Path): String = { + val canonicalRoot = root.toFile.getCanonicalPath + val canonicalPath = path.toFile.getCanonicalPath + + val rootElems = canonicalRoot.split("/").toList + val pathElems = canonicalPath.split("/").toList + val common = pathElems.zip(rootElems).takeWhile { case (a, b) => a == b } + + if (common.length == 1) canonicalPath + else { + val levels = rootElems.length - common.length + val relativePath = (0 until levels).map(_ => "../").mkString + pathVariable + "/" + relativePath + pathElems.drop(common.length).mkString("/") + } + } +} diff --git a/src/test/scala/seed/generation/IdeaSpec.scala b/src/test/scala/seed/generation/IdeaSpec.scala index 5b9534f..96439c7 100644 --- a/src/test/scala/seed/generation/IdeaSpec.scala +++ b/src/test/scala/seed/generation/IdeaSpec.scala @@ -8,6 +8,7 @@ import seed.Cli.{Command, PackageConfig} import seed.{Log, cli} import seed.artefact.ArtefactResolution import seed.config.BuildConfig +import seed.generation.util.PathUtil import seed.model.Build.{Module, Project} import seed.model.Platform.JVM import seed.model.{Build, Config} @@ -15,16 +16,16 @@ import seed.model.{Build, Config} object IdeaSpec extends SimpleTestSuite { test("Normalise paths") { assertEquals( - Idea.normalisePath(Idea.ModuleDir, Paths.get("/tmp"))(Paths.get("/tmp")), + PathUtil.normalisePath(Idea.ModuleDir, Paths.get("/tmp"))(Paths.get("/tmp")), "$MODULE_DIR$/") assertEquals( - Idea.normalisePath(Idea.ModuleDir, Paths.get("/tmp/.idea/modules"))( + PathUtil.normalisePath(Idea.ModuleDir, Paths.get("/tmp/.idea/modules"))( Paths.get("/tmp/src") ), "$MODULE_DIR$/../../src") assertEquals( - Idea.normalisePath(Idea.ModuleDir, Paths.get(".idea/modules"))(Paths.get("/tmp/build")), + PathUtil.normalisePath(Idea.ModuleDir, Paths.get(".idea/modules"))(Paths.get("/tmp/build")), "/tmp/build") } diff --git a/src/test/scala/seed/generation/util/PathUtilSpec.scala b/src/test/scala/seed/generation/util/PathUtilSpec.scala new file mode 100644 index 0000000..5b7c3ca --- /dev/null +++ b/src/test/scala/seed/generation/util/PathUtilSpec.scala @@ -0,0 +1,18 @@ +package seed.generation.util + +import java.nio.file.Paths + +import minitest.SimpleTestSuite + +object PathUtilSpec extends SimpleTestSuite { + test("Default build paths") { + val projectPath = Paths.get("test/compiler-options") + + assertEquals( + PathUtil.buildPath(projectPath, tmpfs = false), + Paths.get("test/compiler-options/build")) + assertEquals( + PathUtil.buildPath(projectPath, tmpfs = true), + Paths.get("/tmp/build-compiler-options")) + } +} diff --git a/src/test/scala/seed/generation/util/ProjectGeneration.scala b/src/test/scala/seed/generation/util/ProjectGeneration.scala index e0a3152..f45f571 100644 --- a/src/test/scala/seed/generation/util/ProjectGeneration.scala +++ b/src/test/scala/seed/generation/util/ProjectGeneration.scala @@ -14,8 +14,10 @@ object ProjectGeneration { val bloopPath = projectPath.resolve(".bloop") val buildPath = projectPath.resolve("build") + val bloopBuildPath = buildPath.resolve("bloop") - Set(bloopPath, buildPath).foreach(Files.createDirectories(_)) + Set(bloopPath, buildPath, bloopBuildPath) + .foreach(Files.createDirectories(_)) val resolvedIvyPath = Coursier.DefaultIvyPath val resolvedCachePath = Coursier.DefaultCachePath @@ -38,6 +40,7 @@ object ProjectGeneration { projectPath, bloopPath, buildPath, + bloopBuildPath, build, resolution, compilerResolution,