Smithy Gradle Plugins#
The Smithy Gradle plugins integrate Smithy with the Gradle build system. These plugin can build artifacts from Smithy models, and generate JARs for Smithy models and model projections.
Plugins#
Two official Gradle plugins are available:
- smithy-base:
- This plugin configures the SourceSet's and Configuration's
for a Smithy project. It also creates the base
smithyBuild
task for the project that builds the Smithy models and build artifacts for the project.
- smithy-jar:
- Adds Smithy files to an existing JAR created by a
jar
task (usually created by the Java plugin or Kotlin JVM plugin). Thesmithy-jar
plugin also adds build metadata and tags to the JAR's MANIFEST. Thesmithy-jar
plugin applies thesmithy-base
plugin when applied.
Which plugin should I use?
Most users should use the smithy-jar
plugin. If you are building shared model packages,
custom traits, custom linters, or codegen integrations you should use the smithy-jar
plugin.
If you are writing a code generator you can use the smithy-base
plugin to set up basic
Configuration's and provide access to Smithy gradle tasks.
Applying Plugins#
The Smithy Gradle plugins are applied using the plugins
block of a gradle build script.
The following example configures a project to use the smithy-base
Gradle plugin:
plugins {
id("software.amazon.smithy.gradle.smithy-base").version("1.1.0")
}
plugins {
id 'software.amazon.smithy.gradle.smithy-base' version '1.1.0'
}
Note
The smithy-jar
plugin requires that a jar task be registered
before it is applied. The Java Plugin and Kotlin JVM plugin
are commonly used plugins that both register a jar task.
Complete Examples#
For several complete examples, see the examples directory of the Smithy Gradle plugins repository, or check out the Quick start guide for a tutorial on creating a Smithy model and building it with the Smithy Gradle plugin.
The examples in from the examples directory can be copied into your workspace using
the Smithy CLI init
command as follows:
smithy init -t <EXAMPLE_NAME> -o <OUTPUT_DIRECTORY> --url https://github.com/smithy-lang/smithy-gradle-plugin
You can list all examples available in the Smithy Gradle plugins repository with the following command:
smithy init --list --url https://github.com/smithy-lang/smithy-gradle-plugin
Building Smithy models#
The smithyBuild
task that builds smithy models operates in two different modes:
- If no
projection
is specified for theSmithyExtension
, then the task runs a "source" build using the "source" projection. - If a
projection
is specified for theSmithyExtension
, then the task runs in "projection" mode.
Building a source model#
A "source" build is run when no sourceProjection
is configured in
SmithyExtension
. To prevent accidentally relying on Smithy models
that are only available as build dependencies, Smithy models are discovered
using only the model sources and runtimeClasspath
.
The following example build.gradle.kts
will build a Smithy model using a
"source" build:
plugins {
`java-library`
id("software.amazon.smithy.gradle.smithy-jar").version("1.1.0")
}
// The SmithyExtension is used to customize the build. This example
// doesn't set any values and can be completely omitted.
smithy {}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation("software.amazon.smithy:smithy-model:1.53.0")
// These are just examples of dependencies. This model has a dependency on
// a "common" model package and uses the external AWS traits.
implementation("com.foo.baz:foo-model-internal-common:1.0.0")
implementation("software.amazon.smithy:smithy-aws-traits:1.53.0")
}
plugins {
id 'java-library'
'software.amazon.smithy.gradle.smithy-jar' version '1.1.0'
}
// The SmithyExtension is used to customize the build. This example
// doesn't set any values and can be completely omitted.
smithy {}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation 'software.amazon.smithy:smithy-model:1.53.0'
// These are just examples of dependencies. This model has a dependency on
// a "common" model package and uses the external AWS traits.
implementation 'com.foo.baz:foo-model-internal-common:1.0.0'
implementation 'software.amazon.smithy:smithy-aws-traits:1.53.0'
}
Generating a projection#
A "projection" build is run when a projection is
specified in the SmithyExtension
. You might create a projection of
a model if you need to maintain an internal version of a model that contains
more information and features than an external version of a model published to
your customers.
Any projected models should be added to the smithyBuild
configuration. This
prevents packages with projected models from appearing as dependencies of the
projected version of the model.
The following example gradle build script will run a "projection" build using the "external" projection.
plugins {
`java-library`
id("software.amazon.smithy.gradle.smithy-jar").version("1.1.0")
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation("software.amazon.smithy:smithy-aws-traits:1.53.0")
// Take a dependency on the internal model package. This
// dependency *must* be a smithyBuild dependency to ensure
// that is does not appear in the generated JAR.
smithyBuild("com.foo.baz:foo-internal-model:1.0.0")
}
smithy {
// Use the "external" projection. This projection must be found in the
// smithy-build.json file. This also ensures that models found in the
// foo-internal-package that weren't filtered out are copied into the
// projection created by this package.
sourceProjection.set("external")
projectionSourceTags.addAll("com.foo.baz:foo-internal-model")
}
plugins {
id 'java-library'
id 'software.amazon.smithy.gradle.smithy-jar' version '1.1.0'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation 'software.amazon.smithy:smithy-aws-traits:1.53.0'
// Take a dependency on the internal model package. This
// dependency *must* be a smithyBuild dependency to ensure
// that is does not appear in the generated JAR.
smithyBuild 'com.foo.baz:foo-internal-model:1.0.0'
}
smithy {
// Use the "external" projection. This projection must be found in the
// smithy-build.json file. This also ensures that models found in the
// foo-internal-package that weren't filtered out are copied into the
// projection created by this package.
sourceProjection = "external"
projectionSourceTags += ["com.foo.baz:foo-internal-model"]
}
Because the sourceProjection
of the SmithyExtension
was set to external
, a
smithy-build.json
file must be found that defines the external
projection. For example:
{
"version": "1.0",
"projections": {
"external": {
"transforms": [
{
"name": "excludeShapesByTag",
"args": {
"tags": ["internal"]
}
},
{
"name": "excludeShapesByTrait",
"args": {
"traits": ["internal"]
}
},
{
"name": "excludeMetadata",
"args": {
"keys": ["suppressions", "validators"]
}
},
{
"name": "removeUnusedShapes"
}
]
}
}
}
Projection tags#
Projections can project and filter shapes from dependencies into the output model.
"Projecting" a dependency into your output JAR will include that model in the output
sources of your project. Downstream consumers of your JAR will then no longer need to
include the "projected" dependency as a dependency of their project to resolve the
"projected" shapes. You need to specify which dependencies are being projected into
your JAR by setting the projectionSourceTags
property.
smithy {
sourceProjection.set("external")
projectionSourceTags.addAll("Foo", "com.baz:bar")
}
smithy {
projection = "external"
projectionSourceTags += setOf("Foo", "com.baz:bar")
}
Tags are used to logically group packages to make it easier to build
projections. The tags
property is used to add Smithy-Tags
attribute
to the JAR MANIFEST.
smithy {
tags.addAll("X", "foobaz-model")
}
smithy {
tags += ["X", "foobaz-model"]
}
For example, if your service is made up of 10 packages, you can add the
"foobaz-model" Smithy tag to each package so that the only value that needs
to be provided for tags
to correctly project your model is "foobaz-model".
When building a model package, the Smithy Gradle plugin will automatically add the group name of the package being built, the group name + ":" + name of the package, and group name + ":" + name + ":" version. This allows models to always be queried by group and artifact names in addition to custom tags.
Building artifacts from Smithy models#
If a smithy-build.json file is found at the root of the project,
then it will be used to generate artifacts from the Smithy
model. Smithy uses build plugins to generate projection artifacts. To
use a smithy build plugin with Gradle, first add it as a smithyBuild
dependency in
the Gradle build script. The plugin will then be discoverable and can be configured
in the smithy-build.json
file.
For example, to generate a Typescript client from a Smithy model, add the
smithy-typescript-codegen
package as a smithyBuild
dependency:
dependencies {
// ...
// This dependency is required in order to apply the "typescript-client-codegen"
// plugin in smithy-build.json
smithyBuild("software.amazon.smithy.typescript:smithy-typescript-codegen:0.25.0")
}
dependencies {
// ...
// This dependency is required in order to apply the "typescript-client-codegen"
// plugin in smithy-build.json
smithyBuild 'software.amazon.smithy.typescript:smithy-typescript-codegen:0.25.0'
}
The plugin can then be configured in the smithy-build.json
to generate a typescript client
from a Smithy model:
{
"version": "1.0",
"plugins": {
"typescript-client-codegen": {
"package": "@smithy/typescript-client-codegen-example",
"packageVersion": "0.0.1",
"packageJson": {
"license": "MIT"
}
}
}
}
Configuration#
Smithy model sources#
Smithy gradle plugins assume Smithy model files (*.smithy
) are organized in a similar way as Java source files,
in sourceSets. The smithy-base plugin adds a new sources block named smithy to every sourceSet.
By default, this source block will include Smithy models in:
model/
src/$sourceSetName/smithy
src/$sourceSetName/resources/META-INF/smithy
New source directories can be added to a smithy sources block as follows:
sourceSets {
main {
smithy {
srcDir("includes")
}
}
}
sourceSets {
main {
smithy {
srcDir 'includes'
}
}
}
Models found in these directories are combined into a flattened directory
structure and used to validate and build the Smithy model. A Smithy manifest
file is automatically created for the detected models, and it, along with the
model files, are placed in the META-INF/smithy/
resource of the created
JAR. Any project that depends on this created JAR will be able to find
and use the Smithy models contained in the JAR when using model discovery.
Dependencies#
The Smithy build plugins use two different configurations to search for dependencies such as shared models or smithy build plugins:
runtimeClasspath
:- Runtime dependencies that will be required by any output JARS or publications. For example, a shared model package or Java trait definition.
smithyBuild
:- Build dependencies that are not required by output models.
Runtime dependencies can be added directly to the runtimeClasspath
configuration
or to a configuration that extends runtimeClasspath
, such as the implementation
configuration added by the java-library
plugin.
Build Dependencies#
The smithy-base
plugin adds a smithyBuild
Configuration that can be used to specify
dependencies used when calling smithy build
. These dependencies will not be included in any output JARs or publications. They are akin to compileOnly
dependencies for Java projects. Smithy build plugins and projected dependencies should be
included in the smithyBuild configuration. For example:
dependencies {
smithyBuild("com.example.software:build-only:1.0.0")
}
dependencies {
smithyBuild 'com.example.software:build-only:1.0.0'
}
Smithy extension properties#
The Smithy Gradle plugins are configured using the SmithyExtension extension:
smithy {
projection.set("foo")
}
smithy {
projection = "foo"
}
This extension supports the following properties:
Property | Type | Description |
---|---|---|
format | boolean |
Flag indicating whether to format Smithy source files. By default, the Smithy CLI format
command is executed on all source directories. This opinionated formatter follows the best practices
recommended by the Smithy team. It is possible to disable the formatter by setting this flag to false . |
smithyBuildConfigs | FileCollection |
Sets a custom collection of smithy-build.json files to use when building the model. |
sourceProjection | String |
Sets the projection name to use as the source (primary) projection.
The smithy sources for this projection will be packaged in output JARs by the smithy-jar
plugin. There must be a corresponding projection definition in the smithy-build.json
file in the project. Defaults to "source" . |
projectionSourceTags | Set<String> |
Get the tags that are searched for in classpaths when determining which models are projected into the created JAR. This plugin will look through the JARs in the buildscript classpath to see if they contain a META-INF/MANIFEST.MF attribute named "Smithy-Tags" that matches any of the given projection source tags. The Smithy models found in each matching JAR are copied into the JAR being projected. This allows a projection JAR to aggregate models into a single JAR. |
tags | Set<String> |
Set the tags that are added to the JAR. These tags are placed in the META-INF/MANIFEST.MF attribute named "Smithy-Tags" as a comma separated list. JARs with Smithy-Tags can be queried when building projections so that the Smithy models found in each matching JAR are placed into the projection JAR. |
allowUnknownTraits | boolean |
Sets whether or not unknown traits in the model should be ignored. By default, the build will fail if unknown traits are encountered. |
fork | boolean |
By default, the CLI is run in the same process as Gradle, but inside a thread with a custom class loader. This should work in most cases, but there is an option to run inside a process if necessary. |
outputDirectory | Directory |
Defines where Smithy build artifacts are written. |
Customize output directory#
By default, Smithy build artifacts will be placed in the project build
directory in a smithyprojections/
directory. There are two ways to override
the output directory. The first method is to set the outputDirectory
property
in the smithy-build.json
config for your Smithy project. For example:
{
"outputDirectory": "build/output"
}
The second method is to configure the plugin using the smithy
extension:
smithy {
outputDirectory.set(file("path/to/output"))
}
smithy {
outputDirectory = file("path/to/output")
}
Note
Setting the output directory on the plugin extension will
override any outputDirectory
property set in the
smithy-build config.
Set smithy-build
configs to use#
By default, the plugin will look for a file called smithy-build.json
at the project's root and will use that as the smithy-build config
for your project. If no smithy-build.json file is found, then an empty build config
is used to build the project.
Alternatively, you can explicitly configure one or more smithy-build configs to use for your project as follows:
smithy {
smithyBuildConfigs.set(files("smithy-build-config.json"))
}
smithy {
smithyBuildConfigs = files("smithy-build-config.json")
}
Set Smithy Tags to add to a JAR#
When the smithy-jar plugin is applied to a project, it can add a number of Smithy tags to the MANIFEST of a generated JAR. These tags can be used by downstream consumers to filter which models to include in projections. Tags can be configured for the plugin as follows:
smithy {
tags.addAll("tag1", "anotherTag", "anotherTag2")
}
smithy {
tags += ["tag1", "anotherTag", "anotherTag2"]
}
Fork a new process when executing Smithy CLI commands#
By default, Smithy CLI commands are run in the
same process as Gradle, but inside a thread with a custom class loader.
This should work in most cases, but there is an option to run inside a
process if necessary. To run Smithy CLI commands in a process set the
fork
configuration option to true:
smithy {
fork.set(true)
}
smithy {
fork = true
}
Disable Smithy Formatter#
By default, the Smithy CLI format
command is
executed on all source directories. This opinionated formatter follows
the best practices recommended by the Smithy team. It is possible to disable
the formatter by setting the format setting on the plugin extension to false:
smithy {
format.set(false)
}
smithy {
format = false
}