Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ package software.amazon.smithy.swift.codegen

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Just a general Q on manifest generator. AFAIK we don't generate Package.swift for smithy-swift, and this is just used downstream in aws-sdk-swift when we generate Package.swift files for services - am I correct in my understanding there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. The only packages that use the PackageManifestGenerator are service clients. Runtime modules in both aws-sdk-swift and in smithy-swift will have hand-maintained manifests.

import software.amazon.smithy.codegen.core.SymbolDependency
import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
import kotlin.jvm.optionals.getOrNull

val PACKAGE_MANIFEST_NAME = "Package.swift.txt"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package.swift is temporarily renamed so that Xcode does not try to interpret each service client as its own Swift package just yet.


class PackageManifestGenerator(val ctx: ProtocolGenerator.GenerationContext) {

fun writePackageManifest(dependencies: List<SymbolDependency>) {
ctx.delegator.useFileWriter("Package.swift") { writer ->
writer.write("// swift-tools-version:\$L", ctx.settings.swiftVersion)
ctx.delegator.useFileWriter(PACKAGE_MANIFEST_NAME) { writer ->
writer.write("// swift-tools-version: \$L", ctx.settings.swiftVersion)
writer.write("")
writer.write("import PackageDescription")
writer.write("")
Expand All @@ -27,49 +30,64 @@ class PackageManifestGenerator(val ctx: ProtocolGenerator.GenerationContext) {
writer.write(".library(name: \$S, targets: [\$S])", ctx.settings.moduleName, ctx.settings.moduleName)
}

val externalDependencies = dependencies.filter { it.getProperty("url", String::class.java).get().isNotEmpty() }
val dependenciesByURL = externalDependencies.distinctBy { it.expectProperty("url", String::class.java) }
val externalDependencies = dependencies.filter {
it.getProperty("url", String::class.java).getOrNull() != null ||
it.getProperty("scope", String::class.java).getOrNull() != null
}
val dependenciesByURL = externalDependencies.distinctBy {
it.getProperty("url", String::class.java).getOrNull()
?: "${it.getProperty("scope", String::class.java).get()}.${it.packageName}"
}
Copy link
Contributor Author

@jbelkins jbelkins Jun 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"External dependencies" are all the dependencies of this package that aren't built-in (i.e. Swift or Foundation.)
"Dependencies by URL" are written to the package dependencies section.


writer.openBlock("dependencies: [", "],") {
dependenciesByURL.forEach { dependency ->
writer.openBlock(".package(", "),") {

val localPath = dependency.expectProperty("localPath", String::class.java)
if (localPath.isNotEmpty()) {
writer.write("path: \$S", localPath)
} else {
val dependencyURL = dependency.expectProperty("url", String::class.java)
writer.write("url: \$S,", dependencyURL)
writer.write("from: \$S", dependency.version)
}
}
}
dependenciesByURL.forEach { writePackageDependency(writer, it) }
}

val dependenciesByTarget = externalDependencies.distinctBy { it.expectProperty("target", String::class.java) + it.packageName }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Dependencies by target" are written to the service client target's dependencies section.


writer.openBlock("targets: [", "]") {
writer.openBlock(".target(", "),") {
writer.write("name: \$S,", ctx.settings.moduleName)
writer.openBlock("dependencies: [", "]") {
for (dependency in dependenciesByTarget) {
writer.openBlock(".product(", "),") {
val target = dependency.expectProperty("target", String::class.java)
writer.write("name: \$S,", target)
writer.write("package: \$S", dependency.packageName)
}
}
writer.openBlock("dependencies: [", "],") {
dependenciesByTarget.forEach { writeTargetDependency(writer, it) }
}
writer.openBlock("resources: [", "]") {
writer.write(".process(\"Resources\")")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each service client has a Package.version file in the Resources subfolder that needs to be included with the service client.

}
}
writer.openBlock(".testTarget(", ")") {
writer.write("name: \$S,", ctx.settings.testModuleName)
writer.openBlock("dependencies: [", "]") {
writer.write("\$S,", ctx.settings.moduleName)
writer.write(".product(name: \"SmithyTestUtil\", package: \"smithy-swift\"),")
SwiftDependency.SMITHY_TEST_UTIL.dependencies.forEach { writeTargetDependency(writer, it) }
}
}
}
}
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two private methods below just write a package or target dependency from a SymbolDependency, and are used in the appropriate sections above.

private fun writePackageDependency(writer: SwiftWriter, dependency: SymbolDependency) {
writer.openBlock(".package(", "),") {
val scope = dependency.getProperty("scope", String::class.java).getOrNull()
scope?.let {
writer.write("id: \$S,", "$it.${dependency.packageName}")
}
val url = dependency.getProperty("url", String::class.java).getOrNull()
url?.let {
writer.write("url: \$S,", it)
}
writer.write("from: \$S", dependency.version)
}
}

private fun writeTargetDependency(writer: SwiftWriter, dependency: SymbolDependency) {
writer.openBlock(".product(", "),") {
val target = dependency.expectProperty("target", String::class.java)
writer.write("name: \$S,", target)
val scope = dependency.getProperty("scope", String::class.java).getOrNull()
val packageName = scope?.let { "$it.${dependency.packageName}" } ?: dependency.packageName
writer.write("package: \$S", packageName)
}
}
}
Loading