一.环境变量配置说明
- flutter_home
通过 命令增加flutter的安装目录到系统环境变量
vim ~/.bash_profile
export PATH=$PATH:/xxxxx/xxxx/flutter/bin
- dart_home
位于flutter_home下的cache目录下的dart_sdk下 flutter_home/cache/dart_sdk
二.入口 flutter/flutter.bat
flutter build aar
这是一条构建aar包的命令,由于用到了flutter命令,根据我们设置的环境变量可知,启动代码位于 flutter的安装目录下的bin文件夹下的flutter中,(windows下为flutter.bat)
"%dart%" --disable-dart-dev --packages="%flutter_tools_dir%\.packages" %FLUTTER_TOOL_ARGS% "%snapshot_path%" %* & exit /B !ERRORLEVEL!
打印下环境变量,实际执行代码为
flutter_home/cache/dart-sdk/bin/dart FLUTTER_TOOL_ARGS= SNAPSHOT_PATH=flutter_home/bin/cache/flutter_tools.snapshot build aar
它会调用flutter_tools.snaphot,
snapshot 是dart五种编译产物的其中一种,可以通过命令指定要生成的产物:
dart compile aot-snapshot bin/myapp.dart
,五种产物分别是:
- exe
- aot-snapshot
- jit-snapshot
- kernal
- js
它与java的模型简单映射是这样:
- java - dart
- jar - snapshot
- jvm - dartvm
因此我们可以找到flutter_tool.dart看下后面的流程
三.flutter_tool.dart
位于flutter_home/../packages/flutter_tools/bin下
代码很简单,执行了executable.dart的main方法
import 'package:flutter_tools/executable.dart' as executable;
void main(List<String> args) {
executable.main(args);
}
四.executable.dart
位于flutter_home/../packages/flutter_tools/lib/executable.dart
主要执行逻辑为BuildCommand()
List<FlutterCommand> generateCommands({
@required bool verboseHelp,
@required bool verbose,
}) => <FlutterCommand>[ AnalyzeCommand( verboseHelp: verboseHelp, fileSystem: globals.fs, platform: globals.platform, processManager: globals.processManager, logger: globals.logger, terminal: globals.terminal, artifacts: globals.artifacts, ), AssembleCommand(verboseHelp: verboseHelp, buildSystem: globals.buildSystem), AttachCommand(verboseHelp: verboseHelp), BuildCommand(verboseHelp: verboseHelp), ....
五.build.dart
位于位于flutter_home/../packages/flutter_tools/bin/src/build.dart中,我们就找到了BuildAarCommand ,也就是开始的flutter build aar命令
class BuildCommand extends FlutterCommand {
BuildCommand({ bool verboseHelp = false }) {
_addSubcommand(BuildAarCommand(verboseHelp: verboseHelp));
_addSubcommand(BuildApkCommand(verboseHelp: verboseHelp));
...
}
}
}
六.build_aar.dart
位于位于flutter_home/../packages/flutter_tools/bin/src/commands/build_aar.dart中
实际运行方法runCommand中的androidBuilder.buildAar
@override
Future<FlutterCommandResult> runCommand() async {
....
await androidBuilder.buildAar(
project: _getProject(),
target: targetFile.path,
androidBuildInfo: androidBuildInfo,
outputDirectoryPath: stringArg('output-dir'),
buildNumber: buildNumber,
);
return FlutterCommandResult.success();
}
七.android_builder.dart
位于flutter_home/../packages/flutter_tools/bin/src/android/android_builder.dart中,它是一个抽象类,它的实现在gradle.dart中的AndroidGradleBuilder
八.gradle.dart
位于flutter_home/../packages/flutter_tools/bin/src/android/gradle.dart中
@override
Future<void> buildAar({
required FlutterProject project,
required Set<AndroidBuildInfo> androidBuildInfo,
required String target,
String? outputDirectoryPath,
required String buildNumber,
}) async {
Directory outputDirectory =
_fileSystem.directory(outputDirectoryPath ?? project.android.buildDirectory);
//设置输出目录
if (project.isModule) {
// Module projects artifacts are located in `build/host`.
outputDirectory = outputDirectory.childDirectory('host');
}
//执行buildaar
for (final AndroidBuildInfo androidBuildInfo in androidBuildInfo) {
await buildGradleAar(
project: project,
androidBuildInfo: androidBuildInfo,
target: target,
outputDirectory: outputDirectory,
buildNumber: buildNumber,
);
}
//输出使用方法
printHowToConsumeAar(
buildModes: androidBuildInfo
.map<String>((AndroidBuildInfo androidBuildInfo) {
return androidBuildInfo.buildInfo.modeName;
}).toSet(),
androidPackage: project.manifest.androidPackage,
repoDirectory: getRepoDirectory(outputDirectory),
buildNumber: buildNumber,
logger: _logger,
fileSystem: _fileSystem,
);
}
这个方法包含三部分
- 1.设置build输出目录为./build/host
- 2.执行buildaar命令
- 3.输出使用帮助信息
其中第二步是我们需要的
九.正主gradle.dart - buildGradleAar
Future<void> buildGradleAar({
required FlutterProject project,
required AndroidBuildInfo androidBuildInfo,
required String target,
required Directory outputDirectory,
required String buildNumber,
}) async {
assert(project != null);
assert(target != null);
assert(androidBuildInfo != null);
assert(outputDirectory != null);
final FlutterManifest manifest = project.manifest;
if (!manifest.isModule && !manifest.isPlugin) {
throwToolExit('AARs can only be built for plugin or module projects.');
}
final BuildInfo buildInfo = androidBuildInfo.buildInfo;
final String aarTask = getAarTaskFor(buildInfo);
final Status status = _logger.startProgress(
"Running Gradle task '$aarTask'...",
);
final String flutterRoot = _fileSystem.path.absolute(Cache.flutterRoot!);
final String initScript = _fileSystem.path.join(
flutterRoot,
'packages',
'flutter_tools',
'gradle',
'aar_init_script.gradle',
);
final List<String> command = <String>[
_gradleUtils.getExecutable(project),
'-I=$initScript',
'-Pflutter-root=$flutterRoot',
'-Poutput-dir=${outputDirectory.path}',
'-Pis-plugin=${manifest.isPlugin}',
'-PbuildNumber=$buildNumber'
];
if (_logger.isVerbose) {
command.add('-Pverbose=true');
} else {
command.add('-q');
}
if (!buildInfo.androidGradleDaemon) {
command.add('--no-daemon');
}
if (target != null && target.isNotEmpty) {
command.add('-Ptarget=$target');
}
command.addAll(androidBuildInfo.buildInfo.toGradleConfig());
if (buildInfo.dartObfuscation && buildInfo.mode != BuildMode.release) {
_logger.printStatus(
'Dart obfuscation is not supported in ${toTitleCase(buildInfo.friendlyModeName)}'
' mode, building as un-obfuscated.',
);
}
if (_artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = _artifacts as LocalEngineArtifacts;
final Directory localEngineRepo = _getLocalEngineRepo(
engineOutPath: localEngineArtifacts.engineOutPath,
androidBuildInfo: androidBuildInfo,
fileSystem: _fileSystem,
);
_logger.printTrace(
'Using local engine: ${localEngineArtifacts.engineOutPath}\n'
'Local Maven repo: ${localEngineRepo.path}'
);
command.add('-Plocal-engine-repo=${localEngineRepo.path}');
command.add('-Plocal-engine-build-mode=${buildInfo.modeName}');
command.add('-Plocal-engine-out=${localEngineArtifacts.engineOutPath}');
// Copy the local engine repo in the output directory.
try {
copyDirectory(
localEngineRepo,
getRepoDirectory(outputDirectory),
);
} on FileSystemException catch (error, st) {
throwToolExit(
'Failed to copy the local engine ${localEngineRepo.path} repo '
'in ${outputDirectory.path}: $error, $st'
);
}
command.add('-Ptarget-platform=${_getTargetPlatformByLocalEnginePath(
localEngineArtifacts.engineOutPath)}');
} else if (androidBuildInfo.targetArchs.isNotEmpty) {
final String targetPlatforms = androidBuildInfo.targetArchs
.map(getPlatformNameForAndroidArch).join(',');
command.add('-Ptarget-platform=$targetPlatforms');
}
command.add(aarTask);
final Stopwatch sw = Stopwatch()
..start();
RunResult result;
try {
result = await _processUtils.run(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath!,
},
);
} finally {
status.stop();
}
_usage.sendTiming('build', 'gradle-aar', sw.elapsed);
if (result.exitCode != 0) {
_logger.printStatus(result.stdout, wrap: false);
_logger.printError(result.stderr, wrap: false);
throwToolExit(
'Gradle task $aarTask failed with exit code ${result.exitCode}.',
exitCode: result.exitCode,
);
}
final Directory repoDirectory = getRepoDirectory(outputDirectory);
if (!repoDirectory.existsSync()) {
_logger.printStatus(result.stdout, wrap: false);
_logger.printError(result.stderr, wrap: false);
throwToolExit(
'Gradle task $aarTask failed to produce $repoDirectory.',
exitCode: exitCode,
);
}
_logger.printStatus(
'${_logger.terminal.successMark} Built ${_fileSystem.path.relative(repoDirectory.path)}.',
color: TerminalColor.green,
);
}
}
它的流程包含
- 1.组装命令,也就是在command.add(aarTask); 之前的代码 ,这个aartask 就是我们常用的assembleAarRelease,组装完之后,完整命令如下:
./gradlew \
-I=/Users/hucaihua/Documents/soft/flutter/packages/flutter_tools/gradle/aar_init_script.gradle \
-Pflutter-root=/Users/hucaihua/Documents/soft/flutter \
-Poutput-dir=/Users/hucaihua/Documents/code/git/hive/dartmodule/build/host \
-Pis-plugin=false \
-PbuildNumber=1.0.1 \
-Pverbose=true \
-Ptarget=lib/main.dart -Pdart-obfuscation=false \
-Ptrack-widget-creation=true \
-Ptree-shake-icons=true \
-Ptarget-platform=android-arm,android-arm64 assembleAarRelease
这里会依赖aar_init_script.gradle 这个脚本,它会先执行gradle构建流程中的init和configure流程,并在执行assembleAarRelease结束之后,挂一个mavenpublish任务,将构建的aar推送到本地maven仓库
- 2.执行命令,这里要主意的是,它切换了工作目录project.android.hostAppGradleRoot.path,实际上代表的就是.android目录,如果直接在根目录下执行./gradlew assembleAarRelease 会提示找不projectEvaluated{} ,因为根目录不是一个android的project
try {
result = await _processUtils.run(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath!,
},
);
} finally {
status.stop();
}
执行完这个命令就进入了gradle构建命令assembleAarRelease 进行aar的打包