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.

Migrate to version 0.10.0+

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). The smithy-jar plugin also adds build metadata and tags to the JAR's MANIFEST. The smithy-jar plugin applies the smithy-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:

build.gradle.kts#
plugins {
    id("software.amazon.smithy.gradle.smithy-base").version("1.1.0")
}
build.gradle#
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:

  1. If no projection is specified for the SmithyExtension, then the task runs a "source" build using the "source" projection.
  2. If a projection is specified for the SmithyExtension, 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:

build.gradle.kts#
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")
}
build.gradle#
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.

build.gradle.kts#
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")
}
build.gradle#
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:

smithy-build.json#
{
    "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.

build.gradle.kts#
smithy {
    sourceProjection.set("external")
    projectionSourceTags.addAll("Foo", "com.baz:bar")
}
build.gradle#
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.

build.gradle.kts#
smithy {
    tags.addAll("X", "foobaz-model")
}
build.gradle#
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:

build.gradle.kts#
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")
}
build.gradle#
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:

smithy-build.json#
{
    "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:

build.gradle.kts#
sourceSets {
    main {
        smithy {
            srcDir("includes")
        }
    }
}
build.gradle#
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:

build.gradle.kts#
dependencies {
    smithyBuild("com.example.software:build-only:1.0.0")
}
build.gradle#
dependencies {
    smithyBuild 'com.example.software:build-only:1.0.0'
}

Smithy extension properties#

The Smithy Gradle plugins are configured using the SmithyExtension extension:

build.gradle.kts#
smithy {
    projection.set("foo")
}
build.gradle#
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:

smithy-build.json#
{
    "outputDirectory": "build/output"
}

The second method is to configure the plugin using the smithy extension:

build.gradle.kts#
smithy {
    outputDirectory.set(file("path/to/output"))
}
build.gradle#
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:

build.gradle.kts#
smithy {
    smithyBuildConfigs.set(files("smithy-build-config.json"))
}
build.gradle#
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:

build.gradle.kts#
smithy {
    tags.addAll("tag1", "anotherTag", "anotherTag2")
}
build.gradle#
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:

build.gradle.kts#
smithy {
    fork.set(true)
}
build.gradle#
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:

build.gradle.kts#
smithy {
    format.set(false)
}
build.gradle#
smithy {
    format = false
}