10. Gradle自身源代码编译流程 | 您所在的位置:网站首页 › 编译gradle › 10. Gradle自身源代码编译流程 |
10. Gradle自身源代码编译流程
发布时间:2020-06-30 20:27:27
来源:网络
阅读:1358
作者:rongwei84n
栏目:系统运维
一句话概括Gradle自身源代码编译流程-用gradle来编译Gradle 下面我们正式开始分析: 因为我们拿到源代码后,首先接触的是gradlew.bat,也就是Gradle源代码自身编译的命令。所以,我们还是从这个脚本开始分析。 一. Eclipse打开源代码 为了方便修改代码,我选择用Eclipse来打开这个工程。步骤是: File->New->Java Project->Use default location去掉勾选->Browse选择Gradle源代码目录->finish 二. gradlew.bat脚本 1. 还是从我们编译Gradle源代码的命令入手 gradlew.bat assemble 那首先来看下gradlw.bat : @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS=-Xmx1024m -Dfile.encoding=UTF-8 @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega执行gradlew.bat assemble时,首先来看看这个脚本里面的各个变量值: CLASSPATH:gradle-3.1\\gradle\wrapper\gradle-wrapper.jar 表示的是gradle源代码里面gralde\wrapper\目录下gradle-wrapper.jar,这个jar也是待会要执行的编译操作要运行的jar。 DEFAULT_JVM_OPTS:-Xmx1024m -Dfile.encoding=UTF-8 表示的是Java虚拟机配置 JAVA_OPTS:空 GRADLE_OPTS:空 CMD_LINE_ARGS:assemble 表示要执行的gradle task名字 然后接下来,就会执行重要的一句,启动gradle-wrapper.jar里面的GradleWrapperMain.main函数。 @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%那这里可能有个疑问,就是此时Gradle源代码还没有编译出来,哪来的gradle-wrapper.jar。这个问题就像鸡和蛋的问题,先有鸡还是先有蛋。 Gradle的做法是先有鸡后有蛋,那第一只鸡哪来的呢? Gradle是自己给它造了一只鸡。 请看gradle\wrapper\gradle-wrapper.jar 所以这里有个小的细节要提醒下大家,大家修改完GradleWrapperMain这个类之后,比如打印了日志,如果要验证它的结果,需要首先执行几个步骤 gradlew.bat assemble 进行编译把编译出来的gradle-wrapper.jar覆盖到gradle\wrapper\目录下。 再执行一个gradlew.bat assemble就可以在命令行里面验证。 三. GradleWrapperMain 文件路径: gradle-3.1\subprojects\wrapper\src\main\java\org\gradle\wrapper\GradleWrapperMain.java GradleWrapperMain位置subprojects里面,Gradle源代码把各个工程拆分成各个模块,类似于插件的方式。 也就是说每个功能都拆分成一个插件,然后使用的时候进行配置,比如某个插件需要依赖于哪几个插件,那就直接配置上就可以。 配置的路径在每个插件的jar包里面,名称叫做xxx-classpath.properties,里面有个projects属性,配置了这个插件依赖的插件(也可以叫项目或者模块)。 这种设计思想可以让整个项目层次清晰,同时便于多个团队间合作开发。 看下GradleWrapperMain的main函数: public class GradleUserHomeLookup { public static final String DEFAULT_GRADLE_USER_HOME = System.getProperty("user.home") + "/.gradle"; public static final String GRADLE_USER_HOME_PROPERTY_KEY = "gradle.user.home"; public static final String GRADLE_USER_HOME_ENV_KEY = "GRADLE_USER_HOME"; public static File gradleUserHome() { String gradleUserHome; if ((gradleUserHome = System.getProperty(GRADLE_USER_HOME_PROPERTY_KEY)) != null) { return new File(gradleUserHome); } if ((gradleUserHome = System.getenv(GRADLE_USER_HOME_ENV_KEY)) != null) { return new File(gradleUserHome); } return new File(DEFAULT_GRADLE_USER_HOME); } } public static void main(String[] args) throws Exception { File wrapperJar = wrapperJar(); System.out.println("wrapperJar: " + wrapperJar); File propertiesFile = wrapperProperties(wrapperJar); File rootDir = rootDir(wrapperJar); ... File gradleUserHome = gradleUserHome(options); System.out.println("gradleUserHome: " + gradleUserHome + " rootDir: " + rootDir + " options: " + options+ "propertiesFile: " + propertiesFile); addSystemProperties(gradleUserHome, rootDir); Logger logger = logger(options); WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile); wrapperExecutor.execute( args, new Install(logger, new Download(logger, "gradlew", wrapperVersion()), new PathAssembler(gradleUserHome)), new BootstrapMainStarter()); }打印的log是: wrapperJar: E:\work_space\gradle-source-from-csdn\gradle-3.1\gradle\wrapper\gradle-wrapper.jar gradleUserHome: D:\gradle_jar_cache rootDir: E:\work_space\gradle-source-from-csdn\gradle-3.1 options: options: , extraArguments: 'assemble', removedOptions: propertiesFile: E:\work_space\gradle-source-from-csdn\gradle-3.1\gradle\wrapper\gradle-wrapper.properties这个日志已经很清楚的说明了各个变量的值。 需要说明的一点是gradleUserHome,这个是Gradle下载其他Jar包的存放地址,默认是c盘的user/xxx/.gradle/目录。 但是这个目录是可以配置的,配置GRADLE_USER_HOME环境变量即可。 这点从上面代码getUserHome可以清楚的看到。 另外,程序的最后面Gradle执行WrapperExecutor.execute(xxxx)方法,这个比较关键。 四. WrapperExecutor.execute 1. execute public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception { File gradleHome = install.createDist(config); bootstrapMainStarter.start(args, gradleHome); }可以看到execute里面没有什么东西,调用的是传入的install.createDist和bootstrapMainStarter.start方法,所以,需要分析下这两个方法。 2. Install.createDist new Install(logger, new Download(logger, "gradlew", wrapperVersion()), new PathAssembler(gradleUserHome)) public File createDist(final WrapperConfiguration configuration) throws Exception { final URI distributionUrl = configuration.getDistribution(); final String distributionSha256Sum = configuration.getDistributionSha256Sum(); final PathAssembler.LocalDistribution localDistribution = pathAssembler.getDistribution(configuration); final File distDir = localDistribution.getDistributionDir(); final File localZipFile = localDistribution.getZipFile(); System.out.println("distributionUrl: " + distributionUrl + " distributionSha256Sum: " + distributionSha256Sum + " localDistribution: " + localDistribution + " distDir: " + distDir + " localZipFile: " + localZipFile); return exclusiveFileAccessManager.access(localZipFile, new Callable() { public File call() throws Exception { final File markerFile = new File(localZipFile.getParentFile(), localZipFile.getName() + ".ok"); if (distDir.isDirectory() && markerFile.isFile()) { return getAndVerifyDistributionRoot(distDir, distDir.getAbsolutePath()); } boolean needsDownload = !localZipFile.isFile(); if (needsDownload) { File tmpZipFile = new File(localZipFile.getParentFile(), localZipFile.getName() + ".part"); tmpZipFile.delete(); logger.log("Downloading " + distributionUrl); download.download(distributionUrl, tmpZipFile); tmpZipFile.renameTo(localZipFile); } List topLevelDirs = listDirs(distDir); for (File dir : topLevelDirs) { logger.log("Deleting directory " + dir.getAbsolutePath()); deleteDir(dir); } verifyDownloadChecksum(configuration.getDistribution().toString(), localZipFile, distributionSha256Sum); logger.log("Unzipping " + localZipFile.getAbsolutePath() + " to " + distDir.getAbsolutePath()); unzip(localZipFile, distDir); File root = getAndVerifyDistributionRoot(distDir, distributionUrl.toString()); setExecutablePermissions(root); markerFile.createNewFile(); return root; } }); }这是打印的日志: distributionUrl: https://services.gradle.org/distributions/gradle-3.1-rc-1-bin.zip distributionSha256Sum: null localDistribution: org.gradle.wrapper.PathAssembler$LocalDistribution@4a574795 distDir: D:\gradle_jar_cache\wrapper\dists\gradle-3.1-rc-1-bin\3uhcvxvcic1j9jh0j26e3y151 localZipFile: D:\gradle_jar_cache\wrapper\dists\gradle-3.1-rc-1-bin\3uhcvxvcic1j9jh0j26e3y151\gradle-3.1-rc-1-bin.zip这里有几个问题: a. 下载zip包 createDist其实就是去下载distributionUrl描述的zip包。 其实这还是个鸡和蛋的问题,Gradle源代码是用Gradle来编译的,现在我们只有源代码,那怎么编译呢? 所以就只能先从服务器上把Gradle zip包下载下来。 b. distributionUrl定义位置 gradle源代码根目录/gradle/wrapper/gradle-wrapper.properties 也就是: #Mon Sep 12 15:17:35 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-rc-1-bin.zip配置文件位置可以在WrapperExecutor的构造函数看出来: ublic static WrapperExecutor forProjectDirectory(File projectDir) { return new WrapperExecutor(new File(projectDir, "gradle/wrapper/gradle-wrapper.properties"), new Properties()); }c. 根据md5计算出zip文件的存放目录 distDir: D:\gradle_jar_cache\wrapper\dists\gradle-3.1-rc-1-bin\3uhcvxvcic1j9jh0j26e3y151distDir目录,也就是下载下来的zip文件存放目录有一串字符串,这是根据md5算出来的,保证唯一性,代码如下: 文件路径: subprojects\wrapper\src\main\java\org\gradle\wrapper\PathAssembler.java /** * This method computes a hash of the provided {@code string}. ** The algorithm in use by this method is as follows: * * Compute the MD5 value of {@code string}. * Truncate leading zeros (i.e., treat the MD5 value as a number). * Convert to base 36 (the characters {@code 0-9a-z}). * */ private String getHash(String string) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); byte[] bytes = string.getBytes(); messageDigest.update(bytes); return new BigInteger(1, messageDigest.digest()).toString(36); } catch (Exception e) { throw new RuntimeException("Could not hash input string.", e); } } d. 执行时机 在第一次执行gradlew.bat assemble的时候会去下载,然后解压。 所以第一次执行gradlew.bat assemble会出现这样日志: Downloading xxxx....... Unzipping..... 3. bootstrapMainStarter.start 文件路径: subprojects\wrapper\src\main\java\org\gradle\wrapper\BootstrapMainStarter.java public void start(String[] args, File gradleHome) throws Exception { System.out.println("BootstrapMainStarter gradleHome: " + gradleHome); if (args != null) { for(int i = 0; i |
CopyRight 2018-2019 实验室设备网 版权所有 |