./gradlew installDebug 命令行启动*
创建项目的时候 IDE 在项目的根目录会生成 gradlew 可执行文件,gradlew
中很多逻辑是判断系统类型的,gradlew.bat 是
windows下的实现思路一样的。这里的主要的逻辑:
- 先定义了 java 程序执行需要的 CLASSPATH 目录
- 然后把 JAVACMD 设置为 java 的入口文件
- 最后把开发者定义的 JAVA_OPTS、DEFAULT_JVM_OPTS、进程名都拼接为一串参数执行
- 文件最后一行的 exec 开始打包,可以替换为 echo 看具体执行的命令是啥
gradle-wrapper.jar 是 gradle 的入口引导
上文中提到的 CLASSPATH 是 gradle 的入口 jar 实际目录在项目根目录
./gradle/wrapper/ 下,并不是 gradle 的编译主逻辑,根据 gradlew
可执行文件指定的入口类 org.gradle.wrapper.GradleWrapperMain
在里面找到了 main 函数,主要逻辑是:
- 加载项目目录下的 gradle.properties 到环境变量
- 解析同目录下的 gradle-wrapper.properties 用 Install 下载指定的url 到本地
- BootstrapMainStarter 加载真正的 org.gradle.launcher.GradleMain
进行打包,如果看源码去目录
~/.gradle/wrapper/dists/gradle-6.7.1-bin/bwlcbys1h7rz3272sye1xwiv6/gradle-6.7.1/lib
这样设计的好处应该是把 gradle 版本和项目解耦,方便替换 gradle
的版本,如果想自定义 gradle 版本或者因为国内访问 url
超时,可以直接替换配置文件的 distributionUrl
GradleUserHomeLookup 里主要声明了默认目录 ~/.gradle
WrapperExecutor 并不是线程池,gradle 代码特色是只要切换一层逻辑就会有
xxxExecutor,Install 和 BootstrapMainStarter 实际是串行的这里使用 org.gradle.debug=true 打断点不生效,因为这里是纯 java 进程没有处理任何
gradle 参数,应该使用 java
-agentlib:jdwp=transport=dt_socket,server=y,address=5006 -classpath
./gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain
installdebug 和 jdb -attach localhost:5006两个命令行进行断点调试
启动 Daemon 进程
- GradleMain.main-> ProcessBootstrap.run 这是一个通用的替换类加载器的反射
- org.gradle.launcher.Main.run-> .doAction
- DefaultCommandLineActionFactory.convert->WithLogging.execute
- ExceptionReportingAction.execute
- NativeServicesInitializingAction.execute
- DefaultCommandLineActionFactory.ParseAndBuildAction.execute
- DefaultCommandLineActionFactory.BuiltInActions.createAction
- BuildActionsFactory.createAction 区分是否要使用守护进程执行
- RunBuildAction.run 使用的 BuildActionExecuter 是上一步提供的
- DaemonClient.execute通过 DefaultDaemonConnector 连接 ExecutorFactory
执行池,只管 dispatch 发送参数,DefaultDaemonStarter 才开始处理 gradle
任务,这里判断 org.gradle.daemon.debug 添加了调试功能,端口固定 5005
- DefaultExecActionFactory.newExec->DefaultExecHandleBuilder
- DefaultExecHandle.start
- ProcessBuilder.start 开启了新的线程
- GradleDaemon.main > ProcessBootstrap.run 是新进程的入口,这里可以用
org.gradle.debug=true 打断点调试了 - DaemonMain.doAction 创建 Daemon 和 DaemonServices,同时在 DaemonServices
的构造方法初始化 GlobalScopeServices 内部使用 DefaultServiceLocator 加载
“META-INF/services/”配置的所有 PluginServiceRegistry,确定了 gradle
能处理哪些任务 - DaemonServerConnector
的实现类DaemonTcpServerConnector(TcpIncomingConnector).start()
当每次执行./gradlew installDebug 的时候 TcpIncomingConnector 会收到
ConnectCompletion 再包装成 SynchronizedDispatchConnection(SocketConnection)
分发给 DefaultIncomingConnectionHandler.handle 切换线程转到
ConnectionWorker.run ,经过DefaultDaemonConnection.receive 读取到Command
实际类型为
org.gradle.launcher.daemon.protocol.Build(ExecuteBuildAction(StartParameterInternal(DefaultTaskExecutionRequest(args))))
里面包含里命令的字符串,然后handleCommand
处理数据。这是一个死循环,这个进程前面的逻辑只执行一次 - DaemonCommandExecuter.executeCommand-> DaemonCommandExecution. proceed
- 顺序执行下面的 action,都是在 DaemonServices.createDaemonCommandActions 创建
Daemon的时候准备好的,实例创建一次执行多次。前面都是验证是否可以执行的,最后一个
ExecuteBuild 触发 BuildExecuter。这种一层套一层的设计思路在 gradle
源码中十分普遍
- HandleStop
- HandleInvalidateVirtualFileSystem
- HandleCancel
- HandleReportStatus
- ReturnResult
- StartBuildOrRespondWithBusy
- EstablishBuildEnvironment
- LogToClient
- LogAndCheckHealth
- ForwardClientInput
- RequestStopIfSingleUsedDaemon
- ResetDeprecationLogger
- WatchForDisconnection
- ExecuteBuild
- 继续往下走主流程,又是一层套一层的执行逻辑,代码风格上 16
点像装饰,这条像代理。我猜不是同一个开发写的,经过同一个老板 review
的,代码风格不一样但是执行逻辑完全一样
- SetupLoggingActionExecuter
- SessionFailureReportingActionExecuter
- StartParamsValidatingActionExecuter
- GradleThreadBuildActionExecuter
- SessionScopeLifecycleBuildActionExecuter
这前面的都只创建一个实例,在LauncherServices$ToolingGlobalScopeServices.createBuildExecuter
创建,所有的参数保存在BuildSessionState 中 - SubscribableBuildActionExecuter
后面的每次执行到任务创建LauncherServices$ToolingBuildSessionScopeServices.
createBuildExecuter - ContinuousBuildActionExecuter
- BuildTreeScopeLifecycleBuildActionExecuter ,所有的参数保存在 BuildTreeState
中 - InProcessBuildActionExecuter 由 DefaultIncludedBuildRegistry.createRootBuild
方法把前面收到的 ExecuteBuildAction 转换为
GradleBuildController(DefaultGradleLauncher(BuildScopeServices(DefaultServiceRegistry.OwnServices(BuildDefinition(StartParameter)))))
- LauncherServices.createActionExecuter 创建了下面这些Runner
- RunAsBuildOperationBuildActionRunner
- BuildCompletionNotifyingBuildActionRunner
- FileSystemWatchingBuildActionRunner
- ValidatingBuildActionRunner
- BuildOutcomeReportingBuildActionRunner
- ChainingBuildActionRunner
- ExecuteBuildActionRunner
ServiceRegistry 和 PluginServiceRegistry 所有逻辑的起始
- ServiceRegistry 的主要实现类有
DaemonServices、DaemonClientServices、ConnectorServiceRegistry
都是随进程存活
- findProviderMethods 把当前实现类的所有方法转成 FactoryMethodService 存到
allServices.providersByType<返回类型,FactoryMethodService > 里。 - get(Class) 直接拿到上面的 FactoryMethodService extends SingletonService
如果第一次调用 create 一个实例,再次调用返回缓存。因为这个 Daemon
进程不销毁,无论多少次构建任务都只创建一次实例 - getFactory(Class) 和上面一点很像,支持创建出来的是个池子,比如
ConnectorServiceRegistry
- PluginServiceRegistry 的主要实现类有
DependencyServices、LauncherServices、ExecutionServices、MavenPublishServices、XcodeServices
分别对应了 gradle
的不同能力,每种能力对不同的生命周期可以添加不同的处理方式,生命周期从长到短依次为:
- registerGlobalServices 全局进程内唯一 GlobalScopeServices.configure 执行
- registerGradleUserHomeServices 不是安卓项目目录, 是 gradle 的目录,和
Global 基本一样,正常不会搞多个 gradle 目录,在
BuildSessionState(GradleUserHomeScopeServiceRegistry) 根据传入的传入的 file
目录缓存在 map 里,GradleUserHomeScopeServices.configure 执行 - registerBuildSessionServices 每一次执行命令 ,在 BuildSessionState
构造方法同时初始化 BuildSessionScopeServices.configure 执行,相比
GradleUserHome 不会缓存 - registerBuildTreeServices 每个命令的任务树 在 BuildTreeState
构造方法同时初始化 BuildTreeScopeServices.configure 执行 - registerBuildServices 任务树的 build 阶段,随 DefaultGradleLauncher
初始化执行 BuildScopeServices 并注册 ServiceRegistration - registerGradleServices 随 GradleScopeServices 初始化执行
- registerSettingsServices 随 DefaultSettings 初始化的 SettingsScopeServices
内部执行,DefaultGradleLauncher.prepareSettings 阶段执行 - registerProjectServices 随 DefaultProject 初始化的 ProjectScopeServices
内部执行,DefaultGradleLauncher.prepareProjects 阶段执行,需要注意每个
android 项目下的 module 和 DefaultProject 一一对应,每个 build.gradle
都对应不同的 DefaultProject,根据层级有父子关系
gradle 里的 execute 大部分不是创建线程执行,只是因为当前逻辑需要分层。可能是要加 log,可能是为了抓
Exception 不让进程崩溃,也可能是为了统计执行时间等等gradle 里的接口定义的十分多,为有能力的开发者提供了足够的扩展性,现有的实现类一般都以
Default+接口名
GradleBuildController.execute -> DefaultGradleLauncher.doClassicBuildStages 分为 4 个主要步骤
- 初始化 setting.gradle 根据 include 的目录层级创建树状关系的
DefaultProjectDescriptor
- prepareSettings
- BuildOperationFiringSettingsPreparer$LoadBuild.run
- DefaultSettingsPreparer.prepareSettings
- DefaultSettingsLoader.findAndLoadSettings
- BuildOperationSettingsProcessor.process
- RootBuildCacheControllerSettingsProcessor.process
- SettingsEvaluatedCallbackFiringSettingsProcessor.process
- ScriptEvaluatingSettingsProcessor.process
- 创建 setting.gradle 对应的 DefaultSettings :SettingsFactory.createSettings
-> DependencyInjectingInstantiator.newInstance ->
ClassBuilderImpl.generate - 为 DefaultSettings 赋值 :applySettingsScript ->
BuildOperationScriptPlugin.apply ->
DefaultScriptPluginFactory$ScriptPluginImpl.apply 解析 gradle
文件,主要是赋值 rootProjectDescriptor
- 每一个 build.gradle 初始化一个 DefaultProject
- prepareProjects
- BuildOperationFiringProjectsPreparer$ConfigureBuild.run
- BuildTreePreparingProjectsPreparer.prepareProjects -> 这里初始化了 buildSrc 自定义插件
- 创建 build.gradle 对应的 DefaultProject :NotifyingBuildLoader.load ->
ProjectPropertySettingBuildLoader.load ->
InstantiatingBuildLoader.createProjects -> ProjectFactory.createProject ->
DependencyInjectingInstantiator.newInstance -> ClassBuilderImpl.generate 和
settings.gradle 同级目录的 build.gradle 是 rootProject,在
InstantiatingBuildLoader.createProjects -> createChildProjectsRecursively
每一个 module 都会创建一个 childProject - 解析 build.gradle 为 DefaultProject 赋值:prepareProjects ->
DefaultProjectsPreparer.prepareProjects ->
TaskPathProjectEvaluator.configureHierarchy -> DefaultProject.evaluate ->
LifecycleProjectEvaluator$EvaluateProject.run ->
这里解析plugin,包含安卓的和自定义的,所有的需要的 task 都在这里添加到
project 里,ConfigureActionsProjectEvaluator.evaluate 包含三个步骤
- PluginsProjectConfigureActions.execute
- HelpTasksAutoApplyAction.execute
- KotlinScriptingModelBuildersRegistrationAction.execute
- BuildInitAutoApplyAction.execute
- WrapperPluginAutoApplyAction.execute
- BuildScriptProcessor.execute -> BuildOperationScriptPlugin.apply ->
DefaultScriptPluginFactory$ScriptPluginImpl.apply - DelayedConfigurationActions
- 构建任务树生成 TaskGraph
- prepareTaskExecution
- BuildOperationFiringTaskExecutionPreparer.prepareForTaskExecution
- BuildOperationFiringTaskExecutionPreparer$CalculateTaskGraph.run ->
populateTaskGraph - DefaultTaskExecutionPreparer.prepareForTaskExecution ->
DefaultBuildConfigurationActionExecuter.select
- ExcludedTaskFilteringBuildConfigurationAction.configure
过滤配置的不需要执行的 task - DefaultTasksBuildExecutionAction.configure 如果没带任何参数执行默认的 task
- TaskNameResolvingBuildConfigurationAction.configure ->
DefaultTaskExecutionGraph(DefaultExecutionPlan).addEntryTasks -> doAddNodes
-> LocalTaskNode.resolveDependencies -> getDependencies ->
TaskDependencyResolver.resolveDependenciesFor ->
CompositeResolvedArtifactSet.visitDependencies ->
FailureCollectingTaskDependencyResolveContext.add 判断是否为
TaskDependencyContainer 递归添加所有依赖的 task - DefaultTaskExecutionGraph.populate
- DefaultExecutionPlan.determineExecutionPlan
根据依赖关系生成任务先后执行的队列,可以说是 gradle 的核心
- 执行构建任务
- runWork
- BuildOperationFiringBuildWorkerExecutor.execute
- BuildOperationFiringBuildWorkerExecutor$ExecuteTasks.run
- DeprecateUndefinedBuildWorkExecutor.execute
- IncludedBuildLifecycleBuildWorkExecutor.execute
- DefaultBuildWorkExecutor.execute
- DryRunBuildExecutionAction.execute 使用 --dry-run 参数调过所有 task
的执行(调试用) - SelectedTaskExecutionAction.execute 执行 DefaultTaskExecutionGraph
- DefaultTaskExecutionGraph.execute -> executeWithServices
- DefaultPlanExecutor.process
- DefaultPlanExecutor.startAdditionalWorkers
开启多线程后面的方法都是多线程执行的 - DefaultPlanExecutor$ExecutorWorker.run -> executeNextNode 拆分每一个 Node
- DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute
- DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute
- WorkNodeExecutor.execute 如果需要同步执行 实现接口 SelfExecutingNode
- LocalTaskNodeExecutor.execute
- EventFiringTaskExecuter.execute 返回值 TaskExecuterResult 放到 context 里
- CatchExceptionTaskExecuter
- SkipOnlyIfTaskExecuter.execute 跳过 enable=false 的任务
- SkipTaskWithNoActionsExecuter.execute 跳过不包含任何 action 的任务
- ResolveTaskExecutionModeExecuter.execute
- FinalizePropertiesTaskExecuter.execute
- CleanupStaleOutputsExecuter.execute
- ExecuteActionsTaskExecuter.execute -> executeIfValid
- DefaultWorkExecutor.execute 返回值 return new TaskExecuterResult 参数放到 context 里,下面的这堆都从 ExecutionGradleServices.createWorkExecutor 里创建
- LoadExecutionStateStep.execute 这里开始切换了代码风格,Task 不放参数里
- MarkSnapshottingInputsStartedStep.execute
- SkipEmptyWorkStep.execute 调过没有输入的 task
- ValidateStep.execute
- CaptureStateBeforeExecutionStep.execute
- ResolveCachingStateStep.execute
- MarkSnapshottingInputsFinishedStep.execute
- ResolveChangesStep.execute
- SkipUpToDateStep.execute 检查是否有变化的文件,调过没有变化的task
- RecordOutputsStep.execute
- StoreExecutionStateStep.execute
- CacheStep.execute -> executeWithoutCache
- BroadcastChangingOutputsStep.execute
- SnapshotOutputsStep.execute
- CreateOutputsStep.execute
- TimeoutStep.execute -> executeWithoutTimeout 检查是否配置了这个任务的超时时间
- CancelExecutionStep.execute
- ResolveInputChangesStep.execute
- CleanupOutputsStep.execute
- ExecuteStep.execute -> executeWithPreviousOutputFiles
- ExecuteActionsTaskExecuter$TaskExecution.execute -> executeWithPreviousOutputFiles -> executeActions 反射到 task 的实现类执行
下载 dependencies 依赖库
- prepareProjects 解析 build.gradle 阶段
DefaultScriptPluginFactory$ScriptPluginImpl.apply 继续往下看 - DefaultPluginRequestApplicator.applyPlugins ->
defineScriptHandlerClassScope - DefaultScriptHandler.getScriptClassPath
- DefaultScriptClassPathResolver.resolveClassPath
- CompositeBuildClassPathInitializer.execute
- DefaultConfiguration$ConfigurationArtifactCollection.getArtifacts ->
ensureResolved
- DefaultConfiguration$ConfigurationFileCollection.getSelectedArtifacts ->
resolveToStateOrLater -> resolveExclusively -> resolveGraphIfRequired ->
ErrorHandlingConfigurationResolver((ShortCircuitEmptyConfigurationResolver(DefaultConfigurationResolver(DefaultArtifactDependencyResolver)))).resolveGraph
-> DependencyGraphBuilder.attachToTargetRevisionsSerially ->
EdgeState.attachToTargetConfigurations -> calculateTargetConfigurations ->
ComponentState.getMetadata -> resolve -> ClientModuleResolver. resolve ->
RepositoryChainComponentMetaDataResolver.resolveModule -> findBestMatch
配置了多个仓库会挨个遍历找到第一个 Resolved 的 - DefaultLenientConfiguration.select
- DefaultVisitedArtifactResults.select
- CompositeResolvedArtifactSet.of
- DefaultLenientConfiguration.visitArtifacts
- ParallelResolveArtifactSet$VisitingSet.visit
- ParallelResolveArtifactSet$VisitingSet$StartVisitAction.execute
- CompositeResolvedArtifactSet.startVisit
- ArtifactBackedResolvedVariant$SingleArtifactSet.startVisit
如果需要下载到本地 - DefaultBuildOperationQueue.add 内部有线程池 下面的调用都为异步执行
- DownloadArtifactFile.run
- DefaultResolvedArtifact.getFile
- DefaultArtifactSet$LazyArtifactSource.create
- RepositoryChainArtifactResolver.resolveArtifact ->
- getLocalAccess() 加载本地缓存
- CachingModuleComponentRepository$LocateInCacheRepositoryAccess.resolveArtifact -> resolveArtifactFromCache 返回值 cache 是 null 或 isMissing
都会走到下载依赖 - InMemoryModuleArtifactCache.lookup
- DefaultModuleArtifactCache.lookup -> super.lookup ->
getPersistentCache().get(key); - WritableArtifactCacheLockingManager$CacheLockingPersistentCache.useCache
- DefaultCacheFactory$ReferenceTrackingCache($DirCacheReference(DefaultPersistentDirectoryStore(DefaultCacheAccess))).useCache
- DefaultMultiProcessSafePersistentIndexedCache(BTreePersistentIndexedCache($HeaderBlock.getRoot().load().StateCheckBlockStore(StateCheckBlockStore(FreeListBlockStore(CachingBlockStore(FileBackedBlockStore(FileBackedBlockStore$BlockImpl))))).read())).get
主要逻辑是读 module-artifact.bin 缓存找到确切的缓存文
- getRemoteAccess() 如果本地缓存没读到 下载依赖并缓存
- CachingModuleComponentRepository$ResolveAndCacheRepositoryAccess.resolveArtifact
- ExternalResourceResolver$RemoteRepositoryAccess.resolveArtifact
- DefaultExternalResourceArtifactResolver.resolveArtifact ->
downloadStaticResource -> downloadByUrl - DefaultCacheAwareExternalResourceAccessor.getResource ->
ProducerGuard$AdaptiveProducerGuard.guardByKey - BuildOperationFiringExternalResourceDecorator.getMetaData
- AccessorBackedExternalResource(DefaultExternalResourceConnector(HttpResourceAccessor)).getMetaData
- HttpClientHelper.performHead -> performRawHead -> performRequest ->
executeGetOrHead -> performHttpRequest -> getClient().execute
这里使用了HttpClient 开启了同步下载流程
安卓编译流程-是 gradle 的一种插件
- JavaCompile 只编译改过的类
- IncrementalInputsTaskAction.doExecute
- JavaCompile.performIncrementalCompilation -> performCompilation
- CompileJavaBuildOperationReportingCompiler.execute
- IncrementalResultStoringCompiler.execute
- SelectiveCompiler.execute ->
JavaRecompilationSpecProvider.provideRecompilationSpec ->
JavaRecompilationSpecProvider.processOtherChanges ->
SourceFileChangeProcessor.processChange ->
PreviousCompilation.getDependents -> ClassSetAnalysis.getRelevantDependents
根据上次编译结果有依赖关系的类都会重新编译 - ModuleApplicationNameWritingCompiler.execute
- AnnotationProcessorDiscoveringCompiler.execute
- NormalizingJavaCompiler.execute -> delegateAndHandleErrors
- JdkJavaCompiler.execute
- ResourceCleaningCompilationTask.call
- AnnotationProcessingCompileTask.call
- IncrementalCompileTask.call
- JavacTaskImpl. call 这里就是 jdk 的 javac
- 执行自定义插件
………… 后面还有很多 Android build tools 流程,未完待续
编译流程的运用
- 自定义插件
- AnnoationProcessor 是 javac 的一种代码注入技术,在 JavaCompile 期间处理。比如ButterKnife
- 定义 javax.annotation.processing.AbstractProcessor 的实现类
- 添加 src/main/resources/META-INF/services/javax.annotation.processing.Processor
- 添加 src/main/resources/META-INF/gradle/incremental.annotation.processors
- 引用 annotationProcessor project(path: ‘:compiler’)
- 简单的Demo https://github.com/dingshaoran/WMRouter
- GradlePlugin 安卓编译流程的一部分 后面也会讲一下具体实现流程
- 提高编译速度
- gradle.properties 里添加配置
org.gradle.unsafe.configuration-cache=true。DefaultGradleLauncher.doBuildStages
里判断了 DefaultConfigurationCache.canLoad -> isConfigurationCacheEnabled
-> startParameter.isEnabled -> isConfigurationCache - 用 nexus 搭建自己的 maven 仓库,把 repositories
所有的地址都配进去,不直接使用多个 repo 防止
RepositoryChainComponentMetaDataResolver.findBestMatch 遍历多个 repo - gradle.properties 里添加配置 org.gradle.caching=true
- 遵循 Gradle 的编码原则,相比普通的 javac gradle 实现了增量编译,如果不按照 gradle 规则会影响打包速度 https://docs.gradle.org/7.1/userguide/java_plugin.html#sec:incremental_compile