-
概述
点击Android Studio的运行按钮,你会在Run面板的第一行看到如下信息:
18:56:07: Executing 'assembleRelease'... ...
assembleRelease是一个当前Task的名字:
截屏2023-10-20 18.58.30.png可见,Run按钮背后的动作也会执行了assembleRelease这个Task,和我们手动使用gradlew构建是一样的:
./gradlew assembleRelease
gradle就是通过gradlew这个程序完成构建的,我们就从这个命令入手看看gradle是如何工作的。
-
gradlew脚本
在每个项目的根目录下都会有一个叫做gradlew的脚本,方便我们通过./gradlew就可以调用,脚本内容简化如下:
#!/usr/bin/env sh # 运行脚本时键入的名字,这里是"./gradlew" PRG="$0" # -h判断文件是否是软连接,如果是软连接,则找到源文件 while [ -h "$PRG" ] ; do #通过ls得到该文件的信息 ls=`ls -ld "$PRG"` #expr expr1 : expr2的意思是盘端expr1是否是expr2的格式,软连接的标志就是ls信息中的最后为"link_name->original_file",因为正则表达式内含有(),因此如果匹配成功,link的值是()内匹配到的子字符串 link=`expr "$ls" : '.*-> \(.*\)$'` # 如果匹配到了,则PRG就是指向源文件(就不是软连接了,就可以跳出循环) if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done # 保存当前目录,即脚本gradlew的父目录,也就是App项目目录 SAVED="`pwd`" # 真正的gradlew脚本源文件目录,如果当前脚本不是软link的话则和SAVED是一样的 cd "`dirname \"$PRG\"`/" >/dev/null # APP_HOME是gradlew脚本源文件所在的目录,即App项目目录 APP_HOME="`pwd -P`" # 回到调用脚本的目录 cd "$SAVED" >/dev/null APP_NAME="Gradle" # 当前调用的脚本名字,如果是软link的话可能不叫“gradlew”,否则就是“gradlew” APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # 打印信息并结束脚本进程 die () { echo echo "$*" echo exit 1 } ... # 指定gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. # 如果JAVA_HOME定义了的话,这个变量来自系统环境变量(-n表示验证字符串是否为空) if [ -n "$JAVA_HOME" ] ; then # 获取java脚本的路径(-x表示是否可执行) if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi ...#容错处理 else # 如果没配置JAVA_HOME环境变量的话,可能是因为java路径被设置到了其他名字的环境变量中,这里直接尝试读取 JAVACMD="java" # 如果没找到java程序文件则结束进程(>/dev/null 2>&1表示不输出任何信息,在这里和which -s作用一样 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH...." fi # cygin等都是虚拟机名字,如果不是虚拟机则进入下面逻辑 if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then # ulimit -H -n表示查询当前进程硬资源的文件描述符的最大打开数量 MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi # 默认设置成查询到的系统默认配置 ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi ... # For Cygwin or MSYS, switch paths to Windows format before running java ...#虚拟机的话需要转换格式 # 该方法把gradlew后面跟的参数转成如:'assembleRelease' \的格式 save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=`save "$@"` # 设置positional parameter为DEFAULT_JVM_OPTS、JAVA_OPTS等参数,最后再加上命令行参数APP_ARGS eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # 执行java命令 #如: #java -Xmx64m -Xms64m SOME_OPTS -Dorg.gradle.appname=gradlew -classpath $APP_HOME/gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain 'assembleRelease' \ exec "$JAVACMD" "$@"
可见,gradlew最后其实是执行了java命令,很明显,要执行的是org.gradle.wrapper.GradleWrapperMain:
public static void main(String[] args) throws Exception { //App项目目录下/gradle/wrapper/gradle-wrapper.jar File wrapperJar = wrapperJar(); //得到gradle-wrapper.properties File propertiesFile = wrapperProperties(wrapperJar); //App项目目录 File rootDir = rootDir(wrapperJar); CommandLineParser parser = new CommandLineParser(); parser.allowUnknownOptions(); parser.option(new String[]{"g", "gradle-user-home"}).hasArgument(); parser.option(new String[]{"q", "quiet"}); SystemPropertiesCommandLineConverter converter = new SystemPropertiesCommandLineConverter(); converter.configure(parser); //解析参数(这是第一次解析,目的是为了设置系统级变量) ParsedCommandLine options = parser.parse(args); Properties systemProperties = System.getProperties(); //将解析出的gradlew脚本中配置的系统变量设置到系统的Properties中 systemProperties.putAll(converter.convert(options, new HashMap())); //获取安装时在user.home下生成的.gradle目录,是一个隐藏目录,比如我的是/User/mph/.gradle File gradleUserHome = gradleUserHome(options); //将.gradle目录下的gradle.properties和App项目目录下的gradle.properties的属性都设置到系统的Properties中 addSystemProperties(systemProperties, gradleUserHome, rootDir); Logger logger = logger(options); //WrapperExecutor构造中会把gradle/wrapper/gradle-wrapper.properties的值读取保存 WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile); wrapperExecutor.execute(args, new Install(logger, new Download(logger, "gradlew", "0"), new PathAssembler(gradleUserHome, rootDir)), new BootstrapMainStarter()); }
可知,{user_home}/.gradle/gradle.properties是总的配置,对所有项目都有效;{App项目目录}/gradle.properties是只用于当前项目的配置。
继续看execute方法:
public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception { //gradleHome是下载完后的当前使用版本的gradle跟目录,比如如果我用的是gradle-7.4-all,那最终路径是:/User/mph/.gradle/wrapper/dists/gradle-7.4-all/aadb4xli5jkdsnukm30eibyiu(生成的hash码)/gradle-7.4, File gradleHome = install.createDist(this.config); bootstrapMainStarter.start(args, gradleHome); }
其实aadb4xli5jkdsnukm30eibyiu文件夹下有多个文件,最终会取第一个,也就是gradle-7.4文件夹:
Mac-mini:aadb4xli5jkdsnukm30eibyiu hardy$ ls gradle-7.4 gradle-7.4-all.zip.lck gradle-7.4-all.zip gradle-7.4-all.zip.ok
createDist方法中做了什么呢?其实gradle/wrapper/gradle-wrapper.properties中的信息是这样的:
distributionBase=GRADLE_USER_HOME distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME
createDist方法就是会按照这个属性文件中配置的地址去下载所有的当前版本的gradle需要的文件保存到其指定的路径中去,然后解压。
随后调用BootstrapMainStarter.start方法:
public void start(String[] args, File gradleHome) throws Exception { //正则表达式tip:.是特殊符号,表示任意字符,因此\.表示.符号本身 //查找在{gradleHome}/lib下的所有名字为“gradle-launcher-.*\.jar”的文件,取第一个,这里是gradle-launcher-7.4.jar File gradleJar = findLauncherJar(gradleHome); if (gradleJar == null) { throw... } else { //使用gradle-launcher-x.x.jar为源创建ClassLoader URLClassLoader contextClassLoader = new URLClassLoader(new URL[]{gradleJar.toURI().toURL()}, ClassLoader.getSystemClassLoader().getParent()); Thread.currentThread().setContextClassLoader(contextClassLoader); Class<?> mainClass = contextClassLoader.loadClass("org.gradle.launcher.GradleMain"); Method mainMethod = mainClass.getMethod("main", String[].class); mainMethod.invoke((Object)null, args); if (contextClassLoader instanceof Closeable) { contextClassLoader.close(); } } }
可见,最后使用反射调用org.gradle.launcher.GradleMain的main方法:
public static void main(String[] args) throws Exception { String javaVersion = System.getProperty("java.specification.version"); //我这里是7.4版本,需要java1.8及以上 if (javaVersion.equals("1.6") || javaVersion.equals("1.7")) { String gradleVersion = GradleVersionNumberLoader.loadGradleVersionNumber(); System.err.printf("%s %s requires Java 1.8 or later to run. You are currently using Java %s.%n", "Gradle", gradleVersion, javaVersion); System.exit(1); } Class<?> mainClass = Class.forName("org.gradle.launcher.bootstrap.ProcessBootstrap"); Method mainMethod = mainClass.getMethod("run", String.class, String[].class); mainMethod.invoke((Object)null, "org.gradle.launcher.Main", args); }
调用ProcessBootstrap的run方法:
public static void run(String mainClassName, String[] args) { try { runNoExit(mainClassName, args); System.exit(0); } catch (Throwable var3) { var3.printStackTrace(); System.exit(1); } }
private static void runNoExit(String mainClassName, String[] args) throws Exception { ClassPathRegistry classPathRegistry = new DefaultClassPathRegistry(new ClassPathProvider[]{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("ant-loader", antClasspath); ClassLoader runtimeClassLoader = new VisitableURLClassLoader("ant-and-gradle-loader", antClassLoader, runtimeClasspath); ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(runtimeClassLoader); try { //传过来的mainClassName是org.gradle.launcher.Main, Class<?> mainClass = runtimeClassLoader.loadClass(mainClassName); Object entryPoint = mainClass.getConstructor().newInstance(); Method mainMethod = mainClass.getMethod("run", String[].class); mainMethod.invoke(entryPoint, args); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); ClassLoaderUtils.tryClose(runtimeClassLoader); ClassLoaderUtils.tryClose(antClassLoader); } }
Main的run方法来自其父类EntryPoint,内部调用了Main的实现方法doAction:
protected void doAction(String[] args, ExecutionListener listener) { this.createActionFactory().convert(Arrays.asList(args)).execute(listener); } CommandLineActionFactory createActionFactory() { return new DefaultCommandLineActionFactory(); }
convert返回一个DefaultCommandLineActionFactory.WithLogging:
public CommandLineExecution convert(List<String> args) { ServiceRegistry loggingServices = this.createLoggingServices(); LoggingConfiguration loggingConfiguration = new DefaultLoggingConfiguration(); return new DefaultCommandLineActionFactory.WithLogging(loggingServices, args, loggingConfiguration, new DefaultCommandLineActionFactory.ParseAndBuildAction(loggingServices, args), new BuildExceptionReporter((StyledTextOutputFactory)loggingServices.get(StyledTextOutputFactory.class), loggingConfiguration, clientMetaData())); }
它的execute方法如下:
public void execute(ExecutionListener executionListener) { BuildOptionBackedConverter<LoggingConfiguration> loggingBuildOptions = new BuildOptionBackedConverter(new LoggingConfigurationBuildOptions()); InitialPropertiesConverter propertiesConverter = new InitialPropertiesConverter(); BuildLayoutConverter buildLayoutConverter = new BuildLayoutConverter(); LayoutToPropertiesConverter layoutToPropertiesConverter = new LayoutToPropertiesConverter(new BuildLayoutFactory()); BuildLayoutResult buildLayout = buildLayoutConverter.defaultValues(); CommandLineParser parser = new CommandLineParser(); propertiesConverter.configure(parser); buildLayoutConverter.configure(parser); loggingBuildOptions.configure(parser); parser.allowUnknownOptions(); //allowMixedOptions设置为true,此后onNonOption方法中会用到这个值 parser.allowMixedSubcommandsAndOptions(); try { //解析参数(第二次解析,为了获取其他参数属性) //这一次解析加载属性最终都是为了Logger服务的 ParsedCommandLine parsedCommandLine = parser.parse(this.args); InitialProperties initialProperties = propertiesConverter.convert(parsedCommandLine); buildLayout = buildLayoutConverter.convert(initialProperties, parsedCommandLine, (File)null); AllProperties properties = layoutToPropertiesConverter.convert(initialProperties, buildLayout); ... } ... ... try { Action<ExecutionListener> exceptionReportingAction = new ExceptionReportingAction(this.reporter, loggingManager, new NativeServicesInitializingAction(buildLayout, this.loggingConfiguration, loggingManager, new WelcomeMessageAction(buildLayout, new DebugLoggerWarningAction(this.loggingConfiguration, this.action)))); //开启任务Action的入口 exceptionReportingAction.execute(executionListener); } finally { loggingManager.stop(); } }
这里有三个Convertor,loggingBuildOptions就不看了,是关于日志打印的。分成configure和convert两段逻辑。
先看configure阶段做了什么。
首先是InitialPropertiesConverter,它的configure方法最终会调用CommandLineParser的option方法:给其内部的optionsByString添加key为"D"和"system-prop"的两个CommandLineOption(这俩是一样的):
CommandLineOption option = new CommandLineOption(Arrays.asList(options)); //var7是字符串数组["D","system-prop"] Iterator var7 = option.getOptions().iterator(); while(var7.hasNext()) { String optionStr = (String)var7.next(); this.optionsByString.put(optionStr, option); }
第二个是BuildLayoutConverter,它的configure方法最终也是和上面一样会给CommandLineParser的optionsByString添加key为“gradle-user-home”和“g”的两个相同CommandLineOption。
然后调用CommandLineParser的parse方法:
public ParsedCommandLine parse(Iterable<String> commandLine) throws CommandLineArgumentException { //configure阶段的optionsByString拿来创建出ParsedCommandLine的optionsByString(此时内容变成了ParsedCommandLineOption) ParsedCommandLine parsedCommandLine = new ParsedCommandLine(new HashSet(this.optionsByString.values())); CommandLineParser.ParserState parseState = new CommandLineParser.BeforeFirstSubCommand(parsedCommandLine); Iterator var4 = commandLine.iterator(); while(true) { while(true) { while(var4.hasNext()) { String arg = (String)var4.next(); //这里只贴出和gradlew脚本中相关的解析代码 //maybeStartOption最终:arg.matches("(?s)-.+"),即匹配“-Xxx”格式的 if (((CommandLineParser.ParserState)parseState).maybeStartOption(arg)) { if (arg.equals("--")) { ... } else { CommandLineParser.OptionParserState parsedOption; if (arg.matches("--[^=]+")) { ... } else { CommandLineParser.OptionParserState parsedOption; if (arg.matches("(?s)--[^=]+=.*")) { ... } else if (arg.matches("(?s)-[^=]=.*")) { //-Dorg.gradle.appname=$APP_BASE_NAME在此解析,(?s)是单行模式的意思 parsedOption = ((CommandLineParser.ParserState)parseState).onStartOption(arg, arg.substring(1, 2)); //(1,2)取的就是D,(3)取的就是D后面的值,也就是org.gradle.appname=$APP_BASE_NAME parseState = parsedOption.onArgument(arg.substring(3)); } else { //-classpath在此解析 assert arg.matches("(?s)-[^-].*"); ... } } } } else { //其他格式的参数,比如'assembleRelease' \ parseState = ((CommandLineParser.ParserState)parseState).onNonOption(arg); } } //无操作 ((CommandLineParser.ParserState)parseState).onCommandLineEnd(); return parsedCommandLine; } } }
onStartOption中如果配置参数可知则会返回CommandLineParser.KnownOptionParserState对象,它的onArgument会把设置的属性值存到其values集合中,并且调用onComplete方法:
public CommandLineParser.ParserState onComplete() { if (this.getHasArgument() && this.values.isEmpty()) { ... } else { //把之前CommandLineOption的options添加到ParsedCommandLine的presentOptions中 ParsedCommandLineOption parsedOption = this.commandLine.addOption(this.optionString.option, this.option); if (this.values.size() + parsedOption.getValues().size() > 1 && !this.option.getAllowsMultipleArguments()) { ... } else { Iterator var2 = this.values.iterator(); while(var2.hasNext()) { String value = (String)var2.next(); //把解析的属性值添加到ParsedCommandLineOption的values集合中 parsedOption.addArgument(value); } ... return this.state; } } }
其他格式的就是执行gradlew命令后面跟着的命令行参数(positional parameter)的onNonOption方法如下:
public CommandLineParser.ParserState onNonOption(String arg) { this.commandLine.addExtraValue(arg); return (CommandLineParser.ParserState)(CommandLineParser.this.allowMixedOptions ? CommandLineParser.this.new AfterFirstSubCommand(this.commandLine) : new CommandLineParser.AfterOptions(this.commandLine)); }
因为前面设置了allowMixedOptions为true,所以这里返回AfterFirstSubCommand,不过这不重要,主要是addExtraValue方法中会把参数保存到
ParsedCommandLine的extraArguments
。然后开始convert阶段。
首先调用InitialPropertiesConverter的convert方法:
public InitialProperties convert(ParsedCommandLine commandLine) { final Map<String, String> requestedSystemProperties = (Map)this.systemPropertiesCommandLineConverter.convert(commandLine, new HashMap()); return new InitialProperties() { public Map<String, String> getRequestedSystemProperties() { return Collections.unmodifiableMap(requestedSystemProperties); } }; }
它返回一个InitialProperties,它的getRequestedSystemProperties方法的返回值来自于systemPropertiesCommandLineConverter.convert,systemPropertiesCommandLineConverter是SystemPropertiesCommandLineConverter:
public Map<String, String> convert(ParsedCommandLine options, Map<String, String> properties) throws CommandLineArgumentException { //这里通过option方法获取前面configure阶段设置的CommandLineOption Iterator var3 = options.option(this.getPropertyOption()).getValues().iterator(); while(var3.hasNext()) { //例如-D属性,这里会取出org.gradle.appname=$APP_BASE_NAME String keyValueExpression = (String)var3.next(); int pos = keyValueExpression.indexOf("="); if (pos < 0) { properties.put(keyValueExpression, ""); } else { //设置到properties这个Map中 properties.put(keyValueExpression.substring(0, pos), keyValueExpression.substring(pos + 1)); } } return properties; }
这个properties返回赋值给requestedSystemProperties,这样就解析拿到了设置的系统属性值集合。
接下来调用的是BuildLayoutConverter的convert方法:
//会调用这个重载方法,即convert(systemProperties, commandLine, null, (parameters) -> {}); public BuildLayoutResult convert(InitialProperties systemProperties, ParsedCommandLine commandLine, @Nullable File workingDir, Consumer<BuildLayoutParameters> defaults) { BuildLayoutParameters layoutParameters = new BuildLayoutParameters(); ... //这里拿到之前的requestedSystemProperties Map<String, String> requestedSystemProperties = systemProperties.getRequestedSystemProperties(); //即PropertiesConverter的convert(尝试读取user.dir的值设置成gradleUserHomeDir) (new BuildLayoutParametersBuildOptions()).propertiesConverter().convert(requestedSystemProperties, layoutParameters); //尝试读取gradle-user-home的值设置成gradleUserHomeDir,并且尝试读取project-dir设置了ProjectDir this.buildLayoutConverter.convert(commandLine, layoutParameters); return new BuildLayoutConverter.Result(layoutParameters); }
PropertiesConverter的convert方法:
public T convert(Map<String, String> args, T target) throws CommandLineArgumentException { //得到两个option:GradleUserHomeOption和ProjectDirOption Iterator var3 = BuildOptionSet.this.getAllOptions().iterator(); while(var3.hasNext()) { BuildOption<? super T> option = (BuildOption)var3.next(); //ProjectDirOption其实最终没调用applyTo方法(因为其gradleProperty是null) option.applyFromProperty(args, target); } return target; }
applyFromProperty方法会调用applyTo方法。
GradleUserHomeOption的applyTo方法负责读取gradle.user.home属性值赋值给BuildLayoutParameters的gradleUserHomeDir:
public void applyTo(String value, BuildLayoutParameters settings, Origin origin) { Transformer<File, String> resolver = new BasicFileResolver(settings.getCurrentDir()); //transform方法中就是解析gradle.user.home属性值(file:格式或路径格式) settings.setGradleUserHomeDir((File)resolver.transform(value)); }
BuildLayoutParameters的构造方法中:
public BuildLayoutParameters() { //获取gradle安装目录、gradle.user.home目录和SystemProperties所在目录(user.dir设置的值) this(findGradleInstallationHomeDir(), findGradleUserHomeDir(), (File)null, FileUtils.canonicalize(SystemProperties.getInstance().getCurrentDir())); }
紧接着的this.buildLayoutConverter.convert方法其实和上一步的流程差不多,它会通过applyFromCommandLine方法(也就是尝试从命令行参数中获取配置信息)设置gradle.user.home,最终执行的是applyFromCommandLine方法:
public void applyFromCommandLine(ParsedCommandLine options, T settings) { Iterator var3 = this.commandLineOptionConfigurations.iterator(); while(var3.hasNext()) { CommandLineOptionConfiguration config = (CommandLineOptionConfiguration)var3.next(); //这里的config.getLongOption()是“gradle-user-home”、“project-dir” if (options.hasOption(config.getLongOption())) { String value = options.option(config.getLongOption()).getValue(); this.applyTo(value, settings, Origin.forCommandLine(config.getLongOption())); } } }
这里没从ParsedCommandLine中读取到这两项配置(未在命令行参数中配置这俩选项)。上面的这两步也可以说明为什么命令行参数优先级会高于配置文件,因为命令行参数会后读取进行覆盖(如果有的话)。
最后是LayoutToPropertiesConverter的convert方法:
public AllProperties convert(InitialProperties initialProperties, BuildLayoutResult layout) { BuildLayoutParameters layoutParameters = new BuildLayoutParameters(); //从上面的结果中获取currentDir、projectDir、gradleUserHomeDir、gradleInstallationHomeDir设置到layoutParameters中 layout.applyTo(layoutParameters); //把所有的属性都解析到properties这个Map中 Map<String, String> properties = new HashMap(); //加载{user_home}/.gradle/gradle.properties的属性 this.configureFromHomeDir(layoutParameters.getGradleInstallationHomeDir(), properties); //加载{App项目跟目录(settings所在目录)}/gradle.properties的属性 this.configureFromBuildDir(layoutParameters.getSearchDir(), properties); //这里其实就是获取命令行配置的.gradle/gradle.properties文件(如果有的话),为了遵循命令行参数优先 this.configureFromHomeDir(layout.getGradleUserHomeDir(), properties); //获取所有JVM默认配置属性 this.configureFromSystemPropertiesOfThisJvm((Map)Cast.uncheckedNonnullCast(properties)); properties.putAll(initialProperties.getRequestedSystemProperties()); return new LayoutToPropertiesConverter.Result(properties, initialProperties); }
看一下configureFromHomeDir方法:
private void configureFromHomeDir(File gradleUserHomeDir, Map<String, String> result) { //比如:/User/mph/.gradle/gradle.properties this.maybeConfigureFrom(new File(gradleUserHomeDir, "gradle.properties"), result); } private void maybeConfigureFrom(File propertiesFile, Map<String, String> result) { ... Properties properties = new Properties(); ... FileInputStream inputStream = new FileInputStream(propertiesFile); ... //会检索每一行的信息,按照=或:划分成key和value properties.load(inputStream); ... Iterator var10 = properties.keySet().iterator(); while(var10.hasNext()) { final Object key = var10.next(); ... result.put(key.toString(), properties.get(key).toString()); ... } }
接下来是configureFromBuildDir方法:
private void configureFromBuildDir(File currentDir, Map<String, String> result) { BuildLayout layout = this.buildLayoutFactory.getLayoutFor(currentDir, true); this.maybeConfigureFrom(new File(layout.getRootDirectory(), "gradle.properties"), result); }
首先layoutParameters.getSearchDir()方法如下:
public File getSearchDir() { return this.projectDir != null ? this.projectDir : this.currentDir; }
这里的projectDir是null(看构造),因此返回的是currentDir,同样在构造时初始化,值为系统属性“user.dir”的value,这个值是JVM默认属性之一,在JVM启动的时候就自动初始化了,代表着用户当前目录(gradlew的所在目录),即App项目根目录。
configureFromBuildDir方法中会调用getLayoutFor方法:
BuildLayout getLayoutFor(File currentDir, File stopAt) { //找到项目根目录下的settings文件 File settingsFile = this.findExistingSettingsFileIn(currentDir); if (settingsFile != null) { return this.layout(currentDir, settingsFile); } else { //如果buildSrc目录的情况,则一直往上查询到拥有settings文件的目录 for(File candidate = currentDir.getParentFile(); candidate != null && !candidate.equals(stopAt); candidate = candidate.getParentFile()) { settingsFile = this.findExistingSettingsFileIn(candidate); if (settingsFile != null) { return this.layout(candidate, settingsFile); } } return this.layout(currentDir, new File(currentDir, "settings.gradle")); } } /** * 可见,找到了项目根目录下的settings文件(可能是settings.gradle,也可能是settings.kt,所以这里没限制后缀) **/ @Nullable public File findExistingSettingsFileIn(File directory) { return this.scriptFileResolver.resolveScriptFile(directory, "settings"); } private BuildLayout layout(File rootDir, File settingsFile) { return new BuildLayout(rootDir, settingsFile.getParentFile(), FileUtils.canonicalize(settingsFile), this.scriptFileResolver); }
后面也是调用maybeConfigureFrom方法load文件获取属性。
DefaultCommandLineActionFactory的execute方法最后的ExceptionReportingAction.execute开始会开启一个调用链,依次调用:
- WelcomeMessageAction.execute方法
- DebugLoggerWarningAction.execute方法
- DefaultCommandLineActionFactory.ParseAndBuildAction.execute方法
DefaultCommandLineActionFactory.ParseAndBuildAction.execute方法如下:
public void execute(ExecutionListener executionListener) { List<CommandLineAction> actions = new ArrayList(); // actions.add(new DefaultCommandLineActionFactory.BuiltInActions()); //添加BuildActionsFactory DefaultCommandLineActionFactory.this.createActionFactories(this.loggingServices, actions); CommandLineParser parser = new CommandLineParser(); Iterator var4 = actions.iterator(); while(var4.hasNext()) { CommandLineAction actionx = (CommandLineAction)var4.next(); //BuiltInActions的configureCommandLineParser中设置了“help”、“h”、“?”和“v”、“version”这些基础option //BuildActionsFactory的configureCommandLineParser中又会执行一遍上面的解析加载属性过程(为什么又再次重复?我唯一能想到的就是解偶,之前的此操作是为了Logger服务的,这里的则是为了执行Action) actionx.configureCommandLineParser(parser); } Object action; try { //第三次解析了 ParsedCommandLine commandLine = parser.parse(this.args); //创建任务action action = this.createAction(actions, parser, commandLine); } catch (CommandLineArgumentException var6) { action = new DefaultCommandLineActionFactory.CommandLineParseFailureAction(parser, var6); } //执行action,比如:“assembleRelease” ((Action)action).execute(executionListener); }
createAction方法中会调用BuildActionsFactory的createAction方法:
public Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine) { Parameters parameters = this.parametersConverter.convert(commandLine, (File)null); ... } else if (parameters.getDaemonParameters().isEnabled()) { return this.runBuildWithDaemon(parameters.getStartParameter(), parameters.getDaemonParameters()); } else { return this.canUseCurrentProcess(parameters.getDaemonParameters()) ? this.runBuildInProcess(parameters.getStartParameter(), parameters.getDaemonParameters()) : this.runBuildInSingleUseDaemon(parameters.getStartParameter(), parameters.getDaemonParameters()); } }
parametersConverter是ParametersConverter,它的convert方法如下:
public Parameters convert(ParsedCommandLine args, @Nullable File currentDir) throws CommandLineArgumentException { //前三行和前面分析的一样 InitialProperties initialProperties = this.initialPropertiesConverter.convert(args); BuildLayoutResult buildLayout = this.buildLayoutConverter.convert(initialProperties, args, currentDir); AllProperties properties = this.layoutToPropertiesConverter.convert(initialProperties, buildLayout); StartParameterInternal startParameter = new StartParameterInternal(); //这行很关键 this.startParameterConverter.convert(args, buildLayout, properties, startParameter); DaemonParameters daemonParameters = new DaemonParameters(buildLayout, this.fileCollectionFactory, properties.getRequestedSystemProperties()); this.daemonParametersConverter.convert(args, properties, daemonParameters); return new Parameters(buildLayout, startParameter, daemonParameters); }
StartParameterConverter的convert方法如下:
public StartParameterInternal convert(ParsedCommandLine parsedCommandLine, BuildLayoutResult buildLayout, AllProperties properties, StartParameterInternal startParameter) throws CommandLineArgumentException { buildLayout.applyTo(startParameter); ... if (!parsedCommandLine.getExtraArguments().isEmpty()) { //终于用到了extraArguments,还记得吗,它是gradlew后面跟着的命令行参数,也就是通常使用的任务名,比如assembleRelease,它会赋值给StartParameter的taskRequests startParameter.setTaskNames(parsedCommandLine.getExtraArguments()); } //gradle的build参数option都在这里 this.buildOptionsConverter.convert(parsedCommandLine, properties, startParameter); return startParameter; }
最终返回一个Parameters:
public Parameters(BuildLayoutResult layout, StartParameterInternal startParameter, DaemonParameters daemonParameters) { this.layout = layout; this.startParameter = startParameter; this.daemonParameters = daemonParameters; }
BuildActionsFactory的createAction方法中构造完Parameters之后会根据状态执行不同的方法,比如是否在后台执行,最终都会执行到runBuildAndCloseServices方法,该方法最终会返回一个RunBuildAction,它的run方法如下:
public void run() { try { BuildActionResult result = this.executer.execute(new ExecuteBuildAction(this.startParameter), this.buildActionParameters, new DefaultBuildRequestContext(new DefaultBuildRequestMetaData(this.clientMetaData, this.startTime, ((ConsoleDetector)this.sharedServices.get(ConsoleDetector.class)).isConsoleInput()), new DefaultBuildCancellationToken(), new NoOpBuildEventConsumer())); if (result.hasFailure()) { throw new ReportedException(); } } finally { if (this.stoppable != null) { this.stoppable.stop(); } } }
executer是怎么来的?
以runBuildInProcess为例:private Runnable runBuildInProcess(StartParameterInternal startParameter, DaemonParameters daemonParameters) { ServiceRegistry globalServices = ServiceRegistryBuilder.builder().displayName("Global services").parent(this.loggingServices).parent(NativeServices.getInstance()).provider(new GlobalScopeServices(startParameter.isContinuous())).build(); return this.runBuildAndCloseServices(startParameter, daemonParameters, (BuildActionExecuter)globalServices.get(BuildExecuter.class), globalServices, globalServices.get(GradleUserHomeScopeServiceRegistry.class)); }
ServiceRegistryBuilder的build方法返回一个DefaultServiceRegistry:
public ServiceRegistry build() { DefaultServiceRegistry registry = new DefaultServiceRegistry(this.displayName, (ServiceRegistry[])this.parents.toArray(new ServiceRegistry[0])); Iterator var2 = this.providers.iterator(); while(var2.hasNext()) { Object provider = var2.next(); registry.addProvider(provider); } return registry; }
这里会调用DefaultServiceRegistry的addProvider方法,传入的是前面ServiceRegistryBuilder的provider方法中的参数GlobalScopeServices:
public DefaultServiceRegistry addProvider(Object provider) { this.assertMutable(); this.findProviderMethods(provider); return this; }
findProviderMethods如下:
private void findProviderMethods(Object target) { Class<?> type = target.getClass(); //这个方法中会检索target(即provider方法的参数,这里是GlobalScopeServices)所有的方法,将create开头的非静态方法添加到RelevantMethods的factories集合、void configure方法添加到configurers集合、以create或decorate开头的且参数中至少有一个和该方法返回类型一致的方法添加到decorators集合 //注意这些方法都会被封装到MethodHandleBasedServiceMethod RelevantMethods methods = RelevantMethods.getMethods(type); Iterator var4 = methods.decorators.iterator(); ServiceMethod method; while(var4.hasNext()) { method = (ServiceMethod)var4.next(); if (this.parentServices == null) { throw new ServiceLookupException(String.format("Cannot use decorator method %s.%s() when no parent registry is provided.", type.getSimpleName(), method.getName())); } this.ownServices.add(new DefaultServiceRegistry.FactoryMethodService(this, target, method)); } var4 = methods.factories.iterator(); while(var4.hasNext()) { method = (ServiceMethod)var4.next(); this.ownServices.add(new DefaultServiceRegistry.FactoryMethodService(this, target, method)); } var4 = methods.configurers.iterator(); //执行configure方法 while(var4.hasNext()) { method = (ServiceMethod)var4.next(); this.applyConfigureMethod(method, target); } }
applyConfigureMethod方法中会调用所有provider的configure方法,这里呢就是
NativeServices和GlobalScopeServices的void configure方法会被调用
,其他两种方法集合都以FactoryMethodService的形式添加到了ownServices。然后globalServices.get(BuildExecuter.class)就是DefaultServiceRegistry的get方法被调用,最终调用的是find方法:
public Object find(Type serviceType) throws ServiceLookupException { assertValidServiceType(unwrap(serviceType)); Service provider = this.getService(serviceType); return provider == null ? null : provider.get(); } private Service getService(Type serviceType) { this.serviceRequested(); return find(serviceType, this.allServices); }
getService方法最终调用了另一个find方法:
private static Service find(Type serviceType, ServiceProvider serviceProvider) { ... if (serviceType instanceof Class) { ... return serviceProvider.getService(serviceType); } ... }
serviceProvider在这里就是上面传入的allServices,这个值来自于构造时:
this.allServices = new DefaultServiceRegistry.CompositeServiceProvider(new ServiceProvider[]{this.ownServices, this.parentServices});
CompositeServiceProvider的getService方法如下:
public Service getService(Type serviceType) { ServiceProvider[] var2 = this.serviceProviders; int var3 = var2.length; for(int var4 = 0; var4 < var3; ++var4) { ServiceProvider serviceProvider = var2[var4]; Service service = serviceProvider.getService(serviceType); if (service != null) { return service; } } return null; }
因此我们得去ownServices和parentServices中找service。ownServices是DefaultServiceRegistry.OwnServices,它的getService中又会先从其providersByType中先找到ServiceProvider,然后再调用它的getService(type)方法得到结果。ownServices的providersByType默认的只有一个type为ServiceRegistry.class类型的ServiceProvider,其他的会从putServiceType方法添加,而这个方法会被collectProvidersForClassHierarchy方法调用:
public void collectProvidersForClassHierarchy(DefaultServiceRegistry.ClassInspector inspector, Class<?> serviceType, ServiceProvider serviceProvider) { Iterator var4 = inspector.getHierarchy(serviceType).iterator(); while(var4.hasNext()) { Class<?> type = (Class)var4.next(); ... //按照type保存ServiceProvider this.putServiceType(type, serviceProvider); } }
collectProvidersForClassHierarchy又被add方法调用,因此ServiceProvider就是调用add方法时传入的DefaultServiceRegistry.FactoryMethodService。
type是在collectProvidersForClassHierarchy方法中通过inspector.getHierarchy(serviceType)内产生的,也就是DefaultServiceRegistry.ClassInspector,它的getHierarchy方法如下:
Set<Class<?>> getHierarchy(Class<?> type) { return this.getDetailsForClass(type).types; } private DefaultServiceRegistry.ClassInspector.ClassDetails getDetailsForClass(Class<?> type) { DefaultServiceRegistry.ClassInspector.ClassDetails classDetails = (DefaultServiceRegistry.ClassInspector.ClassDetails)this.classes.get(type); if (classDetails == null) { //构造时传入ClassDetails的types DefaultServiceRegistry.ClassInspector.ClassDetails newDetails = new DefaultServiceRegistry.ClassInspector.ClassDetails(type); classDetails = (DefaultServiceRegistry.ClassInspector.ClassDetails)this.classes.putIfAbsent(type, newDetails); if (classDetails == null) { classDetails = newDetails; } } return classDetails; }
collectProvidersForClassHierarchy方法传入的type是serviceProvider.serviceClass,我们去看DefaultServiceRegistry.FactoryMethodService的serviceClass,最终找到type就是Method的返回类型。
找到DefaultServiceRegistry.FactoryMethodService后就要看他的getService方法,这个方法定义在它的一个上上级的父类SingletonService中:
public Service getService(Type serviceType) { //isSatisfiedBy判断type是否一致,一致则执行prepare方法 return !DefaultServiceRegistry.isSatisfiedBy(serviceType, this.serviceType) ? null : this.prepare(); }
prepare最终会返回DefaultServiceRegistry.FactoryMethodService本身,这就是find(Type serviceType)方法中的provider,它的的get方法返回值最终是调用了DefaultServiceRegistry.FactoryService的createServiceInstance方法创建的:
protected Object createServiceInstance() { //参数在前面的prepare方法里初始化的 Object[] params = this.assembleParameters(); Object result = this.invokeMethod(params); this.paramServices = null; return result; }
invokeMethod方法在其子类DefaultServiceRegistry.FactoryMethodService中实现:
protected Object invokeMethod(Object[] params) { ... return this.method.invoke(this.target, params); }
可见,这里使用反射调用了method。因此我们要找到executer,就得找到这个method的返回值,但很遗憾,没有找到BuildExecuter。
那就去找ownServices.add方法都在哪调用的,发现是在DefaultServiceRegistry的add方法中,于是我们去找所有调用这个方法的地方希望能找到答案。
最终我们在GlobalScopeServices的configure方法(之前说过这个方法先前已被调用)里找到:
void configure(ServiceRegistration registration, ClassLoaderRegistry classLoaderRegistry) { List<PluginServiceRegistry> pluginServiceFactories = (new DefaultServiceLocator(new ClassLoader[]{classLoaderRegistry.getRuntimeClassLoader(), classLoaderRegistry.getPluginsClassLoader()})).getAll(PluginServiceRegistry.class); Iterator var4 = pluginServiceFactories.iterator(); while(var4.hasNext()) { PluginServiceRegistry pluginServiceRegistry = (PluginServiceRegistry)var4.next(); //将加载的所有class都加到providers中 registration.add(PluginServiceRegistry.class, pluginServiceRegistry); //对每个加载的class都调用其registerGlobalServices方法 pluginServiceRegistry.registerGlobalServices(registration); } registration.add(BuildLayoutFactory.class); registration.add(DefaultScriptFileResolver.class); }
DefaultServiceLocator的getAll方法 -> findFactoriesForServiceType方法 -> implementationsOf方法 -> findServiceImplementations方法:
private <T> List<Class<? extends T>> findServiceImplementations(Class<T> serviceType) throws IOException { //在对应版本的gradle jar包下找到这个目录,serviceType是PluginServiceRegistry.class String resourceName = "META-INF/services/" + serviceType.getName(); Set<String> implementationClassNames = new HashSet(); List<Class<? extends T>> implementations = new ArrayList(); Iterator var5 = this.classLoaders.iterator(); while(var5.hasNext()) { ClassLoader classLoader = (ClassLoader)var5.next(); Enumeration resources = classLoader.getResources(resourceName); while(resources.hasMoreElements()) { URL resource = (URL)resources.nextElement(); List implementationClassNamesFromResource = this.extractImplementationClassNames(resource); ... Iterator var10 = implementationClassNamesFromResource.iterator(); while(var10.hasNext()) { String implementationClassName = (String)var10.next(); if (implementationClassNames.add(implementationClassName)) { try { //加载class Class<?> implClass = classLoader.loadClass(implementationClassName); ... implementations.add(implClass.asSubclass(serviceType)); } ... } } } } return implementations; }
META-INF/services/下面有很多类的全包名配置文件:
截屏2023-10-24 20.17.28.png找到PluginServiceRegistry这个配置,内容截取如下:
org.gradle.tooling.internal.provider.LauncherServices ...
这里面配置了很多class的全包名路径,findServiceImplementations方法就是加载这里面所有的class。最终我在LauncherServices中找到了答案,LauncherServices执行registerGlobalServices方法后会注册一个叫LauncherServices.ToolingGlobalScopeServices的provider:
public void registerGlobalServices(ServiceRegistration registration) { registration.addProvider(new LauncherServices.ToolingGlobalScopeServices()); }
这个类中有一个方法:
BuildExecuter createBuildExecuter(StyledTextOutputFactory styledTextOutputFactory, LoggingManagerInternal loggingManager, WorkValidationWarningReporter workValidationWarningReporter, GradleUserHomeScopeServiceRegistry userHomeServiceRegistry, ServiceRegistry globalServices) { return new SetupLoggingActionExecuter(loggingManager, new SessionFailureReportingActionExecuter(styledTextOutputFactory, Time.clock(), workValidationWarningReporter, new StartParamsValidatingActionExecuter(new BuildSessionLifecycleBuildActionExecuter(userHomeServiceRegistry, globalServices)))); }
可见其返回值正是我们苦苦寻找的BuildExecuter类型。所以executer的真实面目就是SetupLoggingActionExecuter,它的execute方法用了代理模式来进行链式调用(就像RxJava原理),这个过程中,StartParamsValidatingActionExecuter会验证诸如项目目录是否是文件夹、settings文件是否存在等条件,最后调用到BuildSessionLifecycleBuildActionExecuter的execute方法,其内部也是使用代理模式一直委派,直到BuildTreeLifecycleBuildActionExecutor的execute方法,然后又经过一系列的链式调用直到DefaultBuildTreeLifecycleController的doScheduleAndRunTasks方法(以上过程超级绕,):
private ExecutionResult<Void> doScheduleAndRunTasks() { //taskGraph是构建的任务图 return (ExecutionResult)this.taskGraph.withNewWorkGraph((graph) -> { //最终会通过StartParameter中的信息找到settings和其他gradle脚本,根据他们的信息初始化任务图的信息 this.workPreparer.scheduleRequestedTasks(graph); //执行任务 return this.workExecutor.execute(graph); }); }
看一下scheduleRequestedTasks流程:
public void scheduleRequestedTasks(BuildTreeWorkGraph graph) { this.buildController.prepareToScheduleTasks(); graph.scheduleWork((builder) -> { builder.withWorkGraph(this.targetBuild, WorkGraphBuilder::addRequestedTasks); }); }
prepareToScheduleTasks在DefaultBuildLifecycleController中实现:
public void prepareToScheduleTasks() { this.state.maybeTransition(DefaultBuildLifecycleController.State.Configure, DefaultBuildLifecycleController.State.TaskSchedule, () -> { this.hasTasks = true; this.modelController.prepareToScheduleTasks(); }); }
state是DefaultBuildLifecycleController.State:
private static enum State implements org.gradle.internal.model.StateTransitionController.State { //四个状态,配置、任务排序、准备运行、结束 Configure, TaskSchedule, ReadyToRun, Finished; private State() { } }
这四个状态就是Build的四个阶段。
maybeTransition的前两个参数是fromState和toState,标志着prepareToScheduleTasks方法是从Configure阶段到TaskSchedule阶段,maybeTransition方法中会调用doTransition方法,其会调用action的run方法,也就是maybeTransition的第三个参数,即会执行this.modelController.prepareToScheduleTasks方法,modelController的实现类我们去看VintageBuildModelController(还有一个ConfigurationCacheAwareBuildModelController,它是关于配置热构建的时候用的,这里只关注全新构建过程):
public void prepareToScheduleTasks() { //这个流程会解析读取settings脚本 this.prepareSettings(); //这个流程会解析读取其他脚本文件 this.prepareProjects(); }
prepareSettings中:
private void prepareSettings() { this.state.transitionIfNotPreviously(VintageBuildModelController.Stage.Created, VintageBuildModelController.Stage.SettingsLoaded, () -> { this.settingsPreparer.prepareSettings(this.gradle); }); }
这里的state是VintageBuildModelController.Stage:
private static enum Stage implements State { //settings脚本加载的三个状态,创建、加载、配置 Created, SettingsLoaded, Configured; private Stage() { } }
和maybeTransition相似的逻辑,最终会执行settingsPreparer.prepareSettings方法,即DefaultSettingsPreparer的prepareSettings方法:
public void prepareSettings(GradleInternal gradle) { SettingsLoader settingsLoader = gradle.isRootBuild() ? this.settingsLoaderFactory.forTopLevelBuild() : this.settingsLoaderFactory.forNestedBuild(); settingsLoader.findAndLoadSettings(gradle); }
去DefaultSettingsLoader看findAndLoadSettings方法:
public SettingsInternal findAndLoadSettings(GradleInternal gradle) { StartParameter startParameter = gradle.getStartParameter(); SettingsLocation settingsLocation = this.buildLayoutFactory.getLayoutFor(new BuildLayoutConfiguration(startParameter)); //解析脚本 SettingsInternal settings = this.findSettingsAndLoadIfAppropriate(gradle, startParameter, settingsLocation, gradle.getClassLoaderScope()); ProjectSpec spec = ProjectSpecs.forStartParameter(startParameter, settings); if (this.useEmptySettings(spec, settings, startParameter)) { settings = this.createEmptySettings(gradle, startParameter, settings.getClassLoaderScope()); } this.setDefaultProject(spec, settings); return settings; }
findSettingsAndLoadIfAppropriate方法如下:
private SettingsInternal findSettingsAndLoadIfAppropriate(GradleInternal gradle, StartParameter startParameter, SettingsLocation settingsLocation, ClassLoaderScope classLoaderScope) { SettingsInternal settings = this.settingsProcessor.process(gradle, settingsLocation, classLoaderScope, startParameter); this.validate(settings); return settings; }
然后执行ScriptEvaluatingSettingsProcessor的process方法,其中会通过applySettingsScript方法调用到ScriptPluginFactorySelector的create方法:
public ScriptPlugin create(ScriptSource scriptSource, ScriptHandler scriptHandler, ClassLoaderScope targetScope, ClassLoaderScope baseScope, boolean topLevelScript) { ScriptPlugin scriptPlugin = this.scriptPluginFactoryFor(scriptSource.getFileName()).create(scriptSource, scriptHandler, targetScope, baseScope, topLevelScript); return new BuildOperationScriptPlugin(scriptPlugin, this.buildOperationExecutor, this.userCodeApplicationContext); }
//根据gradle脚本后缀名决定使用哪一种ScriptPluginFactory,针对.gradle.kts的会应用KotlinScriptPluginFactory,针对gradle后缀的或者其他未知的扩展名会应用 private ScriptPluginFactory scriptPluginFactoryFor(String fileName) { Iterator var2 = this.scriptingLanguages().iterator(); ScriptingLanguage scriptingLanguage; do { if (!var2.hasNext()) { return this.defaultScriptPluginFactory; } scriptingLanguage = (ScriptingLanguage)var2.next(); } while(!fileName.endsWith(scriptingLanguage.getExtension())); String provider = scriptingLanguage.getProvider(); if (provider != null) { return this.instantiate(provider); } else { return this.defaultScriptPluginFactory; } } private List<ScriptingLanguage> scriptingLanguages() { return ScriptingLanguages.all(); }
ScriptingLanguages.ALL如下:
private static final List<ScriptingLanguage> ALL = Collections.unmodifiableList(Arrays.asList(scriptingLanguage(".gradle", (String)null), scriptingLanguage(".gradle.kts", "org.gradle.kotlin.dsl.provider.KotlinScriptPluginFactory")));
可见,这里支持两种gradle脚本扩展名,一种是.gradle,一种是.gradle.kts(这种需要KotlinScriptPluginFactory来解析),即支持kotlin语法。
网友评论