では早速入って行きましょう。
public static int main( String[] args, ClassWorld classWorld )
{
MavenCli cli = new MavenCli();
return cli.doMain( new CliRequest( args, classWorld ) );
}
このmain()メソッドはMavenCliクラス内に定義されているので、一行目は自分自身のインスタンスを作成しているってことですね。そして、そのdoMain()というメソッドをさらに呼び出す訳ですが、この時main()メソッドの引数として受け取ったものを、CliRequestというクラスでラッピングしてから渡している。ということでまずは、CliRequestクラスがどんなものか見てみみますかね。
public class CliRequest
{
String[] args;
CommandLine commandLine;
ClassWorld classWorld;
String workingDirectory;
File multiModuleProjectDirectory;
boolean debug;
boolean quiet;
boolean showErrors = true;
Properties userProperties = new Properties();
Properties systemProperties = new Properties();
MavenExecutionRequest request;
CliRequest( String[] args, ClassWorld classWorld )
{
this.args = args;
this.classWorld = classWorld;
this.request = new DefaultMavenExecutionRequest();
}
public String[] getArgs()
{
return args;
}
public CommandLine getCommandLine()
{
return commandLine;
}
public ClassWorld getClassWorld()
{
return classWorld;
}
public String getWorkingDirectory()
{
return workingDirectory;
}
public File getMultiModuleProjectDirectory()
{
return multiModuleProjectDirectory;
}
public boolean isDebug()
{
return debug;
}
public boolean isQuiet()
{
return quiet;
}
public boolean isShowErrors()
{
return showErrors;
}
public Properties getUserProperties()
{
return userProperties;
}
public Properties getSystemProperties()
{
return systemProperties;
}
public MavenExecutionRequest getRequest()
{
return request;
}
public void setUserProperties( Properties properties )
{
this.userProperties.putAll( properties );
}
}
ざっと見る限りは、今後の実行に必要な値をこのクラスのフィールドとして保持しておくという、便利クラスな感じですね。
フィールドはprivateでもないし、セッターもないので、外から直接セットするんでしょう。となるとゲッターもいらない気がするけど、一応一通りのゲッターは用意されてそう。コンストラクタは引数で受け取ったものを素直にフィールドにセットしつつ、requestフィールドに関しては、DefaultMavenExecutionRequestといういかにもデフォルトチックなクラスのインスタンスをセットしてますな。いずれデフォルト以外のものがセットされたりするんでしょうねぇ。
CliRequest( String[] args, ClassWorld classWorld )
{
this.args = args;
this.classWorld = classWorld;
this.request = new DefaultMavenExecutionRequest();
}
で、このcliRequestを引数にdoMain()を呼び出していると。そのdoMain()を見ていくと、例外ハンドリングを丁寧にやってる感じですが、その辺りはとりあえず置いておいて、try句の中を先にみましょ。
引数として受け取ったcliRequestを次々と異なるメソッドに渡して行っている。何も考えずに一番上から順に見ていこう。
public int doMain( CliRequest cliRequest )
{
PlexusContainer localContainer = null;
try
{
initialize( cliRequest );
cli( cliRequest );
logging( cliRequest );
version( cliRequest );
properties( cliRequest );
localContainer = container( cliRequest );
commands( cliRequest );
configure( cliRequest );
toolchains( cliRequest );
populateRequest( cliRequest );
encryption( cliRequest );
repository( cliRequest );
return execute( cliRequest );
}
catch ( ExitException e )
{
return e.exitCode;
}
catch ( UnrecognizedOptionException e )
{
// pure user error, suppress stack trace
return 1;
}
catch ( BuildAbort e )
{
CLIReportingUtils.showError( slf4jLogger, "ABORTED", e, cliRequest.showErrors );
return 2;
}
catch ( Exception e )
{
CLIReportingUtils.showError( slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors );
return 1;
}
finally
{
if ( localContainer != null )
{
localContainer.dispose();
}
}
}
initialize( cliRequest );
cli( cliRequest );
logging( cliRequest );
version( cliRequest );
properties( cliRequest );
localContainer = container( cliRequest );
commands( cliRequest );
configure( cliRequest );
toolchains( cliRequest );
populateRequest( cliRequest );
encryption( cliRequest );
repository( cliRequest );
execute( cliRequest );
1.initialize( cliRequest )
void initialize( CliRequest cliRequest )
throws ExitException
{
if ( cliRequest.workingDirectory == null )
{
cliRequest.workingDirectory = System.getProperty( "user.dir" );
}
if ( cliRequest.multiModuleProjectDirectory == null )
{
String basedirProperty = System.getProperty( MULTIMODULE_PROJECT_DIRECTORY );
if ( basedirProperty == null )
{
System.err.format( "-D%s system propery is not set."
+ " Check $M2_HOME environment variable and mvn script match.", MULTIMODULE_PROJECT_DIRECTORY );
throw new ExitException( 1 );
}
File basedir = basedirProperty != null ? new File( basedirProperty ) : new File( "" );
try
{
cliRequest.multiModuleProjectDirectory = basedir.getCanonicalFile();
}
catch ( IOException e )
{
cliRequest.multiModuleProjectDirectory = basedir.getAbsoluteFile();
}
}
//
// Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
// Windows paths.
//
String mavenHome = System.getProperty( "maven.home" );
if ( mavenHome != null )
{
System.setProperty( "maven.home", new File( mavenHome ).getAbsolutePath() );
}
}
cliRequest.workingDirectoryとcliRequest.multiModuleProjectDirectoryへの設定を行っているようだが、現時点でこれらのフィールドには値を入れていないはずなので、このメソッドで値がセットされることになる。
cliRequest.workingDirectoryに関しては、システムプロパティの「user.dir」をセット。cliRequest.multiModuleProjectDirectoryはちょっと複雑なことしてんな。まず、MULTIMODULE_PROJECT_DIRECTORYは"maven.multiModuleProjectDirectory"と定義されているので、このプロパティを読み込むんだが、こいつが設定されていないと例外をスローしているので、ここまでの時点で絶対に設定されていないといけないようだ。
さて、今まで見てきた中で、こんなプロパティを設定してたっけか??と、記憶を辿ってみると、、、。
そういえば、ありました↓こんなのが。
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "${M2_HOME}"/boot/plexus-classworlds-*.jar \
"-Dclassworlds.conf=${M2_HOME}/bin/m2.conf" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${CLASSWORLDS_LAUNCHER} "$@"
その1:まずは入り口を探しましょ で見たのを覚えているだろうか。この下から2行目でバッチリ指定してますな。
この時は、MAVEN_PROJECTBASEDIRになにが設定されるのかまでは見てなかったので、ちょっと見てみましょか。
以下は、/usr/local/Cellar/maven/3.3.3/libexec/bin/mvnから関係あるところだけを抜粋したもの。
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
local basedir=$(pwd)
local wdir=$(pwd)
while [ "$wdir" != '/' ] ; do
wdir=$(cd "$wdir/.."; pwd)
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
done
echo "${basedir}"
}
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
最後の行で、${MAVEN
BASEDIR:-$(findmavenbasedir)}としているので、MAVENBASEDIRがセットされていればその値が、そうでないならば、find__maven_basedir()の実行結果が_MAVEN_PROJECTBASEDIRにセットされるということになりますね。この関数はカレントディレクトリを上位に辿って行って、最初に見つかった.mvnサブディレクトリがあるディレクトリをベースディレクトリに設定するってことのようです。
元のinitialize()メソッドに戻ると、basedirPropertyをもとにFileオブジェクトを生成し、そのgetCanonicalFile()メソッドの戻り値をcliRequest.multiModuleProjectDirectoryに設定してますね。この呼び出しで例外が出た場合は、getAbsoluteFile()を利用している。
このgetCanonicalFile()ってどんなメソッドなんだろか?ちょいと調べると、getAbsoluteFile()よりもこのメソッドを使うべきなようですね。参考
String path = "/tmp/../tmp/../tmp/../";
// 上のようなヘンテコなパスをもとに、getCanonicalFile()とgetAbsoluteFile()を呼び出してみると、、
File abspath = new File(path).getAbsoluteFile().getParentFile();
// -> "/tmp/../tmp/../tmp/.."という文字列処理しただけの結果に。
File canpath = new File(path).getCanonicalFile().getParentFile();
// -> ".."などの冗長部分を取り除き、"/"というちゃんと意味を理解して正しいパスにしてくれる。
initialize()メソッドの最後は、システムプロパティからmaven.homeを取り出し、getAbsolutePath()を使って絶対パスに変換して再設定している。コメントを読む限りは、Windowsのドライブパス関係の混乱を避けるためらしいが、本質でもないので先に進んでおこう。これで1. initialize()は見終わったので、次は、2. cli( cliRequest );