Skip to content

Commit ef63b0a

Browse files
cattibrieBorja Lorente
authored andcommitted
Support scrooge-generator compiler flags in thrift_library rule (#6)
* Add option parsing file * Add OptionParser * Add scopt dependency * Lint reformat
1 parent fef0a61 commit ef63b0a

File tree

11 files changed

+284
-60
lines changed

11 files changed

+284
-60
lines changed

scala/private/macros/scala_repositories.bzl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ def scala_repositories(
8383
"io_bazel_rules_scala_scalactic",
8484
"io_bazel_rules_scala_scala_xml",
8585
"io_bazel_rules_scala_scala_parser_combinators",
86+
# Remove this dependency when ScroogeConfig and ScroogeOptionParser are added to the com.twitter.scrooge
87+
"io_bazel_rules_scala_scopt",
8688
],
8789
maven_servers = _default_maven_server_urls(),
8890
fetch_sources = fetch_sources,
@@ -128,3 +130,9 @@ def scala_repositories(
128130
name = "io_bazel_rules_scala/dependency/scala/scalactic/scalactic",
129131
actual = "@io_bazel_rules_scala_scalactic",
130132
)
133+
134+
# Remove this dependency when ScroogeConfig and ScroogeOptionParser are added to the com.twitter.scrooge
135+
native.bind(
136+
name = "io_bazel_rules_scala/dependency/scala/scopt",
137+
actual = "@io_bazel_rules_scala_scopt",
138+
)

src/scala/io/bazel/rules_scala/scrooge_support/BUILD

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,18 @@ scala_library(
1111
visibility = ["//visibility:public"],
1212
deps = [
1313
"//twitter_scrooge:compiler_classpath",
14+
# Remove this dependency when ScroogeConfig and ScroogeOptionParser are added to the com.twitter.scrooge
15+
":option_parser",
16+
],
17+
)
18+
19+
# Remove this target when ScroogeConfig and ScroogeOptionParser are added to the com.twitter.scrooge
20+
scala_library(
21+
name = "option_parser",
22+
srcs = ["ScroogeOptionParser.scala"],
23+
visibility = ["//visibility:public"],
24+
deps = [
25+
"//external:io_bazel_rules_scala/dependency/scala/scopt",
26+
"//external:io_bazel_rules_scala/dependency/thrift/scrooge_generator",
1427
],
1528
)

src/scala/io/bazel/rules_scala/scrooge_support/Compiler.scala

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ package io.bazel.rules_scala.scrooge_support
3333

3434
import com.twitter.scrooge._
3535
import com.twitter.scrooge.ast.Document
36-
import com.twitter.scrooge.backend.{GeneratorFactory, ScalaGenerator, ServiceOption}
36+
import com.twitter.scrooge.backend.{GeneratorFactory, ScalaGenerator}
3737
import com.twitter.scrooge.frontend.{FileParseException, TypeResolver, ThriftParser, Importer, MultiImporter, ZipImporter}
38+
import com.twitter.scrooge.java_generator.ApacheJavaGenerator
3839
import java.io.{File, FileWriter}
3940
import java.nio.file.Paths
4041
import java.util.jar.{ JarFile, JarEntry }
@@ -43,9 +44,6 @@ import scala.collection.concurrent.TrieMap
4344
import scala.collection.mutable
4445

4546
object CompilerDefaults {
46-
var language: String = "scala"
47-
var defaultNamespace: String = "thrift"
48-
4947
def listJar(_jar: File): List[String] =
5048
try {
5149
val files = List.newBuilder[String]
@@ -65,46 +63,34 @@ object CompilerDefaults {
6563
}
6664
}
6765

68-
class Compiler {
69-
val defaultDestFolder = "."
70-
var destFolder: String = defaultDestFolder
66+
class Compiler(val config: ScroogeConfig) {
7167
// These are jars we are including, but are not compiling
72-
val includeJars = new mutable.ListBuffer[String]
68+
val includeJars = config.includePaths
7369
// these are the jars we want to compile into scala source jars
74-
val compileJars = new mutable.ListBuffer[String]
75-
val flags = new mutable.HashSet[ServiceOption]
76-
val namespaceMappings = new mutable.HashMap[String, String]
77-
var verbose = false
78-
var strict = true
79-
var skipUnchanged = false
80-
var experimentFlags = new mutable.ListBuffer[String]
81-
var fileMapPath: scala.Option[String] = None
70+
val compileJars = config.thriftFiles
71+
val experimentFlags = config.languageFlags
8272
var fileMapWriter: scala.Option[FileWriter] = None
83-
var dryRun: Boolean = false
84-
var language: String = CompilerDefaults.language
85-
var defaultNamespace: String = CompilerDefaults.defaultNamespace
86-
var scalaWarnOnJavaNSFallback: Boolean = false
8773

8874

8975
def run() {
9076
// if --gen-file-map is specified, prepare the map file.
91-
fileMapWriter = fileMapPath.map { path =>
77+
fileMapWriter = config.fileMapPath.map { path =>
9278
val file = new File(path)
9379
val dir = file.getParentFile
9480
if (dir != null && !dir.exists()) {
9581
dir.mkdirs()
9682
}
97-
if (verbose) {
83+
if (config.verbose) {
9884
println("+ Writing file mapping to %s".format(path))
9985
}
10086
new FileWriter(file)
10187
}
10288

10389
val allJars: List[File] =
104-
((includeJars.toList) ::: (compileJars.toList))
90+
((includeJars) ::: (compileJars))
10591
.map { path => (new File(path)).getCanonicalFile }
10692

107-
val isJava = language.equals("java")
93+
val isJava = config.language.equals("java")
10894
val documentCache = new TrieMap[String, Document]
10995

11096
// compile
@@ -125,35 +111,37 @@ class Compiler {
125111
val importer = rootImporter.copy(focus = focus) +: rootImporter
126112
val parser = new ThriftParser(
127113
importer,
128-
strict,
114+
config.strict,
129115
defaultOptional = isJava,
130116
skipIncludes = false,
131117
documentCache
132118
)
133119
parser.logger.setLevel(Level.OFF) // scrooge warns on file names with "/"
134-
val doc = parser.parseFile(inputFile).mapNamespaces(namespaceMappings.toMap)
120+
val doc = parser.parseFile(inputFile).mapNamespaces(config.namespaceMappings)
135121

136-
if (verbose) println("+ Compiling %s".format(inputFile))
122+
if (config.verbose) println("+ Compiling %s".format(inputFile))
137123
val resolvedDoc = TypeResolver()(doc)
138124
val generator = GeneratorFactory(
139-
language,
125+
config.language,
140126
resolvedDoc,
141-
defaultNamespace,
127+
config.defaultNamespace,
142128
experimentFlags)
143129

144130
generator match {
145-
case g: ScalaGenerator => g.warnOnJavaNamespaceFallback = scalaWarnOnJavaNSFallback
131+
case g: ScalaGenerator => g.warnOnJavaNamespaceFallback = config.scalaWarnOnJavaNSFallback
132+
case g: ApacheJavaGenerator => g.serEnumType = config.javaSerEnumType
146133
case _ => ()
147134
}
148135

149136
val generatedFiles = generator(
150-
flags.toSet,
151-
new File(destFolder),
152-
dryRun
137+
config.flags,
138+
new File(config.destFolder),
139+
config.dryRun,
140+
config.genAdapt
153141
).map {
154142
_.getPath
155143
}
156-
if (verbose) {
144+
if (config.verbose) {
157145
println("+ Generated %s".format(generatedFiles.mkString(", ")))
158146
}
159147
fileMapWriter.foreach { w =>
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*
2+
* Copyright 2020 Twitter, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
* not use this file except in compliance with the License. You may obtain
6+
* a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.bazel.rules_scala.scrooge_support
17+
18+
import com.twitter.scrooge.backend.{
19+
GeneratorFactory,
20+
WithFinagle,
21+
WithJavaPassThrough,
22+
ServiceOption
23+
}
24+
import java.io.File
25+
import java.util.Properties
26+
import scopt.OptionParser
27+
28+
case class ScroogeConfig(
29+
destFolder: String = ".",
30+
includePaths: List[String] = List(),
31+
thriftFiles: List[String] = List(),
32+
flags: Set[ServiceOption] = Set(),
33+
namespaceMappings: Map[String, String] = Map(),
34+
verbose: Boolean = false,
35+
strict: Boolean = true,
36+
genAdapt: Boolean = false,
37+
skipUnchanged: Boolean = false,
38+
languageFlags: Seq[String] = Seq(),
39+
fileMapPath: scala.Option[String] = None,
40+
dryRun: Boolean = false,
41+
language: String = "scala",
42+
defaultNamespace: String = "thrift",
43+
scalaWarnOnJavaNSFallback: Boolean = false,
44+
javaSerEnumType: Boolean = false)
45+
46+
object ScroogeOptionParser {
47+
48+
/** Optionally returns config with parsed values from args.
49+
* @param args command line arguments.
50+
* @param defaultConfig config with configurable defaults that is used to store parsed args.
51+
*/
52+
def parseOptions(
53+
args: Seq[String],
54+
defaultConfig: ScroogeConfig = ScroogeConfig()
55+
): Option[ScroogeConfig] = {
56+
val buildProperties = new Properties
57+
scala.Option(getClass.getResource("build.properties")) foreach { resource =>
58+
buildProperties.load(resource.openStream)
59+
}
60+
61+
val parser = new OptionParser[ScroogeConfig]("scrooge") {
62+
help("help").text("show this help screen")
63+
64+
override def showUsageOnError: Option[Boolean] = Some(true)
65+
66+
opt[Unit]('V', "version")
67+
.action { (_, c) =>
68+
println("scrooge " + buildProperties.getProperty("version", "0.0"))
69+
println(" build " + buildProperties.getProperty("build_name", "unknown"))
70+
println(" git revision " + buildProperties.getProperty("build_revision", "unknown"))
71+
System.exit(0)
72+
c
73+
}
74+
.text("print version and quit")
75+
76+
opt[Unit]('v', "verbose")
77+
.action((_, c) => c.copy(verbose = true))
78+
.text("log verbose messages about progress")
79+
80+
opt[String]('d', "dest")
81+
.valueName("<path>")
82+
.action((d, c) => c.copy(destFolder = d))
83+
.text("write generated code to a folder (default: %s)".format(defaultConfig.destFolder))
84+
85+
opt[String]("import-path")
86+
.unbounded()
87+
.valueName("<path>")
88+
.action { (path, c) =>
89+
val includePaths = path.split(File.pathSeparator) ++: c.includePaths
90+
c.copy(includePaths = includePaths)
91+
}
92+
.text(
93+
"[DEPRECATED] path(s) to search for included thrift files (may be used multiple times)"
94+
)
95+
96+
opt[String]('i', "include-path")
97+
.unbounded()
98+
.valueName("<path>")
99+
.action { (path, c) =>
100+
val includePaths = path.split(File.pathSeparator) ++: c.includePaths
101+
c.copy(includePaths = includePaths)
102+
}
103+
.text("path(s) to search for included thrift files (may be used multiple times)")
104+
105+
opt[String]('n', "namespace-map")
106+
.unbounded()
107+
.valueName("<oldname>=<newname>")
108+
.action { (mapping, c) =>
109+
mapping.split("=") match {
110+
case Array(from, to) =>
111+
c.copy(namespaceMappings = c.namespaceMappings + (from -> to))
112+
}
113+
}
114+
.text("map old namespace to new (may be used multiple times)")
115+
116+
opt[String]("default-java-namespace")
117+
.unbounded()
118+
.valueName("<name>")
119+
.action((name, c) => c.copy(defaultNamespace = name))
120+
.text(
121+
"Use <name> as default namespace if the thrift file doesn't define its own namespace. " +
122+
"If this option is not specified either, then use \"thrift\" as default namespace"
123+
)
124+
125+
opt[Unit]("disable-strict")
126+
.action((_, c) => c.copy(strict = false))
127+
.text("issue warnings on non-severe parse errors instead of aborting")
128+
129+
opt[String]("gen-file-map")
130+
.valueName("<path>")
131+
.action((path, c) => c.copy(fileMapPath = Some(path)))
132+
.text(
133+
"generate map.txt in the destination folder to specify the mapping from input thrift files to output Scala/Java files"
134+
)
135+
136+
opt[Unit]("dry-run")
137+
.action((_, c) => c.copy(dryRun = true))
138+
.text(
139+
"parses and validates source thrift files, reporting any errors, but" +
140+
" does not emit any generated source code. can be used with " +
141+
"--gen-file-mapping to get the file mapping"
142+
)
143+
144+
opt[Unit]('s', "skip-unchanged")
145+
.action((_, c) => c.copy(skipUnchanged = true))
146+
.text("Don't re-generate if the target is newer than the input")
147+
148+
opt[String]('l', "language")
149+
.action((languageString, c) => c.copy(language = languageString))
150+
.validate { language =>
151+
if (GeneratorFactory.languages.toList contains language.toLowerCase)
152+
success
153+
else
154+
failure("language option %s not supported".format(language))
155+
}
156+
.text(
157+
"name of language to generate code in (currently supported languages: " +
158+
GeneratorFactory.languages.toList.mkString(", ") + ")"
159+
)
160+
161+
opt[Unit]("java-ser-enum-type")
162+
.action((_, c) => c.copy(javaSerEnumType = true))
163+
.text("Encode a thrift enum as o.a.t.p.TType.ENUM instead of TType.I32")
164+
165+
opt[String]("language-flag")
166+
.valueName("<flag>")
167+
.unbounded()
168+
.action { (flag, c) =>
169+
val languageFlags = c.languageFlags :+ flag
170+
c.copy(languageFlags = languageFlags)
171+
}
172+
.text(
173+
"Pass arguments to supported language generators"
174+
)
175+
176+
opt[Unit]("scala-warn-on-java-ns-fallback")
177+
.action((_, c) => c.copy(scalaWarnOnJavaNSFallback = true))
178+
.text("Print a warning when the scala generator falls back to the java namespace")
179+
180+
opt[Unit]("finagle")
181+
.action((_, c) => c.copy(flags = c.flags + WithFinagle))
182+
.text("generate finagle classes")
183+
184+
opt[Unit]("gen-adapt")
185+
.action((_, c) => c.copy(genAdapt = true))
186+
.text("Generate code for adaptive decoding for scala.")
187+
188+
opt[Unit]("java-passthrough")
189+
.action((_, c) => c.copy(flags = c.flags + WithJavaPassThrough))
190+
.text("Generate Java code with PassThrough")
191+
192+
arg[String]("<files...>")
193+
.unbounded()
194+
.action { (files, c) =>
195+
// append files to preserve the order of thrift files from command line.
196+
val thriftFiles = c.thriftFiles :+ files
197+
c.copy(thriftFiles = thriftFiles)
198+
}
199+
.text("thrift files to compile")
200+
}
201+
parser.parse(args, defaultConfig)
202+
}
203+
}

src/scala/scripts/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ scala_library(
1010
"//src/java/io/bazel/rulesscala/worker",
1111
"//src/scala/io/bazel/rules_scala/scrooge_support:compiler",
1212
"//twitter_scrooge:scrooge_generator_classpath",
13+
# Remove this dependency when ScroogeConfig and ScroogeOptionParser are added to the com.twitter.scrooge
14+
"//src/scala/io/bazel/rules_scala/scrooge_support:option_parser",
1315
],
1416
)
1517

0 commit comments

Comments
 (0)