Programming

Gradle 9 ReplaceToken Configuration Cache: Dynamic Properties Guide

Learn how to properly configure ReplaceToken-filter with dynamic properties in Gradle 9 using Configuration Cache. Avoid serialization errors with gradle.properties and providers.

1 answer 1 view

How to properly configure ReplaceToken-filter with dynamic properties in Gradle 9 with Configuration Cache? The current approach using project.extra fails with the error ‘cannot serialize object of type org.gradle.api.internal.project.DefaultProject’. What is the recommended approach for property-based filtering that works with Gradle 9’s Configuration Cache?

Gradle 9’s Configuration Cache requires serializable property sources for ReplaceToken filtering, as using project.extra causes serialization errors. The recommended approach leverages gradle.properties, project.properties, or providers to dynamically supply values to the filter. This ensures compatibility with Gradle’s optimized serialization mechanism while maintaining dynamic token replacement functionality.

Contents

Understanding the Configuration Cache Error

The error “cannot serialize object of type org.gradle.api.internal.project.DefaultProject” occurs because Gradle’s Configuration Cache requires all configuration-time objects to be serializable. When using project.extra or other Project-level objects, Gradle cannot serialize these reference objects, causing cache failures. According to the official Gradle documentation, this cache improves build performance by storing configuration results, but requires all referenced objects to be serializable.

This serialization issue is particularly problematic when working with dynamic properties in resource filtering tasks. The Stack Overflow discussion highlights that any direct reference to project objects like project.extra will fail with this error in Gradle 9.

For ReplaceToken filtering in Gradle 9 with Configuration Cache enabled, there are three recommended approaches:

  1. Using gradle.properties or project.properties for simple key-value pairs
  2. Leveraging providers with project.provider for dynamic values
  3. Combining both approaches for complex scenarios

The Gradle Cookbook specifically recommends using Property and providers returned from org.gradle.api.provider.ProviderFactory for values captured from the project. These approaches work seamlessly with the ReplaceTokens filter and maintain Configuration Cache compatibility.

Using gradle.properties and project.properties

The simplest approach for serializable properties is using gradle.properties or project.properties files. Gradle automatically serializes these properties, making them cache-friendly. For example:

properties
# gradle.properties
my_token=secret_value
app_version=1.0.0

In your build.gradle, you can then reference these properties directly:

gradle
tasks.register<Copy>("processResources") {
    from("src/main/resources")
    into("$buildDir/processed")
    filtering {
        properties(mapOf(
            "TOKEN" to project.properties["my_token"],
            "VERSION" to project.properties["app_version"]
        ))
    }
}

This approach works well with ReplaceTokens filtering and is fully compatible with Gradle 9’s Configuration Cache. The properties files are automatically captured during configuration, making them ideal for static or rarely changing values.

Leveraging Providers for Dynamic Values

For dynamic values that need to be calculated at execution time, use providers. The official documentation emphasizes that Provider objects are handled specially in the serialization process and are cache-friendly.

gradle
val myToken = providers.provider { 
    System.getenv("MY_TOKEN") ?: "defaultToken" 
}

val appVersion = providers.provider { 
    "v${project.properties["version"]}" 
}

tasks.register<Copy>("processResources") {
    from("src/main/resources")
    into("$buildDir/processed")
    filtering {
        properties(mapOf(
            "TOKEN" to myToken,
            "VERSION" to appVersion
        ))
    }
}

This approach allows for dynamic values while maintaining Configuration Cache compatibility, as noted in the Gradle requirements documentation. Providers evaluate lazily at execution time, making them perfect for environment variables, calculated values, or runtime information.

Advanced Configuration with ReplaceToken

For more complex scenarios with multiple tokens, you can combine approaches. Based on the Gradle Forums discussion, you can use a map with each token:

gradle
// Define tokens in gradle.properties or as providers
val buildTokens = mapOf(
    "ENV" to providers.provider { System.getenv("ENV") ?: "dev" },
    "VERSION" to providers.provider { project.version.toString() },
    "BUILD_DATE" to providers.provider { java.time.LocalDate.now().toString() }
)

tasks.register<Copy>("processResources") {
    from("src/main/resources")
    into("$buildDir/processed")
    
    // Apply ReplaceToken filter for each token
    buildTokens.forEach { (key, value) ->
        filter(ReplaceTokens, tokens = mapOf(key to value))
    }
}

This approach provides maximum flexibility while maintaining Configuration Cache compatibility. You can mix static properties from files with dynamic providers to create comprehensive token replacement strategies.

Troubleshooting Common Issues

If you encounter serialization errors with Configuration Cache, consider these troubleshooting steps:

  1. Check for non-serializable objects: Review your build scripts for any direct references to non-serializable objects like project.extra.

  2. Use providers: Convert any dynamic values to use providers as shown above.

  3. Temporarily disable cache: For debugging, you can temporarily disable the Configuration Cache with --no-configuration-cache, but this is not a long-term solution.

  4. Update Gradle: Ensure you’re using the latest Gradle version, as Configuration Cache continues to evolve with each release.

  5. Review project dependencies: Some plugins may not be fully compatible with Configuration Cache yet. Check plugin documentation for updates.

Sources

Conclusion

Successfully configuring ReplaceToken-filter with dynamic properties in Gradle 9 requires abandoning project.extra in favor of serializable property sources. By using gradle.properties files, project.properties, or providers, you can maintain dynamic token replacement functionality while ensuring Configuration Cache compatibility. This approach not only resolves the serialization error but also leverages Gradle’s optimized caching mechanism for improved build performance. The recommended patterns provide flexibility for both static and dynamic property scenarios, making your build scripts both robust and cache-friendly.

Authors
Verified by moderation
Moderation
Gradle 9 ReplaceToken Configuration Cache: Dynamic Properties Guide