Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BuildTarget: Expose source paths as environment variable #87

Merged
merged 1 commit into from
Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,9 +525,12 @@ class = { module = "keyboard:jvm", main = "keyboard.Bundle" }

In addition to `BUILD_PATH`, we also set the environment variable `MODULE_PATH` when running classes. The module path is the root path of the Seed project which contains the referenced module. This environment variable is not set for commands since their current working directory will point to the module path.

For two equivalent examples of using code generation, please refer to these links:
It is possible for multiple modules to share a code generator. To access the source paths of all modules that depend on a generator, access the environment variable `MODULE_SOURCE_PATHS`. Its value uses the system path separator character (`java.io.File.pathSeparatorChar`).

For examples of using code generation, please refer to these links:

* [Class target](test/custom-class-target/)
* [Class target shared by multiple modules](test/custom-class-target-shared/)
* [Command target](test/custom-command-target/)

### Compiler plug-ins
Expand Down
44 changes: 42 additions & 2 deletions src/main/scala/seed/cli/BuildTarget.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,42 @@ object BuildTarget {
case _ => List()
}.distinct

val inheritedTargets = modules
val flattenedDeps = modules
.flatMap {
case util.Target.Parsed(module, Some(Left(platform))) =>
BuildConfig.collectModuleDepsBase(build, module.module, platform)
case util.Target.Parsed(_, Some(Right(_))) => List()
case util.Target.Parsed(module, None) =>
module.name +: BuildConfig.collectModuleDeps(build, module.module)
}
.flatMap(m => build(m).module.target.keys.toList.map(m -> _))

// Determine direct parents of build targets
type BuildTarget = (String, String) // module, target name
type ParentModule = String
val parentModules: Map[BuildTarget, List[ParentModule]] =
flattenedDeps
.flatMap { parent =>
BuildConfig
.allTargets(build, parent)
.flatMap {
case (m, t) => BuildConfig.platformModule(build(m).module, t)
}
.flatMap(_.moduleDeps)
.flatMap(
module =>
build(module).module.target.keys.toList
.map(target => (module, target) -> parent)
)
}
.groupBy(_._1)
.mapValues(_.map(_._2))

val inheritedTargets: List[BuildTarget] = flattenedDeps
.flatMap(
module =>
build(module).module.target.keys.toList
.map(target => (module, target))
)
.distinct

if (targets.nonEmpty)
Expand All @@ -58,6 +85,16 @@ object BuildTarget {
val modulePath = build(m).path
val target = build(m).module.target(t)

val moduleSourcePaths = parentModules((m, t)).flatMap { module =>
val targets = BuildConfig.allTargets(build, module)
targets
.flatMap {
case (m, t) => BuildConfig.platformModule(build(m).module, t)
}
.flatMap(_.sources)
.map(_.toAbsolutePath.toString)
}

target.`class` match {
case Some(c) =>
val bloopName =
Expand All @@ -68,6 +105,7 @@ object BuildTarget {
customLog,
customLog.info(_),
Some(modulePath.toAbsolutePath.toString),
moduleSourcePaths,
Some(buildPath.toAbsolutePath.toString)
)(args: _*)

Expand All @@ -83,6 +121,7 @@ object BuildTarget {
ProcessHelper.runShell(
modulePath,
cmd,
moduleSourcePaths,
buildPath.toAbsolutePath.toString,
customLog,
customLog.info(_)
Expand All @@ -96,6 +135,7 @@ object BuildTarget {
val process = ProcessHelper.runShell(
modulePath,
cmd,
moduleSourcePaths,
buildPath.toAbsolutePath.toString,
customLog,
customLog.info(_)
Expand Down
16 changes: 16 additions & 0 deletions src/main/scala/seed/process/ProcessHelper.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package seed.process

import java.io.File
import java.nio.ByteBuffer
import java.nio.file.Path

Expand Down Expand Up @@ -65,6 +66,7 @@ object ProcessHelper {
cwd: Path,
cmd: List[String],
modulePath: Option[String] = None,
moduleSourcePaths: List[String] = List(),
buildPath: Option[String] = None,
log: Log,
onStdOut: String => Unit,
Expand All @@ -84,6 +86,16 @@ object ProcessHelper {
log.debug(s"Module path: ${Ansi.italic(mp)}", detail = true)
}

pb.environment()
.put(
"MODULE_SOURCE_PATHS",
moduleSourcePaths.mkString(File.pathSeparatorChar.toString)
)
log.debug(
s"Module source paths: ${moduleSourcePaths.map(Ansi.italic).mkString(", ")}",
detail = true
)

buildPath.foreach { bp =>
pb.environment().put("BUILD_PATH", bp)
log.debug(s"Build path: ${Ansi.italic(bp)}", detail = true)
Expand Down Expand Up @@ -125,13 +137,15 @@ object ProcessHelper {
log: Log,
onStdOut: String => Unit,
modulePath: Option[String] = None,
moduleSourcePaths: List[String] = List(),
buildPath: Option[String] = None,
verbose: Boolean = true
)(args: String*): Process =
runCommand(
cwd,
List("bloop") ++ args,
modulePath,
moduleSourcePaths,
buildPath,
log,
output => onStdOut(output),
Expand All @@ -141,6 +155,7 @@ object ProcessHelper {
def runShell(
cwd: Path,
command: String,
moduleSourcePaths: List[String] = List(),
buildPath: String,
log: Log,
onStdOut: String => Unit
Expand All @@ -149,6 +164,7 @@ object ProcessHelper {
cwd,
List("/bin/sh", "-c", command),
None,
moduleSourcePaths,
Some(buildPath),
log,
onStdOut
Expand Down
35 changes: 27 additions & 8 deletions src/test/scala/seed/generation/BloopIntegrationSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ object BloopIntegrationSpec extends TestSuite[Unit] {
def buildCustomTarget(
name: String,
expectFailure: Boolean = false
): Future[List[String]] = {
val path = Paths.get(s"test/$name")
): Future[Either[List[String], List[String]]] = {
val path = Paths.get("test", name)

val config = BuildConfig.load(path, Log.urgent).get
import config._
Expand Down Expand Up @@ -317,7 +317,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] {
if (expectFailure)
RTS.unsafeRunToFuture(uio).failed.map { _ =>
assert(lines.nonEmpty)
lines.toList
Left(lines.toList)
} else {
RTS.unsafeRunSync(uio)

Expand All @@ -327,20 +327,38 @@ object BloopIntegrationSpec extends TestSuite[Unit] {
TestProcessHelper
.runBloop(buildPath)("run", "demo")
.map { x =>
assertEquals(x.split("\n").count(_ == "42"), 1)
Files.delete(generatedFile)
assertEquals(lines.toList, List())
List()
Right(x.split("\n").toList)
}
}
}

testAsync("Build project with custom class target") { _ =>
buildCustomTarget("custom-class-target").map(_ => ())
buildCustomTarget("custom-class-target").map(
lines => assertEquals(lines.right.get.count(_ == "42"), 1)
)
}

testAsync(
"Build project with custom class target (shared by multiple modules)"
) { _ =>
buildCustomTarget("custom-class-target-shared").map(
lines =>
assertEquals(
lines.right.get.map(_.split("test/").last),
List(
"custom-class-target-shared/template1",
"custom-class-target-shared/template2"
)
)
)
}

testAsync("Build project with custom command target") { _ =>
buildCustomTarget("custom-command-target").map { _ =>
buildCustomTarget("custom-command-target").map { lines =>
assertEquals(lines.right.get.count(_ == "42"), 1)

val path = tempPath
.resolve("custom-command-target")
.resolve(".bloop")
Expand Down Expand Up @@ -368,7 +386,8 @@ object BloopIntegrationSpec extends TestSuite[Unit] {
log =>
// Must indicate correct position
assert(
log.exists(_.contains("[2:41]: not found: value invalidIdentifier"))
log.left.get
.exists(_.contains("[2:41]: not found: value invalidIdentifier"))
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ object TestProcessHelper {
cwd,
cmd,
None,
List(),
None,
Log.urgent,
out => sb.append(out + "\n")
Expand Down
22 changes: 22 additions & 0 deletions test/custom-class-target-shared/build.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[project]
scalaVersion = "2.13.0"

[module.utils.jvm]
sources = ["utils"]

[module.utils.target.gen-sources]
class = ["utils:jvm", "GenerateSources"]
await = true

# `template1` and `template2` share the code generator `utils:gen-sources`
[module.template1.jvm]
moduleDeps = ["utils"]
sources = ["template1"]

[module.template2.jvm]
moduleDeps = ["utils"]
sources = ["template2"]

[module.demo.jvm]
moduleDeps = ["template1", "template2"]
sources = ["demo"]
3 changes: 3 additions & 0 deletions test/custom-class-target-shared/demo/Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object Main {
def main(args: Array[String]): Unit = Generated.modulePaths.foreach(println)
}
16 changes: 16 additions & 0 deletions test/custom-class-target-shared/utils/GenerateSources.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import java.io.File
import java.nio.file.{Files, Paths, StandardOpenOption}

object GenerateSources {
def main(args: Array[String]): Unit = {
val modulePath = sys.env("MODULE_PATH")
val moduleSourcePaths =
sys.env("MODULE_SOURCE_PATHS").split(File.pathSeparatorChar)
val generatedPath =
Paths.get(modulePath).resolve("demo").resolve("Generated.scala")
val pathsScala = moduleSourcePaths.mkString("\"", "\", \"", "\"")
val output = s"object Generated { val modulePaths = List($pathsScala) }"

Files.write(generatedPath, output.getBytes)
}
}