gnodet commented on code in PR #12357: URL: https://github.com/apache/maven/pull/12357#discussion_r3471942617
########## impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/SourceStrategy.java: ########## @@ -0,0 +1,550 @@ +/* + * 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.cling.invoker.mvnup.goals; + +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import eu.maveniverse.domtrip.Document; +import eu.maveniverse.domtrip.Element; +import org.apache.maven.api.cli.mvnup.UpgradeOptions; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.di.Priority; +import org.apache.maven.api.di.Singleton; +import org.apache.maven.cling.invoker.mvnup.UpgradeContext; + +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.ARTIFACT_ID; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.BUILD; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.CONFIGURATION; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.GROUP_ID; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PLUGIN; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PLUGINS; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PLUGIN_MANAGEMENT; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PROPERTIES; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.SOURCE_DIRECTORY; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.TEST_SOURCE_DIRECTORY; +import static eu.maveniverse.domtrip.maven.MavenPomElements.ModelVersions.MODEL_VERSION_4_1_0; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Plugins.DEFAULT_MAVEN_PLUGIN_GROUP_ID; + +/** + * Strategy for migrating legacy source configuration to Maven 4.1.0+ {@code <source>} elements. + * + * <p>Handles four migration phases: + * <ol> + * <li>Compiler properties ({@code maven.compiler.release}, {@code maven.compiler.source/target}) + * → {@code <source><targetVersion>}</li> + * <li>Compiler plugin configuration ({@code <release>}, {@code <source>/<target>}) + * → {@code <source><targetVersion>}</li> + * <li>Custom source/test directories → {@code <source>} with {@code <directory>}</li> + * <li>Resource directories → {@code <source>} with {@code <lang>resources</lang>}</li> + * </ol> + */ +@Named +@Singleton +@Priority(20) +public class SourceStrategy extends AbstractUpgradeStrategy { + + private static final String MAVEN_COMPILER_RELEASE = "maven.compiler.release"; + private static final String MAVEN_COMPILER_SOURCE = "maven.compiler.source"; + private static final String MAVEN_COMPILER_TARGET = "maven.compiler.target"; + private static final String MAVEN_COMPILER_PLUGIN = "maven-compiler-plugin"; + + private static final String DEFAULT_SOURCE_DIR = "src/main/java"; + private static final String DEFAULT_TEST_SOURCE_DIR = "src/test/java"; + private static final String DEFAULT_RESOURCE_DIR = "src/main/resources"; + private static final String DEFAULT_TEST_RESOURCE_DIR = "src/test/resources"; + + @Override + public boolean isApplicable(UpgradeContext context) { + UpgradeOptions options = getOptions(context); + + if (options.all().orElse(false)) { + return true; + } + + String modelVersion = options.modelVersion().orElse(null); + return modelVersion != null && ModelVersionUtils.isVersionGreaterOrEqual(modelVersion, MODEL_VERSION_4_1_0); + } + + @Override + public String getDescription() { + return "Migrating source configuration to <source> elements"; + } + + @Override + protected UpgradeResult doApply(UpgradeContext context, Map<Path, Document> pomMap) { + Set<Path> processedPoms = new HashSet<>(); + Set<Path> modifiedPoms = new HashSet<>(); + Set<Path> errorPoms = new HashSet<>(); + + for (Map.Entry<Path, Document> entry : pomMap.entrySet()) { + Path pomPath = entry.getKey(); + Document pomDocument = entry.getValue(); + processedPoms.add(pomPath); + + String currentVersion = ModelVersionUtils.detectModelVersion(pomDocument); + context.info(pomPath + " (current: " + currentVersion + ")"); + context.indent(); + + try { + if (!ModelVersionUtils.isVersionGreaterOrEqual(currentVersion, MODEL_VERSION_4_1_0)) { + context.success("Skipping (model version " + currentVersion + " < 4.1.0)"); + continue; + } + + boolean hasChanges = migrateSources(context, pomDocument); + + if (hasChanges) { + modifiedPoms.add(pomPath); + context.success("Source configuration migrated to <source> elements"); + } else { + context.success("No source configuration to migrate"); + } + } catch (Exception e) { + context.failure("Failed to migrate source configuration: " + e.getMessage()); + errorPoms.add(pomPath); + } finally { + context.unindent(); + } + } + + return new UpgradeResult(processedPoms, modifiedPoms, errorPoms); + } + + private boolean migrateSources(UpgradeContext context, Document pomDocument) { + Element root = pomDocument.root(); + boolean hasChanges = false; + + String targetVersion = extractTargetVersionFromProperties(context, root); + + if (targetVersion == null) { + targetVersion = extractTargetVersionFromCompilerPlugin(context, root); + } else { + cleanupCompilerPluginConfig(context, root); + } + + if (targetVersion != null) { + Element sourcesElement = ensureSourcesElement(root); + Element sourceElement = DomUtils.insertNewElement("source", sourcesElement); + DomUtils.insertContentElement(sourceElement, "targetVersion", targetVersion); + context.detail("Set targetVersion: " + targetVersion); + hasChanges = true; + } Review Comment: Fixed. Now uses `findOrCreateMainJavaSource(sourcesElement)` instead of `DomUtils.insertNewElement(\"source\", sourcesElement)` when adding `targetVersion`. This reuses an existing main/java `<source>` element (e.g. one with a custom `<directory>`) instead of creating a duplicate. Added a test that verifies a POM with an existing `<source><directory>...</directory></source>` gets `targetVersion` added to the same element. — _Claude Opus 4.6, on behalf of Guillaume Nodet_ ########## impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/SourceStrategy.java: ########## @@ -0,0 +1,550 @@ +/* + * 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.cling.invoker.mvnup.goals; + +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import eu.maveniverse.domtrip.Document; +import eu.maveniverse.domtrip.Element; +import org.apache.maven.api.cli.mvnup.UpgradeOptions; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.di.Priority; +import org.apache.maven.api.di.Singleton; +import org.apache.maven.cling.invoker.mvnup.UpgradeContext; + +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.ARTIFACT_ID; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.BUILD; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.CONFIGURATION; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.GROUP_ID; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PLUGIN; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PLUGINS; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PLUGIN_MANAGEMENT; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PROPERTIES; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.SOURCE_DIRECTORY; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.TEST_SOURCE_DIRECTORY; +import static eu.maveniverse.domtrip.maven.MavenPomElements.ModelVersions.MODEL_VERSION_4_1_0; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Plugins.DEFAULT_MAVEN_PLUGIN_GROUP_ID; + +/** + * Strategy for migrating legacy source configuration to Maven 4.1.0+ {@code <source>} elements. + * + * <p>Handles four migration phases: + * <ol> + * <li>Compiler properties ({@code maven.compiler.release}, {@code maven.compiler.source/target}) + * → {@code <source><targetVersion>}</li> + * <li>Compiler plugin configuration ({@code <release>}, {@code <source>/<target>}) + * → {@code <source><targetVersion>}</li> + * <li>Custom source/test directories → {@code <source>} with {@code <directory>}</li> + * <li>Resource directories → {@code <source>} with {@code <lang>resources</lang>}</li> + * </ol> + */ +@Named +@Singleton +@Priority(20) +public class SourceStrategy extends AbstractUpgradeStrategy { + + private static final String MAVEN_COMPILER_RELEASE = "maven.compiler.release"; + private static final String MAVEN_COMPILER_SOURCE = "maven.compiler.source"; + private static final String MAVEN_COMPILER_TARGET = "maven.compiler.target"; + private static final String MAVEN_COMPILER_PLUGIN = "maven-compiler-plugin"; + + private static final String DEFAULT_SOURCE_DIR = "src/main/java"; + private static final String DEFAULT_TEST_SOURCE_DIR = "src/test/java"; + private static final String DEFAULT_RESOURCE_DIR = "src/main/resources"; + private static final String DEFAULT_TEST_RESOURCE_DIR = "src/test/resources"; + + @Override + public boolean isApplicable(UpgradeContext context) { + UpgradeOptions options = getOptions(context); + + if (options.all().orElse(false)) { + return true; + } + + String modelVersion = options.modelVersion().orElse(null); + return modelVersion != null && ModelVersionUtils.isVersionGreaterOrEqual(modelVersion, MODEL_VERSION_4_1_0); + } + + @Override + public String getDescription() { + return "Migrating source configuration to <source> elements"; + } + + @Override + protected UpgradeResult doApply(UpgradeContext context, Map<Path, Document> pomMap) { + Set<Path> processedPoms = new HashSet<>(); + Set<Path> modifiedPoms = new HashSet<>(); + Set<Path> errorPoms = new HashSet<>(); + + for (Map.Entry<Path, Document> entry : pomMap.entrySet()) { + Path pomPath = entry.getKey(); + Document pomDocument = entry.getValue(); + processedPoms.add(pomPath); + + String currentVersion = ModelVersionUtils.detectModelVersion(pomDocument); + context.info(pomPath + " (current: " + currentVersion + ")"); + context.indent(); + + try { + if (!ModelVersionUtils.isVersionGreaterOrEqual(currentVersion, MODEL_VERSION_4_1_0)) { + context.success("Skipping (model version " + currentVersion + " < 4.1.0)"); + continue; + } + + boolean hasChanges = migrateSources(context, pomDocument); + + if (hasChanges) { + modifiedPoms.add(pomPath); + context.success("Source configuration migrated to <source> elements"); + } else { + context.success("No source configuration to migrate"); + } + } catch (Exception e) { + context.failure("Failed to migrate source configuration: " + e.getMessage()); + errorPoms.add(pomPath); + } finally { + context.unindent(); + } + } + + return new UpgradeResult(processedPoms, modifiedPoms, errorPoms); + } + + private boolean migrateSources(UpgradeContext context, Document pomDocument) { + Element root = pomDocument.root(); + boolean hasChanges = false; + + String targetVersion = extractTargetVersionFromProperties(context, root); + + if (targetVersion == null) { + targetVersion = extractTargetVersionFromCompilerPlugin(context, root); + } else { + cleanupCompilerPluginConfig(context, root); + } Review Comment: Fixed. `cleanupPluginSectionConfig()` now receives the migrated value and only removes `<release>`, `<source>`, or `<target>` from the compiler plugin configuration when the value matches what was migrated to properties. If the plugin config intentionally differs (e.g. property has `release=17` but plugin overrides with `release=21`), it is left in place. Added a test verifying that differing plugin config is preserved. — _Claude Opus 4.6, on behalf of Guillaume Nodet_ -- 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]
