Skip to content
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a420b85
fix: Isolate codegen classpath from ModelAssembler
ghostbuster91 Apr 23, 2025
aa437f5
Different way to delegate to bootstrap
ghostbuster91 Apr 23, 2025
b38340f
extract transformers
ghostbuster91 Apr 29, 2025
f5ce586
working version
ghostbuster91 Apr 29, 2025
309c392
Move some stuff back to the codegen module
ghostbuster91 Apr 29, 2025
178b661
Revert "Move some stuff back to the codegen module"
ghostbuster91 Apr 29, 2025
0b20c98
Switch to snapshot version of alloy
ghostbuster91 Apr 30, 2025
92e845d
Publish codegenProtocol before compiling protocol
ghostbuster91 Apr 30, 2025
f77241c
Use correct version of codegenProtocolDependency
ghostbuster91 Apr 30, 2025
d9442a4
Fix broken test
ghostbuster91 Apr 30, 2025
00d7c10
Increase maxArity for AwsComplianceSuite to 2048
ghostbuster91 Apr 30, 2025
1e20168
Remove leftovers from discoverModels
ghostbuster91 Apr 30, 2025
13918ec
Move tests back to codegen module
ghostbuster91 Apr 30, 2025
d706e09
Fix leftovers from discoverModels
ghostbuster91 Apr 30, 2025
7be32d6
Remove alloy completly from codegenProtocol
ghostbuster91 Apr 30, 2025
d23dc14
Remove unused var
ghostbuster91 Apr 30, 2025
5b19156
Revert to previous arlib version
ghostbuster91 Apr 30, 2025
22fef11
Back to custom alloy version
ghostbuster91 Apr 30, 2025
68cbdef
Pass alloy-openapi as an external dependency
ghostbuster91 May 2, 2025
20d6c22
Merge branch 'series/0.18' into classpath-isolation
ghostbuster91 May 6, 2025
fc5a060
Fix merge gone wrong
ghostbuster91 May 7, 2025
1fd810d
pass validator also to assembler
ghostbuster91 May 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 65 additions & 7 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports"
ThisBuild / dynverSeparator := "-"
ThisBuild / versionScheme := Some("early-semver")
ThisBuild / mimaBaseVersion := "0.18.0"
ThisBuild / resolvers += "Sonatype OSS Snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots"

Global / onChangedBuildSource := ReloadOnSourceChanges

Expand Down Expand Up @@ -56,6 +57,7 @@ lazy val root = project
lazy val allModules = Seq(
core,
codegen,
codegenProtocol,
docs,
millCodegenPlugin,
json,
Expand Down Expand Up @@ -416,14 +418,33 @@ lazy val `aws-http4s` = projectMatrix
.jsPlatform(latest2ScalaVersions, jsDimSettings)
.nativePlatform(allNativeScalaVersions, nativeDimSettings)

lazy val ensureCodegenDepsPublished = Def.taskDyn {
val scalaAxis = virtualAxes.value
.collectFirst { case VirtualAxis.ScalaVersionAxis(v, _) =>
v
}
.getOrElse(sys.error("No ScalaVersion axis found"))

val protocolTask = protocol.jvm(autoScalaLibrary = false) / publishLocal
val codegenTask = codegenProtocol.jvm(scalaAxis) / publishLocal

Def.task {
streams.value.log.info(
s"[codegen] Publishing protocol + codegenProtocol for Scala $scalaAxis"
)
protocolTask.value
codegenTask.value
}
}

/**
* This module contains the logic used at build time for reading smithy
* models and rendering Scala (or openapi) code.
*/
lazy val codegen = projectMatrix
.in(file("modules/codegen"))
.enablePlugins(BuildInfoPlugin)
.dependsOn(protocol)
.dependsOn(protocol, codegenProtocol)
.jvmPlatform(buildtimejvmScala2Versions, jvmDimSettings)
.settings(
buildInfoKeys := Seq[BuildInfoKey](
Expand Down Expand Up @@ -456,14 +477,29 @@ lazy val codegen = projectMatrix
scalacOptions := scalacOptions.value
.filterNot(Seq("-Ywarn-value-discard", "-Wvalue-discard").contains),
bloopEnabled := true,
Compile / compile := (Compile / compile)
.dependsOn(ensureCodegenDepsPublished)
.value,
Compile / sourceGenerators += {
sourceManaged
.map(AwsBoilerplate.generate(_))
.taskValue,
},
(Compile / compile) := (Compile / compile)
.dependsOn((protocol.jvm(autoScalaLibrary = false) / publishLocal))
.value
}
)

lazy val codegenProtocol = projectMatrix
.in(file("modules/codegen-protocol"))
.dependsOn(protocol)
.jvmPlatform(buildtimejvmScala2Versions, jvmDimSettings)
.settings(
libraryDependencies ++= Seq(
Dependencies.Smithy.model % "provided",
Dependencies.Smithy.build % "provided",
Dependencies.collectionsCompat.value
),
scalacOptions := scalacOptions.value
.filterNot(Seq("-Ywarn-value-discard", "-Wvalue-discard").contains),
bloopEnabled := true
)

/**
Expand Down Expand Up @@ -970,6 +1006,9 @@ lazy val bootstrapped = projectMatrix
.in(file("modules/bootstrapped"))
.dependsOn(cats, `aws-kernel`, complianceTests)
.disablePlugins(ScalafixPlugin)
.settings(
scalacOptions := scalacOptions.value.filterNot(_ == "-Xfatal-warnings")
)
.settings(
Compile / headerSources := Nil,
// Setting ScalaPB to generate Scala code from proto files generated by
Expand Down Expand Up @@ -1144,6 +1183,11 @@ def dumpModel(config: Configuration): Def.Initialize[Task[Seq[File]]] =
) / Compile / mainClass).value.getOrElse(
throw new Exception("No main class found")
)
val repos =
(config / resolvers).?.value.toList.flatten.collect {
case m: MavenRepository =>
m.root
}

import sjsonnew._
import BasicJsonProtocol._
Expand Down Expand Up @@ -1175,9 +1219,13 @@ def dumpModel(config: Configuration): Def.Initialize[Task[Seq[File]]] =
)
val s = (config / streams).value

val args =
val repositoriesOpt = if (repos.nonEmpty) {
List("--repositories", repos.mkString(","))
} else Nil
val transformersOpt =
if (transforms.isEmpty) List.empty
else List("--transformers", transforms.mkString(","))
val args = repositoriesOpt ++ transformersOpt
val cached =
Tracked.inputChanged[List[String], Seq[File]](
s.cacheStoreFactory.make("input")
Expand Down Expand Up @@ -1230,6 +1278,11 @@ def genSmithyImpl(config: Configuration) = Def.task {
.getAbsolutePath()
val allowedNS = (config / allowedNamespaces).?.value.filterNot(_.isEmpty)
val skip = (config / smithy4sSkip).?.value.getOrElse(Seq.empty)
val repos =
(config / resolvers).?.value.toList.flatten.collect {
case m: MavenRepository =>
m.root
}
val smithy4sDeps =
(config / smithy4sDependencies).?.value.getOrElse(Seq.empty).map {
moduleId =>
Expand Down Expand Up @@ -1311,12 +1364,17 @@ def genSmithyImpl(config: Configuration) = Def.task {
if (smithy4sDeps.nonEmpty)
List("--dependencies", smithy4sDeps.mkString(","))
else Nil
val repositoriesOpt = if (repos.nonEmpty) {
List("--repositories", repos.mkString(","))
} else Nil

val args = outputOpt ++
resourceOutputOpt ++
allowedNsOpt ++
inputs ++
skipOpt ++
dependenciesOpt
dependenciesOpt ++
repositoriesOpt

val cp = codegenCp
.map(_.getAbsolutePath())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ object AwsComplianceSuite extends ProtocolComplianceSuite {

val jsonDecoders =
smithy4s.json.Json.payloadCodecs.withJsoniterCodecCompiler {
smithy4s.json.Json.jsoniter.withMapOrderPreservation(true)
smithy4s.json.Json.jsoniter
.withMapOrderPreservation(true)
.withMaxArity(2048)
}.decoders

override def dynamicSchemaIndexLoader: IO[DynamicSchemaIndex] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,6 @@ object CodegenCommand {
.orNone
.map(_.getOrElse(Set.empty))

val discoverModelsOpt =
Opts
.flag(
long = "discover-models",
help =
"Indicates whether the model assembler should try to discover models in the classpath"
)
.orFalse

val allowedNSOpt: Opts[Option[Set[String]]] =
Opts
.option[List[String]](
Expand Down Expand Up @@ -106,7 +97,6 @@ object CodegenCommand {
outputOpt,
resourceOutputOpt,
skipOpts,
discoverModelsOpt,
allowedNSOpt,
excludedNSOpt,
repositoriesOpt,
Expand All @@ -118,7 +108,7 @@ object CodegenCommand {
)
.mapN {
// format: off
case (output, resourseOutput, skip, discoverModels, allowedNS, excludedNS, repositories, dependencies, transformers, localJars, specsArgs, smithyBuild) =>
case (output, resourseOutput, skip, allowedNS, excludedNS, repositories, dependencies, transformers, localJars, specsArgs, smithyBuild) =>
// format: on
val dependenciesWithDefaults = {
import Defaults._
Expand All @@ -129,7 +119,6 @@ object CodegenCommand {
output.getOrElse(os.pwd),
resourseOutput.getOrElse(os.pwd),
skip,
discoverModels,
allowedNS,
excludedNS,
repositories.getOrElse(List.empty),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ object CommandParsingSpec extends FunSuite {
output = os.pwd,
resourceOutput = os.pwd,
skip = Set.empty,
discoverModels = false,
allowedNS = None,
excludedNS = None,
repositories = Nil,
Expand Down Expand Up @@ -88,7 +87,6 @@ object CommandParsingSpec extends FunSuite {
output = os.pwd / "target",
resourceOutput = os.pwd / "target" / "openapi",
skip = Set(FileType.Openapi, FileType.Scala),
discoverModels = false,
allowedNS = Some(Set("name1", "name2")),
excludedNS = None,
repositories = List("repo1", "repo2"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
ThisBuild / resolvers += "Sonatype OSS Snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots"

lazy val root = (project in file("."))
.enablePlugins(Smithy4sCodegenPlugin)
.settings(
scalaVersion := "2.13.16",
libraryDependencies ++= Seq(
"com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value
"com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value,
"com.disneystreaming.alloy" %% "alloy-openapi" % "0.3.17-1-e0ac29-SNAPSHOT"
),
Compile / smithyBuild := Some(baseDirectory.value / "smithy-build.json"),
TaskKey[Unit]("checkOpenApi") := {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private[smithy4s] object JsonConverters {
)

// format: off
type GenTarget = List[PathRef] :*: os.Path :*: os.Path :*: Set[FileType] :*: Boolean:*: Option[Set[String]] :*: Option[Set[String]] :*: List[String] :*: List[String] :*: List[String] :*: List[PathRef] :*: Option[PathRef] :*: LNil
type GenTarget = List[PathRef] :*: os.Path :*: os.Path :*: Set[FileType] :*: Option[Set[String]] :*: Option[Set[String]] :*: List[String] :*: List[String] :*: List[String] :*: List[PathRef] :*: Option[PathRef] :*: LNil
// format: on

// `output` and `resourceOutput` are intentionally serialized as paths
Expand All @@ -79,7 +79,6 @@ private[smithy4s] object JsonConverters {
("output", ca.output) :*:
("resourceOutput", ca.resourceOutput) :*:
("skip", ca.skip) :*:
("discoverModels", ca.discoverModels) :*:
("allowedNS", ca.allowedNS) :*:
("excludedNS", ca.excludedNS) :*:
("repositories", ca.repositories) :*:
Expand All @@ -94,7 +93,6 @@ private[smithy4s] object JsonConverters {
(_, output) :*:
(_, resourceOutput) :*:
(_, skip) :*:
(_, discoverModels) :*:
(_, allowedNS) :*:
(_, excludedNS) :*:
(_, repositories) :*:
Expand All @@ -107,7 +105,6 @@ private[smithy4s] object JsonConverters {
output,
resourceOutput,
skip,
discoverModels,
allowedNS,
excludedNS,
repositories,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,6 @@ object Smithy4sCodegenPlugin extends AutoPlugin {
output = os.Path(outputPath),
resourceOutput = os.Path(resourceOutputPath),
skip = skipSet,
discoverModels = false,
allowedNS = allowedNamespaces,
excludedNS = excludedNamespaces,
repositories = res,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2021-2025 Disney Streaming
*
* Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://disneystreaming.github.io/TOST-1.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package smithy4s.codegen.transformers

import software.amazon.smithy.build.ProjectionTransformer
import software.amazon.smithy.build.TransformContext
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes._
import software.amazon.smithy.model.traits._

import java.util.function.Function
import java.util.ServiceLoader
import scala.jdk.CollectionConverters._
import software.amazon.smithy.model.node.Node

@annotation.nowarn("msg=class EnumTrait in package traits is deprecated")
private[codegen] final class OpenEnumTransformer extends ProjectionTransformer {
override def getName: String = OpenEnumTransformer.name

private val awsNamespacePrefix = "com.amazonaws"

def transform(ctx: TransformContext): Model = {

val loader =
ServiceLoader.load(classOf[TraitService], getClass().getClassLoader())

@annotation.nowarn("msg=method mapValues in trait MapOps is deprecated")
val services = loader
.iterator()
.asScala
.toList
.groupBy(_.getShapeId())
.mapValues(_.head)
.toMap

val openEnumId = ShapeId.from("alloy#openEnum")

services.get(openEnumId) match {
case Some(openEnumTraitService) =>
val shapeMapper: Function[Shape, Shape] = { (shp: Shape) =>
val openEnumTrait =
openEnumTraitService.createTrait(openEnumId, Node.objectNode())
shp match {
case shp
if !shp.getId.getNamespace.startsWith(awsNamespacePrefix) =>
shp
case e: EnumShape =>
e.toBuilder.addTrait(openEnumTrait).build()
case e: IntEnumShape =>
e.toBuilder.addTrait(openEnumTrait).build()
case t: Shape if t.hasTrait(classOf[EnumTrait]) =>
(Shape
.shapeToBuilder(t): AbstractShapeBuilder[_, _])
.addTrait(openEnumTrait)
.build()
case other => other
}
}
ctx.getTransformer().mapShapes(ctx.getModel(), shapeMapper)

case None => ctx.getModel()
}

}

}

object OpenEnumTransformer {
val name: String = "OpenEnumTransformer"
}
1 change: 0 additions & 1 deletion modules/codegen/src/smithy4s/codegen/CodegenArgs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ final case class CodegenArgs(
output: os.Path,
resourceOutput: os.Path,
skip: Set[FileType],
discoverModels: Boolean,
allowedNS: Option[Set[String]],
excludedNS: Option[Set[String]],
repositories: List[String],
Expand Down
Loading