blob: 6e73882927f99269d3f94e8e5f70f98176994cce [file] [log] [blame]
// 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'),
];
}