guide
In the last article, we did the Apply Patch file, so let’s take a look at what the Apply file does. Dart file has been changed and a new aspectd.dart file has been added
Common. Dart files
The directory where this file resides:
packages/flutter_tools/lib/build_system/targets/common.dart
Copy the code
You can see the following code added to the build method:
@override Future<void> build(Environment Environment) async {// This is the original code await buildImpl(Environment); / / if this is the new code (await AspectdHook. IsAspectdEnabled ()) {await AspectdHook () runBuildDillCommand (environment); }}Copy the code
AspectdHook.isAspectdEnabled()
The above code calls the AspectdHook. IsAspectdEnabled (), and see it done
static Future<bool> isAspectdEnabled() async { final Directory currentDirectory = globals.fs.currentDirectory; Final Directory aspectdDirectory = getAspectdImplDirectory(currentDirectory); // If the directory does not exist, return false without using aspectD logic if (! aspectdDirectory.existsSync()) { return false; } // Get the.packages file under the aspectD_imple project. So we need to perform first pub get final String aspectdImplPackagesPath = globals. Fs. Path. Join (aspectdDirectory. Absolute path, '.packages'); // Use the data in the.package file to obtain the aspectd directory, and thus obtain the directory where frontend_server.dart.snapshot is located. See below the final Directory flutterFrontendServerDirectory = await getFlutterFrontendServerDirectory (aspectdImplPackagesPath); // Check if the aspectD_impl project does not exist or the frontend_server.dart.snapshot directory does not exist and the corresponding file does not exist. (aspectdDirectory.existsSync() && flutterFrontendServerDirectory.existsSync() && currentDirectory.absolute.path ! = aspectdDirectory.absolute.path && globals.fs .file(globals.fs.path.join(aspectdDirectory.path, 'pubspec.yaml')) .existsSync() && globals.fs .file( globals.fs.path.join(aspectdDirectory.path, '.packages')) .existsSync() && globals.fs .file(globals.fs.path.join( aspectdDirectory.path, 'lib', aspectdImplPackageName + '.dart')) .existsSync())) { return false; } // Generate frontend_server.dart.snapshot, See below return await checkAspectdFlutterFrontendServerSnapshot (aspectdImplPackagesPath); }Copy the code
Here’s how to get the aspectD_impl directory
const String aspectdImplPackageRelPath = '.. '; const String aspectdImplPackageName = 'aspectd_impl'; . . . static Directory getAspectdImplDirectory(Directory rootProjectDir) { return globals.fs.directory(globals.fs.path.normalize(globals.fs.path.join( rootProjectDir.path, aspectdImplPackageRelPath, aspectdImplPackageName))); }Copy the code
Get the project corresponding to AspectD and the flutter_frontend_server directory under the project
Static Future < Directory > getFlutterFrontendServerDirectory (String packagesPath) async {/ / find aspectd corresponds to the path of the project, Add the path corresponding to flutter_frontend_server return globals.fs.directory(globals.fs.path.join((await) getPackagePathFromConfig(packagesPath, 'aspectd')).absolute.path, 'lib', 'src', 'flutter_frontend_server')); }Copy the code
static Future<Directory> getPackagePathFromConfig(String packageConfigPath, String packageName) async {// Fetch. Information in package Final PackageConfig PackageConfig = await loadPackageConfigWithLogging(globals.fs.file(packageConfigPath),logger: globals.logger,); if ((packageConfig? .packages? .length ?? 0) > 0) {final Package aspectdPackage = packageConfig. Packages. ToList () firstWhere (/ / to find the information we are looking for (Package element) = > element.name == packageName, orElse: () => null); / / return to find the path of return globals. Fs. Directory (aspectdPackage. Root. ToFilePath ()); } return null; }Copy the code
Generate frontend_server. Dart. The snapshot
const String frontendServerDartSnapshot = 'frontend_server.dart.snapshot'; static Future<bool> checkAspectdFlutterFrontendServerSnapshot( String packagesPath) async { // Obtain the parent directory frontend_server.dart.snapshot. And the corresponding path final file Directory flutterFrontendServerDirectory = await getFlutterFrontendServerDirectory (packagesPath); final String aspectdFlutterFrontendServerSnapshot = globals.fs.path.join(flutterFrontendServerDirectory.absolute.path,frontendServerDartSnapshot); / / access to the system of frontend_server. Dart. The snapshot the path of the corresponding final String defaultFlutterFrontendServerSnapshot = globals.artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk); // If frontend_server.dart. Snapshot does not exist, create if (! Globals. Fs. The file (aspectdFlutterFrontendServerSnapshot). ExistsSync ()) {/ / pub in getDartSdkDependency execution Get to get the.package in the aspectD project, and get dartSdkDir, Final String dartSdkDir = await getDartSdkDependency((await getPackagePathFromConfig(packagesPath, 'aspectd')).absolute.path); / / get flutter_frontend_server folder package_config. Json final String frontendServerPackageConfigJsonFile = '${flutterFrontendServerDirectory.absolute.path}/package_config.json'; // Get rebased_package_config.json in flutter_frontend_server. Toward inside put things beneath the final String rebasedFrontendServerPackageConfigJsonFile = '${flutterFrontendServerDirectory.absolute.path}/rebased_package_config.json'; / / read package_config. Data in json String frontendServerPackageConfigJson = globals.fs.file(frontendServerPackageConfigJsonFile).readAsStringSync(); // Select * from *; /.. /.. /third_party/dart/ instead of the real dartSdkDir directory, Namely above the kernel directory frontendServerPackageConfigJson = frontendServerPackageConfigJson. ReplaceAll ('.. /.. /.. /third_party/dart/', dartSdkDir); // Write the above replaced data to the rebased_package_config.json file globals.fs.file(rebasedFrontendServerPackageConfigJsonFile).writeAsStringSync(frontendServerPackageConfigJson); Final List<String> commands = <String>[ globals.artifacts.getArtifactPath(Artifact.engineDartBinary), '--deterministic', '--packages=$rebasedFrontendServerPackageConfigJsonFile', '--snapshot=$aspectdFlutterFrontendServerSnapshot', '--snapshot-kind=kernel', '${flutterFrontendServerDirectory.absolute.path}/starter.dart' ]; / / execute commands generated frontend_server. Dart. The snapshot final ProcessResult ProcessResult = await globals. The processManager. Run (commands); // Delete the rebased_package_config.json file (frontend_server.dart. So I don't need to) globals. Fs. The file (rebasedFrontendServerPackageConfigJsonFile). DeleteSync (); If (processResult.exitCode! = 0 || globals.fs.file(aspectdFlutterFrontendServerSnapshot).existsSync() ==false) { throwToolExit('Aspectd unexpected error: ${processResult.stderr.toString()}'); }} // Check whether frontend_server.dart. Snapshot exists in the system. Exist deleted the if (globals. Fs. The file (defaultFlutterFrontendServerSnapshot). ExistsSync ()) { globals.fs.file(defaultFlutterFrontendServerSnapshot).deleteSync(); } / / just generated file copy to system globals. Fs. The file (aspectdFlutterFrontendServerSnapshot). CopySync (defaultFlutterFrontendServerSnapshot); return true; }Copy the code
Static Future<String> getDartSdkDependency(String aspectdDir) Async {// Perform pub under aspectdDir (aspectD project) Get to generate. Package files final ProcessResult ProcessResult = await globals. The processManager. Run (< String > [globals. Fs. Path. Join ( globals.artifacts.getArtifactPath(Artifact.engineDartSdkPath), 'bin', 'pub'), 'get', '--verbosity=warning' ], workingDirectory: aspectdDir, environment: <String, String>{'FLUTTER_ROOT': Cache.flutterRoot}); If (processResult.exitCode! = 0) { throwToolExit( 'Aspectd unexpected error: ${processResult.stderr.toString()}'); } // Find the directory corresponding to the kernel according to the.package file. Final Directory kernelDir = await getPackagePathFromConfig(globals.fs.path.join(aspectdDir, '.packages'), 'kernel'); return kernelDir.parent.parent.uri.toString(); }Copy the code
In summary, when the isAspectdEnabled method is called, two things are done: 1. Check whether aspectD_impl and AspectD projects exist. If not, do not use AspectD to prevent other projects from having problems. Generate the frontend_server.dart.snapshot file and replace it in the system.
What frontend_server.dart. Snapshot does: Compile our DART code into app.dill
AspectdHook().runBuildDillCommand(environment)
After doing some preparatory work, it’s time to actually compile the DART code into DILL
Future<void> runBuildDillCommand(Environment Environment) async {// Change the current pointing Directory of the system to final Directory under aspectD_impl aspectdDir = getAspectdImplDirectory(globals.fs.currentDirectory); final Directory previousDirectory = globals.fs.currentDirectory; globals.fs.currentDirectory = aspectdDir; // Specify the directory where the product is located, And String relativeDir =, the directory where you compile environment.outputDir.absolute.path.substring(environment.projectDir.absolute.path.length + 1); final String outputDir = globals.fs.path.join(aspectdDir.path, relativeDir); final String buildDir =globals.fs.path.join(aspectdDir.path, '.dart_tool', 'flutter_build'); // Specify the dart file to compile. In this case, aspectd_imp.dart. See below for the final Map<String, String> defines environment. defines[kTargetFile] = globals.fs.path.join(aspectdDir.path, 'lib', aspectdImplPackageName + '.dart'); Final Environment auxEnvironment = Environment(projectDir: aspectdDir, outputDir: globals.fs.directory(outputDir), cacheDir: environment.cacheDir, flutterRootDir: environment.flutterRootDir, fileSystem: environment.fileSystem, logger: environment.logger, artifacts: environment.artifacts, processManager: environment.processManager, engineVersion: environment.engineVersion, buildDir: globals.fs.directory(buildDir), defines: defines, inputs: environment.inputs); const KernelSnapshot auxKernelSnapshot = KernelSnapshot(); / / compile, obtain product final CompilerOutput CompilerOutput = await auxKernelSnapshot. BuildImpl (auxEnvironment); // Copy the generated artifacts into the.dart_tool/flutter_build/ directory of the project we wrote (because the app.dill artifacts generated above are in the aspectd_impl project) final String aspectdDill = compilerOutput.outputFilename; final File originalDillFile = globals.fs.file(globals.fs.path.join(environment.buildDir.absolute.path, 'app.dill')); / / this is write our app that exist in the project. The dill backup if (originalDillFile. ExistsSync ()) { originalDillFile.renameSync(originalDillFile.absolute.path + '.bak'); } / / specific copy of the app. The method of dill globals. Fs. The file (aspectdDill). CopySync (originalDillFile. Absolute. Path); globals.fs.currentDirectory = previousDirectory; }Copy the code
import 'package:sensors_demo/main.dart' as app; // The following is the imported code to insert, and the annotation inside it enables it to compile import 'sensorsdatA_aop_imp.dart' without being referenced; import 'sa_autotrack.dart'; Void main()=> app.main(); void main()=> app.main();Copy the code
conclusion
What does aspectD do?
- Determine if there is an Aspect_IMPl project at the level above the current executing project, and follow aspectD logic if there is.
- Generate the frontend_server.dart.snapshot file and replace the corresponding file in the FLUTTER SDK. The Flutter_frontend_server file in the AspectD source code does just that. (Frontend_server.dart. Snapshot is used to compile DART code into DILL)
- Compile the code we wrote and the code we want to insert into app.dill. The code we wrote calls the main method in our aspect_IMPl project via the main method in our project. The inserted code is implemented through annotations, which are converted into concrete code to be inserted into the abstract syntax tree (AST) during the compilation of dart to app.dill by frontend_server.dart.snapshot. The files involved are those in Transformer in the AspectD source code. This is why you should replace the system’s frontend_server.dart.snapshot file with your own, because aspectd’s frontend_server.dart.snapshot can convert annotations into concrete code to be inserted into the AST. The resulting app.dill contains all the code.
For the sake of understanding, which is a little wordy and savvy, look directly at the picture provided by Ali: