由于我们可以把Gradle源代码里面的launcher包丢到gradle libs下面去编译应用程序,所以我们可以用Gradle源代码里面的launcher源代码进行分析。
至于Gradle源代码和gradle bin的关系,可以参考上一篇帖子:
Gradle自身源代码编译
这个很重要,弄清楚鸡和蛋的问题,下面正式开始。
一. GradleMain
文件路径:
subprojects\launcher\src\main\java\org\gradle\launcher\GradleMain.java
public static void main(String[] args) throws Exception {
new ProcessBootstrap().run("org.gradle.launcher.Main", args);
}
public class ProcessBootstrap {
/**
* Sets up the ClassLoader structure for the given class, creates an instance and invokes {@link EntryPoint#run(String[])} on it.
*/
public void run(String mainClassName, String[] args) {
try {
runNoExit(mainClassName, args);
System.exit(0);
} catch (Throwable throwable) {
throwable.printStackTrace();
System.exit(1);
}
}
private void runNoExit(String mainClassName, String[] args) throws Exception {
ClassPathRegistry classPathRegistry = new DefaultClassPathRegistry(new DefaultClassPathProvider(new DefaultModuleRegistry(CurrentGradleInstallation.get())));
ClassLoaderFactory classLoaderFactory = new DefaultClassLoaderFactory();
ClassPath antClasspath = classPathRegistry.getClassPath("ANT");
ClassPath runtimeClasspath = classPathRegistry.getClassPath("GRADLE_RUNTIME");
ClassLoader antClassLoader = classLoaderFactory.createIsolatedClassLoader(antClasspath);
ClassLoader runtimeClassLoader = new VisitableURLClassLoader(antClassLoader, runtimeClasspath);
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(runtimeClassLoader);
try {
Class<?> mainClass = runtimeClassLoader.loadClass(mainClassName);
Object entryPoint = mainClass.newInstance();
Method mainMethod = mainClass.getMethod("run", String[].class);
mainMethod.invoke(entryPoint, new Object[]{args});
} finally {
Thread.currentThread().setContextClassLoader(oldClassLoader);
ClassLoaderUtils.tryClose(runtimeClassLoader);
ClassLoaderUtils.tryClose(antClassLoader);
}
}
}
这行代码其实就是启动org.gradle.launcher.main.run方法
二. org.gradle.launcher.main
其实走的是doAction方法,因为run方法是它的父类EntryPoint里面:
public abstract class EntryPoint {
/**
* Unless the createCompleter() method is overridden, the JVM will exit before returning from this method.
*/
public void run(String[] args) {
RecordingExecutionListener listener = new RecordingExecutionListener();
try {
doAction(args, listener);
} catch (Throwable e) {
createErrorHandler().execute(e);
listener.onFailure(e);
}
Throwable failure = listener.getFailure();
ExecutionCompleter completer = createCompleter();
if (failure == null) {
completer.complete();
} else {
completer.completeWithFailure(failure);
}
}
所以看doAction方法:
public class Main extends EntryPoint {
public static void main(String[] args) {
new Main().run(args);
}
protected void doAction(String[] args, ExecutionListener listener) {
createActionFactory().convert(Arrays.asList(args)).execute(listener);
}
CommandLineActionFactory createActionFactory() {
return new CommandLineActionFactory();
}
}
//createActionFactory.java
public Action<ExecutionListener> convert(List<String> args) {
ServiceRegistry loggingServices = createLoggingServices();
LoggingConfiguration loggingConfiguration = new DefaultLoggingConfiguration();
return new WithLogging(loggingServices,
args,
loggingConfiguration,
new ExceptionReportingAction(
new JavaRuntimeValidationAction(
new ParseAndBuildAction(loggingServices, args)),
new BuildExceptionReporter(loggingServices.get(StyledTextOutputFactory.class), loggingConfiguration, clientMetaData())));
}
从convert方法可以看到返回的是WithLogging对象,但是它里面又包了好几层。
其实它们都是实现了Action<ExecutionListener>接口:
private static class WithLogging implements Action<ExecutionListener> {}
public class ExceptionReportingAction implements Action<ExecutionListener> {}
public class JavaRuntimeValidationAction implements Action<ExecutionListener> {}
private class ParseAndBuildAction implements Action<ExecutionListener> {}
这是装饰者模式。
关于装饰者模式大家可以百度下,大概有两点:
类实现同一个接口
互相包装
打个比方,有这么一个场景:
咖啡店咖啡卖3块一杯,于是我们可以这样设计:
interface Drink{
int price();
}
class Coffee implements Drink{
int price(){return 3;}
}
哪天需要开发了新产品,嗯,加糖的咖啡,需要卖5块,那可以设计如下:
class SugarCoffee implements Drink{
Drink mDrink;
SugarCoffee(Drink drink){
mDrink = drink;
}
int price(){
return 2 + mDrink.price();
}
}
main(){
SugarCoffee sc = new SugarCoffee(new Coffee());
int price = sc.price();
}
嗯,大概就是这个意思。。Gradle这里也用了同样的模式。
最后,我们看最关键的ParseAndBuildAction.execute(xxx)方法
三. ParseAndBuildAction.execute(xxx)
protected void createActionFactories(ServiceRegistry loggingServices, Collection<CommandLineAction> actions) {
actions.add(new GuiActionsFactory());
actions.add(new BuildActionsFactory(loggingServices, new ParametersConverter(), new CachingJvmVersionDetector(new DefaultJvmVersionDetector(new DefaultExecActionFactory(new IdentityFileResolver())))));
}
public void execute(ExecutionListener executionListener) {
System.out.println("ParseAndBuildAction execute");
List<CommandLineAction> actions = new ArrayList<CommandLineAction>();
//添加BuiltInActions
actions.add(new BuiltInActions());
//添加GuiActionsFactory和BuildActionsFactory
createActionFactories(loggingServices, actions);
CommandLineParser parser = new CommandLineParser();
for (CommandLineAction action : actions) {
action.configureCommandLineParser(parser);
}
Action<? super ExecutionListener> action;
try {
ParsedCommandLine commandLine = parser.parse(args);
action = createAction(actions, parser, commandLine);
Exception exx = new Exception("Sandt ParseAndBuildAction execute..");
exx.printStackTrace();
System.out.println("action: " + action);
} catch (CommandLineArgumentException e) {
action = new CommandLineParseFailureAction(parser, e);
}
action.execute(executionListener);
}
private Action<? super ExecutionListener> createAction(Iterable<CommandLineAction> factories, CommandLineParser parser, ParsedCommandLine commandLine) {
for (CommandLineAction factory : factories) {
Runnable action = factory.createAction(parser, commandLine);
if (action != null) {
return Actions.toAction(action);
}
}
throw new UnsupportedOperationException("No action factory for specified command-line arguments.");
}
execute里面首先会添加3个ActionFactory
BuildInActions表示gradle -help和gradle -version 命令
GuiActionsFactory表示gradle -gui命令
BuildActionsFactory表示其他编译命令,比如gradle assemble,下面我们主要分析这种。
调用createAction方法,其实就是挨个调用上面3个ActionsFacory的createAction方法。那我们主要看BuildActionsFactory。
四. BuildActionsFactory.createAction
public Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
Parameters parameters = parametersConverter.convert(commandLine, new Parameters());
parameters.getDaemonParameters().applyDefaultsFor(jvmVersionDetector.getJavaVersion(parameters.getDaemonParameters().getEffectiveJvm()));
if (parameters.getDaemonParameters().isStop()) {
System.out.println("createAction 1");
return stopAllDaemons(parameters.getDaemonParameters(), loggingServices);
}
if (parameters.getDaemonParameters().isStatus()) {
System.out.println("createAction 2");
return showDaemonStatus(parameters.getDaemonParameters(), loggingServices);
}
if (parameters.getDaemonParameters().isForeground()) {
System.out.println("createAction 3");
DaemonParameters daemonParameters = parameters.getDaemonParameters();
ForegroundDaemonConfiguration conf = new ForegroundDaemonConfiguration(
UUID.randomUUID().toString(), daemonParameters.getBaseDir(), daemonParameters.getIdleTimeout(), daemonParameters.getPeriodicCheckInterval());
return new ForegroundDaemonAction(loggingServices, conf);
}
if (parameters.getDaemonParameters().isEnabled()) {
System.out.println("createAction 4");
return runBuildWithDaemon(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
// return runBuildInProcess(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
}
if (canUseCurrentProcess(parameters.getDaemonParameters())) {
System.out.println("createAction 5");
return runBuildInProcess(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
}
System.out.println("createAction 6");
return runBuildInSingleUseDaemon(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
}
编译种类主要分为两种:
在本进程编译
在守护进程编译
选择逻辑是:
如果有守护进程在运行,那么连接守护进程然后在守护进程编译。
如果可以在当前进程编译,那么就在当前进程编译。
如果都不满足,那就启动守护进程,然后进行编译。
判断是否可以在当前进程编译的代码如下:
subprojects\launcher\src\main\java\org\gradle\launcher\daemon\configuration\BuildProcess.java
/**
* Attempts to configure the current process to run with the required build parameters.
* @return True if the current process could be configured, false otherwise.
*/
public boolean configureForBuild(DaemonParameters requiredBuildParameters) {
boolean javaHomeMatch = getJvm().equals(requiredBuildParameters.getEffectiveJvm());
final JvmOptions jvmOptions = new JvmOptions(new IdentityFileResolver());
jvmOptions.systemProperties(getJvmOptions().getImmutableSystemProperties());
List<String> currentImmutables = jvmOptions.getAllImmutableJvmArgs();
List<String> requiredImmutables = requiredBuildParameters.getEffectiveSingleUseJvmArgs();
requiredImmutables.removeAll(DaemonParameters.DEFAULT_JVM_ARGS);
boolean noImmutableJvmArgsRequired = requiredImmutables.equals(currentImmutables);
System.out.println("javaHomeMatch: " + javaHomeMatch
+ " noImmutableJvmArgsRequired: " + noImmutableJvmArgsRequired);
System.out.println("getJvm(): " + getJvm()
+ " requiredBuildParameters.getEffectiveJvm(): " + requiredBuildParameters.getEffectiveJvm());
for (String re : requiredImmutables) {
System.out.println("requiredImmutable: " + re);
}
for (String re : currentImmutables) {
System.out.println("currentImmutable: " + re);
}
if (javaHomeMatch && noImmutableJvmArgsRequired) {
// Set the system properties and use this process
Properties properties = new Properties();
properties.putAll(requiredBuildParameters.getEffectiveSystemProperties());
System.setProperties(properties);
return true;
}
return false;
}
打印日志:
javaHomeMatch: true noImmutableJvmArgsRequired: false
getJvm(): 1.8.0_25 (Oracle Corporation 25.25-b02) requiredBuildParameters.getEffectiveJvm(): 1.8.0_25 (Oracle Corporation 25.25-b02)
requiredImmutable: -Xmx1536m
requiredImmutable: -Dfile.encoding=GBK
requiredImmutable: -Duser.country=CN
requiredImmutable: -Duser.language=zh
requiredImmutable: -Duser.variant
currentImmutable: -Dfile.encoding=GBK
currentImmutable: -Duser.country=CN
currentImmutable: -Duser.language=zh
currentImmutable: -Duser.variant
从日志看环境有点不一样,所以我的环境会采用守护进程的方法。 不过为了调试,我可以修改代码强制采用本进程编译的方式。
这两种编译流程不尽相同,下一篇文章会先讲在本进程编译的流程。然后再讲启动守护进程进程编译流程。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。