| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:meta/meta.dart'; |
| import 'package:native_assets_cli/native_assets_cli.dart' show Asset; |
| import 'package:package_config/package_config_types.dart'; |
| |
| import '../../base/common.dart'; |
| import '../../base/file_system.dart'; |
| import '../../base/platform.dart'; |
| import '../../build_info.dart'; |
| import '../../dart/package_map.dart'; |
| import '../../ios/native_assets.dart'; |
| import '../../macos/native_assets.dart'; |
| import '../../macos/xcode.dart'; |
| import '../../native_assets.dart'; |
| import '../build_system.dart'; |
| import '../depfile.dart'; |
| import '../exceptions.dart'; |
| import 'common.dart'; |
| |
| /// Builds the right native assets for a Flutter app. |
| /// |
| /// The build mode and target architecture can be changed from the |
| /// native build project (Xcode etc.), so only `flutter assemble` has the |
| /// information about build-mode and target architecture. |
| /// Invocations of flutter_tools other than `flutter assemble` are dry runs. |
| /// |
| /// This step needs to be consistent with the dry run invocations in `flutter |
| /// run`s so that the kernel mapping of asset id to dylib lines up after hot |
| /// restart. |
| /// |
| /// [KernelSnapshot] depends on this target. We produce a native_assets.yaml |
| /// here, and embed that mapping inside the kernel snapshot. |
| /// |
| /// The build always produces a valid native_assets.yaml and a native_assets.d |
| /// even if there are no native assets. This way the caching logic won't try to |
| /// rebuild. |
| class NativeAssets extends Target { |
| const NativeAssets({ |
| @visibleForTesting NativeAssetsBuildRunner? buildRunner, |
| }) : _buildRunner = buildRunner; |
| |
| final NativeAssetsBuildRunner? _buildRunner; |
| |
| @override |
| Future<void> build(Environment environment) async { |
| final String? targetPlatformEnvironment = environment.defines[kTargetPlatform]; |
| if (targetPlatformEnvironment == null) { |
| throw MissingDefineException(kTargetPlatform, name); |
| } |
| final TargetPlatform targetPlatform = getTargetPlatformForName(targetPlatformEnvironment); |
| |
| final Uri projectUri = environment.projectDir.uri; |
| final FileSystem fileSystem = environment.fileSystem; |
| final File packagesFile = fileSystem |
| .directory(projectUri) |
| .childDirectory('.dart_tool') |
| .childFile('package_config.json'); |
| final PackageConfig packageConfig = await loadPackageConfigWithLogging( |
| packagesFile, |
| logger: environment.logger, |
| ); |
| final NativeAssetsBuildRunner buildRunner = _buildRunner ?? |
| NativeAssetsBuildRunnerImpl( |
| projectUri, |
| packageConfig, |
| fileSystem, |
| environment.logger, |
| ); |
| |
| final List<Uri> dependencies; |
| switch (targetPlatform) { |
| case TargetPlatform.ios: |
| final String? iosArchsEnvironment = environment.defines[kIosArchs]; |
| if (iosArchsEnvironment == null) { |
| throw MissingDefineException(kIosArchs, name); |
| } |
| final List<DarwinArch> iosArchs = iosArchsEnvironment.split(' ').map(getDarwinArchForName).toList(); |
| final String? environmentBuildMode = environment.defines[kBuildMode]; |
| if (environmentBuildMode == null) { |
| throw MissingDefineException(kBuildMode, name); |
| } |
| final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode); |
| final String? sdkRoot = environment.defines[kSdkRoot]; |
| if (sdkRoot == null) { |
| throw MissingDefineException(kSdkRoot, name); |
| } |
| final EnvironmentType environmentType = environmentTypeFromSdkroot(sdkRoot, environment.fileSystem)!; |
| dependencies = await buildNativeAssetsIOS( |
| environmentType: environmentType, |
| darwinArchs: iosArchs, |
| buildMode: buildMode, |
| projectUri: projectUri, |
| codesignIdentity: environment.defines[kCodesignIdentity], |
| fileSystem: fileSystem, |
| buildRunner: buildRunner, |
| yamlParentDirectory: environment.buildDir.uri, |
| ); |
| case TargetPlatform.darwin: |
| final String? darwinArchsEnvironment = environment.defines[kDarwinArchs]; |
| if (darwinArchsEnvironment == null) { |
| throw MissingDefineException(kDarwinArchs, name); |
| } |
| final List<DarwinArch> darwinArchs = darwinArchsEnvironment.split(' ').map(getDarwinArchForName).toList(); |
| final String? environmentBuildMode = environment.defines[kBuildMode]; |
| if (environmentBuildMode == null) { |
| throw MissingDefineException(kBuildMode, name); |
| } |
| final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode); |
| (_, dependencies) = await buildNativeAssetsMacOS( |
| darwinArchs: darwinArchs, |
| buildMode: buildMode, |
| projectUri: projectUri, |
| codesignIdentity: environment.defines[kCodesignIdentity], |
| yamlParentDirectory: environment.buildDir.uri, |
| fileSystem: fileSystem, |
| buildRunner: buildRunner, |
| ); |
| case TargetPlatform.tester: |
| if (const LocalPlatform().isMacOS) { |
| (_, dependencies) = await buildNativeAssetsMacOS( |
| buildMode: BuildMode.debug, |
| projectUri: projectUri, |
| codesignIdentity: environment.defines[kCodesignIdentity], |
| yamlParentDirectory: environment.buildDir.uri, |
| fileSystem: fileSystem, |
| buildRunner: buildRunner, |
| flutterTester: true, |
| ); |
| } else { |
| // TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757 |
| // Write the file we claim to have in the [outputs]. |
| await writeNativeAssetsYaml(<Asset>[], environment.buildDir.uri, fileSystem); |
| dependencies = <Uri>[]; |
| } |
| case TargetPlatform.android_arm: |
| case TargetPlatform.android_arm64: |
| case TargetPlatform.android_x64: |
| case TargetPlatform.android_x86: |
| case TargetPlatform.android: |
| case TargetPlatform.fuchsia_arm64: |
| case TargetPlatform.fuchsia_x64: |
| case TargetPlatform.linux_arm64: |
| case TargetPlatform.linux_x64: |
| case TargetPlatform.web_javascript: |
| case TargetPlatform.windows_x64: |
| // TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757 |
| // Write the file we claim to have in the [outputs]. |
| await writeNativeAssetsYaml(<Asset>[], environment.buildDir.uri, fileSystem); |
| dependencies = <Uri>[]; |
| } |
| |
| final File nativeAssetsFile = environment.buildDir.childFile('native_assets.yaml'); |
| final Depfile depfile = Depfile( |
| <File>[ |
| for (final Uri dependency in dependencies) fileSystem.file(dependency), |
| ], |
| <File>[ |
| nativeAssetsFile, |
| ], |
| ); |
| final File outputDepfile = environment.buildDir.childFile('native_assets.d'); |
| if (!outputDepfile.parent.existsSync()) { |
| outputDepfile.parent.createSync(recursive: true); |
| } |
| environment.depFileService.writeToFile(depfile, outputDepfile); |
| if (!await nativeAssetsFile.exists()) { |
| throwToolExit("${nativeAssetsFile.path} doesn't exist."); |
| } |
| if (!await outputDepfile.exists()) { |
| throwToolExit("${outputDepfile.path} doesn't exist."); |
| } |
| } |
| |
| @override |
| List<String> get depfiles => <String>[ |
| 'native_assets.d', |
| ]; |
| |
| @override |
| List<Target> get dependencies => <Target>[]; |
| |
| @override |
| List<Source> get inputs => const <Source>[ |
| Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart'), |
| // If different packages are resolved, different native assets might need to be built. |
| Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'), |
| ]; |
| |
| @override |
| String get name => 'native_assets'; |
| |
| @override |
| List<Source> get outputs => const <Source>[ |
| Source.pattern('{BUILD_DIR}/native_assets.yaml'), |
| ]; |
| } |