diff --git a/build.sbt b/build.sbt index bbe4e0e39..6517ba273 100644 --- a/build.sbt +++ b/build.sbt @@ -114,6 +114,7 @@ lazy val core = projectMatrix "smithy.waiters", "smithy4s.api" ), + genDiscoverModels := true, Compile / sourceGenerators := Seq(genSmithyScala(Compile).taskValue), Compile / sourceGenerators += sourceDirectory .map(Boilerplate.gen(_, Boilerplate.BoilerplateModule.Core)) @@ -140,18 +141,15 @@ lazy val core = projectMatrix (ThisBuild / baseDirectory).value / "sampleSpecs" / "adtMember.smithy" ), (Test / sourceGenerators) := Seq(genSmithyScala(Test).taskValue), - testFrameworks += new TestFramework("weaver.framework.CatsEffect") - // TODO: bring back - // Compile / packageSrc / mappings ++= { - // val base = (Compile / sourceManaged).value - // val files = (Compile / managedSources).value - // files.map(f => - // ( - // f, - // f.relativeTo(base).get.getPath - // ) - // ) - // } + testFrameworks += new TestFramework("weaver.framework.CatsEffect"), + Compile / packageSrc / mappings ++= { + val base = (Compile / sourceManaged).value + val files = (Compile / managedSources).value + files + .map(f => (f, f.relativeTo(base))) + // this excludes modules/core/src/generated/PartiallyAppliedStruct.scala + .collect { case (f, Some(relF)) => f -> relF.getPath() } + } ) .jvmPlatform(allJvmScalaVersions, jvmDimSettings) .jsPlatform(allJsScalaVersions, jsDimSettings) @@ -192,6 +190,7 @@ lazy val `aws-kernel` = projectMatrix Dependencies.Weaver.scalacheck.value % Test ), testFrameworks += new TestFramework("weaver.framework.CatsEffect"), + genDiscoverModels := true, Compile / allowedNamespaces := Seq( "aws.api", "aws.auth", @@ -247,6 +246,7 @@ lazy val `aws-http4s` = projectMatrix .dependsOn(aws) .settings( isCE3 := true, + Test / genDiscoverModels := true, libraryDependencies ++= { Seq( Dependencies.Http4s.client.value, @@ -272,16 +272,7 @@ lazy val codegen = projectMatrix .dependsOn(openapi) .jvmPlatform(buildtimejvmScala2Versions, jvmDimSettings) .settings( - buildInfoKeys := Seq[BuildInfoKey]( - organization, - version, - scalaBinaryVersion, - "protocolArtifact" -> (protocol - .jvm(autoScalaLibrary = false) / moduleName).value, - "smithyOrg" -> Dependencies.Smithy.model.organization, - "smithyVersion" -> Dependencies.Smithy.model.revision, - "smithyArtifact" -> Dependencies.Smithy.model.name - ), + buildInfoKeys := Seq[BuildInfoKey](version, scalaBinaryVersion), buildInfoPackage := "smithy4s.codegen", isCE3 := true, libraryDependencies ++= Seq( @@ -716,6 +707,7 @@ lazy val genSmithyOpenapiOutput = SettingKey[File]("genSmithyOpenapiOutput") lazy val allowedNamespaces = SettingKey[Seq[String]]("allowedNamespaces") lazy val genSmithyDependencies = SettingKey[Seq[String]]("genSmithyDependencies") +lazy val genDiscoverModels = SettingKey[Boolean]("genDiscoverModels") (ThisBuild / smithySpecs) := Seq.empty @@ -727,9 +719,6 @@ def genSmithyResources(config: Configuration) = genSmithyImpl(config).map(_._2) * library code, aws-specific code. */ def genSmithyImpl(config: Configuration) = Def.task { - // codegen needs the `protocol` jar to be published - (protocol.jvm(autoScalaLibrary = false) / publishLocal).value - val inputFiles = (config / smithySpecs).value val outputDir = (config / genSmithyOutput).?.value .getOrElse((config / sourceManaged).value) @@ -741,6 +730,8 @@ def genSmithyImpl(config: Configuration) = Def.task { val allowedNS = (config / allowedNamespaces).?.value.filterNot(_.isEmpty) val smithyDeps = (config / genSmithyDependencies).?.value.getOrElse(List.empty) + val discoverModels = + (config / genDiscoverModels).?.value.getOrElse(false) val codegenCp = (`codegen-cli`.jvm(Smithy4sPlugin.Scala213) / Compile / fullClasspath).value @@ -765,6 +756,7 @@ def genSmithyImpl(config: Configuration) = Def.task { val args = List("--output", outputDir) ++ List("--openapi-output", openapiOutputDir) ++ + (if (discoverModels) List("--discover-models") else Nil) ++ (if (allowedNS.isDefined) List("--allowed-ns", allowedNS.get.mkString(",")) else Nil) ++ inputs diff --git a/modules/codegen-cli/src/smithy4s/codegen/cli/CodegenCommand.scala b/modules/codegen-cli/src/smithy4s/codegen/cli/CodegenCommand.scala index 82fe64e38..74a543898 100644 --- a/modules/codegen-cli/src/smithy4s/codegen/cli/CodegenCommand.scala +++ b/modules/codegen-cli/src/smithy4s/codegen/cli/CodegenCommand.scala @@ -69,6 +69,15 @@ object CodegenCommand { ) .orFalse + 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[String]( @@ -93,6 +102,7 @@ object CodegenCommand { openApiOutputOpt, skipScalaOpt, skipOpenapiOpt, + discoverModelsOpt, allowedNSOpt, excludedNSOpt, repositoriesOpt, @@ -102,7 +112,7 @@ object CodegenCommand { ) .mapN { // format: off - case (output, openApiOutput, skipScala, skipOpenapi, allowedNS, excludedNS, repositories, dependencies, transformers, specsArgs) => + case (output, openApiOutput, skipScala, skipOpenapi, discoverModels, allowedNS, excludedNS, repositories, dependencies, transformers, specsArgs) => // format: on CodegenArgs( specsArgs, @@ -110,6 +120,7 @@ object CodegenCommand { openApiOutput.getOrElse(os.pwd), skipScala, skipOpenapi, + discoverModels, allowedNS, excludedNS, repositories.getOrElse(List.empty), diff --git a/modules/codegen-cli/src/smithy4s/codegen/cli/DumpModel.scala b/modules/codegen-cli/src/smithy4s/codegen/cli/DumpModel.scala index 5423c611e..6bd846500 100644 --- a/modules/codegen-cli/src/smithy4s/codegen/cli/DumpModel.scala +++ b/modules/codegen-cli/src/smithy4s/codegen/cli/DumpModel.scala @@ -26,7 +26,8 @@ object DumpModel { args.specs.map(_.toIO).toSet, args.dependencies, args.repositories, - args.transformers + args.transformers, + discoverModels = false ) Node.prettyPrintJson(ModelSerializer.builder().build.serialize(model)) diff --git a/modules/codegen-cli/test/src/smithy4s/codegen/cli/CommandParsingSpec.scala b/modules/codegen-cli/test/src/smithy4s/codegen/cli/CommandParsingSpec.scala index b2a479ecb..bfb7ad399 100644 --- a/modules/codegen-cli/test/src/smithy4s/codegen/cli/CommandParsingSpec.scala +++ b/modules/codegen-cli/test/src/smithy4s/codegen/cli/CommandParsingSpec.scala @@ -31,6 +31,7 @@ object CommandParsingSpec extends FunSuite { openapiOutput = os.pwd, skipScala = false, skipOpenapi = false, + discoverModels = false, allowedNS = None, excludedNS = None, repositories = Nil, @@ -77,6 +78,7 @@ object CommandParsingSpec extends FunSuite { openapiOutput = os.pwd / "target" / "openapi", skipScala = true, skipOpenapi = true, + discoverModels = false, allowedNS = Some(Set("name1", "name2")), excludedNS = None, repositories = List("repo1", "repo2"), diff --git a/modules/codegen-plugin/src/smithy4s/codegen/CodegenPlugin.scala b/modules/codegen-plugin/src/smithy4s/codegen/CodegenPlugin.scala index aa6e5c3c5..905871972 100644 --- a/modules/codegen-plugin/src/smithy4s/codegen/CodegenPlugin.scala +++ b/modules/codegen-plugin/src/smithy4s/codegen/CodegenPlugin.scala @@ -133,8 +133,8 @@ object Smithy4sCodegenPlugin extends AutoPlugin { * to decide whether or not Codegen should run. */ def cachedSmithyCodegen(conf: Configuration) = Def.task { - val outputPath = (conf / smithy4sOutputDir).value.getAbsolutePath() - val openApiOutputPath = (conf / smithy4sOpenapiDir).value.getAbsolutePath() + val outputPath = (conf / smithy4sOutputDir).value + val openApiOutputPath = (conf / smithy4sOpenapiDir).value val allowedNamespaces = (conf / smithy4sAllowedNamespaces).?.value.map(_.toSet) val excludedNamespaces = @@ -149,7 +149,7 @@ object Smithy4sCodegenPlugin extends AutoPlugin { val out = streams.value val cacheFile = out.cacheDirectory / s"smithy4s_${scalaBinaryVersion.value}" - + // This is important - it's what re-triggers this task on file changes val _ = (conf / smithy4sCodegen).inputFileChanges @@ -169,6 +169,7 @@ object Smithy4sCodegenPlugin extends AutoPlugin { openapiOutput = os.Path(openApiOutputPath), skipScala = false, skipOpenapi = false, + discoverModels = true, // we need protocol here allowedNS = allowedNamespaces, excludedNS = excludedNamespaces, repositories = res, diff --git a/modules/codegen/src/smithy4s/codegen/Codegen.scala b/modules/codegen/src/smithy4s/codegen/Codegen.scala index 89488d2f4..ee29305e2 100644 --- a/modules/codegen/src/smithy4s/codegen/Codegen.scala +++ b/modules/codegen/src/smithy4s/codegen/Codegen.scala @@ -31,7 +31,8 @@ object Codegen { self => args.specs.map(_.toIO).toSet, args.dependencies, args.repositories, - args.transformers + args.transformers, + args.discoverModels ) val scalaFiles = if (!args.skipScala) { @@ -95,7 +96,8 @@ object Codegen { self => Renderer(amended) } .map { result => - val relPath = os.RelPath(result.namespace.replace('.', '/')) + val relPath = + os.RelPath(result.namespace.split('.').toIndexedSeq, ups = 0) (relPath, result.name, result.content) } } diff --git a/modules/codegen/src/smithy4s/codegen/CodegenArgs.scala b/modules/codegen/src/smithy4s/codegen/CodegenArgs.scala index 7cb2a2089..552990647 100644 --- a/modules/codegen/src/smithy4s/codegen/CodegenArgs.scala +++ b/modules/codegen/src/smithy4s/codegen/CodegenArgs.scala @@ -22,6 +22,7 @@ case class CodegenArgs( openapiOutput: os.Path, skipScala: Boolean, skipOpenapi: Boolean, + discoverModels: Boolean, allowedNS: Option[Set[String]], excludedNS: Option[Set[String]], repositories: List[String], diff --git a/modules/codegen/src/smithy4s/codegen/ModelLoader.scala b/modules/codegen/src/smithy4s/codegen/ModelLoader.scala index a6446c841..4b7f8debf 100644 --- a/modules/codegen/src/smithy4s/codegen/ModelLoader.scala +++ b/modules/codegen/src/smithy4s/codegen/ModelLoader.scala @@ -22,30 +22,33 @@ import coursier.parse.RepositoryParser import software.amazon.smithy.build.ProjectionTransformer import software.amazon.smithy.build.TransformContext import software.amazon.smithy.model.Model +import software.amazon.smithy.model.loader.ModelAssembler import java.io.File import java.net.URL import java.net.URLClassLoader object ModelLoader { - private val requiredDeps = - s"${smithy4s.codegen.BuildInfo.organization}:${smithy4s.codegen.BuildInfo.protocolArtifact}:${smithy4s.codegen.BuildInfo.version}" :: - s"${smithy4s.codegen.BuildInfo.smithyOrg}:${smithy4s.codegen.BuildInfo.smithyArtifact}:${smithy4s.codegen.BuildInfo.smithyVersion}" :: - Nil def load( specs: Set[File], dependencies: List[String], repositories: List[String], - transformers: List[String] + transformers: List[String], + discoverModels: Boolean ): (ClassLoader, Model) = { - val allDeps = dependencies ++ requiredDeps + val allDeps = dependencies val maybeDeps = resolveDependencies(allDeps, repositories) val currentClassLoader = this.getClass().getClassLoader() // Loads a model using whatever's on the current classpath (in particular, anything that // might be provided by smithy4s itself out of the box) - val modelBuilder = Model.builder() + val modelBuilder = Model + .assembler() + .addClasspathModels(currentClassLoader, discoverModels) + .assemble() + .unwrap() + .toBuilder() maybeDeps.foreach { deps => // Loading the model just from upstream dependencies, in isolation @@ -69,7 +72,9 @@ object ModelLoader { } val modelAssembler = - Model.assembler(validatorClassLoader).addModel(modelBuilder.build()) + Model + .assembler(validatorClassLoader) + .addModel(modelBuilder.build()) specs.map(_.toPath()).foreach { modelAssembler.addImport @@ -127,4 +132,24 @@ object ModelLoader { else None } + implicit class ModelAssemblerOps(assembler: ModelAssembler) { + def addImports(urls: List[java.net.URL]): ModelAssembler = { + urls.foreach(assembler.addImport) + assembler + } + + def addClasspathModels( + classLoader: ClassLoader, + flag: Boolean + ): ModelAssembler = { + val smithy4sResources = List( + "META-INF/smithy/smithy4s.smithy", + "META-INF/smithy/smithy4s.meta.smithy" + ).map(classLoader.getResource) + + if (flag) assembler.discoverModels(classLoader) + else addImports(smithy4sResources) + } + } + }