Skip to content

Commit

Permalink
Merge pull request #25 from lucidsoftware/finish-conversion-to-worker
Browse files Browse the repository at this point in the history
Update all the things and fix/finish the worker implementation
  • Loading branch information
jjudd authored Sep 11, 2024
2 parents 0933e8a + 8abfbe5 commit 65f49e4
Show file tree
Hide file tree
Showing 14 changed files with 940 additions and 405 deletions.
5 changes: 5 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ common --noenable_bzlmod

common:ci --color=yes

test --test_output=errors

build --experimental_worker_multiplex_sandboxing
build --experimental_worker_cancellation

build --java_language_version="21"
build --java_runtime_version="remotejdk_21"
build --tool_java_language_version="21"
Expand Down
1 change: 1 addition & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ default_java_toolchain(
name = "repository_default_toolchain_21",
configuration = DEFAULT_TOOLCHAIN_CONFIGURATION,
java_runtime = "@rules_java//toolchains:remotejdk_21",
javac_supports_worker_multiplex_sandboxing = True,
# some of the default options make scala compilation fail in the test package
misc = [opt for opt in DEFAULT_JAVACOPTS if not opt.startswith("-Xep")],
source_version = "21",
Expand Down
18 changes: 9 additions & 9 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# rules_java
http_archive(
name = "rules_java",
sha256 = "647bb31c0d51882549def6f67ee9078df697043406ed4a5144bbdf3b17f91e33",
sha256 = "41131de4417de70b9597e6ebd515168ed0ba843a325dc54a81b92d7af9a7b3ea",
urls = [
"https://github.com/bazelbuild/rules_java/releases/download/7.8.0/rules_java-7.8.0.tar.gz",
"https://github.com/bazelbuild/rules_java/releases/download/7.9.0/rules_java-7.9.0.tar.gz",
],
)

Expand Down Expand Up @@ -89,11 +89,11 @@ load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()

# higherkindness/rules_scala
rules_scala_annex_version = "c8c4345e3f354753ed4ae7830618467ab59262c6"
rules_scala_annex_version = "f23c16037db66efb541dbbf5e17e6604886c85ff"

http_archive(
name = "rules_scala_annex",
integrity = "sha256-pmES8mOOeirB5woNYT2w97+5+C0Bt8ghrj9lHBKkMy8=",
integrity = "sha256-b/cPeh6J1Mq63u6fSWdEHAKL/kWfPhZcNL7m9If7PWM=",
strip_prefix = "rules_scala-{}".format(rules_scala_annex_version),
type = "zip",
url = "https://github.com/lucidsoftware/rules_scala/archive/{}.zip".format(rules_scala_annex_version),
Expand Down Expand Up @@ -154,19 +154,19 @@ load_env_vars(
####################################################################

## For tests
play_version = "2.7" # This doesn't actually matter, since we're not using the default compilers rules_play_routes provides
play_version = "3.0" # This doesn't actually matter, since we're not using the default compilers rules_play_routes provides

rules_play_routes_version = "8dbe5ee4359c30cfb7d368fed9b2df59c9665eb1"
rules_play_routes_version = "22a30c6d2d315e532b4e1963bb9e8a167c470545"

http_archive(
name = "io_bazel_rules_play_routes",
sha256 = "d93e6d53440a53da4c33f78736b8b78c9a1e84623bcccd6a1cbff55e1c318c97",
name = "rules_play_routes",
sha256 = "cc9e431be031f775da1610341dd4429ff27e7c1e191f8a3018e8b39da4ca00f1",
strip_prefix = "rules_play_routes-{}".format(rules_play_routes_version),
type = "zip",
url = "https://github.com/lucidsoftware/rules_play_routes/archive/{}.zip".format(rules_play_routes_version),
)

load("@io_bazel_rules_play_routes//:workspace.bzl", "play_routes_repositories")
load("@rules_play_routes//:workspace.bzl", "play_routes_repositories")

play_routes_repositories(play_version)

Expand Down
3 changes: 3 additions & 0 deletions play-routes-compiler/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ scala_library(
tags = ["maven_coordinates=com.lucidchart:" + artifact_id + ":{pom_version}"],
visibility = ["//visibility:public"],
deps = [
"//third_party/bazel/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/jarhelper",
"@play_routes_compiler_cli_maven//:com_github_scopt_scopt_3",
"@play_routes_compiler_cli_maven//:org_playframework_play_routes_compiler_3",
"@rules_scala_annex//src/main/scala/higherkindness/rules_scala/common/error",
"@rules_scala_annex//src/main/scala/higherkindness/rules_scala/common/sandbox",
"@rules_scala_annex//src/main/scala/higherkindness/rules_scala/common/worker",
],
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,69 +1,95 @@
package rulesplayroutes.routes

import com.google.devtools.build.buildjar.jarhelper.JarCreator
import higherkindness.rules_scala.common.error.AnnexWorkerError
import higherkindness.rules_scala.common.worker.WorkerMain
import higherkindness.rules_scala.common.sandbox.SandboxUtil
import java.io.{File, PrintStream}
import java.nio.file.{Files, Paths}
import java.nio.file.{Files, Path, Paths}
import play.routes.compiler._
import play.routes.compiler.RoutesCompiler.RoutesCompilerTask
import scala.collection.mutable.ListBuffer
import scala.jdk.CollectionConverters._
import scala.io.Source
import scala.util.{Try, Success, Failure}
import scopt.Read
import scala.Console._
import scopt.OptionParser

object CommandLinePlayRoutesCompiler extends WorkerMain[Unit] {
implicit val listPathRead: Read[List[Path]] = Read.reads { commaList =>
commaList.split(',').view.map(Paths.get(_)).toList
}

case class Config(
sources: Seq[File] = Seq.empty[File],
generatedDirectory: File = new File("."),
additionalImports: Seq[String] = Seq.empty[String],
routesGenerator: RoutesGenerator = InjectedRoutesGenerator,
generateReverseRouter: Boolean = false,
namespaceReverserRouter: Boolean = false,
generateForwardsRouter: Boolean = true
var sources: List[Path] = List.empty[Path],
var outputDirectory: Path = Paths.get("."),
var outputSrcJar: Path = Paths.get("."),
var additionalImports: List[String] = List.empty[String],
var routesGenerator: RoutesGenerator = InjectedRoutesGenerator,
var generateReverseRouter: Boolean = false,
var namespaceReverserRouter: Boolean = false,
var generateForwardsRouter: Boolean = true
)

val parser = new OptionParser[Config]("scopt") {
def parser(workDir: Path) = new OptionParser[Config]("scopt") {
head("Command Line Play Routes Compiler", "0.1")

arg[File]("<outputDirectory>").required().action { (value, config) =>
config.copy(generatedDirectory = value)
arg[Path]("<outputDirectory>").required().action { (outputDirectory, config) =>
config.outputDirectory = SandboxUtil.getSandboxPath(workDir, outputDirectory)
config
}.text("directory to output compiled routes to")

arg[Seq[File]]("<source1>,<source2>...").unbounded().required().action { (value, config) =>
config.copy(sources = value)
arg[Path]("<outputSrcJar>").required().action { (outputSrcJar, config) =>
config.outputSrcJar = SandboxUtil.getSandboxPath(workDir, outputSrcJar)
config
}.text("file to output srcjar containing compiled routes to")

arg[List[Path]]("<source1>,<source2>...").unbounded().required().action { (sources, config) =>
config.sources = sources.map(SandboxUtil.getSandboxPath(workDir, _))
config
}.text("routes to compile")

opt[String]('i', "routesImport").valueName("<import>").unbounded().action { (value, config) =>
config.copy(additionalImports = config.additionalImports ++ Seq(value))
opt[String]('i', "routesImport").valueName("<import>").unbounded().action { (routesImport, config) =>
config.additionalImports = config.additionalImports ++ List(routesImport)
config
}.text("Imports for the router")

opt[String]('g', "routesGenerator").valueName("<generator>").maxOccurs(1).action { (value, config) =>
config.copy(routesGenerator = {
opt[String]('g', "routesGenerator").valueName("<generator>").maxOccurs(1).action { (routesGeneratorClassString, config) =>
config.routesGenerator = {
try {
val name = if value.endsWith("$") then value else value + "$"
val name = if (routesGeneratorClassString.endsWith("$")) {
routesGeneratorClassString
} else {
routesGeneratorClassString + "$"
}
val clazz = java.lang.Class.forName(name, true, getClass.getClassLoader)
clazz.getField("MODULE$").get(null).asInstanceOf[RoutesGenerator]
} catch {
case e: Exception => {
throw new Exception(
s"Could not instantiate a routes generator from the given class: ${value}",
s"Could not instantiate a routes generator from the given class: ${routesGeneratorClassString}",
e,
)
}
}
})
}
config
}.text("The full class of the routes generator, e.g., play.routes.compiler.InjectedRoutesGenerator")

opt[Unit]('r', "generateReverseRouter").maxOccurs(1).action { (value, config) =>
config.copy(generateReverseRouter = true)
config.generateReverseRouter = true
config
}.text("Whether the reverse router should be generated. Setting to false may reduce compile times if it's not needed.")

opt[Unit]('n', "namespaceReverserRouter").maxOccurs(1).action { (value, config) =>
config.copy(namespaceReverserRouter = true)
config.namespaceReverserRouter = true
config
}.text("Whether the reverse router should be namespaced. Useful if you have many routers that use the same actions.")

opt[Boolean]('f', "generateForwardsRouter").maxOccurs(1).action { (value, config) =>
config.copy(generateForwardsRouter = value)
config.generateForwardsRouter = value
config
}.text("Whether the forwards router should be generated. Setting this to false should allow us to only generate reverse routes for a project")
}

Expand All @@ -78,43 +104,63 @@ object CommandLinePlayRoutesCompiler extends WorkerMain[Unit] {
/**
* Do Play Routes compilation and return true if things succeeded, otherwise return false.
*/
def compilePlayRoutes(config: Config): Boolean = {
config.sources.forall { file =>
private def compilePlayRoutes(config: Config, out: PrintStream): Try[Unit] = Try {
config.sources.foreach { path =>
RoutesCompiler.compile(
RoutesCompilerTask(
file,
path.toFile,
config.additionalImports,
config.generateForwardsRouter,
config.generateReverseRouter,
config.generateReverseRouter,
config.namespaceReverserRouter
),
config.routesGenerator,
config.generatedDirectory
config.outputDirectory.toFile(),
) match {
case Right(generatedFiles) =>
generatedFiles.foreach { f =>
stripHeader(f.getPath)
}
true
case Left(errors) =>
Console.err.println(s"${RESET}${RED}Play Routes Compilation Error:${RESET} Failed to compile routes for ${file}. Errors: ${errors}")
false
throw new Exception(
s"${RESET}${RED}Play Routes Compilation Error:${RESET} Failed to compile routes for ${path}. Errors: ${errors}"
)
}
}
}

def generateJar(config: Config): Unit = {
val jarCreator = new JarCreator(config.outputSrcJar)
jarCreator.addDirectory(config.outputDirectory)
jarCreator.setCompression(false)
jarCreator.setNormalize(true)
jarCreator.setVerbose(false)
jarCreator.execute()
}

/**
* Read any args passed in via files, so we can pass them to the arg parser
*/
private def readArgsFromArgFiles(args: Array[String]): List[String] = {
val builder = new ListBuffer[String]()
args.foreach {
case arg if arg.startsWith("@") => builder.addAll(Files.readAllLines(Paths.get(arg.tail)).asScala)
case arg => builder.addOne(arg)
}
builder.result()
}

override def init(args: Option[Array[String]]): Unit = ()

protected def work(ctx: Unit, args: Array[String], out: PrintStream): Unit = {
val isSuccess = parser.parse(args, Config())
.map(compilePlayRoutes)
.getOrElse(false)
protected def work(ctx: Unit, args: Array[String], out: PrintStream, workDir: Path, verbosity: Int): Unit = {
val config = parser(workDir).parse(
readArgsFromArgFiles(args), Config()
).getOrElse(throw new AnnexWorkerError(1))

if (isSuccess) {
System.exit(0)
} else {
System.exit(1)
compilePlayRoutes(config, out) match {
case Success(_) => generateJar(config)
case Failure(e) => throw new AnnexWorkerError(1, "Failed to compile play routes", e)
}
}
}
Loading

0 comments on commit 65f49e4

Please sign in to comment.