由于我们可以把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
从日志看环境有点不一样,所以我的环境会采用守护进程的方法。 不过为了调试,我可以修改代码强制采用本进程编译的方式。
这两种编译流程不尽相同,下一篇文章会先讲在本进程编译的流程。然后再讲启动守护进程进程编译流程。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。