cowwoc commented on code in PR #389: URL: https://github.com/apache/maven-build-cache-extension/pull/389#discussion_r4446575086
########## src/main/java/org/apache/maven/buildcache/xml/PluginParameterLoader.java: ########## @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.buildcache.xml; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.maven.buildcache.xml.PluginParameterDefinition.GoalParameterDefinition; +import org.apache.maven.buildcache.xml.PluginParameterDefinition.ParameterDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * Loads plugin parameter definitions from classpath resources. + * Definitions are stored in src/main/resources/plugin-parameters/{artifactId}.xml + */ +class PluginParameterLoader { + + private static final Logger LOGGER = LoggerFactory.getLogger(PluginParameterLoader.class); + private static final String PARAMETER_DIR = "plugin-parameters/"; + + private final Map<String, PluginParameterDefinition> definitions = new ConcurrentHashMap<>(); + + /** + * Load parameter definitions for a plugin by artifact ID only (no version matching) + */ + PluginParameterDefinition load(String artifactId) { + return load(artifactId, null); + } + + /** + * Load parameter definitions for a plugin by artifact ID and version. + * If version is provided, finds the best matching definition (highest minVersion <= actual version). + * If version is null, returns any definition for the artifactId. + */ + PluginParameterDefinition load(String artifactId, String pluginVersion) { + String cacheKey = artifactId + (pluginVersion != null ? ":" + pluginVersion : ""); + + if (definitions.containsKey(cacheKey)) { + return definitions.get(cacheKey); + } + + String resourcePath = PARAMETER_DIR + artifactId + ".xml"; + InputStream resourceStream = getClass().getClassLoader().getResourceAsStream(resourcePath); + + if (resourceStream == null) { + LOGGER.debug("No parameter definition found for plugin: {}", artifactId); + return null; + } + + try (InputStream inputStream = resourceStream) { + List<PluginParameterDefinition> allDefinitions = parseDefinitions(inputStream); + + PluginParameterDefinition bestMatch = findBestMatch(allDefinitions, pluginVersion); + + if (bestMatch != null) { + definitions.put(cacheKey, bestMatch); + LOGGER.debug( + "Loaded parameter definition for {}:{} (minVersion: {}): {} goals, {} total parameters", + artifactId, + pluginVersion != null ? pluginVersion : "any", + bestMatch.getMinVersion() != null ? bestMatch.getMinVersion() : "none", + bestMatch.getGoals().size(), + bestMatch.getGoals().values().stream() + .mapToInt(goalDefinition -> + goalDefinition.getParameters().size()) + .sum()); + } + + return bestMatch; + } catch (ParserConfigurationException | SAXException | IOException e) { + LOGGER.warn("Failed to load parameter definition for {}: {}", artifactId, e.getMessage(), e); + return null; + } + } + + /** + * Find the best matching definition for a plugin version. + * Returns the definition with the highest minVersion that is <= pluginVersion. + * If pluginVersion is null, returns the first definition (or the one without minVersion). + */ + private PluginParameterDefinition findBestMatch(List<PluginParameterDefinition> definitions, String pluginVersion) { + if (definitions.isEmpty()) { + return null; + } + + if (pluginVersion == null) { + // No version specified, prefer definition without minVersion, otherwise return first + return definitions.stream() + .filter(definition -> definition.getMinVersion() == null) + .findFirst() + .orElse(definitions.get(0)); + } + + // Find highest minVersion that's <= pluginVersion + PluginParameterDefinition bestMatch = null; + String bestMinVersion = null; + + for (PluginParameterDefinition definition : definitions) { + String minVersion = definition.getMinVersion(); + + // Definition without minVersion applies to all versions + if (minVersion == null) { + if (bestMatch == null) { + bestMatch = definition; + } + continue; + } + + // Check if this definition applies to the plugin version + if (compareVersions(minVersion, pluginVersion) <= 0) { + // minVersion <= pluginVersion, so this definition applies + if (bestMinVersion == null || compareVersions(minVersion, bestMinVersion) > 0) { + // This is a better match (higher minVersion) + bestMatch = definition; + bestMinVersion = minVersion; + } + } + } + + return bestMatch; + } + + /** + * Compare two version strings. + * Returns: negative if the first version is older, zero if equal, positive if newer. + */ + private int compareVersions(String firstVersion, String secondVersion) { + String[] firstVersionParts = firstVersion.split("\\."); + String[] secondVersionParts = secondVersion.split("\\."); + + int maxLength = Math.max(firstVersionParts.length, secondVersionParts.length); + + for (int i = 0; i < maxLength; i++) { + int firstPart = i < firstVersionParts.length ? parseVersionPart(firstVersionParts[i]) : 0; + int secondPart = i < secondVersionParts.length ? parseVersionPart(secondVersionParts[i]) : 0; + + if (firstPart != secondPart) { + return Integer.compare(firstPart, secondPart); + } + } + + return 0; + } + + private int parseVersionPart(String part) { + try { + // Handle qualifiers like "3.8.0-SNAPSHOT" - just use numeric part + int dashIndex = part.indexOf('-'); + if (dashIndex > 0) { + part = part.substring(0, dashIndex); + } + return Integer.parseInt(part); + } catch (NumberFormatException e) { + return 0; + } + } + + /** + * Parse plugin parameter definitions from XML. + * Supports multiple <plugin> elements in a single file for version-specific definitions. + */ + private List<PluginParameterDefinition> parseDefinitions(InputStream inputStream) + throws ParserConfigurationException, SAXException, IOException { + List<PluginParameterDefinition> definitions = new ArrayList<>(); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(inputStream); + + Element root = document.getDocumentElement(); + + // Check if root is a single <plugin> or if we need to look for multiple + if ("plugin".equals(root.getLocalName())) { + // Single plugin definition + definitions.add(parsePluginDefinition(root)); + } else { + // Look for multiple <plugin> elements + NodeList pluginNodes = root.getElementsByTagNameNS("*", "plugin"); + for (int i = 0; i < pluginNodes.getLength(); i++) { + Element pluginElement = (Element) pluginNodes.item(i); + definitions.add(parsePluginDefinition(pluginElement)); + } + } + + return definitions; + } + + private PluginParameterDefinition parsePluginDefinition(Element pluginElement) { + String groupId = getTextContent(pluginElement, "groupId"); + String actualArtifactId = getTextContent(pluginElement, "artifactId"); + String minVersion = getTextContent(pluginElement, "minVersion"); + + PluginParameterDefinition definition = new PluginParameterDefinition(groupId, actualArtifactId, minVersion); + + NodeList goalsNodes = pluginElement.getElementsByTagNameNS("*", "goals"); + if (goalsNodes.getLength() > 0) { + Element goalsElement = (Element) goalsNodes.item(0); + NodeList goalNodes = goalsElement.getElementsByTagNameNS("*", "goal"); + + for (int i = 0; i < goalNodes.getLength(); i++) { + Element goalElement = (Element) goalNodes.item(i); + parseGoal(goalElement, definition); + } + } + + return definition; + } + + private void parseGoal(Element goalElement, PluginParameterDefinition definition) { + String goalName = getTextContent(goalElement, "name"); + GoalParameterDefinition goal = new GoalParameterDefinition(goalName); + + NodeList parametersNodes = goalElement.getElementsByTagNameNS("*", "parameters"); + if (parametersNodes.getLength() > 0) { + Element parametersElement = (Element) parametersNodes.item(0); + NodeList parameterNodes = parametersElement.getElementsByTagNameNS("*", "parameter"); + + for (int i = 0; i < parameterNodes.getLength(); i++) { + Element parameterElement = (Element) parameterNodes.item(i); + ParameterDefinition parameterDefinition = parseParameter(parameterElement); + goal.addParameter(parameterDefinition); + } + } + + definition.addGoal(goalName, goal); + } + + private ParameterDefinition parseParameter(Element parameterElement) { + String name = getTextContent(parameterElement, "name"); + boolean cacheKey = Boolean.parseBoolean(getTextContent(parameterElement, "cache-key")); Review Comment: Fixed in f8dc720: descriptor parsing now accepts both XML boolean spellings (`true`/`1` and `false`/`0`) and rejects invalid values. Added a loader test covering all four spellings. ########## src/main/resources/plugin-parameters/maven-compiler-plugin.xml: ########## @@ -0,0 +1,318 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<plugin xmlns="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0 plugin-parameters.xsd"> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <minVersion>3.11.0</minVersion> + + <goals> + <goal> + <name>compile</name> + <parameters> + <!-- cache-key=true entries explain why a value change must cause a cache miss. --> + <parameter> + <name>source</name> + <cache-key>true</cache-key> + <description>Source JDK version for compilation; changing it can change generated bytecode or compilation success</description> + </parameter> + <parameter> + <name>target</name> + <cache-key>true</cache-key> + <description>Target JDK version for compiled bytecode; changing it changes generated bytecode</description> + </parameter> + <parameter> + <name>release</name> + <cache-key>true</cache-key> + <description>JDK release version (combines source and target); changing it changes generated bytecode and API compatibility</description> + </parameter> + <parameter> + <name>encoding</name> + <cache-key>true</cache-key> + <description>Source file encoding; changing it can change parsed source and generated bytecode</description> + </parameter> + <parameter> + <name>debug</name> + <cache-key>true</cache-key> + <description>Include debugging information in compiled bytecode; changing it changes artifact contents</description> + </parameter> + <parameter> + <name>debuglevel</name> + <cache-key>true</cache-key> + <description>Level of debugging information (lines, vars, source); changing it changes artifact contents</description> + </parameter> + <parameter> + <name>optimize</name> + <cache-key>true</cache-key> + <description>Optimize compiled bytecode; changing it changes artifact contents</description> + </parameter> + <parameter> + <name>compilerArgs</name> + <cache-key>true</cache-key> + <description>Additional compiler arguments; changing them can change generated bytecode or compilation success</description> + </parameter> + <parameter> + <name>compilerArgument</name> + <cache-key>true</cache-key> + <description>Single additional compiler argument; changing it can change generated bytecode or compilation success</description> + </parameter> + <parameter> + <name>annotationProcessorPaths</name> + <cache-key>true</cache-key> + <description>Classpath for annotation processors; changing it can change generated sources or compilation success</description> + </parameter> + <parameter> + <name>annotationProcessors</name> + <cache-key>true</cache-key> + <description>Annotation processors to run; changing them can change generated sources or compilation success</description> + </parameter> + <parameter> + <name>proc</name> + <cache-key>true</cache-key> + <description>Annotation processing mode (none, only, proc); changing it can change generated sources or compilation success</description> + </parameter> + <parameter> + <name>executable</name> + <cache-key>true</cache-key> + <description>Path to javac executable; changing it can change the compiler output or compilation success</description> + </parameter> + <parameter> + <name>parameters</name> + <cache-key>true</cache-key> + <description>Generate metadata for method parameters; changing it changes bytecode contents</description> + </parameter> + <parameter> + <name>enablePreview</name> + <cache-key>true</cache-key> + <description>Enable preview features; changing it can change generated bytecode or compilation success</description> + </parameter> + + <!-- No cache-key annotation means false; these settings do not invalidate the cache. --> + <parameter> + <name>verbose</name> + <description>Verbose compiler output</description> + </parameter> + <parameter> + <name>showWarnings</name> + <cache-key>true</cache-key> + <description>Show compilation warnings (can change the build outcome with failOnWarning)</description> + </parameter> + <parameter> + <name>showDeprecation</name> + <cache-key>true</cache-key> + <description>Show deprecation warnings (can change the build outcome with failOnWarning)</description> + </parameter> + <parameter> + <name>skipMain</name> + <cache-key>true</cache-key> + <description>Skip compiling main sources; changing it changes whether main bytecode is produced</description> + </parameter> + <parameter> + <name>failOnError</name> + <cache-key>true</cache-key> + <description>Fail build on compilation error (changes the build outcome)</description> + </parameter> + <parameter> + <name>failOnWarning</name> + <cache-key>true</cache-key> + <description>Fail build on compilation warning (changes the build outcome)</description> + </parameter> + <parameter> + <name>fork</name> + <cache-key>true</cache-key> + <description>Fork compiler into separate process (can change the compiler, output, or outcome)</description> + </parameter> + <parameter> + <name>maxmem</name> + <cache-key>true</cache-key> + <description>Maximum memory for compiler process (can change the build outcome)</description> + </parameter> + <parameter> + <name>meminitial</name> + <cache-key>true</cache-key> + <description>Initial memory for compiler process (can change the build outcome)</description> + </parameter> + <parameter> + <name>compilerReuseStrategy</name> + <description>Strategy for reusing compiler instances</description> + </parameter> + <parameter> + <name>forceJavacCompilerUse</name> + <cache-key>true</cache-key> + <description>Force use of javac compiler; changing it can affect compiler output</description> + </parameter> + <parameter> + <name>staleMillis</name> + <cache-key>true</cache-key> + <description>Staleness check interval for incremental compilation (can change generated files)</description> + </parameter> + <parameter> + <name>useIncrementalCompilation</name> + <cache-key>true</cache-key> + <description>Enable incremental compilation (can change generated files and build outcome)</description> + </parameter> + </parameters> + </goal> + + <goal> + <name>testCompile</name> + <parameters> + <!-- Same parameters as compile goal --> + <parameter> + <name>source</name> + <cache-key>true</cache-key> + <description>Source JDK version for test compilation; changing it can change generated test bytecode or compilation success</description> + </parameter> + <parameter> + <name>target</name> + <cache-key>true</cache-key> + <description>Target JDK version for compiled test bytecode; changing it changes generated test bytecode</description> + </parameter> + <parameter> + <name>release</name> + <cache-key>true</cache-key> + <description>JDK release version for tests; changing it changes generated test bytecode and API compatibility</description> + </parameter> Review Comment: Fixed in f8dc720: `testCompile` now tracks `testSource`, `testTarget`, and `testRelease` alongside the inherited fields. An integration test changes only `maven.compiler.testRelease` and verifies reconciliation rejects the stale cache entry. ########## src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java: ########## @@ -1055,6 +1059,30 @@ private void recordMojoProperties(CompletedExecution execution, MojoExecutionEve } } + private Object normalizeMojoProperty( + String propertyName, Object value, MojoExecution mojoExecution, MavenProject project) { + if (!(value instanceof List) + || !"compilerArgs".equals(propertyName) + || mojoExecution.getPlugin() == null + || !"maven-compiler-plugin".equals(mojoExecution.getPlugin().getArtifactId())) { + return value; + } + + List<?> compilerArguments = (List<?>) value; + List<Object> normalizedArguments = new ArrayList<>(compilerArguments.size()); + for (int i = 0; i < compilerArguments.size(); i++) { + Object argument = compilerArguments.get(i); + if ("--module-version".equals(argument) + && i + 1 < compilerArguments.size() + && Objects.equals(project.getVersion(), compilerArguments.get(i + 1))) { + i++; + } else { + normalizedArguments.add(argument); + } + } + return normalizedArguments.isEmpty() ? null : normalizedArguments; Review Comment: Fixed in f8dc720: tracked values are captured as normalized strings before mojo execution, and cache comparison uses the same normalization. The `--module-version` removal is gone; unit tests cover empty arguments and an explicitly configured module-version pair, and the issue #375 integration test passes. ########## src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java: ########## @@ -253,27 +256,181 @@ public boolean isLogAllProperties(MojoExecution mojoExecution) { } private GoalReconciliation findReconciliationConfig(MojoExecution mojoExecution) { - if (cacheConfig.getExecutionControl() == null) { + if (mojoExecution == null) { return null; } - final ExecutionControl executionControl = cacheConfig.getExecutionControl(); - if (executionControl.getReconcile() == null) { + final String goal = mojoExecution.getGoal(); + final Plugin plugin = mojoExecution.getPlugin(); + + if (plugin == null) { return null; } - final List<GoalReconciliation> reconciliation = - executionControl.getReconcile().getPlugins(); + // First check explicit configuration + if (cacheConfig.getExecutionControl() != null + && cacheConfig.getExecutionControl().getReconcile() != null) { + List<GoalReconciliation> explicitConfigs = + cacheConfig.getExecutionControl().getReconcile().getPlugins(); + for (GoalReconciliation config : explicitConfigs) { + if (isPluginMatch(plugin, config) && Strings.CS.equals(goal, config.getGoal())) { + // Validate explicit config against parameter definitions (with version) + validateReconciliationConfig(config, plugin); + return config; + } + } + } - for (GoalReconciliation goalReconciliationConfig : reconciliation) { - final String goal = mojoExecution.getGoal(); + // Auto-generate from parameter definitions (track all cache-key parameters) + GoalReconciliation autoGenerated = generateReconciliationFromParameters(plugin, goal); + if (autoGenerated != null) { + LOGGER.debug( + "Auto-generated reconciliation config for {}:{} with {} cache-key parameters", + plugin.getArtifactId(), + goal, + autoGenerated.getReconciles() != null + ? autoGenerated.getReconciles().size() + : 0); + } + return autoGenerated; + } - if (isPluginMatch(mojoExecution.getPlugin(), goalReconciliationConfig) - && Strings.CS.equals(goal, goalReconciliationConfig.getGoal())) { - return goalReconciliationConfig; + /** + * Validates a single reconciliation config against plugin parameter definitions. + * Uses plugin version to load the appropriate parameter definition. + */ + private void validateReconciliationConfig(GoalReconciliation config, Plugin plugin) { + String artifactId = config.getArtifactId(); + String goal = config.getGoal(); + String pluginVersion = plugin.getVersion(); + + // Load parameter definition for this plugin with version + PluginParameterDefinition pluginDefinition = parameterLoader.load(artifactId, pluginVersion); + + if (pluginDefinition == null) { + LOGGER.warn( + "No parameter definition found for plugin {}:{} version {}. " + + "Cannot validate reconciliation configuration. " + + "Consider adding a parameter definition file to plugin-parameters/{}.xml", + artifactId, + goal, + pluginVersion != null ? pluginVersion : "unknown", + artifactId); + return; + } + + // Get goal definition + GoalParameterDefinition goalDefinition = pluginDefinition.getGoal(goal); + if (goalDefinition == null) { + LOGGER.warn( + "Goal '{}' not found in parameter definition for plugin {} version {}. " + + "Cannot validate reconciliation configuration.", + goal, + artifactId, + pluginVersion != null ? pluginVersion : "unknown"); + return; + } + + // Validate each tracked property + List<TrackedProperty> properties = config.getReconciles(); + + for (TrackedProperty property : properties) { + String propertyName = property.getPropertyName(); + + if (!goalDefinition.hasParameter(propertyName)) { + LOGGER.warn( + "Unknown parameter '{}' in reconciliation config for {}:{} version {}. " + + "This may indicate a plugin version mismatch or renamed parameter. " + + "Consider updating parameter definition or removing from reconciliation.", + propertyName, + artifactId, + goal, + pluginVersion != null ? pluginVersion : "unknown"); + } else { + ParameterDefinition parameterDefinition = goalDefinition.getParameter(propertyName); + if (!parameterDefinition.isCacheKey()) { + LOGGER.warn( + "Parameter '{}' in reconciliation config for {}:{} is not marked as a cache key. " + + "Changing it will not invalidate the cache according to the plugin definition.", + propertyName, + artifactId, + goal); + } } } - return null; + } + + /** + * Auto-generates a reconciliation config by tracking all cache-key parameters + * from the plugin parameter definition. + * This provides automatic default tracking for any plugin with parameter definitions. + * + * @param plugin The plugin to generate config for + * @param goal The goal name + * @return Auto-generated config tracking all cache-key parameters, or null if no parameter definition exists + */ + private GoalReconciliation generateReconciliationFromParameters(Plugin plugin, String goal) { + String artifactId = plugin.getArtifactId(); + String pluginVersion = plugin.getVersion(); + + LOGGER.debug( + "Attempting to auto-generate reconciliation config for {}:{} version {}", + artifactId, + goal, + pluginVersion); + + // Load parameter definition for this plugin + PluginParameterDefinition pluginDefinition = parameterLoader.load(artifactId, pluginVersion); + if (pluginDefinition == null) { Review Comment: Fixed in f8dc720: descriptor lookup now verifies group ID and artifact ID before version selection. Both automatic reconciliation and explicit-config validation use that lookup; a unit test rejects a `com.example:maven-compiler-plugin` collision. ########## src/main/resources/plugin-parameters/maven-compiler-plugin.xml: ########## @@ -0,0 +1,318 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<plugin xmlns="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0 plugin-parameters.xsd"> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <minVersion>3.11.0</minVersion> + + <goals> + <goal> + <name>compile</name> + <parameters> + <!-- cache-key=true entries explain why a value change must cause a cache miss. --> + <parameter> + <name>source</name> + <cache-key>true</cache-key> + <description>Source JDK version for compilation; changing it can change generated bytecode or compilation success</description> + </parameter> + <parameter> + <name>target</name> + <cache-key>true</cache-key> + <description>Target JDK version for compiled bytecode; changing it changes generated bytecode</description> + </parameter> + <parameter> + <name>release</name> + <cache-key>true</cache-key> + <description>JDK release version (combines source and target); changing it changes generated bytecode and API compatibility</description> + </parameter> + <parameter> + <name>encoding</name> + <cache-key>true</cache-key> + <description>Source file encoding; changing it can change parsed source and generated bytecode</description> + </parameter> + <parameter> + <name>debug</name> + <cache-key>true</cache-key> + <description>Include debugging information in compiled bytecode; changing it changes artifact contents</description> + </parameter> + <parameter> + <name>debuglevel</name> + <cache-key>true</cache-key> + <description>Level of debugging information (lines, vars, source); changing it changes artifact contents</description> + </parameter> + <parameter> + <name>optimize</name> + <cache-key>true</cache-key> + <description>Optimize compiled bytecode; changing it changes artifact contents</description> + </parameter> + <parameter> + <name>compilerArgs</name> + <cache-key>true</cache-key> + <description>Additional compiler arguments; changing them can change generated bytecode or compilation success</description> + </parameter> + <parameter> + <name>compilerArgument</name> + <cache-key>true</cache-key> + <description>Single additional compiler argument; changing it can change generated bytecode or compilation success</description> + </parameter> + <parameter> + <name>annotationProcessorPaths</name> + <cache-key>true</cache-key> + <description>Classpath for annotation processors; changing it can change generated sources or compilation success</description> + </parameter> + <parameter> + <name>annotationProcessors</name> + <cache-key>true</cache-key> + <description>Annotation processors to run; changing them can change generated sources or compilation success</description> + </parameter> + <parameter> + <name>proc</name> + <cache-key>true</cache-key> + <description>Annotation processing mode (none, only, proc); changing it can change generated sources or compilation success</description> + </parameter> + <parameter> + <name>executable</name> + <cache-key>true</cache-key> + <description>Path to javac executable; changing it can change the compiler output or compilation success</description> + </parameter> + <parameter> + <name>parameters</name> + <cache-key>true</cache-key> + <description>Generate metadata for method parameters; changing it changes bytecode contents</description> + </parameter> + <parameter> + <name>enablePreview</name> + <cache-key>true</cache-key> + <description>Enable preview features; changing it can change generated bytecode or compilation success</description> + </parameter> + + <!-- No cache-key annotation means false; these settings do not invalidate the cache. --> + <parameter> + <name>verbose</name> + <description>Verbose compiler output</description> + </parameter> + <parameter> + <name>showWarnings</name> + <cache-key>true</cache-key> + <description>Show compilation warnings (can change the build outcome with failOnWarning)</description> + </parameter> + <parameter> + <name>showDeprecation</name> + <cache-key>true</cache-key> + <description>Show deprecation warnings (can change the build outcome with failOnWarning)</description> + </parameter> + <parameter> + <name>skipMain</name> + <cache-key>true</cache-key> + <description>Skip compiling main sources; changing it changes whether main bytecode is produced</description> + </parameter> + <parameter> + <name>failOnError</name> + <cache-key>true</cache-key> + <description>Fail build on compilation error (changes the build outcome)</description> + </parameter> + <parameter> + <name>failOnWarning</name> + <cache-key>true</cache-key> + <description>Fail build on compilation warning (changes the build outcome)</description> + </parameter> + <parameter> + <name>fork</name> + <cache-key>true</cache-key> + <description>Fork compiler into separate process (can change the compiler, output, or outcome)</description> + </parameter> + <parameter> + <name>maxmem</name> + <cache-key>true</cache-key> + <description>Maximum memory for compiler process (can change the build outcome)</description> + </parameter> + <parameter> + <name>meminitial</name> + <cache-key>true</cache-key> + <description>Initial memory for compiler process (can change the build outcome)</description> + </parameter> + <parameter> + <name>compilerReuseStrategy</name> + <description>Strategy for reusing compiler instances</description> + </parameter> + <parameter> + <name>forceJavacCompilerUse</name> + <cache-key>true</cache-key> + <description>Force use of javac compiler; changing it can affect compiler output</description> + </parameter> + <parameter> + <name>staleMillis</name> + <cache-key>true</cache-key> + <description>Staleness check interval for incremental compilation (can change generated files)</description> + </parameter> + <parameter> + <name>useIncrementalCompilation</name> + <cache-key>true</cache-key> + <description>Enable incremental compilation (can change generated files and build outcome)</description> + </parameter> + </parameters> + </goal> + + <goal> + <name>testCompile</name> + <parameters> + <!-- Same parameters as compile goal --> + <parameter> + <name>source</name> + <cache-key>true</cache-key> + <description>Source JDK version for test compilation; changing it can change generated test bytecode or compilation success</description> + </parameter> + <parameter> + <name>target</name> + <cache-key>true</cache-key> + <description>Target JDK version for compiled test bytecode; changing it changes generated test bytecode</description> + </parameter> + <parameter> + <name>release</name> + <cache-key>true</cache-key> + <description>JDK release version for tests; changing it changes generated test bytecode and API compatibility</description> + </parameter> + <parameter> + <name>encoding</name> + <cache-key>true</cache-key> + <description>Test source file encoding; changing it can change parsed source and generated test bytecode</description> + </parameter> + <parameter> + <name>debug</name> + <cache-key>true</cache-key> + <description>Include debugging information in test bytecode; changing it changes artifact contents</description> + </parameter> + <parameter> + <name>debuglevel</name> + <cache-key>true</cache-key> + <description>Level of debugging information for tests; changing it changes artifact contents</description> + </parameter> + <parameter> + <name>optimize</name> + <cache-key>true</cache-key> + <description>Optimize test bytecode; changing it changes artifact contents</description> + </parameter> + <parameter> + <name>compilerArgs</name> + <cache-key>true</cache-key> Review Comment: Fixed in f8dc720: the before-execution listener now saves immutable parameter values, so `TestCompilerMojo` changes to `compilerArgs` cannot alter the recorded inputs. A JPMS integration test confirms the second clean build skips `compiler:testCompile`. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
