Skip to content

Commit 70060bd

Browse files
committed
Merge branch 'smithy4s-integration' into smithy-simplify
2 parents 97462b6 + e45acf7 commit 70060bd

File tree

5 files changed

+272
-19
lines changed

5 files changed

+272
-19
lines changed

build.sbt

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ inThisBuild(
1717

1818
val scala213 = "2.13.16"
1919
val scala3 = "3.3.5"
20+
val jdkVersion = 11
2021
val allScalaVersions = List(scala213, scala3)
2122
val jvmScalaVersions = allScalaVersions
2223
val jsScalaVersions = allScalaVersions
@@ -34,16 +35,27 @@ val commonSettings = Seq(
3435
mimaPreviousArtifacts := Set(
3536
organization.value %%% name.value % "0.0.7"
3637
),
37-
scalacOptions += "-java-output-version:11"
38+
scalacOptions ++= {
39+
CrossVersion.partialVersion(scalaVersion.value) match {
40+
case Some((2, _)) => Seq(s"-target:jvm-$jdkVersion")
41+
case _ => Seq(s"-java-output-version:$jdkVersion")
42+
}
43+
}
44+
)
45+
46+
val commonJvmSettings = Seq(
47+
javacOptions ++= Seq("--release", jdkVersion.toString)
3848
)
3949

4050
val core = projectMatrix
4151
.in(file("modules") / "core")
4252
.jvmPlatform(
4353
jvmScalaVersions,
44-
Test / unmanagedSourceDirectories ++= Seq(
45-
(projectMatrixBaseDirectory.value / "src" / "test" / "scalajvm-native").getAbsoluteFile
46-
)
54+
Seq(
55+
Test / unmanagedSourceDirectories ++= Seq(
56+
(projectMatrixBaseDirectory.value / "src" / "test" / "scalajvm-native").getAbsoluteFile
57+
)
58+
) ++ commonJvmSettings
4759
)
4860
.jsPlatform(jsScalaVersions)
4961
.nativePlatform(
@@ -63,7 +75,7 @@ val core = projectMatrix
6375

6476
val fs2 = projectMatrix
6577
.in(file("modules") / "fs2")
66-
.jvmPlatform(jvmScalaVersions)
78+
.jvmPlatform(jvmScalaVersions, commonJvmSettings)
6779
.jsPlatform(jsScalaVersions)
6880
.nativePlatform(nativeScalaVersions)
6981
.disablePlugins(AssemblyPlugin)
@@ -78,36 +90,52 @@ val fs2 = projectMatrix
7890

7991
val smithy = projectMatrix
8092
.in(file("modules") / "smithy")
81-
.jvmPlatform(jvmScalaVersions)
82-
.jsPlatform(jsScalaVersions)
83-
.nativePlatform(nativeScalaVersions)
93+
.jvmPlatform(false)
8494
.disablePlugins(AssemblyPlugin, MimaPlugin)
95+
.enablePlugins(SmithyTraitCodegenPlugin)
8596
.settings(
86-
name := "jsonrpclib-smithy"
87-
)
97+
name := "jsonrpclib-smithy",
98+
commonJvmSettings
99+
)
100+
101+
lazy val buildTimeProtocolDependency =
102+
/** By default, smithy4sInternalDependenciesAsJars doesn't contain the jars in the "smithy4s" configuration. We have
103+
* to add them manually - this is the equivalent of a "% Smithy4s"-scoped dependency.
104+
*
105+
* Ideally, this would be
106+
* {{{
107+
* (Compile / smithy4sInternalDependenciesAsJars) ++=
108+
* Smithy4s / smithy4sInternalDependenciesAsJars).value.map(_.data)
109+
* }}}
110+
*
111+
* but that doesn't work because the Smithy4s configuration doesn't extend from Compile so it doesn't have the
112+
* `internalDependencyAsJars` setting.
113+
*/
114+
Compile / smithy4sInternalDependenciesAsJars ++=
115+
(smithy.jvm(autoScalaLibrary = false) / Compile / fullClasspathAsJars).value.map(_.data)
88116

89117
val smithy4s = projectMatrix
90118
.in(file("modules") / "smithy4s")
91-
.jvmPlatform(jvmScalaVersions)
119+
.jvmPlatform(jvmScalaVersions, commonJvmSettings)
92120
.jsPlatform(jsScalaVersions)
93121
.nativePlatform(Seq(scala3))
94122
.disablePlugins(AssemblyPlugin)
95123
.enablePlugins(Smithy4sCodegenPlugin)
96124
.dependsOn(core)
97-
.dependsOn(smithy)
98125
.settings(
99126
name := "jsonrpclib-smithy4s",
100127
commonSettings,
101128
mimaPreviousArtifacts := Set.empty,
102129
libraryDependencies ++= Seq(
103130
"co.fs2" %%% "fs2-core" % fs2Version,
104131
"com.disneystreaming.smithy4s" %%% "smithy4s-json" % smithy4sVersion.value
105-
)
132+
),
133+
buildTimeProtocolDependency
106134
)
107135

108136
val exampleServer = projectMatrix
109137
.in(file("modules") / "examples/server")
110-
.jvmPlatform(List(scala213))
138+
.jvmPlatform(List(scala213), commonJvmSettings)
111139
.dependsOn(fs2)
112140
.settings(
113141
commonSettings,
@@ -125,7 +153,7 @@ val exampleClient = projectMatrix
125153
Seq(
126154
fork := true,
127155
envVars += "SERVER_JAR" -> (exampleServer.jvm(scala213) / assembly).value.toString
128-
)
156+
) ++ commonJvmSettings
129157
)
130158
.disablePlugins(AssemblyPlugin)
131159
.dependsOn(fs2)
@@ -140,18 +168,19 @@ val exampleClient = projectMatrix
140168

141169
val exampleSmithyShared = projectMatrix
142170
.in(file("modules") / "examples/smithyShared")
143-
.jvmPlatform(List(scala213))
171+
.jvmPlatform(List(scala213), commonJvmSettings)
144172
.dependsOn(smithy4s, fs2)
145173
.enablePlugins(Smithy4sCodegenPlugin)
146174
.settings(
147175
commonSettings,
148-
publish / skip := true
176+
publish / skip := true,
177+
buildTimeProtocolDependency
149178
)
150179
.disablePlugins(MimaPlugin)
151180

152181
val exampleSmithyServer = projectMatrix
153182
.in(file("modules") / "examples/smithyServer")
154-
.jvmPlatform(List(scala213))
183+
.jvmPlatform(List(scala213), commonJvmSettings)
155184
.dependsOn(exampleSmithyShared)
156185
.settings(
157186
commonSettings,
@@ -175,7 +204,7 @@ val exampleSmithyClient = projectMatrix
175204
Seq(
176205
fork := true,
177206
envVars += "SERVER_JAR" -> (exampleSmithyServer.jvm(scala213) / assembly).value.toString
178-
)
207+
) ++ commonJvmSettings
179208
)
180209
.dependsOn(exampleSmithyShared)
181210
.settings(

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)