-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a dependency tracking mechanism #607
Merged
Merged
Changes from 1 commit
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
b10343d
Add a dependency tracking mechanism
Baccata 644f3e4
Fix mill module compilation
Baccata 9a1e766
Add dependency-tracking logic to the Smithy4sModule (mill)
Baccata 68437ec
Dogfooding : set-up our own libraries with manifest metadata
Baccata ac1a416
Update documentation ro remove un-necessary dependency declarations
Baccata d952054
Merge remote-tracking branch 'origin/series/0.17' into dependency-tra…
Baccata 5f72f0d
Remove .only
kubukoz 2b8e665
Fix mill module
Baccata a9e0612
Change the build plugins so that libraryDependencies are taken into a…
Baccata 8d406a1
Cleanup
Baccata File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
...degen-plugin/src/sbt-test/codegen-plugin/multimodule-staged/baz/src/main/scala/Test.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright 2021-2022 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 baz | ||
|
||
import foo._ | ||
import bar._ | ||
|
||
object BarTest { | ||
|
||
def main(args: Array[String]): Unit = { | ||
println(Baz(Some(Foo(Some(1))))) | ||
} | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
...egen-plugin/src/sbt-test/codegen-plugin/multimodule-staged/baz/src/main/smithy/bar.smithy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
$version: "2.0" | ||
|
||
namespace baz | ||
|
||
use foo#Foo | ||
|
||
// Checking that Foo can be found by virtue of the upstream `bar` project | ||
// defined as a compile-scope library dependency was published with an indication | ||
// in the manifest that it used the `foo` project for code generation. | ||
structure Baz { | ||
foo: Foo | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -17,6 +17,7 @@ | |||||
package smithy4s.codegen | ||||||
|
||||||
import sbt.Keys._ | ||||||
import java.util.jar.JarFile | ||||||
import sbt.util.CacheImplicits._ | ||||||
import sbt.{fileJsonFormatter => _, _} | ||||||
import JsonConverters._ | ||||||
|
@@ -62,13 +63,44 @@ object Smithy4sCodegenPlugin extends AutoPlugin { | |||||
"Sets whether this project should be used as a Smithy library by packaging the Smithy specs in the resulting jar" | ||||||
) | ||||||
|
||||||
val smithy4sLocalJars = | ||||||
val smithy4sExternalDependenciesAsJars = | ||||||
taskKey[Seq[File]]( | ||||||
List( | ||||||
"List of jars for local dependencies that should be used as sources of Smithy specs.", | ||||||
"Namespaces that were used for code generation in the upstream dependencies will be excluded from code generation in this project.", | ||||||
"By default, this includes the jars produced by packaging your project's build dependencies, so they'll need to be compiled for the codegen task to run.", | ||||||
"You can clear this (set to an empty list) if your Smithy specs don't have dependencies on other module." | ||||||
"List of jars for external dependencies that should be added to the classpath used by Smithy4s during code-generation", | ||||||
"The smithy files and smithy validators contained by these jars are included in the Smithy4s code-generation process", | ||||||
"Namespaces that were used for code generation in these dependencies will be excluded from code generation in this project.", | ||||||
"By default, this includes the jars resulting from the resolution of library dependencies annotated with the `Smithy4s` configuration" | ||||||
).mkString(" ") | ||||||
) | ||||||
|
||||||
val smithy4sExternalCodegenDependenciesAsJars = | ||||||
taskKey[Seq[File]]( | ||||||
List( | ||||||
"List of jars that external dependencies indicate having used during their own code-generation process", | ||||||
"If a project using Smithy4s depends on a library that contains Smithy4s generated code, local Smithy files might need the jars", | ||||||
"that were used by Smithy4s when the library in question was built. By default, Smithy4s adds a line in the jar manifests of the", | ||||||
"projects it is enabled on to inform downstream projects of the jars they might want to pull during their own code-generation.", | ||||||
"This is different from a transitive compile dependency, as the jars used during code-generation might not necessarily end up", | ||||||
"on the compile class-path of a project" | ||||||
).mkString(" ") | ||||||
) | ||||||
|
||||||
val smithy4sInternalDependenciesAsJars = | ||||||
taskKey[Seq[File]]( | ||||||
List( | ||||||
"List of jars of internal dependencies that should be added to the classpath used by Smithy4s during code-generation", | ||||||
"The smithy files and smithy validators contained by these jars are included in the Smithy4s code-generation process", | ||||||
"Namespaces that were used for code generation in these dependencies will be excluded from code generation in this project.", | ||||||
"By default, this includes the jars produced by packaging this project's local dependencies, which implies these should compile for the codegen task to run", | ||||||
"This can be set to an empty list to prevent the inclusion of local dependencies during the code-gen process" | ||||||
).mkString(" ") | ||||||
) | ||||||
|
||||||
val smithy4sAllDependenciesAsJars = | ||||||
taskKey[Seq[File]]( | ||||||
List( | ||||||
"List of all jars for internal and external dependencies that should be used as sources of Smithy specs.", | ||||||
"Namespaces that were used for code generation in these upstream dependencies will be excluded from code generation in this project." | ||||||
).mkString(" ") | ||||||
) | ||||||
|
||||||
|
@@ -93,6 +125,8 @@ object Smithy4sCodegenPlugin extends AutoPlugin { | |||||
smithy4sVersion := BuildInfo.version | ||||||
) | ||||||
|
||||||
private val SMITHY4S_DEPENDENCIES = "smithy4sDependencies" | ||||||
|
||||||
override def projectConfigurations: Seq[Configuration] = Seq(Smithy4s) | ||||||
|
||||||
// Use this with any configuration to enable the codegen in it. | ||||||
|
@@ -103,8 +137,22 @@ object Smithy4sCodegenPlugin extends AutoPlugin { | |||||
config / smithy4sResourceDir := (config / resourceManaged).value, | ||||||
config / smithy4sCodegen := cachedSmithyCodegen(config).value, | ||||||
config / smithy4sSmithyLibrary := true, | ||||||
config / smithy4sLocalJars := (config / internalDependencyAsJars).value | ||||||
.map(_.data), | ||||||
config / smithy4sExternalDependenciesAsJars := { | ||||||
val updateReport = | ||||||
(config / update).value +: (config / transitiveUpdate).value | ||||||
findCodeGenDependencies(updateReport) | ||||||
}, | ||||||
config / smithy4sInternalDependenciesAsJars := { | ||||||
(config / internalDependencyAsJars).value.map(_.data) | ||||||
}, | ||||||
config / smithy4sExternalCodegenDependenciesAsJars := { | ||||||
smithy4sFetchUpstreamCodegenDependencies(config).value | ||||||
}, | ||||||
config / smithy4sAllDependenciesAsJars := { | ||||||
(config / smithy4sExternalDependenciesAsJars).value ++ | ||||||
(config / smithy4sExternalCodegenDependenciesAsJars).value ++ | ||||||
(config / smithy4sInternalDependenciesAsJars).value | ||||||
}, | ||||||
config / sourceGenerators += (config / smithy4sCodegen).map( | ||||||
_.filter(_.ext == "scala") | ||||||
), | ||||||
|
@@ -113,7 +161,24 @@ object Smithy4sCodegenPlugin extends AutoPlugin { | |||||
), | ||||||
config / cleanFiles += (config / smithy4sOutputDir).value, | ||||||
config / cleanFiles += (config / smithy4sResourceDir).value, | ||||||
config / smithy4sModelTransformers := List.empty | ||||||
config / smithy4sModelTransformers := List.empty, | ||||||
config / packageBin / packageOptions += { | ||||||
// This piece of logic aims at tracking the dependencies that Smithy4s used to generate | ||||||
// code at build time, in the manifest of the jar. This helps automatically pulling | ||||||
// the corresponding jars and prevents the users from having to search | ||||||
import java.util.jar.Manifest | ||||||
val manifest = new Manifest | ||||||
val scalaBin = scalaBinaryVersion.?.value | ||||||
val deps = libraryDependencies.value | ||||||
.filter(_.configurations.exists(_.contains(Smithy4s.name))) | ||||||
.flatMap(moduleIdEncode(_, scalaBin)) | ||||||
.mkString(",") | ||||||
|
||||||
manifest | ||||||
.getMainAttributes() | ||||||
.put(new java.util.jar.Attributes.Name(SMITHY4S_DEPENDENCIES), deps) | ||||||
Package.JarManifest(manifest) | ||||||
} | ||||||
) | ||||||
|
||||||
override lazy val projectSettings = | ||||||
|
@@ -125,7 +190,7 @@ object Smithy4sCodegenPlugin extends AutoPlugin { | |||||
|
||||||
private def findCodeGenDependencies( | ||||||
updateReports: Seq[UpdateReport] | ||||||
): List[os.Path] = | ||||||
): List[File] = | ||||||
for { | ||||||
markerConfig <- List(Smithy4s) | ||||||
updateReport <- updateReports.toList | ||||||
|
@@ -134,9 +199,78 @@ object Smithy4sCodegenPlugin extends AutoPlugin { | |||||
artifactFile <- module.artifacts | ||||||
} yield { | ||||||
val (_, file) = artifactFile | ||||||
os.Path(file) | ||||||
file | ||||||
} | ||||||
|
||||||
private def moduleIdEncode( | ||||||
moduleId: ModuleID, | ||||||
scalaBinaryVersion: Option[String] | ||||||
): List[String] = { | ||||||
(moduleId.crossVersion, scalaBinaryVersion) match { | ||||||
case (Disabled, _) => | ||||||
List(s"${moduleId.organization}:${moduleId.name}:${moduleId.revision}") | ||||||
case (_: Binary, Some(sbv)) => | ||||||
List( | ||||||
s"${moduleId.organization}:${moduleId.name}_${sbv}:${moduleId.revision}" | ||||||
) | ||||||
case (_, _) => Nil | ||||||
} | ||||||
} | ||||||
|
||||||
/** | ||||||
* Retrieves the smithy4sDependencies that compile-dependencies may have listed | ||||||
* in their jar manifests when they were packaged. | ||||||
*/ | ||||||
private def smithy4sFetchUpstreamCodegenDependencies( | ||||||
config: Configuration | ||||||
): Def.Initialize[Task[Seq[File]]] = | ||||||
Def.task { | ||||||
val smithy4sDependencies = | ||||||
(config / externalDependencyClasspath).value | ||||||
.map(_.data) | ||||||
.flatMap(extract) | ||||||
def getJars(ids: Seq[ModuleID]): Seq[File] = { | ||||||
val syntheticModule = | ||||||
organization.value % (name.value + "-smithy4s-resolution") % version.value | ||||||
val depRes = (update / dependencyResolution).value | ||||||
val updc = (update / updateConfiguration).value | ||||||
val uwconfig = (update / unresolvedWarningConfiguration).value | ||||||
val modDescr = depRes.moduleDescriptor( | ||||||
syntheticModule, | ||||||
ids.toVector, | ||||||
None | ||||||
) | ||||||
|
||||||
depRes | ||||||
.update( | ||||||
modDescr, | ||||||
updc, | ||||||
uwconfig, | ||||||
streams.value.log | ||||||
) | ||||||
.map(_.allFiles) | ||||||
.fold(uw => throw uw.resolveException, identity) | ||||||
} | ||||||
getJars(smithy4sDependencies) | ||||||
} | ||||||
|
||||||
private lazy val simple = raw"([^:]*):([^:]*):([^:]*)".r | ||||||
private lazy val cross = raw"([^:]*)::([^:]*):([^:]*)".r | ||||||
private def extract(jarFile: java.io.File): Seq[ModuleID] = { | ||||||
val jar = new JarFile(jarFile) | ||||||
Option( | ||||||
jar.getManifest().getMainAttributes().getValue("smithy4sDependencies") | ||||||
daddykotex marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
).toList.flatMap { listString => | ||||||
listString | ||||||
.split(",") | ||||||
.collect { | ||||||
case cross(org, art, version) => org %% art % version | ||||||
case simple(org, art, version) => org % art % version | ||||||
} | ||||||
.toList | ||||||
} | ||||||
} | ||||||
|
||||||
def cachedSmithyCodegen(conf: Configuration) = Def.task { | ||||||
val inputFiles = | ||||||
Option((conf / smithy4sInputDirs).value).toSeq.flatten | ||||||
|
@@ -148,15 +282,8 @@ object Smithy4sCodegenPlugin extends AutoPlugin { | |||||
(conf / smithy4sAllowedNamespaces).?.value.map(_.toSet) | ||||||
val excludedNamespaces = | ||||||
(conf / smithy4sExcludedNamespaces).?.value.map(_.toSet) | ||||||
val updateReport = { | ||||||
(conf / update).value +: (conf / transitiveUpdate).value | ||||||
} | ||||||
|
||||||
val localDependencyJars = | ||||||
(conf / smithy4sLocalJars).value.map(os.Path(_)).toList | ||||||
|
||||||
val externalDependencyJars = findCodeGenDependencies(updateReport) | ||||||
val localJars = localDependencyJars ++ externalDependencyJars | ||||||
val localJars = | ||||||
(conf / smithy4sAllDependenciesAsJars).value.map(os.Path(_)).toList | ||||||
val res = | ||||||
(conf / resolvers).value.toList.collect { case m: MavenRepository => | ||||||
m.root | ||||||
|
@@ -174,7 +301,7 @@ object Smithy4sCodegenPlugin extends AutoPlugin { | |||||
output = os.Path(outputPath), | ||||||
resourceOutput = os.Path(resourceOutputPath), | ||||||
skip = skipSet, | ||||||
discoverModels = true, // we need protocol here | ||||||
discoverModels = false, // we need protocol here | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I guess that comment can be removed now |
||||||
allowedNS = allowedNamespaces, | ||||||
excludedNS = excludedNamespaces, | ||||||
repositories = res, | ||||||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just spitballing, could we use coursier's api to parse the dependency strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and then how do you go from coursier dep to SBT dep ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dunno, if it was a structured parsing result we could probably do that - if there's no way then the answer to my question is "no" ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's postpone this : right now the problem is more about "can we you render a dependency as a coursier-parsable string in both SBT and mill ?" Because I couldn't find a way to do that easily, I have the control over the format and parsing with coursier is not necessary as we're falling in the simplest possible cases (normal java dep, normal scala dep).
If it proves to be limitative we'll revise.