Skip to content

Commit 940423d

Browse files
authored
Don't crash on no input, allow customizing source directory, support external trait providers (#2)
* First couple improvements * Support external trait providers * Add trailing newline * break mima * Add hack
2 parents a456b89 + 7c1034d commit 940423d

File tree

9 files changed

+132
-13
lines changed

9 files changed

+132
-13
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ThisBuild / tlBaseVersion := "0.1"
1+
ThisBuild / tlBaseVersion := "0.2"
22
ThisBuild / organization := "org.polyvariant"
33
ThisBuild / organizationName := "Polyvariant"
44
ThisBuild / startYear := Some(2025)

sbtPlugin/src/main/scala/org/polyvariant/smithytraitcodegen/SmithyTraitCodegen.scala

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import software.amazon.smithy.model.node.ObjectNode
2525
import software.amazon.smithy.traitcodegen.TraitCodegenPlugin
2626

2727
import java.io.File
28+
import software.amazon.smithy.model.transform.ModelTransformer
29+
import java.util.stream.Collectors
30+
import scala.collection.JavaConverters.*
2831

2932
object SmithyTraitCodegen {
3033

@@ -38,12 +41,13 @@ object SmithyTraitCodegen {
3841
targetDir: os.Path,
3942
smithySourcesDir: PathRef,
4043
dependencies: List[PathRef],
44+
externalProviders: List[String],
4145
)
4246

4347
object Args {
4448

4549
// format: off
46-
private type ArgsDeconstructed = String :*: String :*: os.Path :*: PathRef :*: List[PathRef] :*: LNil
50+
private type ArgsDeconstructed = String :*: String :*: os.Path :*: PathRef :*: List[PathRef] :*: List[String] :*: LNil
4751
// format: on
4852

4953
private implicit val pathFormat: JsonFormat[os.Path] = BasicJsonProtocol
@@ -56,6 +60,7 @@ object SmithyTraitCodegen {
5660
("targetDir", args.targetDir) :*:
5761
("smithySourcesDir", args.smithySourcesDir) :*:
5862
("dependencies", args.dependencies) :*:
63+
("externalProviders", args.externalProviders) :*:
5964
LNil
6065
},
6166
{
@@ -64,13 +69,15 @@ object SmithyTraitCodegen {
6469
(_, targetDir) :*:
6570
(_, smithySourcesDir) :*:
6671
(_, dependencies) :*:
72+
(_, externalProviders) :*:
6773
LNil =>
6874
Args(
6975
javaPackage = javaPackage,
7076
smithyNamespace = smithyNamespace,
7177
targetDir = targetDir,
7278
smithySourcesDir = smithySourcesDir,
7379
dependencies = dependencies,
80+
externalProviders = externalProviders,
7481
)
7582
},
7683
)
@@ -104,6 +111,23 @@ object SmithyTraitCodegen {
104111

105112
}
106113

114+
// Hack / workaround for https://github.com/smithy-lang/smithy/pull/2671
115+
private def namespaceHackRequired(ns: String) =
116+
ns.startsWith("smithy") && ns != "smithy" && !ns.startsWith("smithy.")
117+
118+
private def renameNamespaceForHack(ns: String) =
119+
if (namespaceHackRequired(ns))
120+
"hack" + ns
121+
else
122+
ns
123+
124+
private def replaceNamespaceRefsInFile(fileText: String, ns: String) =
125+
if (namespaceHackRequired(ns)) {
126+
fileText.replaceAll(s"hack$ns", ns)
127+
} else {
128+
fileText
129+
}
130+
107131
def generate(args: Args): Output = {
108132
val outputDir = args.targetDir / "smithy-trait-generator-output"
109133
val genDir = outputDir / "java"
@@ -113,13 +137,38 @@ object SmithyTraitCodegen {
113137

114138
val manifest = FileManifest.create(genDir.toNIO)
115139

116-
val model = args
117-
.dependencies
118-
.foldLeft(Model.assembler().addImport(args.smithySourcesDir.path.toNIO)) { case (acc, dep) =>
119-
acc.addImport(dep.path.toNIO)
140+
val model =
141+
args
142+
.dependencies
143+
.foldLeft(Model.assembler().addImport(args.smithySourcesDir.path.toNIO)) {
144+
case (acc, dep) => acc.addImport(dep.path.toNIO)
145+
}
146+
.assemble()
147+
.unwrap() match {
148+
case model =>
149+
if (namespaceHackRequired(args.smithyNamespace)) {
150+
println("Applying namespace workaround - `hack` prefix will be used")
151+
152+
val renames =
153+
model
154+
.shapes()
155+
.collect(Collectors.toList())
156+
.asScala
157+
.filter(_.getId().getNamespace() == args.smithyNamespace)
158+
.map { shp =>
159+
shp.getId() ->
160+
shp.getId().withNamespace(renameNamespaceForHack(shp.getId().getNamespace()))
161+
}
162+
.toMap
163+
.asJava
164+
165+
ModelTransformer
166+
.create()
167+
.renameShapes(model, renames)
168+
} else
169+
model
120170
}
121-
.assemble()
122-
.unwrap()
171+
123172
val context = PluginContext
124173
.builder()
125174
.model(model)
@@ -128,15 +177,35 @@ object SmithyTraitCodegen {
128177
ObjectNode
129178
.builder()
130179
.withMember("package", args.javaPackage)
131-
.withMember("namespace", args.smithyNamespace)
180+
.withMember("namespace", renameNamespaceForHack(args.smithyNamespace))
132181
.withMember("header", ArrayNode.builder.build())
133182
.withMember("excludeTags", ArrayNode.builder.withValue("nocodegen").build())
134183
.build()
135184
)
136185
.build()
137186
val plugin = new TraitCodegenPlugin()
138187
plugin.execute(context)
139-
os.move(genDir / "META-INF", metaDir / "META-INF")
188+
189+
// If there were no shapes to generate, this won't exist
190+
if (os.exists(genDir / "META-INF"))
191+
os.move(genDir / "META-INF", metaDir / "META-INF")
192+
193+
os.walk(genDir)
194+
.filter(os.isFile)
195+
.filter(_.ext == "java")
196+
.foreach { f =>
197+
os.write.over(f, replaceNamespaceRefsInFile(os.read(f), args.smithyNamespace))
198+
}
199+
200+
os
201+
.walk(metaDir, includeTarget = true)
202+
.filter(os.isFile)
203+
.foreach { p =>
204+
if (p.toIO.name == "software.amazon.smithy.model.traits.TraitService") {
205+
args.externalProviders.foreach(provider => os.write.append(p, provider + "\n"))
206+
}
207+
}
208+
140209
Output(metaDir = metaDir.toIO, javaDir = genDir.toIO)
141210
}
142211

sbtPlugin/src/main/scala/org/polyvariant/smithytraitcodegen/SmithyTraitCodegenPlugin.scala

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,26 @@ object SmithyTraitCodegenPlugin extends AutoPlugin {
3939
"Dependencies to be added into codegen model"
4040
)
4141

42+
val smithyTraitCodegenSourceDirectory = settingKey[File](
43+
"The directory where the smithy sources are located"
44+
)
45+
46+
val smithyTraitCodegenTargetDirectory = settingKey[File](
47+
"The directory where the generated Java sources and resources will be placed"
48+
)
49+
50+
val smithyTraitCodegenExternalProviders = settingKey[List[String]](
51+
"External trait provideres"
52+
)
53+
4254
}
4355

4456
import autoImport.*
4557

4658
override def projectSettings: Seq[Setting[?]] = Seq(
59+
smithyTraitCodegenSourceDirectory := (Compile / resourceDirectory).value / "META-INF" / "smithy",
60+
smithyTraitCodegenTargetDirectory := (Compile / target).value,
61+
smithyTraitCodegenExternalProviders := Nil,
4762
Keys.generateSmithyTraits := Def.task {
4863
import sbt.util.CacheImplicits.*
4964
val s = (Compile / streams).value
@@ -64,9 +79,10 @@ object SmithyTraitCodegenPlugin extends AutoPlugin {
6479
val args = SmithyTraitCodegen.Args(
6580
javaPackage = smithyTraitCodegenJavaPackage.value,
6681
smithyNamespace = smithyTraitCodegenNamespace.value,
67-
targetDir = os.Path((Compile / target).value),
68-
smithySourcesDir = PathRef((Compile / resourceDirectory).value / "META-INF" / "smithy"),
82+
targetDir = os.Path(smithyTraitCodegenTargetDirectory.value),
83+
smithySourcesDir = PathRef(smithyTraitCodegenSourceDirectory.value),
6984
dependencies = jars.map(PathRef(_)).toList,
85+
externalProviders = smithyTraitCodegenExternalProviders.value,
7086
)
7187
val cachedCodegen =
7288
Tracked.inputChanged[SmithyTraitCodegen.Args, SmithyTraitCodegen.Output](
@@ -82,7 +98,7 @@ object SmithyTraitCodegenPlugin extends AutoPlugin {
8298
.fold {
8399
SmithyTraitCodegen.generate(codegenArgs)
84100
} { last =>
85-
logger.info(s"Using cached result of smithy-trait-codegen")
101+
logger.info("Using cached result of smithy-trait-codegen")
86102
last
87103
}
88104
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
val traits = project
2+
.enablePlugins(SmithyTraitCodegenPlugin)
3+
.settings(
4+
smithyTraitCodegenDependencies := List(
5+
"io.github.disneystreaming.alloy" % "alloy-core" % "0.3.23"
6+
),
7+
smithyTraitCodegenJavaPackage := "smithy4bazinga",
8+
smithyTraitCodegenNamespace := "smithy4bazinga",
9+
)
10+
11+
val root = project
12+
.in(file("."))
13+
.dependsOn(traits)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
sys.props.get("plugin.version") match {
2+
case Some(x) => addSbtPlugin("org.polyvariant" % "smithy-trait-codegen-sbt" % x)
3+
case _ => sys.error("""|The system property 'plugin.version' is not defined.
4+
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import software.amazon.smithy.model.Model
2+
3+
object DemoApp extends App {
4+
assert(
5+
smithy4bazinga.BazingaTrait.ID.toString() == "smithy4bazinga#bazinga",
6+
s"Expected smithy4bazinga.BazingaTrait.ID to be 'smithy4bazinga#bazinga', but got ${smithy4bazinga.BazingaTrait.ID.toString()}",
7+
)
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
> run
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
smithy4bazinga.smithy
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
$version: "2"
2+
3+
namespace smithy4bazinga
4+
5+
@trait
6+
string bazinga

0 commit comments

Comments
 (0)