2024-07-29
Three thousand lanterns light up the long night, Illuminating the Heavenly Gate and the snowy thousand mountains. --- "Millennium of Candlelight" · Millennium of Candlelight
Gradle Plugin
org.gradle.toolchains.foojay-resolver-convention
The org.gradle.toolchains.foojay-resolver-convention plugin is a Gradle plugin designed to simplify the resolution and configuration of Java toolchains. This plugin leverages services provided by Foojay (Friends of OpenJDK) to automatically resolve and download the required JDK versions, thus simplifying the Java environment configuration for a project.
How the Plugin Works
- Toolchain Configuration: Specify the required Java version and vendor in the
build.gradlefile. - Automatic Download: If the specified JDK is not found locally during the build process, the plugin will automatically download it from the Foojay service.
- Environment Configuration: Once downloaded, the plugin will configure the JDK for the project.
Usage Example
Here is an example configuration that demonstrates how to use the plugin to automatically resolve and download the JDK:
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17) // Specify the required Java version
}
}
tasks.withType(JavaCompile).configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(17)
}
}
Explanation
- Plugin Declaration: Declare the
org.gradle.toolchains.foojay-resolver-conventionplugin in thepluginsblock. - Toolchain Configuration: Configure the required Java version in the
javablock, specifying Java 17 in this example. - Task Configuration: Specify the automatically resolved Java compiler for all
JavaCompiletasks.
Testing Automatic Download
You can test the plugin's automatic download and configuration feature by running the following command in an environment without the pre-installed specified JDK:
./gradlew compileJava
During the build process, Gradle will check if the required JDK is available locally. If not, the plugin will automatically download and configure it.
Additional Configuration Options
You can also specify more options, such as the JDK vendor:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
vendor = JvmVendorSpec.ADOPTIUM // Specify JDK vendor as Adoptium
}
}
This way, you can ensure that all development and build environments use the same JDK version and vendor, reducing issues caused by environmental differences. If you have any other questions or need further assistance, please let me know!
Reference Documentation
For more information on Java toolchains and Foojay services, refer to the Gradle Official Documentation and Foojay Official Website.
Forcing Specific Dependency Version in Gradle
This snippet forces a specific version of the jakarta.xml.bind:jakarta.xml.bind-api dependency to version 3.0.1 through Gradle's dependency resolution strategy. Here's a detailed explanation:
Detailed Configuration
configurations.all { Configuration c ->
c.resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'jakarta.xml.bind' && details.requested.name == 'jakarta.xml.bind-api') {
details.useVersion '3.0.1'
}
}
}
Key Points
- Iterate Over All Configurations:
configurations.alliterates over all configurations in the project (e.g.,compileClasspath,runtimeClasspath, etc.). - Set Resolution Strategy:
c.resolutionStrategy.eachDependencysets a resolution strategy for each dependency. - Check Dependency:
DependencyResolveDetailsobject contains information about the current dependency. You can get the group and name of the dependency throughdetails.requested.groupanddetails.requested.name. - Force Specific Version:
details.useVersion '3.0.1'forces the version of thejakarta.xml.bind:jakarta.xml.bind-apidependency to3.0.1.
Explanation
configurations.all { Configuration c -> ... }: Applies the same dependency resolution strategy to all configurations.c.resolutionStrategy.eachDependency { DependencyResolveDetails details -> ... }: Configures a resolution strategy for each dependency.if (details.requested.group == 'jakarta.xml.bind' && details.requested.name == 'jakarta.xml.bind-api') { ... }: Checks if the current dependency being resolved isjakarta.xml.bind:jakarta.xml.bind-api.details.useVersion '3.0.1': Forces the version of the matching dependency to3.0.1.
Use Cases
This code is particularly useful in the following scenarios:
- Resolving Version Conflicts: When multiple libraries depend on different versions of
jakarta.xml.bind-api, you can use this approach to force a single version, avoiding conflicts. - Ensuring Version Consistency: Ensures that all configurations in the project use the same version of
jakarta.xml.bind-api, improving consistency. - Dependency Upgrades: When you need to upgrade a specific dependency version across the project without modifying each dependency declaration individually, you can use this approach to apply the upgrade uniformly.
Summary
By using this code, you can ensure that all jakarta.xml.bind:jakarta.xml.bind-api dependencies in the project use version 3.0.1. This method is an effective way to manage dependency version consistency and resolve version conflicts.
Configuring Compiler Behavior in Gradle
This code configures compiler options for all tasks of type AbstractCompile. Specifically, it adds the -Werror and -Xlint:all compiler arguments, which affect the behavior of the Java compiler.
Code Explanation
tasks.withType(AbstractCompile).configureEach {
options.compilerArgs <<
"-Werror" <<
"-Xlint:all"
}
Key Points
- Iterate Over Compile Tasks:
tasks.withType(AbstractCompile).configureEachiterates over all tasks of typeAbstractCompile(e.g.,JavaCompile,GroovyCompile, etc.), configuring each one. - Set Compiler Arguments:
options.compilerArgsprovides a list to which you can add compiler arguments. This code adds the-Werrorand-Xlint:allarguments to this list.
Compiler Argument Explanation
-Werror: Treats all warnings as errors. If any warnings are encountered during compilation, the build will fail. This helps ensure code quality by forcing developers to resolve all warnings.-Xlint:all: Enables all compiler warnings. The compiler will report all potential issues, such as unused variables, unhandled exceptions, etc. This helps developers identify and fix potential issues early.
Use Cases
This code is particularly useful in the following scenarios:
- Improving Code Quality: By treating warnings as errors, it ensures that code compiles without any warnings, helping to maintain high code quality.
- Strict Code Review: In team development, forcing the resolution of all warnings helps team members write more standardized and safer code.
- Identifying Potential Issues: Enabling all warnings helps developers identify potential issues in the code, allowing them to fix these issues early in the development process.
Explanation
tasks.withType(AbstractCompile): Gets all tasks of typeAbstractCompile.configureEach: Configures each task.options.compilerArgs: Gets the compiler argument list.<<: Adds new compiler arguments to the list (-Werrorand-Xlint:all).
Complete Example
Assuming your project uses the Java compiler, here is a complete build.gradle file example:
plugins {
id 'java'
}
tasks.withType(AbstractCompile).configureEach {
options.compilerArgs << "-Werror" << "-Xlint:all"
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
}
Effect
By configuring this way, all compile tasks will treat warnings as errors and enable all warnings, thus improving code quality and safety.
Configuring Build Scan Values in Gradle
These two lines of code utilize Gradle's build scan feature to record the JVM version and Gradle version used during the build process. Build scans are powerful tools that capture and analyze build data, helping developers understand and optimize the build process. Specifically, these lines of code add the JVM version and Gradle version information to the build scan for later viewing and analysis.
Code Explanation
buildScan.value(identityPath.path + "#jvmVersion", testJavaRuntimeVersion)
buildScan.value(identityPath.path + "#gradleVersion", testGradleVersion)
Detailed Explanation
-
buildScan.value(key, value): This is a method provided by the Gradle build scan API to add custom values to the build scan.keyis the key for the custom value, andvalueis the content of the custom value. -
identityPath.path + "#jvmVersion"andidentityPath.path + "#gradleVersion":identityPath.path: This is a built-in Gradle property representing the identity path of the current task or project. It is often used to generate unique keys
to ensure that build scan data does not conflict across different tasks or projects.
#jvmVersionand#gradleVersion: These are custom suffixes used to distinguish the recorded values, representing the JVM version and Gradle version, respectively.
testJavaRuntimeVersionandtestGradleVersion:testJavaRuntimeVersion: This is a string representing the Java runtime version used for testing. This value is obtained from project properties or default values in the preceding code.testGradleVersion: This is a string representing the Gradle version used for testing. This value is also obtained from project properties or the current Gradle version in the preceding code.
Purpose and Significance
By adding the JVM version and Gradle version information to the build scan, you can view these details in the build scan report after the build is complete. This is particularly useful in the following situations:
- Debugging and Troubleshooting: When a build fails or encounters issues, knowing the JVM and Gradle versions used can help quickly pinpoint the root cause.
- Performance Analysis: When analyzing build performance, knowing the environment information (such as JVM and Gradle versions) can help optimize the build process.
- Build History Tracking: Recording and tracking the versions used can help maintain build consistency and reproducibility.
Example Explanation
Suppose your build scan report includes the following information:
- Task Path:
:compileJava - JVM Version:
21 - Gradle Version:
7.4
In the build scan, you might see custom values like the following:
:compileJava#jvmVersion:21:compileJava#gradleVersion:7.4
These details help you clearly see the JVM and Gradle versions used for each task in the build scan report, aiding in better understanding and optimizing the build process.
Configuring Javadoc Options in Gradle
This code configures additional Javadoc options for all tasks of type Javadoc. Specifically, it adds the -Xdoclint:none and -quiet options. Here is a detailed explanation:
Code Explanation
tasks.withType(Javadoc).configureEach {
options.addStringOption('Xdoclint:none', '-quiet')
}
Key Points
- Iterate Over Javadoc Tasks:
tasks.withType(Javadoc).configureEachiterates over all tasks of typeJavadoc, configuring each one. - Configure Javadoc Options: Adds specific options to the Javadoc tasks using the
options.addStringOptionmethod.
Javadoc Options Explanation
-Xdoclint:none: Disables all Javadoc lint checks. This means that when generating Javadoc, no strict checks will be performed on the content of the comments, such as format, spelling, and tag usage. This is useful for quickly generating Javadoc or generating documentation when comments are incomplete.-quiet: Minimizes the output from the Javadoc tool during document generation. This reduces noise in the build logs, especially for large projects.
Use Cases
This code is particularly useful in the following scenarios:
- Quickly Generating Javadoc: Quickly generating Javadoc without strict checks on the comment content.
- Reducing Build Log Noise: Using the
-quietoption to reduce output during Javadoc generation, making the build logs cleaner. - Resolving Existing Comment Issues: If there are many incomplete or improperly formatted Javadoc comments in the project, disabling lint checks can prevent build failures or numerous warnings.
Explanation
tasks.withType(Javadoc): Gets all tasks of typeJavadoc.configureEach: Configures each task.options.addStringOption('Xdoclint:none', '-quiet'): Adds the-Xdoclint:noneand-quietoptions to the Javadoc tasks.
Complete Example
Assuming your project needs to add these options to all Javadoc tasks, you can add the following code to your build.gradle file:
plugins {
id 'java'
}
tasks.withType(Javadoc).configureEach {
options.addStringOption('Xdoclint:none', '-quiet')
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
}
Effect
When you run the ./gradlew javadoc task, the Javadoc tool will generate the documentation without performing lint checks and with minimal output. The generated Javadoc might contain improperly formatted comments, but the build process will not fail or generate numerous warnings.
Configuring Plugin Validation Behavior in Gradle
This code configures two options for all tasks of type ValidatePlugins: failOnWarning and enableStricterValidation. Specifically, it sets the task to fail if any warnings occur during plugin validation and enables stricter validation rules. Here is a detailed explanation:
Code Explanation
tasks.withType(ValidatePlugins.class).configureEach {
failOnWarning = true
enableStricterValidation = true
}
Key Points
- Iterate Over ValidatePlugins Tasks:
tasks.withType(ValidatePlugins.class).configureEachiterates over all tasks of typeValidatePlugins, configuring each one. - Configure Validation Options: Sets the
failOnWarningandenableStricterValidationoptions to control the behavior during plugin validation.
Options Explanation
failOnWarning = true: Setting this option totruemeans that if any warnings occur during plugin validation, the task will fail. This helps ensure that the plugin meets higher quality standards and that all potential issues are resolved before release.enableStricterValidation = true: Enables stricter validation rules, meaning the plugin will undergo more rigorous checks during validation to ensure its definition and configuration meet best practices and the latest standards.
Use Cases
This code is particularly useful in the following scenarios:
- Improving Plugin Quality: Ensures that the plugin meets high-quality standards by failing the task if any warnings occur and enabling stricter validation rules.
- Identifying Potential Issues: Helps identify and resolve configuration issues during the development process, avoiding problems after release.
- Following Best Practices: Ensures that the plugin's definition and configuration adhere to Gradle's latest standards and best practices.
Explanation
tasks.withType(ValidatePlugins.class): Gets all tasks of typeValidatePlugins.configureEach: Configures each task.failOnWarning = true: Fails the task if any warnings occur.enableStricterValidation = true: Enables stricter validation rules.
Complete Example
Assuming your project needs to add these options to all ValidatePlugins tasks, you can add the following code to your build.gradle file:
plugins {
id 'java-gradle-plugin'
id 'com.gradle.plugin-publish' version '1.2.1'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
gradlePlugin {
plugins {
simplePlugin {
id = 'com.example.simple-plugin'
implementationClass = 'com.example.SimplePlugin'
}
}
}
tasks.withType(ValidatePlugins.class).configureEach {
failOnWarning = true
enableStricterValidation = true
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
}
pluginBundle {
website = 'https://github.com/example/simple-plugin'
vcsUrl = 'https://github.com/example/simple-plugin'
description = 'A simple Gradle plugin'
tags = ['gradle', 'plugin', 'example']
}
publishing {
publications {
pluginMaven(MavenPublication) {
from components.java
}
}
}
Effect
When you run the ./gradlew validatePlugins task, Gradle will validate your plugin configuration. If any warnings occur during validation, the task will fail. Additionally, stricter validation rules will ensure that your plugin configuration adheres to the latest standards and best practices.
Registering Tasks in Gradle
Detailed Explanation of TaskProvider<JooqGenerate> jooq = project.getTasks().register(taskName, JooqGenerate.class, config, jooqGeneratorRuntimeConfiguration, project.getExtensions());
This code registers a new JooqGenerate task in Gradle. JooqGenerate is a custom task class used to generate jOOQ source code. Here is a detailed explanation of this code:
Code Breakdown
-
project.getTasks().register:project: The current Gradle project object.getTasks(): Gets the task container for the current project.register: Registers a new task.
-
taskName:- The dynamically generated task name, such as
"generateMainJooq"or"generateTestJooq", depending on the configuration name.
- The dynamically generated task name, such as
-
JooqGenerate.class:- The task type is
JooqGenerate, meaning the registered task will be of typeJooqGenerate.
- The task type is
-
config:- A
JooqConfiginstance containingjOOQconfiguration information. This configuration will be passed to theJooqGeneratetask instance.
- A
-
jooqGeneratorRuntimeConfiguration
:
- The runtime configuration for the
jOOQgenerator, typically aFileCollectioncontaining the classpath needed to generatejOOQsource code.
project.getExtensions():- The extension container for the current project, used to get and configure project extensions.
Detailed Explanation
TaskProvider<JooqGenerate> jooq registers a new JooqGenerate task using the project.getTasks().register method and binds it to a TaskProvider. TaskProvider is a way to configure and execute tasks lazily, ensuring that tasks are only configured and executed when actually needed.
Constructor of JooqGenerate Class
In the JooqGenerate class, the constructor is defined as follows:
@Inject
public JooqGenerate(JooqConfig config, FileCollection runtimeClasspath, ExtensionContainer extensions, ObjectFactory objects, ProviderFactory providers, ProjectLayout projectLayout, ExecOperations execOperations, FileSystemOperations fileSystemOperations) {
// Initialize properties
}
Parameter Explanation
config: AJooqConfiginstance containingjOOQconfiguration information.runtimeClasspath: The runtime classpath, of typeFileCollection.extensions: The extension container, of typeExtensionContainer.objects: The object factory, of typeObjectFactory, used to create Gradle-provided objects (such asPropertyandProvider).providers: The provider factory, of typeProviderFactory, used to create and manipulateProviderinstances.projectLayout: The project layout, of typeProjectLayout, containing information about the project directory and file layout.execOperations: The execution operations, of typeExecOperations, used to execute external processes.fileSystemOperations: The file system operations, of typeFileSystemOperations, used to manipulate the file system (such as deleting directories).
Functionality
-
Register Task:
- Registers a new
JooqGeneratetask in the Gradle project using theproject.getTasks().registermethod.
- Registers a new
-
Pass Task Parameters:
- Passes the
config,runtimeClasspath, andextensionsparameters to theJooqGeneratetask constructor.
- Passes the
-
Lazy Configuration:
TaskProviderallows lazy configuration of the task, ensuring that tasks are only configured and executed when actually needed, improving the performance and efficiency of the Gradle build script.
Practical Task Configuration Example
Assuming you have a build.gradle file configured as follows:
plugins {
id 'nu.studer.jooq' version '5.2'
}
jooq {
version = '3.14.0'
edition = 'OSS'
configurations {
main { // Corresponding configuration name
generateSchemaSourceOnCompilation = true
jdbc {
driver = 'org.h2.Driver'
url = 'jdbc:h2:~/test'
user = 'sa'
password = ''
}
generator {
name = 'org.jooq.codegen.DefaultGenerator'
database {
name = 'org.jooq.meta.h2.H2Database'
inputSchema = 'PUBLIC'
}
target {
packageName = 'org.jooq.example'
directory = 'src/generated/jooq'
}
}
}
}
}
In this example, the JooqGenerate task will generate jOOQ source code based on the jooq configuration and place the generated source code in the src/generated/jooq directory.
Summary
This code registers a new JooqGenerate task using TaskProvider and passes the necessary configuration information to the task instance, allowing it to generate jOOQ source code. TaskProvider enables lazy configuration of the task, improving the performance and efficiency of the Gradle build script.
Gradle NamedDomainObjectContainer
To implement a custom Gradle plugin that allows defining a DSL similar to code { configurations { main { ... } } } in the build.gradle file, you need to follow these steps:
1. Create Extension Class
First, create an extension class to define the configuration items in the DSL.
CodeConfig.java
package com.example;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.model.ObjectFactory;
import javax.inject.Inject;
public class CodeConfig {
private final NamedDomainObjectContainer<Configuration> configurations;
@Inject
public CodeConfig(ObjectFactory objects) {
this.configurations = objects.domainObjectContainer(Configuration.class);
}
public NamedDomainObjectContainer<Configuration> getConfigurations() {
return configurations;
}
public static class Configuration {
private final Property<String> a;
private final Property<String> b;
@Inject
public Configuration(ObjectFactory objects) {
this.a = objects.property(String.class);
this.b = objects.property(String.class);
}
public Property<String> getA() {
return a;
}
public Property<String> getB() {
return b;
}
}
}
2. Create Plugin Class
Next, create a plugin class to register and configure the extension.
CodePlugin.java
package com.example;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class CodePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
// Register the 'code' extension
CodeConfig extension = project.getExtensions().create("code", CodeConfig.class, project.getObjects());
// Configure tasks or other behavior based on the extension
project.afterEvaluate(proj -> {
extension.getConfigurations().forEach(config -> {
// Example: Print configuration values
System.out.println("Configuration: " + config.getA().get() + ", " + config.getB().get());
// You can create tasks or apply configurations based on these values
});
});
}
}
3. Apply Plugin and Configure DSL
Apply the plugin and configure the DSL in the build.gradle file.
build.gradle
plugins {
id 'com.example.code-plugin'
}
code {
configurations {
main {
a = '1'
b = 'abcd'
}
// You can define more configurations here
test {
a = '2'
b = 'efgh'
}
}
}
4. Include Plugin Path in settings.gradle
Ensure to include the plugin path in settings.gradle for local development.
settings.gradle
includeBuild 'path/to/your/plugin'
Summary
By following the steps above, you created a custom Gradle plugin that allows defining a DSL similar to code { configurations { main { ... } } } in the build.gradle file. This plugin supports multiple configuration items and allows accessing and using these configuration items after evaluating the build.gradle file.
NamedDomainObjectContainer is a Gradle container used to manage a group of objects with unique names. It extends NamedDomainObjectCollection and adds functionality that makes it very suitable for DSL configuration. For example, NamedDomainObjectContainer is often used in plugin development to provide DSL blocks like configurations { ... } or sourceSets { ... }.
Key Features
- Named Object Management: Each object has a unique name, making it easy to access and manage these objects by name.
- DSL Support: Provides a natural way to configure objects through closures or configuration blocks.
- Automatic Creation: Automatically creates and configures new objects when referenced by name.
Example Explanation
Suppose we are developing a custom plugin that includes a NamedDomainObjectContainer to manage a group of objects named Configuration.
1. Define Configuration Class
First, define a simple Configuration class with some configurable properties.
package com.example;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import javax.inject.Inject;
public class Configuration {
private final Property<String> a;
private final Property<String> b;
@Inject
public Configuration(ObjectFactory objects) {
this.a = objects.property(String.class);
this.b = objects.property(String.class);
}
public Property<String> getA() {
return a;
}
public Property<String> getB() {
return b;
}
}
2. Create CodeConfig Extension Class
Next, create an extension class CodeConfig that contains a NamedDomainObjectContainer<Configuration> to manage Configuration objects.
package com.example;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.model.ObjectFactory;
import javax.inject.Inject;
public class CodeConfig {
private final NamedDomainObjectContainer<Configuration> configurations;
@Inject
public CodeConfig(ObjectFactory objects) {
this.configurations = objects.domainObjectContainer(Configuration.class);
}
public NamedDomainObjectContainer<Configuration> getConfigurations() {
return configurations;
}
}
3. Create Plugin Class
Next, create a plugin class CodePlugin to register the CodeConfig extension and process the configuration after the project is evaluated.
package com.example;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class CodePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
// Register the 'code' extension
CodeConfig extension = project.getExtensions().create("code", CodeConfig.class,
project.getObjects());
// Configure tasks or other behavior based on the extension
project.afterEvaluate(proj -> {
extension.getConfigurations().forEach(config -> {
// Example: Print configuration values
System.out.println("Configuration: " + config.getA().get() + ", " + config.getB().get());
// You can create tasks or apply configurations based on these values
});
});
}
}
4. Configure Plugin
Finally, apply the plugin and configure the configurations in the build.gradle file.
plugins {
id 'com.example.code-plugin'
}
code {
configurations {
main {
a = '1'
b = 'abcd'
}
test {
a = '2'
b = 'efgh'
}
}
}
How It Works
- Object Creation and Configuration: When
mainandtestare referenced in theconfigurationsblock,NamedDomainObjectContainerautomatically creates theseConfigurationobjects. - DSL Support: Configures the properties of these objects using closure blocks, e.g.,
aandb. - Lazy Evaluation: After the project is evaluated, the plugin can iterate over the
configurationscontainer and process all defined configurations.
Summary
NamedDomainObjectContainer is a powerful tool in Gradle for managing a group of named objects, especially suitable for developing plugins that require DSL configuration. It provides a natural way to create and configure objects through closures or configuration blocks, making plugin configuration more intuitive and flexible.