gradle-buildconfig-plugin: generating a typed BuildConstants class from your Gradle build
A plugin for generating BuildConstants for any kind of Gradle projects: Java, Kotlin, Groovy, etc. Designed for KTS scripts.
At a glance
- What is it?
- gradle-buildconfig-plugin is an MIT licensed Kotlin plugin that writes a BuildConstants class from values declared in the build script, with typed fields for Java, Kotlin, Groovy and Android projects and optional expect/actual support for Kotlin Multiplatform. It suits teams that want a single place for build-time constants without hand-maintaining a generated file.
- Who is it for?
- gradle-buildconfig-plugin fits a Gradle team that wants typed build constants derived from the build itself, with a Kotlin DSL that reads like the rest of the build script and generated code that matches the project's language, and the choice between a Kotlin object, top-level constants and a Java class covers most existing conventions.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 11 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Build constants as a generated class, not a hand-edited file
gradle-buildconfig-plugin generates BuildConstants for any kind of Gradle project: Java, Kotlin, Android, Groovy, and so on. The primary language of the plugin itself is Kotlin, and the project describes itself as designed for KTS scripts, with experimental support for Kotlin's multi-platform plugin.
The problem it addresses is the one Android developers know well and everyone else hits eventually. You need a value that is decided at build time, the application name, the version, a secret, a feature flag, and you need it inside your application code with a proper type. The conventional solutions are to hand-write a constants file and keep it in sync by hand, or to read the same property twice, once in the build script and once in the code, and hope the two agree.
This plugin makes the build script the single source. You declare the fields in a buildConfig block, and the plugin generates the class, with the value types inferred from what you wrote in the script. The name BuildConstants is the plugin's own naming, and the default class name is BuildConfig with the default package taken from the project group, so the generated file lands where a Gradle user would expect it to without configuration.
Who this is for is a team already on Gradle with a Kotlin build script, who want the constants to be typed rather than stringly typed, and who would rather have the build generate a file than maintain one. The topics on the repository name gradle, gradle-kotlin-dsl, gradle-plugin, kts and kotlin-multiplatform, which is an accurate map of the audience.
The distribution is through the two channels you would expect for a Gradle plugin: the Gradle Plugin Portal, under the id com.github.gmazzo.buildconfig, and Maven Central, under the same group and artifact naming. Both are linked from the badges at the top of the README.
Declaring fields in KTS, and what comes out the other end
The Kotlin DSL usage is the one the plugin is designed around. On your build.gradle.kts you add the plugin id alongside your language plugin, and then fill in a buildConfig block:
plugins {
id("org.jetbrains.kotlin.jvm") version "<kotlin version>"
id("com.github.gmazzo.buildconfig") version "<current version>"
}
buildConfig {
buildConfigField("APP_NAME", project.name)
buildConfigField("APP_VERSION", provider { "\"${project.version}\"" })
buildConfigField("APP_SECRET", "Z3JhZGxlLWphdmEtYnVpbGRjb25maWctcGx1Z2lu")
buildConfigField<String>("OPTIONAL", null)
buildConfigField("FEATURE_ENABLED", true)
buildConfigField("MAGIC_NUMBERS", intArrayOf(1, 2, 3, 4))
buildConfigField("STRING_LIST", arrayOf("a", "b", "c"))
buildConfigField("MAP", mapOf("a" to 1, "b" to 2))
buildConfigField("FILE", File("aFile"))
buildConfigField("URI", uri("https://example.io"))
}The generated Kotlin file is an object with internal visibility, and the field types are derived from the declaration:
internal object BuildConfig {
internal const val APP_NAME: String = "kts"
internal const val APP_VERSION: String = "\"0.1.0-demo\""
internal const val APP_SECRET: String = "Z3JhZGxlLWphdmEtYnVpbGRjb25maWctcGx1Z2lu"
internal val OPTIONAL: String? = null
internal const val FEATURE_ENABLED: Boolean = true
internal val MAGIC_NUMBERS: IntArray = intArrayOf(1, 2, 3)
internal val STRING_LIST: Array<String> = arrayOf("a", "b", "c")
internal val MAP: Map<String, Int> = mapOf("a" to 1, "b" to 2)
internal val FILE: File = java.io.File("aFile")
internal val URI: URI = java.net.URI.create("https://example.io")
}Two details here are worth more than the rest. First, the type comes from the value: a Boolean produces a Boolean, a map produces a Map with generic parameters, a File and a URI produce the fully qualified java types, and a null with an explicit generic type produces a nullable field. Second, the value can be a provider, so the field is not resolved at configuration time but when the file is written, which is what lets the project version land in the generated source.
One inconsistency in the README is worth knowing about if you follow the example exactly. The build script declares MAGIC_NUMBERS as intArrayOf(1, 2, 3, 4) while the shown generated output contains intArrayOf(1, 2, 3). Treat the generated snippet as illustrative of the types rather than as the exact output of the snippet above it.
The same plugin in Groovy produces a Java class
The Groovy path is the mirror image, and it exists because a plugin that only works in one build language is a plugin half the projects cannot use.
In build.gradle you apply the plugin id with a version, and the field declarations use an explicit type as the first argument:
plugins {
id 'java'
id 'com.github.gmazzo.buildconfig' version < current version >
}
buildConfig {
buildConfigField(String, 'APP_NAME', project.name)
buildConfigField(String, "APP_VERSION", provider { project.version })
buildConfigField(String, 'APP_SECRET', "Z3JhZGxlLWphdmEtYnVpbGRjb25maWctcGx1Z2lu")
buildConfigField(String, 'OPTIONAL', null)
buildConfigField(boolean, 'FEATURE_ENABLED', true)
buildConfigField(int[], "MAGIC_NUMBERS", [1, 2, 3])
buildConfigField('List<String>', "STRING_LIST", ["a", "b", "c"])
buildConfigField(Map.class, "MAP", [a: 1, b: 2])
buildConfigField(File.class, "FILE", new File("aFile"))
buildConfigField(URI.class, "URI", uri("https://example.io"))
}The output is a Java class rather than a Kotlin object, and the shapes differ in ways that matter if you read code written against one of them. The Groovy output uses public static final for the constants, the array and map types become the Java equivalents, and the map is rendered with java.util.Map.of. The Kotlin output marks the object internal, so a Kotlin consumer in the same module sees it and a consumer outside the module does not.
That difference in visibility is a real decision rather than an accident. Internal visibility in Kotlin means the generated class is not part of your published API surface, which is usually what you want for a build-time constants object, and the plugin lets you change it.
The Groovy example also shows the type declaration being explicit, where the KTS version infers it. That is inherent to the two languages: Groovy has no static type for an arbitrary literal in this position, so the plugin asks for it.
Choosing the class name, the package and the output language
The generated file is not fixed, and the README shows the knobs in one block:
buildConfig {
className("MyConfig") // forces the class name. Defaults to 'BuildConfig'
packageName("com.foo") // forces the package. Defaults to '${project.group}'
useJavaOutput() // forces the outputType to 'java'
useKotlinOutput() // forces the outputType to 'kotlin', generating an `object`
useKotlinOutput {
topLevelConstants = true
} // forces the outputType to 'kotlin', generating top-level declarations
useKotlinOutput { internalVisibility = false } // makes `BuildConfig` class `public` (defaults to `internal`)
}The three output modes map onto three real conventions. useJavaOutput produces a Java class, which is what a Java or Android project expects. useKotlinOutput produces a Kotlin object, which is the default and the form shown in the main example. The topLevelConstants option produces top-level declarations instead, so the constants are referenced without a class qualifier.
That last mode is the one to look at if you are matching an existing codebase, because it changes how the generated code is called:
const val APP_NAME: String = "example-kts"
const val APP_VERSION: String = "0.0.1"The class name and package are also settable, and the defaults follow the project, with BuildConfig as the class name and the project group as the package. Setting a package explicitly is how the generated file lands in a package that already exists in your source tree, which matters for a multi-module build where several modules generate a class.
One small piece of behaviour in the README is worth flagging, because it is easy to trip over: if you add in your build.gradle.kts, the generated class is com.foo.MyConfig in a MyConfig.java file. The example block in the README mixes the Kotlin DSL calls with a note about a Java output, so read the two together rather than assuming every call applies to the same output mode.
Several classes from one build, and the expect/actual experiment
Two features go beyond the single generated class, and both are documented in enough detail to be usable.
The first is secondary classes. The README's framing is that sometimes one generated class does not fit your needs or your code style, and the syntax nests a forClass block:
buildConfig {
buildConfigField("FIELD1", "field1")
forClass("OtherClass") {
buildConfigField("FIELD2", "field2")
}
forClass(packageName = "", className = "RootConfig") {
buildConfigField("FIELD3", "field3")
}
}That produces three files, com.github.gmazzo.BuildConfig, com.github.gmazzo.OtherClass and RootConfig in the root package. The root package case is worth noting because the second argument of forClass is a named pair, packageName and className, so an empty package name is expressible rather than something you work around.
The second is Kotlin Multiplatform support, and it is the part to treat carefully. The README describes the feature as similar to Android's buildConfigField, where you can consume an expect BuildConfig class from common code and provide different values for each platform target:
buildConfig {
buildConfigField("COMMON_VALUE", "aCommonValue") // any constant value will be automatically 'expect'ed and propagated to the actual classes
buildConfigField("IS_MOBILE", expect(false)) // with a default
buildConfigField("PLATFORM", expect<String>()) // without a default
}The mechanism is that a constant value is automatically expect-ed and propagated to the actual classes, while expect() lets you give a field a default or leave it without one, in which case each platform has to provide it.
The catch is the word experimental in the README's own description, and the fact that the code block ends mid-example. For a shared module in a multiplatform project, the API shape is what you need, but the maturity is not settled, and the documentation for the expect and actual generation is not in the visible README. That is the part to test before you build on it.
Editorial conclusion
gradle-buildconfig-plugin fits a Gradle team that wants typed build constants derived from the build itself, with a Kotlin DSL that reads like the rest of the build script and generated code that matches the project's language, and the choice between a Kotlin object, top-level constants and a Java class covers most existing conventions. It is the wrong choice if you already have a working mechanism for the same values, since the plugin adds a code generation step to every build, and it is the wrong choice for a Kotlin Multiplatform project that needs the expect/actual support, because the README labels that support experimental. Before you adopt it, read the generated output examples in the README rather than assuming the field types you declare are the types you get, and pin the plugin version in your build script, since the version line changes with every release.
Frequently asked questions
How do I add gradle-buildconfig-plugin to a Gradle build?
In build.gradle.kts apply the plugin id `com.github.gmazzo.buildconfig` with a version alongside your language plugin, then fill in a `buildConfig` block with `buildConfigField` calls. The plugin is published on the Gradle Plugin Portal and on Maven Central.
What types can a generated build config field have?
The README's example shows String, nullable String, Boolean, IntArray, Array<String>, Map<String, Int>, File, URI and a custom data class, with the type inferred from the value in KTS and declared explicitly in Groovy.
Can I generate more than one constants class?
Yes. You can nest `forClass("OtherClass")` blocks inside the buildConfig block to generate additional classes, and `forClass(packageName = "", className = "RootConfig")` produces a class in the root package.
Does the plugin support Kotlin Multiplatform projects?
It has experimental support for Kotlin's multi-platform plugin, with an expect BuildConfig class consumable from common code and per-platform values, using buildConfigField with expect() to set a default or to require each platform to supply the value.
Can the generated class be made public instead of internal?
Yes. The README shows `useKotlinOutput { internalVisibility = false }` making the BuildConfig class public, since the default for the Kotlin output is internal visibility.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/gmazzo-gradle-buildconfig-plugin)Was this useful?