Skip to content

Commit 6269973

Browse files
committed
Generate java parts for smithy traits
1 parent c3ce708 commit 6269973

File tree

6 files changed

+237
-1
lines changed

6 files changed

+237
-1
lines changed

build.sbt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ val smithy = projectMatrix
8080
.in(file("modules") / "smithy")
8181
.jvmPlatform(false)
8282
.disablePlugins(AssemblyPlugin, MimaPlugin)
83+
.enablePlugins(SmithyTraitCodegenPlugin)
8384
.settings(
84-
name := "jsonrpclib-smithy"
85+
name := "jsonrpclib-smithy",
8586
)
8687

8788
lazy val buildTimeProtocolDependency =

project/MetaDependencies.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sbt.*
2+
3+
object MetaDependencies {
4+
5+
val smithy = new {
6+
val version = "1.56.0"
7+
8+
val model = "software.amazon.smithy" % "smithy-model" % version
9+
val traitCodegen = "software.amazon.smithy" % "smithy-trait-codegen" % version
10+
}
11+
}

project/PathRef.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import sbt.io.Hash
2+
import sbt.util.FileInfo
3+
import sbt.util.HashFileInfo
4+
import sjsonnew.*
5+
6+
import java.io.File
7+
8+
case class PathRef(path: os.Path)
9+
10+
object PathRef {
11+
12+
def apply(f: File): PathRef = PathRef(os.Path(f))
13+
14+
implicit val pathFormat: JsonFormat[PathRef] =
15+
BasicJsonProtocol.projectFormat[PathRef, HashFileInfo](
16+
p =>
17+
if (os.isFile(p.path)) FileInfo.hash(p.path.toIO)
18+
else
19+
// If the path is a directory, we get the hashes of all files
20+
// then hash the concatenation of the hash's bytes.
21+
FileInfo.hash(
22+
p.path.toIO,
23+
Hash(
24+
os.walk(p.path)
25+
.map(_.toIO)
26+
.map(Hash(_))
27+
.foldLeft(Array.emptyByteArray)(_ ++ _)
28+
)
29+
),
30+
hash => PathRef(hash.file)
31+
)
32+
}

project/SmithyTraitCodegen.scala

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import sbt.*
2+
import sbt.io.IO
3+
import software.amazon.smithy.build.FileManifest
4+
import software.amazon.smithy.build.PluginContext
5+
import software.amazon.smithy.model.node.ArrayNode
6+
import software.amazon.smithy.model.node.ObjectNode
7+
import software.amazon.smithy.model.shapes.ShapeId
8+
import software.amazon.smithy.model.Model
9+
import software.amazon.smithy.traitcodegen.TraitCodegenPlugin
10+
11+
import java.io.File
12+
import java.nio.file.Paths
13+
import java.util.UUID
14+
15+
object SmithyTraitCodegen {
16+
17+
import sjsonnew.*
18+
19+
import BasicJsonProtocol.*
20+
21+
case class Args(targetDir: os.Path, smithySourcesDir: PathRef, dependencies: List[PathRef])
22+
object Args {
23+
24+
// format: off
25+
private type ArgsDeconstructed = os.Path :*: PathRef :*: List[PathRef] :*: LNil
26+
// format: on
27+
28+
private implicit val pathFormat: JsonFormat[os.Path] =
29+
BasicJsonProtocol.projectFormat[os.Path, File](p => p.toIO, file => os.Path(file))
30+
31+
implicit val argsIso =
32+
LList.iso[Args, ArgsDeconstructed](
33+
{ args: Args =>
34+
("targetDir", args.targetDir) :*:
35+
("smithySourcesDir", args.smithySourcesDir) :*:
36+
("dependencies", args.dependencies) :*:
37+
LNil
38+
},
39+
{
40+
case (_, targetDir) :*:
41+
(_, smithySourcesDir) :*:
42+
(_, dependencies) :*:
43+
LNil =>
44+
Args(
45+
targetDir = targetDir,
46+
smithySourcesDir = smithySourcesDir,
47+
dependencies = dependencies
48+
)
49+
}
50+
)
51+
52+
}
53+
54+
case class Output(metaDir: File, javaDir: File)
55+
56+
object Output {
57+
58+
// format: off
59+
private type OutputDeconstructed = File :*: File :*: LNil
60+
// format: on
61+
62+
implicit val outputIso =
63+
LList.iso[Output, OutputDeconstructed](
64+
{ output: Output =>
65+
("metaDir", output.metaDir) :*:
66+
("javaDir", output.javaDir) :*:
67+
LNil
68+
},
69+
{
70+
case (_, metaDir) :*:
71+
(_, javaDir) :*:
72+
LNil =>
73+
Output(
74+
metaDir = metaDir,
75+
javaDir = javaDir
76+
)
77+
}
78+
)
79+
}
80+
81+
def generate(args: Args): Output = {
82+
val outputDir = args.targetDir / "smithy-trait-generator-output"
83+
val genDir = outputDir / "java"
84+
val metaDir = outputDir / "meta"
85+
os.remove.all(outputDir)
86+
List(outputDir, genDir, metaDir).foreach(os.makeDir.all(_))
87+
88+
val manifest = FileManifest.create(genDir.toNIO)
89+
90+
val model = args.dependencies
91+
.foldLeft(Model.assembler().addImport(args.smithySourcesDir.path.toNIO)) { case (acc, dep) =>
92+
acc.addImport(dep.path.toNIO)
93+
}
94+
.assemble()
95+
.unwrap()
96+
val context = PluginContext
97+
.builder()
98+
.model(model)
99+
.fileManifest(manifest)
100+
.settings(
101+
ObjectNode
102+
.builder()
103+
.withMember("package", "jsonrpclib")
104+
.withMember("namespace", "jsonrpclib")
105+
.withMember("header", ArrayNode.builder.build())
106+
.withMember("excludeTags", ArrayNode.builder.withValue("nocodegen").build())
107+
.build()
108+
)
109+
.build()
110+
val plugin = new TraitCodegenPlugin()
111+
plugin.execute(context)
112+
os.move(genDir / "META-INF", metaDir / "META-INF")
113+
Output(metaDir = metaDir.toIO, javaDir = genDir.toIO)
114+
}
115+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import sbt.*
2+
import sbt.plugins.JvmPlugin
3+
import software.amazon.smithy.build.FileManifest
4+
import software.amazon.smithy.build.PluginContext
5+
import software.amazon.smithy.model.node.ArrayNode
6+
import software.amazon.smithy.model.node.ObjectNode
7+
import software.amazon.smithy.model.Model
8+
import software.amazon.smithy.traitcodegen.TraitCodegenPlugin
9+
10+
import Keys.*
11+
12+
object SmithyTraitCodegenPlugin extends AutoPlugin {
13+
override def trigger: PluginTrigger = noTrigger
14+
override def requires: Plugins = JvmPlugin
15+
16+
override def projectSettings: Seq[Setting[?]] =
17+
Seq(
18+
Keys.generateSmithyTraits := Def.task {
19+
import sbt.util.CacheImplicits.*
20+
val s = (Compile / streams).value
21+
val logger = sLog.value
22+
val args = SmithyTraitCodegen.Args(
23+
targetDir = os.Path((Compile / target).value),
24+
smithySourcesDir = PathRef((Compile / resourceDirectory).value / "META-INF" / "smithy"),
25+
dependencies = List.empty
26+
)
27+
val cachedCodegen =
28+
Tracked.inputChanged[SmithyTraitCodegen.Args, SmithyTraitCodegen.Output](
29+
s.cacheStoreFactory.make("smithy-trait-codegen-args")
30+
) {
31+
Function.untupled(
32+
Tracked
33+
.lastOutput[(Boolean, SmithyTraitCodegen.Args), SmithyTraitCodegen.Output](
34+
s.cacheStoreFactory.make("smithy-trait-codegen-output")
35+
) { case ((inputChanged, codegenArgs), cached) =>
36+
cached
37+
.filter(_ => !inputChanged)
38+
.fold {
39+
SmithyTraitCodegen.generate(codegenArgs)
40+
} { last =>
41+
logger.info(s"Using cached result of smithy-trait-codegen")
42+
last
43+
}
44+
}
45+
)
46+
}
47+
cachedCodegen(args)
48+
}.value,
49+
Compile / sourceGenerators += Def.task {
50+
val codegenOutput = (Compile / Keys.generateSmithyTraits).value
51+
cleanCopy(source = codegenOutput.javaDir, target = (Compile / sourceManaged).value / "java")
52+
},
53+
Compile / resourceGenerators += Def.task {
54+
val codegenOutput = (Compile / Keys.generateSmithyTraits).value
55+
cleanCopy(source = codegenOutput.metaDir, target = (Compile / resourceManaged).value)
56+
}.taskValue,
57+
libraryDependencies += "software.amazon.smithy" % "smithy-model" % "1.56.0"
58+
)
59+
60+
private def cleanCopy(source: File, target: File) = {
61+
val sourcePath = os.Path(source)
62+
val targetPath = os.Path(target)
63+
os.remove.all(targetPath)
64+
os.copy(from = sourcePath, to = targetPath, createFolders = true)
65+
os.walk(targetPath).map(_.toIO).filter(_.isFile())
66+
}
67+
68+
object Keys {
69+
val generateSmithyTraits =
70+
taskKey[SmithyTraitCodegen.Output]("Run AWS smithy-trait-codegen on the protocol specs")
71+
}
72+
73+
}

project/build.sbt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
libraryDependencies ++= Seq(
2+
"software.amazon.smithy" % "smithy-trait-codegen",
3+
"software.amazon.smithy" % "smithy-model"
4+
).map(_ % "1.56.0")

0 commit comments

Comments
 (0)