import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
// ライフサイクル・フェーズ・ゴール・プラグインの関係の模型(Maven の実装ではない)。
// ライフサイクル = フェーズの並び(3 つ。default は 23、clean は 3、site は 4)
// プラグイン = ゴールを持つ JAR(compiler は compile / help / testCompile の 3 ゴール)
// 束縛(binding) = 「このフェーズでこのゴール」の表。packaging(jar)ごとに既定がある
// mvn <phase> = そのフェーズが属するライフサイクルを先頭から <phase> まで進め、束縛されたゴールを動かす
// mvn plugin:goal = そのゴール 1 つ(dependency:tree や compiler:compile はフェーズを進めない。
// dependency:analyze のように先に test-compile まで走らせるゴールもあるが、この模型では扱わない)
// フェーズ名と jar の既定の束縛は、Maven 3.9.16 の `mvn help:describe -Dcmd=package` の出力どおり。
class Main {
static final Map<String, List<String>> LIFECYCLES = new LinkedHashMap<>();
static {
LIFECYCLES.put("clean", List.of("pre-clean", "clean", "post-clean"));
LIFECYCLES.put("default", List.of(
"validate", "initialize", "generate-sources", "process-sources", "generate-resources",
"process-resources", "compile", "process-classes", "generate-test-sources", "process-test-sources",
"generate-test-resources", "process-test-resources", "test-compile", "process-test-classes", "test",
"prepare-package", "package", "pre-integration-test", "integration-test", "post-integration-test",
"verify", "install", "deploy"));
LIFECYCLES.put("site", List.of("pre-site", "site", "post-site", "site-deploy"));
}
// プラグイン(prefix)-> 持っているゴール。
// compiler と spring-boot は `help:describe -Dplugin=` の全ゴール。他は実ログか describe の表に出たゴールだけ(全部ではない)
static final Map<String, List<String>> PLUGINS = new LinkedHashMap<>();
static {
PLUGINS.put("clean", List.of("clean"));
PLUGINS.put("resources", List.of("resources", "testResources"));
PLUGINS.put("compiler", List.of("compile", "help", "testCompile"));
PLUGINS.put("surefire", List.of("test"));
PLUGINS.put("jar", List.of("jar"));
PLUGINS.put("install", List.of("install"));
PLUGINS.put("deploy", List.of("deploy"));
PLUGINS.put("dependency", List.of("tree", "analyze", "build-classpath"));
PLUGINS.put("spring-boot", List.of("build-image", "build-image-no-fork", "build-info", "help", "process-aot",
"process-test-aot", "repackage", "run", "start", "stop", "test-run"));
}
// フェーズ -> 束縛されたゴール(複数可)。packaging jar の既定 + 雛形の pom.xml が宣言した Boot プラグインの 1 つ
static final Map<String, List<String>> BINDINGS = new LinkedHashMap<>();
static {
BINDINGS.put("clean", List.of("clean:clean"));
BINDINGS.put("process-resources", List.of("resources:resources"));
BINDINGS.put("compile", List.of("compiler:compile"));
BINDINGS.put("process-test-resources", List.of("resources:testResources"));
BINDINGS.put("test-compile", List.of("compiler:testCompile"));
BINDINGS.put("test", List.of("surefire:test"));
BINDINGS.put("package", List.of("jar:jar", "spring-boot:repackage")); // repackage の割り当ては親 POM の設定
BINDINGS.put("install", List.of("install:install"));
BINDINGS.put("deploy", List.of("deploy:deploy"));
}
static String lifecycleOf(String phase) {
for (var e : LIFECYCLES.entrySet()) if (e.getValue().contains(phase)) return e.getKey();
return null;
}
static void mvn(String... words) {
System.out.println("$ mvn " + String.join(" ", words));
int ran = 0;
for (String w : words) {
if (w.contains(":")) { // plugin:goal -> そのゴールだけ
String[] pg = w.split(":");
List<String> goals = PLUGINS.get(pg[0]);
if (goals == null || !goals.contains(pg[1])) {
System.out.println(" [ERROR] no goal '" + pg[1] + "' in plugin '" + pg[0] + "'");
continue;
}
System.out.println(" --- " + w + " (default-cli) --- <- a goal, no phase advanced");
ran++;
continue;
}
String lc = lifecycleOf(w);
if (lc == null) {
System.out.println(" [ERROR] Unknown lifecycle phase \"" + w + "\". Available lifecycle phases are:");
var all = new java.util.ArrayList<String>();
LIFECYCLES.values().forEach(all::addAll);
System.out.println(" " + String.join(", ", all) + " (" + all.size() + " phases)");
continue;
}
List<String> phases = LIFECYCLES.get(lc);
for (String phase : phases.subList(0, phases.indexOf(w) + 1)) {
for (String goal : BINDINGS.getOrDefault(phase, List.of())) { // 束縛が無いフェーズは何も起きない
System.out.printf(" --- %-26s <- phase '%s' of lifecycle '%s'%n", goal, phase, lc);
ran++;
}
}
}
System.out.println(" => " + ran + " goal(s) ran");
System.out.println();
}
public static void main(String[] args) {
mvn("validate"); // 束縛が無いので 0
mvn("compile");
mvn("package"); // jar:jar の後に spring-boot:repackage
mvn("clean", "package"); // clean ライフサイクル → default ライフサイクル
mvn("clean", "validate"); // clean:clean だけ
mvn("dependency:tree"); // ゴール 1 つ
mvn("compiler:compile"); // 同上。フェーズ compile とは別の指示
mvn("nosuchphase"); // フェーズ名の一覧がエラーに出る
System.out.println("## help:describe -Dplugin=compiler (what a plugin owns)");
System.out.println(" Goal Prefix: compiler, " + PLUGINS.get("compiler").size() + " goals: " + PLUGINS.get("compiler"));
}
}
// 出力(8つのうち先頭の4つ。JDK 22 で実行)
// $ mvn validate
// => 0 goal(s) ran
//
// $ mvn compile
// --- resources:resources <- phase 'process-resources' of lifecycle 'default'
// --- compiler:compile <- phase 'compile' of lifecycle 'default'
// => 2 goal(s) ran
//
// $ mvn package
// --- resources:resources <- phase 'process-resources' of lifecycle 'default'
// --- compiler:compile <- phase 'compile' of lifecycle 'default'
// --- resources:testResources <- phase 'process-test-resources' of lifecycle 'default'
// --- compiler:testCompile <- phase 'test-compile' of lifecycle 'default'
// --- surefire:test <- phase 'test' of lifecycle 'default'
// --- jar:jar <- phase 'package' of lifecycle 'default'
// --- spring-boot:repackage <- phase 'package' of lifecycle 'default'
// => 7 goal(s) ran
//
// $ mvn clean package
// --- clean:clean <- phase 'clean' of lifecycle 'clean'
// --- resources:resources <- phase 'process-resources' of lifecycle 'default'
// ...
// => 8 goal(s) ran