[flutter_tools] Cleanup of native asset related code (removes around 50% of the native asset related code) (#155430)

tl;dr Removes 50% (>1650 locs) of native asset related code in
`packages/flutter_tools`

Before this PR the invocation of dart build/link/dry-run was implemented
per OS. This lead to very large code duplication of almost identical,
but slightly different code. It also led to similarly duplicated test
code.

Almost the entire dart build/link/dry-run implementation is identical
across OSes. There's small variations:

- configuration of the build (e.g. android/macos/ios version, ios sdk, ...)
- determining target locations & copying the final shared libraries

This PR unifies the implementation by reducing the code to basically two
main functions:

  * `runFlutterSpecificDartBuild` which is responsible for
    - obtain flutter configuration
    - perform dart build (& link)
    - determine target location & install binaries

* `runFlutterSpecificDartDryRunOnPlatforms` which is responsible for a
similar (but not same):
    - obtain flutter configuration
    - perform dart dry run
    - determine target location

these two functions will call out to helpers for the OS specific
functionality:

* `_assetTargetLocationsForOS` for determining the location of the code
assets

* `_copyNativeCodeAssetsForOS` for copying the code assets (and possibly
overriting the install name, etc)

=> Since we get rid of the code duplication across OSes and have only a
single code path for the build/link/dry-run, we can also remove the
duplicated tests that were pretty much identical across OSes.

We also harden the building code by adding asserts, e.g.

  * the dry fun functionality should never be used by `flutter test`

  * the `build/native_assets/<os>/native_assets.yaml` should only be used
     by `flutter test` and the dry-run of `flutter run`

=> We change the tests to also comply with these invariants (so the
tests are not testing things that cannot happen in reality)

We also rename `{,Flutter}NativeAssetsBuildRunner` to disambiguate it
from the `package:native_asset_builder`'s `NativeAssetsBuildRunner`.
This commit is contained in:
Martin Kustermann 2024-09-25 22:50:43 +02:00 committed by GitHub
parent 936cfb8edc
commit 621e7ef951
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1169 additions and 2816 deletions

View file

@ -3,22 +3,14 @@
// found in the LICENSE file.
import 'package:meta/meta.dart';
import 'package:native_assets_builder/native_assets_builder.dart' hide NativeAssetsBuildRunner;
import 'package:native_assets_builder/native_assets_builder.dart';
import 'package:package_config/package_config_types.dart';
import '../../android/gradle_utils.dart';
import '../../base/common.dart';
import '../../base/file_system.dart';
import '../../base/platform.dart';
import '../../build_info.dart';
import '../../dart/package_map.dart';
import '../../isolated/native_assets/android/native_assets.dart';
import '../../isolated/native_assets/ios/native_assets.dart';
import '../../isolated/native_assets/linux/native_assets.dart';
import '../../isolated/native_assets/macos/native_assets.dart';
import '../../isolated/native_assets/native_assets.dart';
import '../../isolated/native_assets/windows/native_assets.dart';
import '../../macos/xcode.dart';
import '../build_system.dart';
import '../depfile.dart';
import '../exceptions.dart';
@ -43,20 +35,21 @@ import 'common.dart';
/// rebuild.
class NativeAssets extends Target {
const NativeAssets({
@visibleForTesting NativeAssetsBuildRunner? buildRunner,
@visibleForTesting FlutterNativeAssetsBuildRunner? buildRunner,
}) : _buildRunner = buildRunner;
final NativeAssetsBuildRunner? _buildRunner;
final FlutterNativeAssetsBuildRunner? _buildRunner;
@override
Future<void> build(Environment environment) async {
final String? nativeAssetsEnvironment = environment.defines[kNativeAssets];
final List<Uri> dependencies;
final FileSystem fileSystem = environment.fileSystem;
final File nativeAssetsFile = environment.buildDir.childFile('native_assets.yaml');
final Uri nativeAssetsFileUri = environment.buildDir.childFile('native_assets.yaml').uri;
final DartBuildResult result;
if (nativeAssetsEnvironment == 'false') {
dependencies = <Uri>[];
await writeNativeAssetsYaml(KernelAssets(), environment.buildDir.uri, fileSystem);
result = const DartBuildResult.empty();
await writeNativeAssetsYaml(KernelAssets(), nativeAssetsFileUri, fileSystem);
} else {
final String? targetPlatformEnvironment = environment.defines[kTargetPlatform];
if (targetPlatformEnvironment == null) {
@ -69,8 +62,8 @@ class NativeAssets extends Target {
fileSystem.file(environment.packageConfigPath),
logger: environment.logger,
);
final NativeAssetsBuildRunner buildRunner = _buildRunner ??
NativeAssetsBuildRunnerImpl(
final FlutterNativeAssetsBuildRunner buildRunner = _buildRunner ??
FlutterNativeAssetsBuildRunnerImpl(
projectUri,
environment.packageConfigPath,
packageConfig,
@ -78,102 +71,22 @@ class NativeAssets extends Target {
environment.logger,
);
switch (targetPlatform) {
case TargetPlatform.ios:
dependencies = await _buildIOS(
environment,
projectUri,
fileSystem,
buildRunner,
);
case TargetPlatform.darwin:
dependencies = await _buildMacOS(
environment,
projectUri,
fileSystem,
buildRunner,
);
case TargetPlatform.linux_arm64:
case TargetPlatform.linux_x64:
dependencies = await _buildLinux(
environment,
targetPlatform,
projectUri,
fileSystem,
buildRunner,
);
case TargetPlatform.windows_arm64:
case TargetPlatform.windows_x64:
dependencies = await _buildWindows(
environment,
targetPlatform,
projectUri,
fileSystem,
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 if (const LocalPlatform().isLinux) {
(_, dependencies) = await buildNativeAssetsLinux(
buildMode: BuildMode.debug,
projectUri: projectUri,
yamlParentDirectory: environment.buildDir.uri,
fileSystem: fileSystem,
buildRunner: buildRunner,
flutterTester: true,
);
} else if (const LocalPlatform().isWindows) {
(_, dependencies) = await buildNativeAssetsWindows(
buildMode: BuildMode.debug,
projectUri: projectUri,
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(KernelAssets(), 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:
(_, dependencies) = await _buildAndroid(
environment,
targetPlatform,
projectUri,
fileSystem,
buildRunner,
);
case TargetPlatform.fuchsia_arm64:
case TargetPlatform.fuchsia_x64:
case TargetPlatform.web_javascript:
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
// Write the file we claim to have in the [outputs].
await writeNativeAssetsYaml(KernelAssets(), environment.buildDir.uri, fileSystem);
dependencies = <Uri>[];
}
(result, _) = await runFlutterSpecificDartBuild(
environmentDefines: environment.defines,
buildRunner: buildRunner,
targetPlatform: targetPlatform,
projectUri: projectUri,
nativeAssetsYamlUri : nativeAssetsFileUri,
fileSystem: fileSystem,
);
}
final Depfile depfile = Depfile(
<File>[
for (final Uri dependency in dependencies) fileSystem.file(dependency),
for (final Uri dependency in result.dependencies) fileSystem.file(dependency),
],
<File>[
nativeAssetsFile,
fileSystem.file(nativeAssetsFileUri),
],
);
final File outputDepfile = environment.buildDir.childFile('native_assets.d');
@ -181,188 +94,14 @@ class NativeAssets extends Target {
outputDepfile.parent.createSync(recursive: true);
}
environment.depFileService.writeToFile(depfile, outputDepfile);
if (!await nativeAssetsFile.exists()) {
throwToolExit("${nativeAssetsFile.path} doesn't exist.");
if (!await fileSystem.file(nativeAssetsFileUri).exists()) {
throwToolExit("${nativeAssetsFileUri.path} doesn't exist.");
}
if (!await outputDepfile.exists()) {
throwToolExit("${outputDepfile.path} doesn't exist.");
}
}
Future<List<Uri>> _buildWindows(
Environment environment,
TargetPlatform targetPlatform,
Uri projectUri,
FileSystem fileSystem,
NativeAssetsBuildRunner buildRunner,
) async {
final String? environmentBuildMode = environment.defines[kBuildMode];
if (environmentBuildMode == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
final (_, List<Uri> dependencies) = await buildNativeAssetsWindows(
targetPlatform: targetPlatform,
buildMode: buildMode,
projectUri: projectUri,
yamlParentDirectory: environment.buildDir.uri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
return dependencies;
}
Future<List<Uri>> _buildLinux(
Environment environment,
TargetPlatform targetPlatform,
Uri projectUri,
FileSystem fileSystem,
NativeAssetsBuildRunner buildRunner,
) async {
final String? environmentBuildMode = environment.defines[kBuildMode];
if (environmentBuildMode == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
final (_, List<Uri> dependencies) = await buildNativeAssetsLinux(
targetPlatform: targetPlatform,
buildMode: buildMode,
projectUri: projectUri,
yamlParentDirectory: environment.buildDir.uri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
return dependencies;
}
Future<List<Uri>> _buildMacOS(
Environment environment,
Uri projectUri,
FileSystem fileSystem,
NativeAssetsBuildRunner buildRunner,
) async {
final List<DarwinArch> darwinArchs =
_emptyToNull(environment.defines[kDarwinArchs])
?.split(' ')
.map(getDarwinArchForName)
.toList() ??
<DarwinArch>[DarwinArch.x86_64, DarwinArch.arm64];
final String? environmentBuildMode = environment.defines[kBuildMode];
if (environmentBuildMode == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
final (_, List<Uri> dependencies) = await buildNativeAssetsMacOS(
darwinArchs: darwinArchs,
buildMode: buildMode,
projectUri: projectUri,
codesignIdentity: environment.defines[kCodesignIdentity],
yamlParentDirectory: environment.buildDir.uri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
return dependencies;
}
Future<List<Uri>> _buildIOS(
Environment environment,
Uri projectUri,
FileSystem fileSystem,
NativeAssetsBuildRunner buildRunner,
) {
final List<DarwinArch> iosArchs =
_emptyToNull(environment.defines[kIosArchs])
?.split(' ')
.map(getIOSArchForName)
.toList() ??
<DarwinArch>[DarwinArch.arm64];
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)!;
return buildNativeAssetsIOS(
environmentType: environmentType,
darwinArchs: iosArchs,
buildMode: buildMode,
projectUri: projectUri,
codesignIdentity: environment.defines[kCodesignIdentity],
fileSystem: fileSystem,
buildRunner: buildRunner,
yamlParentDirectory: environment.buildDir.uri,
);
}
Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> _buildAndroid(
Environment environment,
TargetPlatform targetPlatform,
Uri projectUri,
FileSystem fileSystem,
NativeAssetsBuildRunner buildRunner) {
final String? androidArchsEnvironment = environment.defines[kAndroidArchs];
final List<AndroidArch> androidArchs = _androidArchs(
targetPlatform,
androidArchsEnvironment,
);
final int targetAndroidNdkApi =
int.parse(environment.defines[kMinSdkVersion] ?? minSdkVersion);
final String? environmentBuildMode = environment.defines[kBuildMode];
if (environmentBuildMode == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
return buildNativeAssetsAndroid(
buildMode: buildMode,
projectUri: projectUri,
yamlParentDirectory: environment.buildDir.uri,
fileSystem: fileSystem,
buildRunner: buildRunner,
androidArchs: androidArchs,
targetAndroidNdkApi: targetAndroidNdkApi,
);
}
List<AndroidArch> _androidArchs(
TargetPlatform targetPlatform,
String? androidArchsEnvironment,
) {
switch (targetPlatform) {
case TargetPlatform.android_arm:
return <AndroidArch>[AndroidArch.armeabi_v7a];
case TargetPlatform.android_arm64:
return <AndroidArch>[AndroidArch.arm64_v8a];
case TargetPlatform.android_x64:
return <AndroidArch>[AndroidArch.x86_64];
case TargetPlatform.android_x86:
return <AndroidArch>[AndroidArch.x86];
case TargetPlatform.android:
if (androidArchsEnvironment == null) {
throw MissingDefineException(kAndroidArchs, name);
}
return androidArchsEnvironment
.split(' ')
.map(getAndroidArchForName)
.toList();
case TargetPlatform.darwin:
case TargetPlatform.fuchsia_arm64:
case TargetPlatform.fuchsia_x64:
case TargetPlatform.ios:
case TargetPlatform.linux_arm64:
case TargetPlatform.linux_x64:
case TargetPlatform.tester:
case TargetPlatform.web_javascript:
case TargetPlatform.windows_x64:
case TargetPlatform.windows_arm64:
throwToolExit('Unsupported Android target platform: $targetPlatform.');
}
}
@override
List<String> get depfiles => <String>[
'native_assets.d',
@ -390,10 +129,3 @@ class NativeAssets extends Target {
Source.pattern('{BUILD_DIR}/native_assets.yaml'),
];
}
String? _emptyToNull(String? input) {
if (input == null || input.isEmpty) {
return null;
}
return input;
}

View file

@ -2,145 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:native_assets_builder/native_assets_builder.dart'
hide NativeAssetsBuildRunner;
import 'package:native_assets_builder/native_assets_builder.dart';
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import '../../../android/android_sdk.dart';
import '../../../android/gradle_utils.dart';
import '../../../base/common.dart';
import '../../../base/file_system.dart';
import '../../../build_info.dart';
import '../../../build_info.dart' hide BuildMode;
import '../../../globals.dart' as globals;
import '../native_assets.dart';
/// Dry run the native builds.
///
/// This does not build native assets, it only simulates what the final paths
/// of all assets will be so that this can be embedded in the kernel file.
Future<Uri?> dryRunNativeAssetsAndroid({
required NativeAssetsBuildRunner buildRunner,
required Uri projectUri,
bool flutterTester = false,
required FileSystem fileSystem,
}) async {
if (!await nativeBuildRequired(buildRunner)) {
return null;
}
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, OSImpl.android);
final Iterable<KernelAsset> nativeAssetPaths =
await dryRunNativeAssetsAndroidInternal(
fileSystem,
projectUri,
buildRunner,
);
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
KernelAssets(nativeAssetPaths),
buildUri_,
fileSystem,
);
return nativeAssetsUri;
int targetAndroidNdkApi(Map<String, String> environmentDefines) {
return int.parse(environmentDefines[kMinSdkVersion] ?? minSdkVersion);
}
Future<Iterable<KernelAsset>> dryRunNativeAssetsAndroidInternal(
FileSystem fileSystem,
Uri projectUri,
NativeAssetsBuildRunner buildRunner,
) async {
const OSImpl targetOS = OSImpl.android;
globals.logger.printTrace('Dry running native assets for $targetOS.');
final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun(
linkModePreference: LinkModePreferenceImpl.dynamic,
targetOS: targetOS,
workingDirectory: projectUri,
includeParentEnvironment: true,
);
ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult);
// No link hooks in JIT mode.
final List<AssetImpl> nativeAssets = buildDryRunResult.assets;
globals.logger.printTrace('Dry running native assets for $targetOS done.');
final Map<AssetImpl, KernelAsset> assetTargetLocations =
_assetTargetLocations(nativeAssets);
return assetTargetLocations.values;
}
/// Builds native assets.
Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)>
buildNativeAssetsAndroid({
required NativeAssetsBuildRunner buildRunner,
required Iterable<AndroidArch> androidArchs,
required Uri projectUri,
required BuildMode buildMode,
String? codesignIdentity,
Uri? yamlParentDirectory,
required FileSystem fileSystem,
required int targetAndroidNdkApi,
}) async {
const OSImpl targetOS = OSImpl.android;
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOS);
if (!await nativeBuildRequired(buildRunner)) {
final Uri nativeAssetsYaml = await writeNativeAssetsYaml(
KernelAssets(),
yamlParentDirectory ?? buildUri_,
fileSystem,
);
return (nativeAssetsYaml, <Uri>[]);
}
final List<Target> targets = androidArchs.map(_getNativeTarget).toList();
final BuildModeImpl buildModeCli =
nativeAssetsBuildMode(buildMode);
final bool linkingEnabled = buildModeCli == BuildModeImpl.release;
globals.logger
.printTrace('Building native assets for $targets $buildModeCli.');
final List<AssetImpl> nativeAssets = <AssetImpl>[];
final Set<Uri> dependencies = <Uri>{};
for (final Target target in targets) {
final BuildResult buildResult = await buildRunner.build(
linkModePreference: LinkModePreferenceImpl.dynamic,
target: target,
buildMode: buildModeCli,
workingDirectory: projectUri,
includeParentEnvironment: true,
cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl,
targetAndroidNdkApi: targetAndroidNdkApi,
linkingEnabled: linkingEnabled,
);
ensureNativeAssetsBuildSucceed(buildResult);
nativeAssets.addAll(buildResult.assets);
dependencies.addAll(buildResult.dependencies);
if (linkingEnabled) {
final LinkResult linkResult = await buildRunner.link(
linkModePreference: LinkModePreferenceImpl.dynamic,
target: target,
buildMode: buildModeCli,
workingDirectory: projectUri,
includeParentEnvironment: true,
cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl,
targetAndroidNdkApi: targetAndroidNdkApi,
buildResult: buildResult,
);
ensureNativeAssetsLinkSucceed(linkResult);
nativeAssets.addAll(linkResult.assets);
dependencies.addAll(linkResult.dependencies);
}
}
globals.logger.printTrace('Building native assets for $targets done.');
final Map<AssetImpl, KernelAsset> assetTargetLocations =
_assetTargetLocations(nativeAssets);
await _copyNativeAssetsAndroid(buildUri_, assetTargetLocations, fileSystem);
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
KernelAssets(assetTargetLocations.values),
yamlParentDirectory ?? buildUri_,
fileSystem);
return (nativeAssetsUri, dependencies.toList());
}
Future<void> _copyNativeAssetsAndroid(
Future<void> copyNativeCodeAssetsAndroid(
Uri buildUri,
Map<AssetImpl, KernelAsset> assetTargetLocations,
Map<NativeCodeAssetImpl, KernelAsset> assetTargetLocations,
FileSystem fileSystem,
) async {
if (assetTargetLocations.isNotEmpty) {
@ -154,7 +33,7 @@ Future<void> _copyNativeAssetsAndroid(
final Uri archUri = buildUri.resolve('jniLibs/lib/$jniArchDir/');
await fileSystem.directory(archUri).create(recursive: true);
}
for (final MapEntry<AssetImpl, KernelAsset> assetMapping
for (final MapEntry<NativeCodeAssetImpl, KernelAsset> assetMapping
in assetTargetLocations.entries) {
final Uri source = assetMapping.key.file!;
final Uri target = (assetMapping.value.path as KernelAssetAbsolutePath).uri;
@ -171,7 +50,7 @@ Future<void> _copyNativeAssetsAndroid(
}
/// Get the [Target] for [androidArch].
Target _getNativeTarget(AndroidArch androidArch) {
Target getNativeAndroidTarget(AndroidArch androidArch) {
return switch (androidArch) {
AndroidArch.armeabi_v7a => Target.androidArm,
AndroidArch.arm64_v8a => Target.androidArm64,
@ -192,27 +71,27 @@ AndroidArch _getAndroidArch(Target target) {
};
}
Map<AssetImpl, KernelAsset> _assetTargetLocations(
List<AssetImpl> nativeAssets) {
return <AssetImpl, KernelAsset>{
for (final AssetImpl asset in nativeAssets)
Map<NativeCodeAssetImpl, KernelAsset> assetTargetLocationsAndroid(
List<NativeCodeAssetImpl> nativeAssets) {
return <NativeCodeAssetImpl, KernelAsset>{
for (final NativeCodeAssetImpl asset in nativeAssets)
asset: _targetLocationAndroid(asset),
};
}
/// Converts the `path` of [asset] as output from a `build.dart` invocation to
/// the path used inside the Flutter app bundle.
KernelAsset _targetLocationAndroid(AssetImpl asset) {
final LinkModeImpl linkMode = (asset as NativeCodeAssetImpl).linkMode;
KernelAsset _targetLocationAndroid(NativeCodeAssetImpl asset) {
final LinkMode linkMode = asset.linkMode;
final KernelAssetPath kernelAssetPath;
switch (linkMode) {
case DynamicLoadingSystemImpl _:
case DynamicLoadingSystem _:
kernelAssetPath = KernelAssetSystemPath(linkMode.uri);
case LookupInExecutableImpl _:
case LookupInExecutable _:
kernelAssetPath = KernelAssetInExecutable();
case LookupInProcessImpl _:
case LookupInProcess _:
kernelAssetPath = KernelAssetInProcess();
case DynamicLoadingBundledImpl _:
case DynamicLoadingBundled _:
final String fileName = asset.file!.pathSegments.last;
kernelAssetPath = KernelAssetAbsolutePath(Uri(path: fileName));
default:
@ -234,7 +113,6 @@ KernelAsset _targetLocationAndroid(AssetImpl asset) {
/// Should only be invoked if a native assets build is performed. If the native
/// assets feature is disabled, or none of the packages have native assets, a
/// missing NDK is okay.
@override
Future<CCompilerConfigImpl> cCompilerConfigAndroid() async {
final AndroidSdk? androidSdk = AndroidSdk.locateAndroidSdk();
if (androidSdk == null) {

View file

@ -2,147 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:native_assets_builder/native_assets_builder.dart'
hide NativeAssetsBuildRunner;
import 'package:native_assets_builder/native_assets_builder.dart';
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import '../../../base/file_system.dart';
import '../../../build_info.dart';
import '../../../build_info.dart' hide BuildMode;
import '../../../build_info.dart' as build_info;
import '../../../globals.dart' as globals;
import '../macos/native_assets_host.dart';
import '../native_assets.dart';
/// Dry run the native builds.
///
/// This does not build native assets, it only simulates what the final paths
/// of all assets will be so that this can be embedded in the kernel file and
/// the Xcode project.
Future<Uri?> dryRunNativeAssetsIOS({
required NativeAssetsBuildRunner buildRunner,
required Uri projectUri,
required FileSystem fileSystem,
}) async {
if (!await nativeBuildRequired(buildRunner)) {
return null;
}
// TODO(dcharkes): Fetch minimum iOS version from somewhere. https://github.com/flutter/flutter/issues/145104
const int targetIOSVersion = 12;
final Uri buildUri = nativeAssetsBuildUri(projectUri, OSImpl.iOS);
final Iterable<KernelAsset> assetTargetLocations = await dryRunNativeAssetsIOSInternal(
fileSystem,
projectUri,
buildRunner,
);
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
KernelAssets(assetTargetLocations),
buildUri,
fileSystem,
);
return nativeAssetsUri;
}
Future<Iterable<KernelAsset>> dryRunNativeAssetsIOSInternal(
FileSystem fileSystem,
Uri projectUri,
NativeAssetsBuildRunner buildRunner,
) async {
const OSImpl targetOS = OSImpl.iOS;
globals.logger.printTrace('Dry running native assets for $targetOS.');
final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun(
linkModePreference: LinkModePreferenceImpl.dynamic,
targetOS: targetOS,
workingDirectory: projectUri,
includeParentEnvironment: true,
);
ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult);
// No link hooks in JIT.
final List<AssetImpl> nativeAssets = buildDryRunResult.assets;
globals.logger.printTrace('Dry running native assets for $targetOS done.');
return _assetTargetLocations(nativeAssets).values;
}
/// Builds native assets.
Future<List<Uri>> buildNativeAssetsIOS({
required NativeAssetsBuildRunner buildRunner,
required List<DarwinArch> darwinArchs,
required EnvironmentType environmentType,
required Uri projectUri,
required BuildMode buildMode,
String? codesignIdentity,
required Uri yamlParentDirectory,
required FileSystem fileSystem,
}) async {
if (!await nativeBuildRequired(buildRunner)) {
await writeNativeAssetsYaml(KernelAssets(), yamlParentDirectory, fileSystem);
return <Uri>[];
}
final List<Target> targets = darwinArchs.map(_getNativeTarget).toList();
final BuildModeImpl buildModeCli = nativeAssetsBuildMode(buildMode);
final bool linkingEnabled = buildModeCli == BuildModeImpl.release;
const OSImpl targetOS = OSImpl.iOS;
final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
final IOSSdkImpl iosSdk = _getIOSSdkImpl(environmentType);
globals.logger.printTrace('Building native assets for $targets $buildModeCli.');
final List<AssetImpl> nativeAssets = <AssetImpl>[];
final Set<Uri> dependencies = <Uri>{};
for (final Target target in targets) {
final BuildResult buildResult = await buildRunner.build(
linkModePreference: LinkModePreferenceImpl.dynamic,
target: target,
targetIOSSdkImpl: iosSdk,
buildMode: buildModeCli,
workingDirectory: projectUri,
includeParentEnvironment: true,
cCompilerConfig: await buildRunner.cCompilerConfig,
// TODO(dcharkes): Fetch minimum iOS version from somewhere. https://github.com/flutter/flutter/issues/145104
targetIOSVersion: 12,
linkingEnabled: linkingEnabled,
);
ensureNativeAssetsBuildSucceed(buildResult);
nativeAssets.addAll(buildResult.assets);
dependencies.addAll(buildResult.dependencies);
if (linkingEnabled) {
final LinkResult linkResult = await buildRunner.link(
linkModePreference: LinkModePreferenceImpl.dynamic,
target: target,
targetIOSSdkImpl: iosSdk,
buildMode: buildModeCli,
workingDirectory: projectUri,
includeParentEnvironment: true,
cCompilerConfig: await buildRunner.cCompilerConfig,
buildResult: buildResult,
// TODO(dcharkes): Fetch minimum iOS version from somewhere. https://github.com/flutter/flutter/issues/145104
targetIOSVersion: 12,
);
ensureNativeAssetsLinkSucceed(linkResult);
nativeAssets.addAll(linkResult.assets);
dependencies.addAll(linkResult.dependencies);
}
}
globals.logger.printTrace('Building native assets for $targets done.');
final Map<KernelAssetPath, List<AssetImpl>> fatAssetTargetLocations =
_fatAssetTargetLocations(nativeAssets);
await _copyNativeAssetsIOS(
buildUri,
fatAssetTargetLocations,
codesignIdentity,
buildMode,
fileSystem,
);
final Map<AssetImpl, KernelAsset> assetTargetLocations =
_assetTargetLocations(nativeAssets);
await writeNativeAssetsYaml(
KernelAssets(assetTargetLocations.values),
yamlParentDirectory,
fileSystem,
);
return dependencies.toList();
}
IOSSdkImpl _getIOSSdkImpl(EnvironmentType environmentType) {
IOSSdkImpl getIOSSdk(EnvironmentType environmentType) {
return switch (environmentType) {
EnvironmentType.physical => IOSSdkImpl.iPhoneOS,
EnvironmentType.simulator => IOSSdkImpl.iPhoneSimulator,
@ -150,7 +23,7 @@ IOSSdkImpl _getIOSSdkImpl(EnvironmentType environmentType) {
}
/// Extract the [Target] from a [DarwinArch].
Target _getNativeTarget(DarwinArch darwinArch) {
Target getNativeIOSTarget(DarwinArch darwinArch) {
return switch (darwinArch) {
DarwinArch.armv7 => Target.iOSArm,
DarwinArch.arm64 => Target.iOSArm64,
@ -158,13 +31,13 @@ Target _getNativeTarget(DarwinArch darwinArch) {
};
}
Map<KernelAssetPath, List<AssetImpl>> _fatAssetTargetLocations(
List<AssetImpl> nativeAssets) {
Map<KernelAssetPath, List<NativeCodeAssetImpl>> fatAssetTargetLocationsIOS(
List<NativeCodeAssetImpl> nativeAssets) {
final Set<String> alreadyTakenNames = <String>{};
final Map<KernelAssetPath, List<AssetImpl>> result =
<KernelAssetPath, List<AssetImpl>>{};
final Map<KernelAssetPath, List<NativeCodeAssetImpl>> result =
<KernelAssetPath, List<NativeCodeAssetImpl>>{};
final Map<String, KernelAssetPath> idToPath = <String, KernelAssetPath>{};
for (final AssetImpl asset in nativeAssets) {
for (final NativeCodeAssetImpl asset in nativeAssets) {
// Use same target path for all assets with the same id.
final KernelAssetPath path = idToPath[asset.id] ??
_targetLocationIOS(
@ -172,23 +45,24 @@ Map<KernelAssetPath, List<AssetImpl>> _fatAssetTargetLocations(
alreadyTakenNames,
).path;
idToPath[asset.id] = path;
result[path] ??= <AssetImpl>[];
result[path] ??= <NativeCodeAssetImpl>[];
result[path]!.add(asset);
}
return result;
}
Map<AssetImpl, KernelAsset> _assetTargetLocations(
List<AssetImpl> nativeAssets) {
Map<NativeCodeAssetImpl, KernelAsset> assetTargetLocationsIOS(
List<NativeCodeAssetImpl> nativeAssets) {
final Set<String> alreadyTakenNames = <String>{};
final Map<String, KernelAssetPath> idToPath = <String, KernelAssetPath>{};
final Map<AssetImpl, KernelAsset> result = <AssetImpl, KernelAsset>{};
for (final AssetImpl asset in nativeAssets) {
final KernelAssetPath path = idToPath[asset.id] ??
_targetLocationIOS(asset, alreadyTakenNames).path;
final Map<NativeCodeAssetImpl, KernelAsset> result =
<NativeCodeAssetImpl, KernelAsset>{};
for (final NativeCodeAssetImpl asset in nativeAssets) {
final KernelAssetPath path =
idToPath[asset.id] ?? _targetLocationIOS(asset, alreadyTakenNames).path;
idToPath[asset.id] = path;
result[asset] = KernelAsset(
id: (asset as NativeCodeAssetImpl).id,
id: asset.id,
target: Target.fromArchitectureAndOS(asset.architecture!, asset.os),
path: path,
);
@ -196,17 +70,18 @@ Map<AssetImpl, KernelAsset> _assetTargetLocations(
return result;
}
KernelAsset _targetLocationIOS(AssetImpl asset, Set<String> alreadyTakenNames) {
final LinkModeImpl linkMode = (asset as NativeCodeAssetImpl).linkMode;
final KernelAssetPath kernelAssetPath;
KernelAsset _targetLocationIOS(
NativeCodeAssetImpl asset, Set<String> alreadyTakenNames) {
final LinkMode linkMode = asset.linkMode;
final KernelAssetPath kernelAssetPath;
switch (linkMode) {
case DynamicLoadingSystemImpl _:
case DynamicLoadingSystem _:
kernelAssetPath = KernelAssetSystemPath(linkMode.uri);
case LookupInExecutableImpl _:
case LookupInExecutable _:
kernelAssetPath = KernelAssetInExecutable();
case LookupInProcessImpl _:
case LookupInProcess _:
kernelAssetPath = KernelAssetInProcess();
case DynamicLoadingBundledImpl _:
case DynamicLoadingBundled _:
final String fileName = asset.file!.pathSegments.last;
kernelAssetPath = KernelAssetAbsolutePath(frameworkUri(
fileName,
@ -236,11 +111,11 @@ final KernelAssetPath kernelAssetPath;
///
/// Code signing is also done here, so that it doesn't have to be done in
/// in xcode_backend.dart.
Future<void> _copyNativeAssetsIOS(
Future<void> copyNativeCodeAssetsIOS(
Uri buildUri,
Map<KernelAssetPath, List<AssetImpl>> assetTargetLocations,
Map<KernelAssetPath, List<NativeCodeAssetImpl>> assetTargetLocations,
String? codesignIdentity,
BuildMode buildMode,
build_info.BuildMode buildMode,
FileSystem fileSystem,
) async {
if (assetTargetLocations.isNotEmpty) {
@ -250,11 +125,12 @@ Future<void> _copyNativeAssetsIOS(
final Map<String, String> oldToNewInstallNames = <String, String>{};
final List<(File, String, Directory)> dylibs = <(File, String, Directory)>[];
for (final MapEntry<KernelAssetPath, List<AssetImpl>> assetMapping
for (final MapEntry<KernelAssetPath, List<NativeCodeAssetImpl>> assetMapping
in assetTargetLocations.entries) {
final Uri target = (assetMapping.key as KernelAssetAbsolutePath).uri;
final List<File> sources = <File>[
for (final AssetImpl source in assetMapping.value) fileSystem.file(source.file)
for (final NativeCodeAssetImpl source in assetMapping.value)
fileSystem.file(source.file)
];
final Uri targetUri = buildUri.resolveUri(target);
final File dylibFile = fileSystem.file(targetUri);
@ -265,7 +141,8 @@ Future<void> _copyNativeAssetsIOS(
await lipoDylibs(dylibFile, sources);
final String dylibFileName = dylibFile.basename;
final String newInstallName = '@rpath/$dylibFileName.framework/$dylibFileName';
final String newInstallName =
'@rpath/$dylibFileName.framework/$dylibFileName';
final Set<String> oldInstallNames = await getInstallNamesDylib(dylibFile);
for (final String oldInstallName in oldInstallNames) {
oldToNewInstallNames[oldInstallName] = newInstallName;

View file

@ -2,70 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:native_assets_builder/native_assets_builder.dart'
hide NativeAssetsBuildRunner;
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import '../../../base/common.dart';
import '../../../base/file_system.dart';
import '../../../base/io.dart';
import '../../../build_info.dart';
import '../../../globals.dart' as globals;
import '../native_assets.dart';
/// Dry run the native builds.
///
/// This does not build native assets, it only simulates what the final paths
/// of all assets will be so that this can be embedded in the kernel file.
Future<Uri?> dryRunNativeAssetsLinux({
required NativeAssetsBuildRunner buildRunner,
required Uri projectUri,
bool flutterTester = false,
required FileSystem fileSystem,
}) {
return dryRunNativeAssetsSingleArchitecture(
buildRunner: buildRunner,
projectUri: projectUri,
flutterTester: flutterTester,
fileSystem: fileSystem,
os: OSImpl.linux,
);
}
Future<Iterable<KernelAsset>> dryRunNativeAssetsLinuxInternal(
FileSystem fileSystem,
Uri projectUri,
bool flutterTester,
NativeAssetsBuildRunner buildRunner,
) {
return dryRunNativeAssetsSingleArchitectureInternal(
fileSystem,
projectUri,
flutterTester,
buildRunner,
OSImpl.linux,
);
}
Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsLinux({
required NativeAssetsBuildRunner buildRunner,
TargetPlatform? targetPlatform,
required Uri projectUri,
required BuildMode buildMode,
bool flutterTester = false,
Uri? yamlParentDirectory,
required FileSystem fileSystem,
}) {
return buildNativeAssetsSingleArchitecture(
buildRunner: buildRunner,
targetPlatform: targetPlatform,
projectUri: projectUri,
buildMode: buildMode,
flutterTester: flutterTester,
yamlParentDirectory: yamlParentDirectory,
fileSystem: fileSystem,
);
}
/// Flutter expects `clang++` to be on the path on Linux hosts.
///

View file

@ -2,179 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:native_assets_builder/native_assets_builder.dart'
hide NativeAssetsBuildRunner;
import 'package:native_assets_builder/native_assets_builder.dart';
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import '../../../base/file_system.dart';
import '../../../build_info.dart';
import '../../../build_info.dart' hide BuildMode;
import '../../../build_info.dart' as build_info;
import '../../../globals.dart' as globals;
import '../native_assets.dart';
import 'native_assets_host.dart';
/// Dry run the native builds.
///
/// This does not build native assets, it only simulates what the final paths
/// of all assets will be so that this can be embedded in the kernel file and
/// the Xcode project.
Future<Uri?> dryRunNativeAssetsMacOS({
required NativeAssetsBuildRunner buildRunner,
required Uri projectUri,
bool flutterTester = false,
required FileSystem fileSystem,
}) async {
if (!await nativeBuildRequired(buildRunner)) {
return null;
}
final Uri buildUri = nativeAssetsBuildUri(projectUri, OSImpl.macOS);
final Iterable<KernelAsset> nativeAssetPaths = await dryRunNativeAssetsMacOSInternal(
fileSystem,
projectUri,
flutterTester,
buildRunner,
);
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
KernelAssets(nativeAssetPaths),
buildUri,
fileSystem,
);
return nativeAssetsUri;
}
Future<Iterable<KernelAsset>> dryRunNativeAssetsMacOSInternal(
FileSystem fileSystem,
Uri projectUri,
bool flutterTester,
NativeAssetsBuildRunner buildRunner,
) async {
const OSImpl targetOS = OSImpl.macOS;
final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
globals.logger.printTrace('Dry running native assets for $targetOS.');
final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun(
linkModePreference: LinkModePreferenceImpl.dynamic,
targetOS: targetOS,
workingDirectory: projectUri,
includeParentEnvironment: true,
);
ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult);
// No link hooks in JIT mode.
final List<AssetImpl> nativeAssets = buildDryRunResult.assets;
globals.logger.printTrace('Dry running native assets for $targetOS done.');
final Uri? absolutePath = flutterTester ? buildUri : null;
final Map<AssetImpl, KernelAsset> assetTargetLocations =
_assetTargetLocations(
nativeAssets,
absolutePath,
);
return assetTargetLocations.values;
}
/// Builds native assets.
///
/// If [darwinArchs] is omitted, the current target architecture is used.
///
/// If [flutterTester] is true, absolute paths are emitted in the native
/// assets mapping. This can be used for JIT mode without sandbox on the host.
/// This is used in `flutter test` and `flutter run -d flutter-tester`.
Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsMacOS({
required NativeAssetsBuildRunner buildRunner,
List<DarwinArch>? darwinArchs,
required Uri projectUri,
required BuildMode buildMode,
bool flutterTester = false,
String? codesignIdentity,
Uri? yamlParentDirectory,
required FileSystem fileSystem,
}) async {
const OSImpl targetOS = OSImpl.macOS;
final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
if (!await nativeBuildRequired(buildRunner)) {
final Uri nativeAssetsYaml = await writeNativeAssetsYaml(
KernelAssets(),
yamlParentDirectory ?? buildUri,
fileSystem,
);
return (nativeAssetsYaml, <Uri>[]);
}
final List<Target> targets = darwinArchs != null
? darwinArchs.map(_getNativeTarget).toList()
: <Target>[Target.current];
final BuildModeImpl buildModeCli =
nativeAssetsBuildMode(buildMode);
final bool linkingEnabled = buildModeCli == BuildModeImpl.release;
globals.logger
.printTrace('Building native assets for $targets $buildModeCli.');
final List<AssetImpl> nativeAssets = <AssetImpl>[];
final Set<Uri> dependencies = <Uri>{};
for (final Target target in targets) {
final BuildResult buildResult = await buildRunner.build(
linkModePreference: LinkModePreferenceImpl.dynamic,
target: target,
buildMode: buildModeCli,
workingDirectory: projectUri,
includeParentEnvironment: true,
cCompilerConfig: await buildRunner.cCompilerConfig,
// TODO(dcharkes): Fetch minimum MacOS version from somewhere. https://github.com/flutter/flutter/issues/145104
targetMacOSVersion: 13,
linkingEnabled: linkingEnabled,
);
ensureNativeAssetsBuildSucceed(buildResult);
nativeAssets.addAll(buildResult.assets);
dependencies.addAll(buildResult.dependencies);
if (linkingEnabled) {
final LinkResult linkResult = await buildRunner.link(
linkModePreference: LinkModePreferenceImpl.dynamic,
target: target,
buildMode: buildModeCli,
workingDirectory: projectUri,
includeParentEnvironment: true,
cCompilerConfig: await buildRunner.cCompilerConfig,
buildResult: buildResult,
// TODO(dcharkes): Fetch minimum MacOS version from somewhere. https://github.com/flutter/flutter/issues/145104
targetMacOSVersion: 13,
);
ensureNativeAssetsLinkSucceed(linkResult);
nativeAssets.addAll(linkResult.assets);
dependencies.addAll(linkResult.dependencies);
}
}
globals.logger.printTrace('Building native assets for $targets done.');
final Uri? absolutePath = flutterTester ? buildUri : null;
final Map<AssetImpl, KernelAsset> assetTargetLocations =
_assetTargetLocations(nativeAssets, absolutePath);
final Map<KernelAssetPath, List<AssetImpl>> fatAssetTargetLocations =
_fatAssetTargetLocations(nativeAssets, absolutePath);
if (flutterTester) {
await _copyNativeAssetsMacOSFlutterTester(
buildUri,
fatAssetTargetLocations,
codesignIdentity,
buildMode,
fileSystem,
);
} else {
await _copyNativeAssetsMacOS(
buildUri,
fatAssetTargetLocations,
codesignIdentity,
buildMode,
fileSystem,
);
}
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
KernelAssets(assetTargetLocations.values),
yamlParentDirectory ?? buildUri,
fileSystem,
);
return (nativeAssetsUri, dependencies.toList());
}
// TODO(dcharkes): Fetch minimum MacOS version from somewhere. https://github.com/flutter/flutter/issues/145104
const int targetMacOSVersion = 13;
/// Extract the [Target] from a [DarwinArch].
Target _getNativeTarget(DarwinArch darwinArch) {
Target getNativeMacOSTarget(DarwinArch darwinArch) {
return switch (darwinArch) {
DarwinArch.arm64 => Target.macOSArm64,
DarwinArch.x86_64 => Target.macOSX64,
@ -182,15 +24,15 @@ Target _getNativeTarget(DarwinArch darwinArch) {
};
}
Map<KernelAssetPath, List<AssetImpl>> _fatAssetTargetLocations(
List<AssetImpl> nativeAssets,
Map<KernelAssetPath, List<NativeCodeAssetImpl>> fatAssetTargetLocationsMacOS(
List<NativeCodeAssetImpl> nativeAssets,
Uri? absolutePath,
) {
final Set<String> alreadyTakenNames = <String>{};
final Map<KernelAssetPath, List<AssetImpl>> result =
<KernelAssetPath, List<AssetImpl>>{};
final Map<KernelAssetPath, List<NativeCodeAssetImpl>> result =
<KernelAssetPath, List<NativeCodeAssetImpl>>{};
final Map<String, KernelAssetPath> idToPath = <String, KernelAssetPath>{};
for (final AssetImpl asset in nativeAssets) {
for (final NativeCodeAssetImpl asset in nativeAssets) {
// Use same target path for all assets with the same id.
final KernelAssetPath path = idToPath[asset.id] ??
_targetLocationMacOS(
@ -199,25 +41,25 @@ Map<KernelAssetPath, List<AssetImpl>> _fatAssetTargetLocations(
alreadyTakenNames,
).path;
idToPath[asset.id] = path;
result[path] ??= <AssetImpl>[];
result[path] ??= <NativeCodeAssetImpl>[];
result[path]!.add(asset);
}
return result;
}
Map<AssetImpl, KernelAsset> _assetTargetLocations(
List<AssetImpl> nativeAssets,
Map<NativeCodeAssetImpl, KernelAsset> assetTargetLocationsMacOS(
List<NativeCodeAssetImpl> nativeAssets,
Uri? absolutePath,
) {
final Set<String> alreadyTakenNames = <String>{};
final Map<String, KernelAssetPath> idToPath = <String, KernelAssetPath>{};
final Map<AssetImpl, KernelAsset> result = <AssetImpl, KernelAsset>{};
for (final AssetImpl asset in nativeAssets) {
final Map<NativeCodeAssetImpl, KernelAsset> result = <NativeCodeAssetImpl, KernelAsset>{};
for (final NativeCodeAssetImpl asset in nativeAssets) {
final KernelAssetPath path = idToPath[asset.id] ??
_targetLocationMacOS(asset, absolutePath, alreadyTakenNames).path;
idToPath[asset.id] = path;
result[asset] = KernelAsset(
id: (asset as NativeCodeAssetImpl).id,
id: asset.id,
target: Target.fromArchitectureAndOS(asset.architecture!, asset.os),
path: path,
);
@ -226,20 +68,20 @@ Map<AssetImpl, KernelAsset> _assetTargetLocations(
}
KernelAsset _targetLocationMacOS(
AssetImpl asset,
NativeCodeAssetImpl asset,
Uri? absolutePath,
Set<String> alreadyTakenNames,
) {
final LinkModeImpl linkMode = (asset as NativeCodeAssetImpl).linkMode;
final LinkMode linkMode = asset.linkMode;
final KernelAssetPath kernelAssetPath;
switch (linkMode) {
case DynamicLoadingSystemImpl _:
case DynamicLoadingSystem _:
kernelAssetPath = KernelAssetSystemPath(linkMode.uri);
case LookupInExecutableImpl _:
case LookupInExecutable _:
kernelAssetPath = KernelAssetInExecutable();
case LookupInProcessImpl _:
case LookupInProcess _:
kernelAssetPath = KernelAssetInProcess();
case DynamicLoadingBundledImpl _:
case DynamicLoadingBundled _:
final String fileName = asset.file!.pathSegments.last;
Uri uri;
if (absolutePath != null) {
@ -279,11 +121,11 @@ KernelAsset _targetLocationMacOS(
///
/// Code signing is also done here, so that it doesn't have to be done in
/// in macos_assemble.sh.
Future<void> _copyNativeAssetsMacOS(
Future<void> copyNativeCodeAssetsMacOS(
Uri buildUri,
Map<KernelAssetPath, List<AssetImpl>> assetTargetLocations,
Map<KernelAssetPath, List<NativeCodeAssetImpl>> assetTargetLocations,
String? codesignIdentity,
BuildMode buildMode,
build_info.BuildMode buildMode,
FileSystem fileSystem,
) async {
if (assetTargetLocations.isNotEmpty) {
@ -294,11 +136,11 @@ Future<void> _copyNativeAssetsMacOS(
final Map<String, String> oldToNewInstallNames = <String, String>{};
final List<(File, String, Directory)> dylibs = <(File, String, Directory)>[];
for (final MapEntry<KernelAssetPath, List<AssetImpl>> assetMapping
for (final MapEntry<KernelAssetPath, List<NativeCodeAssetImpl>> assetMapping
in assetTargetLocations.entries) {
final Uri target = (assetMapping.key as KernelAssetAbsolutePath).uri;
final List<File> sources = <File>[
for (final AssetImpl source in assetMapping.value) fileSystem.file(source.file),
for (final NativeCodeAssetImpl source in assetMapping.value) fileSystem.file(source.file),
];
final Uri targetUri = buildUri.resolveUri(target);
final String name = targetUri.pathSegments.last;
@ -373,11 +215,11 @@ Future<void> _copyNativeAssetsMacOS(
/// so that the referenced library can be found the dynamic linker.
///
/// Code signing is also done here.
Future<void> _copyNativeAssetsMacOSFlutterTester(
Future<void> copyNativeCodeAssetsMacOSFlutterTester(
Uri buildUri,
Map<KernelAssetPath, List<AssetImpl>> assetTargetLocations,
Map<KernelAssetPath, List<NativeCodeAssetImpl>> assetTargetLocations,
String? codesignIdentity,
BuildMode buildMode,
build_info.BuildMode buildMode,
FileSystem fileSystem,
) async {
if (assetTargetLocations.isNotEmpty) {
@ -388,11 +230,11 @@ Future<void> _copyNativeAssetsMacOSFlutterTester(
final Map<String, String> oldToNewInstallNames = <String, String>{};
final List<(File, String)> dylibs = <(File, String)>[];
for (final MapEntry<KernelAssetPath, List<AssetImpl>> assetMapping
for (final MapEntry<KernelAssetPath, List<NativeCodeAssetImpl>> assetMapping
in assetTargetLocations.entries) {
final Uri target = (assetMapping.key as KernelAssetAbsolutePath).uri;
final List<File> sources = <File>[
for (final AssetImpl source in assetMapping.value) fileSystem.file(source.file),
for (final NativeCodeAssetImpl source in assetMapping.value) fileSystem.file(source.file),
];
final Uri targetUri = buildUri.resolveUri(target);
final File dylibFile = fileSystem.file(targetUri);

View file

@ -4,13 +4,13 @@
// Shared logic between iOS and macOS implementations of native assets.
import 'package:native_assets_cli/native_assets_cli.dart' show Architecture;
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import '../../../base/common.dart';
import '../../../base/file_system.dart';
import '../../../base/io.dart';
import '../../../build_info.dart';
import '../../../build_info.dart' as build_info;
import '../../../convert.dart';
import '../../../globals.dart' as globals;
@ -99,13 +99,13 @@ Future<void> setInstallNamesDylib(
String newInstallName,
Map<String, String> oldToNewInstallNames,
) async {
final ProcessResult setInstallNamesResult = await globals.processManager.run(
final ProcessResult setInstallNamesResult = await globals.processManager.run(
<String>[
'install_name_tool',
'-id',
newInstallName,
for (final MapEntry<String, String> entry in oldToNewInstallNames.entries)
...<String>['-change', entry.key, entry.value],
for (final MapEntry<String, String> entry in oldToNewInstallNames
.entries) ...<String>['-change', entry.key, entry.value],
dylibFile.path,
],
);
@ -135,18 +135,16 @@ Future<Set<String>> getInstallNamesDylib(File dylibFile) async {
return <String>{
for (final List<String> architectureSection
in parseOtoolArchitectureSections(installNameResult.stdout as String).values)
in parseOtoolArchitectureSections(installNameResult.stdout as String).values)
// For each architecture, a separate install name is reported, which are
// not necessarily the same.
architectureSection.single,
};
}
Future<void> codesignDylib(
String? codesignIdentity,
BuildMode buildMode,
build_info.BuildMode buildMode,
FileSystemEntity target,
) async {
if (codesignIdentity == null || codesignIdentity.isEmpty) {
@ -157,7 +155,7 @@ Future<void> codesignDylib(
'--force',
'--sign',
codesignIdentity,
if (buildMode != BuildMode.release) ...<String>[
if (buildMode != build_info.BuildMode.release) ...<String>[
// Mimic Xcode's timestamp codesigning behavior on non-release binaries.
'--timestamp=none',
],

View file

@ -6,16 +6,12 @@
import 'package:native_assets_cli/native_assets_cli.dart';
import '../../../base/os.dart';
import '../../../base/platform.dart';
import '../../../build_info.dart';
import '../../../globals.dart' as globals;
import '../../../native_assets.dart';
import '../../../project.dart';
import '../linux/native_assets.dart';
import '../macos/native_assets.dart';
import '../native_assets.dart';
import '../windows/native_assets.dart';
class TestCompilerNativeAssetsBuilderImpl
implements TestCompilerNativeAssetsBuilder {
@ -31,57 +27,36 @@ class TestCompilerNativeAssetsBuilderImpl
}
Future<Uri?> testCompilerBuildNativeAssets(BuildInfo buildInfo) async {
Uri? nativeAssetsYaml;
if (!buildInfo.buildNativeAssets) {
nativeAssetsYaml = null;
} else {
final Uri projectUri = FlutterProject.current().directory.uri;
final NativeAssetsBuildRunner buildRunner = NativeAssetsBuildRunnerImpl(
projectUri,
buildInfo.packageConfigPath,
buildInfo.packageConfig,
globals.fs,
globals.logger,
);
if (globals.platform.isMacOS) {
(nativeAssetsYaml, _) = await buildNativeAssetsMacOS(
buildMode: buildInfo.mode,
projectUri: projectUri,
flutterTester: true,
fileSystem: globals.fs,
buildRunner: buildRunner,
);
} else if (globals.platform.isLinux) {
(nativeAssetsYaml, _) = await buildNativeAssetsLinux(
buildMode: buildInfo.mode,
projectUri: projectUri,
flutterTester: true,
fileSystem: globals.fs,
buildRunner: buildRunner,
);
} else if (globals.platform.isWindows) {
final TargetPlatform targetPlatform;
if (globals.os.hostPlatform == HostPlatform.windows_x64) {
targetPlatform = TargetPlatform.windows_x64;
} else {
targetPlatform = TargetPlatform.windows_arm64;
}
(nativeAssetsYaml, _) = await buildNativeAssetsWindows(
buildMode: buildInfo.mode,
targetPlatform: targetPlatform,
projectUri: projectUri,
flutterTester: true,
fileSystem: globals.fs,
buildRunner: buildRunner,
);
} else {
await ensureNoNativeAssetsOrOsIsSupported(
projectUri,
const LocalPlatform().operatingSystem,
globals.fs,
buildRunner,
);
}
return null;
}
final Uri projectUri = FlutterProject.current().directory.uri;
final FlutterNativeAssetsBuildRunner buildRunner = FlutterNativeAssetsBuildRunnerImpl(
projectUri,
buildInfo.packageConfigPath,
buildInfo.packageConfig,
globals.fs,
globals.logger,
);
if (!globals.platform.isMacOS &&
!globals.platform.isLinux &&
!globals.platform.isWindows) {
await ensureNoNativeAssetsOrOsIsSupported(
projectUri,
const LocalPlatform().operatingSystem,
globals.fs,
buildRunner,
);
return null;
}
final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: buildInfo.mode.cliName,
},
buildRunner: buildRunner,
targetPlatform: TargetPlatform.tester,
projectUri: projectUri,
fileSystem: globals.fs);
return nativeAssetsYaml;
}

View file

@ -2,70 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:native_assets_builder/native_assets_builder.dart'
hide NativeAssetsBuildRunner;
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import '../../../base/file_system.dart';
import '../../../build_info.dart';
import '../../../globals.dart' as globals;
import '../../../windows/visual_studio.dart';
import '../native_assets.dart';
/// Dry run the native builds.
///
/// This does not build native assets, it only simulates what the final paths
/// of all assets will be so that this can be embedded in the kernel file.
Future<Uri?> dryRunNativeAssetsWindows({
required NativeAssetsBuildRunner buildRunner,
required Uri projectUri,
bool flutterTester = false,
required FileSystem fileSystem,
}) {
return dryRunNativeAssetsSingleArchitecture(
buildRunner: buildRunner,
projectUri: projectUri,
flutterTester: flutterTester,
fileSystem: fileSystem,
os: OSImpl.windows,
);
}
Future<Iterable<KernelAsset>> dryRunNativeAssetsWindowsInternal(
FileSystem fileSystem,
Uri projectUri,
bool flutterTester,
NativeAssetsBuildRunner buildRunner,
) {
return dryRunNativeAssetsSingleArchitectureInternal(
fileSystem,
projectUri,
flutterTester,
buildRunner,
OSImpl.windows,
);
}
Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)>
buildNativeAssetsWindows({
required NativeAssetsBuildRunner buildRunner,
TargetPlatform? targetPlatform,
required Uri projectUri,
required BuildMode buildMode,
bool flutterTester = false,
Uri? yamlParentDirectory,
required FileSystem fileSystem,
}) {
return buildNativeAssetsSingleArchitecture(
buildRunner: buildRunner,
targetPlatform: targetPlatform,
projectUri: projectUri,
buildMode: buildMode,
flutterTester: flutterTester,
yamlParentDirectory: yamlParentDirectory,
fileSystem: fileSystem,
);
}
Future<CCompilerConfigImpl> cCompilerConfigWindows() async {
final VisualStudio visualStudio = VisualStudio(

View file

@ -5,6 +5,7 @@
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/android/gradle_utils.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
@ -14,7 +15,7 @@ import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/isolated/native_assets/android/native_assets.dart';
import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import 'package:package_config/package_config_types.dart';
@ -48,173 +49,6 @@ void main() {
projectUri = environment.projectDir.uri;
});
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
expect(
await dryRunNativeAssetsAndroid(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
),
null,
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('build with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await buildNativeAssetsAndroid(
androidArchs: <AndroidArch>[AndroidArch.arm64_v8a],
targetAndroidNdkApi: 21,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsAndroid(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('dry run with assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: FakeNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.macOS,
architecture: ArchitectureImpl.arm64,
file: Uri.file('libbar.so'),
),
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.macOS,
architecture: ArchitectureImpl.x64,
file: Uri.file('libbar.so'),
),
],
),
);
final Uri? nativeAssetsYaml = await dryRunNativeAssetsAndroid(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for android.',
'Dry running native assets for android done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/android/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
contains('package:bar/bar.dart'),
);
expect(buildRunner.buildDryRunInvocations, 1);
expect(buildRunner.linkDryRunInvocations, 0);
});
testUsingContext('build with assets but not enabled', () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsAndroid(
androidArchs: <AndroidArch>[AndroidArch.arm64_v8a],
targetAndroidNdkApi: 21,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('build no assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
await buildNativeAssetsAndroid(
androidArchs: <AndroidArch>[AndroidArch.arm64_v8a],
targetAndroidNdkApi: 21,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
);
expect(
environment.buildDir.childFile('native_assets.yaml'),
exists,
);
});
for (final BuildMode buildMode in <BuildMode>[
BuildMode.debug,
BuildMode.release,
@ -227,17 +61,17 @@ void main() {
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
final File dylibAfterCompiling = fileSystem.file('libbar.so');
// The mock doesn't create the file, so create it here.
await dylibAfterCompiling.create();
final FakeNativeAssetsBuildRunner buildRunner =
FakeNativeAssetsBuildRunner(
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: FakeNativeAssetsBuilderResult(
buildResult: FakeFlutterNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
@ -249,22 +83,25 @@ void main() {
],
),
);
await buildNativeAssetsAndroid(
androidArchs: <AndroidArch>[AndroidArch.arm64_v8a],
targetAndroidNdkApi: 21,
await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: buildMode.cliName,
kMinSdkVersion: minSdkVersion,
},
targetPlatform: TargetPlatform.android_arm64,
projectUri: projectUri,
buildMode: buildMode,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Building native assets for [android_arm64] $buildMode.',
'Building native assets for [android_arm64] done.',
'Building native assets for android_arm64 $buildMode.',
'Building native assets for android_arm64 $buildMode done.',
]),
);
expect(
environment.buildDir.childFile('native_assets.yaml'),
exists,
@ -287,12 +124,16 @@ void main() {
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.create(recursive: true);
await buildNativeAssetsAndroid(
androidArchs: <AndroidArch>[AndroidArch.x86_64],
targetAndroidNdkApi: 21,
await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
kMinSdkVersion: minSdkVersion,
},
targetPlatform: TargetPlatform.android_x64,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: _BuildRunnerWithoutNdk(),
);
@ -308,16 +149,19 @@ void main() {
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsAndroid(
androidArchs: <AndroidArch>[AndroidArch.arm64_v8a],
targetAndroidNdkApi: 21,
() => runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
kMinSdkVersion: minSdkVersion,
},
targetPlatform: TargetPlatform.android_arm64,
projectUri: projectUri,
buildMode: BuildMode.debug,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: _BuildRunnerWithoutNdk(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
@ -330,73 +174,9 @@ void main() {
);
});
testUsingContext('Native assets dry run error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsAndroid(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: const FakeNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building (dry run) native assets failed. See the logs for more details.',
),
);
});
testUsingContext('Native assets build error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
for (final String hook in <String>['Building', 'Linking']) {
expect(
() => buildNativeAssetsAndroid(
androidArchs: <AndroidArch>[AndroidArch.arm64_v8a],
targetAndroidNdkApi: 21,
projectUri: projectUri,
buildMode: BuildMode.release,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: FakeNativeAssetsBuilderResult(
success: hook != 'Building',
),
linkResult: FakeNativeAssetsBuilderResult(
success: hook != 'Linking',
),
),
),
throwsToolExit(
message:
'$hook native assets failed. See the logs for more details.',
),
);
}
});
}
class _BuildRunnerWithoutNdk extends FakeNativeAssetsBuildRunner {
class _BuildRunnerWithoutNdk extends FakeFlutterNativeAssetsBuildRunner {
_BuildRunnerWithoutNdk({
super.packagesWithNativeAssetsResult = const <Package>[],
});

View file

@ -76,21 +76,29 @@ void main() {
iosEnvironment.defines.remove(kIosArchs);
final NativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner();
final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner();
await NativeAssets(buildRunner: buildRunner).build(iosEnvironment);
final File nativeAssetsYaml =
iosEnvironment.buildDir.childFile('native_assets.yaml');
final File depsFile = iosEnvironment.buildDir.childFile('native_assets.d');
expect(depsFile, exists);
expect(nativeAssetsYaml, exists);
});
testUsingContext('NativeAssets throws error if missing sdk root', () async {
testUsingContext('NativeAssets throws error if missing sdk root', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
}, () async {
await createPackageConfig(iosEnvironment);
final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('foo', iosEnvironment.projectDir.uri),
]);
iosEnvironment.defines.remove(kSdkRoot);
expect(const NativeAssets().build(iosEnvironment), throwsA(isA<MissingDefineException>()));
expect(NativeAssets(buildRunner: buildRunner).build(iosEnvironment), throwsA(isA<MissingDefineException>()));
});
// The NativeAssets Target should _always_ be creating a yaml an d file.
@ -109,7 +117,7 @@ void main() {
() async {
await createPackageConfig(iosEnvironment);
final NativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner();
final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner();
await NativeAssets(buildRunner: buildRunner).build(iosEnvironment);
expect(iosEnvironment.buildDir.childFile('native_assets.d'), exists);
@ -182,9 +190,9 @@ void main() {
() async {
await createPackageConfig(iosEnvironment);
final NativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[Package('foo', iosEnvironment.buildDir.uri)],
buildResult: FakeNativeAssetsBuilderResult(
buildResult: FakeFlutterNativeAssetsBuilderResult(
assets: <native_assets_cli.AssetImpl>[
native_assets_cli.NativeCodeAssetImpl(
id: 'package:foo/foo.dart',
@ -243,11 +251,11 @@ void main() {
await createPackageConfig(androidEnvironment);
await fileSystem.file('libfoo.so').create();
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('foo', androidEnvironment.buildDir.uri)
],
buildResult: FakeNativeAssetsBuilderResult(
buildResult: FakeFlutterNativeAssetsBuilderResult(
assets: <native_assets_cli.AssetImpl>[
if (hasAssets)
native_assets_cli.NativeCodeAssetImpl(

View file

@ -4,6 +4,7 @@
import 'package:file/file.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
@ -14,15 +15,15 @@ import 'package:package_config/package_config_types.dart';
/// Mocks all logic instead of using `package:native_assets_builder`, which
/// relies on doing process calls to `pub` and the local file system.
class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner {
FakeNativeAssetsBuildRunner({
class FakeFlutterNativeAssetsBuildRunner implements FlutterNativeAssetsBuildRunner {
FakeFlutterNativeAssetsBuildRunner({
this.hasPackageConfigResult = true,
this.packagesWithNativeAssetsResult = const <Package>[],
this.onBuild,
this.buildDryRunResult = const FakeNativeAssetsBuilderResult(),
this.buildResult = const FakeNativeAssetsBuilderResult(),
this.linkResult = const FakeNativeAssetsBuilderResult(),
this.linkDryRunResult = const FakeNativeAssetsBuilderResult(),
this.buildDryRunResult = const FakeFlutterNativeAssetsBuilderResult(),
this.buildResult = const FakeFlutterNativeAssetsBuilderResult(),
this.linkResult = const FakeFlutterNativeAssetsBuilderResult(),
this.linkDryRunResult = const FakeFlutterNativeAssetsBuilderResult(),
CCompilerConfigImpl? cCompilerConfigResult,
CCompilerConfigImpl? ndkCCompilerConfigImplResult,
}) : cCompilerConfigResult = cCompilerConfigResult ?? CCompilerConfigImpl(),
@ -96,18 +97,6 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner {
return buildDryRunResult;
}
@override
Future<native_assets_builder.LinkDryRunResult> linkDryRun({
required bool includeParentEnvironment,
required LinkModePreferenceImpl linkModePreference,
required OSImpl targetOS,
required Uri workingDirectory,
required native_assets_builder.BuildDryRunResult buildDryRunResult,
}) async {
linkDryRunInvocations++;
return linkDryRunResult;
}
@override
Future<bool> hasPackageConfig() async {
hasPackageConfigInvocations++;
@ -129,13 +118,13 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner {
cCompilerConfigResult;
}
final class FakeNativeAssetsBuilderResult
final class FakeFlutterNativeAssetsBuilderResult
implements
native_assets_builder.BuildResult,
native_assets_builder.BuildDryRunResult,
native_assets_builder.LinkResult,
native_assets_builder.LinkDryRunResult {
const FakeNativeAssetsBuilderResult({
const FakeFlutterNativeAssetsBuilderResult({
this.assets = const <AssetImpl>[],
this.assetsForLinking = const <String, List<AssetImpl>>{},
this.dependencies = const <Uri>[],
@ -158,7 +147,7 @@ final class FakeNativeAssetsBuilderResult
class FakeHotRunnerNativeAssetsBuilder implements HotRunnerNativeAssetsBuilder {
FakeHotRunnerNativeAssetsBuilder(this.buildRunner);
final NativeAssetsBuildRunner buildRunner;
final FlutterNativeAssetsBuildRunner buildRunner;
@override
Future<Uri?> dryRun({
@ -169,11 +158,15 @@ class FakeHotRunnerNativeAssetsBuilder implements HotRunnerNativeAssetsBuilder {
required PackageConfig packageConfig,
required Logger logger,
}) {
return dryRunNativeAssets(
final List<TargetPlatform> targetPlatforms = flutterDevices
.map((FlutterDevice d) => d.targetPlatform)
.nonNulls
.toList();
return runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: buildRunner,
flutterDevices: flutterDevices,
targetPlatforms: targetPlatforms,
);
}
}

View file

@ -58,11 +58,11 @@ void main() {
(fakeFlutterDevice.devFS! as FakeDevFs).baseUri = Uri.parse('file:///base_uri');
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', fileSystem.currentDirectory.uri),
],
buildDryRunResult: FakeNativeAssetsBuilderResult(
buildDryRunResult: FakeFlutterNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
@ -127,11 +127,11 @@ void main() {
(fakeFlutterDevice.devFS! as FakeDevFs).baseUri = Uri.parse('file:///base_uri');
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', fileSystem.currentDirectory.uri),
],
buildDryRunResult: FakeNativeAssetsBuilderResult(
buildDryRunResult: FakeFlutterNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',

View file

@ -13,9 +13,8 @@ import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/isolated/native_assets/ios/native_assets.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart'
hide Target;
import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart' hide Target;
import 'package:native_assets_cli/native_assets_cli_internal.dart'
as native_assets_cli;
import 'package:package_config/package_config_types.dart';
@ -50,174 +49,6 @@ void main() {
projectUri = environment.projectDir.uri;
});
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
expect(
await dryRunNativeAssetsIOS(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
),
null,
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('build with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await buildNativeAssetsIOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64],
environmentType: EnvironmentType.simulator,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsIOS(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('dry run with assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: FakeNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.macOS,
architecture: ArchitectureImpl.arm64,
file: Uri.file('libbar.dylib'),
),
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.macOS,
architecture: ArchitectureImpl.x64,
file: Uri.file('libbar.dylib'),
),
],
),
);
final Uri? nativeAssetsYaml = await dryRunNativeAssetsIOS(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for ios.',
'Dry running native assets for ios done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/ios/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
contains('package:bar/bar.dart'),
);
expect(buildRunner.buildDryRunInvocations, 1);
expect(buildRunner.linkDryRunInvocations, 0);
});
testUsingContext('build with assets but not enabled', () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsIOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64],
environmentType: EnvironmentType.simulator,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('build no assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
await buildNativeAssetsIOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64],
environmentType: EnvironmentType.simulator,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
);
expect(
environment.buildDir.childFile('native_assets.yaml'),
exists,
);
});
for (final BuildMode buildMode in <BuildMode>[
BuildMode.debug,
BuildMode.release,
@ -330,15 +161,15 @@ void main() {
}
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
final FakeNativeAssetsBuildRunner buildRunner =
FakeNativeAssetsBuildRunner(
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
onBuild: (native_assets_cli.Target target) =>
FakeNativeAssetsBuilderResult(
FakeFlutterNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
@ -355,22 +186,25 @@ void main() {
file: Uri.file('${target.architecture}/libbuz.dylib'),
),
],
),
),
);
await buildNativeAssetsIOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64, DarwinArch.x86_64],
environmentType: EnvironmentType.simulator,
await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: buildMode.cliName,
kSdkRoot: '.../iPhone Simulator',
kIosArchs: 'arm64 x86_64',
},
targetPlatform: TargetPlatform.ios,
projectUri: projectUri,
buildMode: buildMode,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Building native assets for [ios_arm64, ios_x64] $buildMode.',
'Building native assets for [ios_arm64, ios_x64] done.',
'Building native assets for [ios_arm64, ios_x64] $buildMode done.',
]),
);
expect(
@ -385,69 +219,4 @@ void main() {
);
});
}
testUsingContext('Native assets dry run error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsIOS(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: const FakeNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building (dry run) native assets failed. See the logs for more details.',
),
);
});
testUsingContext('Native assets build error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
for (final String hook in <String>['Building', 'Linking']) {
expect(
() => buildNativeAssetsIOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64],
environmentType: EnvironmentType.simulator,
projectUri: projectUri,
buildMode: BuildMode.release,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: FakeNativeAssetsBuilderResult(
success: hook != 'Building',
),
linkResult: FakeNativeAssetsBuilderResult(
success: hook != 'Linking',
),
),
),
throwsToolExit(
message:
'$hook native assets failed. See the logs for more details.',
),
);
}
});
}

View file

@ -4,7 +4,6 @@
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
@ -15,7 +14,6 @@ import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/dart/package_map.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/isolated/native_assets/linux/native_assets.dart';
import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart'
hide Target;
@ -51,51 +49,21 @@ void main() {
projectUri = environment.projectDir.uri;
});
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
expect(
await dryRunNativeAssetsLinux(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
),
null,
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('build with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await buildNativeAssetsLinux(
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('does not throw if clang not present but no native assets present', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.create(recursive: true);
await buildNativeAssetsLinux(
await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.linux_x64,
projectUri: projectUri,
buildMode: BuildMode.debug,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: _BuildRunnerWithoutClang(),
);
@ -105,300 +73,6 @@ void main() {
);
});
testUsingContext('dry run for multiple OSes with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await dryRunNativeAssetsMultipleOSes(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[
TargetPlatform.darwin,
TargetPlatform.ios,
],
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsLinux(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('dry run with assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: FakeNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.linux,
architecture: ArchitectureImpl.x64,
file: Uri.file('libbar.so'),
),
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.linux,
architecture: ArchitectureImpl.arm64,
file: Uri.file('libbar.so'),
),
],
),
);
final Uri? nativeAssetsYaml = await dryRunNativeAssetsLinux(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for linux.',
'Dry running native assets for linux done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/linux/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
contains('package:bar/bar.dart'),
);
expect(buildRunner.buildDryRunInvocations, 1);
expect(buildRunner.linkDryRunInvocations, 0);
});
testUsingContext('build with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsLinux(
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('build no assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux(
targetPlatform: TargetPlatform.linux_x64,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/linux/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
isNot(contains('package:bar/bar.dart')),
);
expect(
environment.projectDir
.childDirectory('build')
.childDirectory('native_assets')
.childDirectory('linux'),
exists,
);
});
for (final bool flutterTester in <bool>[false, true]) {
String testName = '';
if (flutterTester) {
testName += ' flutter tester';
}
for (final BuildMode buildMode in <BuildMode>[
BuildMode.debug,
BuildMode.release,
]) {
testUsingContext('build with assets $buildMode$testName',
overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir
.childDirectory('.dart_tool')
.childFile('package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final File dylibAfterCompiling = fileSystem.file('libbar.so');
// The mock doesn't create the file, so create it here.
await dylibAfterCompiling.create();
final FakeNativeAssetsBuildRunner buildRunner =
FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: FakeNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.linux,
architecture: ArchitectureImpl.x64,
file: dylibAfterCompiling.uri,
),
],
),
);
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux(
targetPlatform: TargetPlatform.linux_x64,
projectUri: projectUri,
buildMode: buildMode,
fileSystem: fileSystem,
flutterTester: flutterTester,
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Building native assets for linux_x64 $buildMode.',
'Building native assets for linux_x64 done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/linux/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
stringContainsInOrder(<String>[
'package:bar/bar.dart',
if (flutterTester)
// Tests run on host system, so the have the full path on the system.
'- ${projectUri.resolve('build/native_assets/linux/libbar.so').toFilePath()}'
else
// Apps are a bundle with the dylibs on their dlopen path.
'- libbar.so',
]),
);
expect(buildRunner.buildInvocations, 1);
expect(
buildRunner.linkInvocations,
buildMode == BuildMode.release ? 1 : 0,
);
});
}
}
testUsingContext('Native assets dry run error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsLinux(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: const FakeNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building (dry run) native assets failed. See the logs for more details.',
),
);
});
testUsingContext('Native assets build error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsLinux(
targetPlatform: TargetPlatform.linux_x64,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: const FakeNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building native assets failed. See the logs for more details.',
),
);
});
// This logic is mocked in the other tests to avoid having test order
// randomization causing issues with what processes are invoked.
// Exercise the parsing of the process output in this separate test.
@ -436,14 +110,14 @@ void main() {
packageConfigFile,
logger: environment.logger,
);
final NativeAssetsBuildRunner runner =
NativeAssetsBuildRunnerImpl(projectUri, packageConfigFile.path, packageConfig, fileSystem, logger);
final FlutterNativeAssetsBuildRunner runner =
FlutterNativeAssetsBuildRunnerImpl(projectUri, packageConfigFile.path, packageConfig, fileSystem, logger);
final CCompilerConfigImpl result = await runner.cCompilerConfig;
expect(result.compiler, Uri.file('/some/path/to/clang'));
});
}
class _BuildRunnerWithoutClang extends FakeNativeAssetsBuildRunner {
class _BuildRunnerWithoutClang extends FakeFlutterNativeAssetsBuildRunner {
@override
Future<CCompilerConfigImpl> get cCompilerConfig async =>
throwToolExit('Failed to find clang++ on the PATH.');

View file

@ -13,10 +13,8 @@ import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/dart/package_map.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/isolated/native_assets/macos/native_assets.dart';
import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart'
hide Target;
import 'package:native_assets_cli/native_assets_cli_internal.dart' hide Target;
import 'package:native_assets_cli/native_assets_cli_internal.dart'
as native_assets_cli;
import 'package:package_config/package_config_types.dart';
@ -51,207 +49,9 @@ void main() {
projectUri = environment.projectDir.uri;
});
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
expect(
await dryRunNativeAssetsMacOS(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
),
null,
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('build with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await buildNativeAssetsMacOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64],
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run for multiple OSes with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await dryRunNativeAssetsMultipleOSes(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[
TargetPlatform.darwin,
TargetPlatform.ios,
],
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsMacOS(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('dry run with assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: FakeNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.macOS,
architecture: ArchitectureImpl.arm64,
file: Uri.file('libbar.dylib'),
),
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.macOS,
architecture: ArchitectureImpl.x64,
file: Uri.file('libbar.dylib'),
),
],
),
);
final Uri? nativeAssetsYaml = await dryRunNativeAssetsMacOS(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for macos.',
'Dry running native assets for macos done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/macos/native_assets.yaml'),
);
final String nativeAssetsYamlContents =
await fileSystem.file(nativeAssetsYaml).readAsString();
expect(
nativeAssetsYamlContents,
contains('package:bar/bar.dart'),
);
expect(buildRunner.buildDryRunInvocations, 1);
expect(buildRunner.linkDryRunInvocations, 0);
// Check that the framework uri is identical for both archs.
final String pathSeparator = const LocalPlatform().pathSeparator;
expect(
nativeAssetsYamlContents,
stringContainsInOrder(
<String>[
'bar.framework${pathSeparator}bar',
'bar.framework${pathSeparator}bar',
],
),
);
});
testUsingContext('build with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsMacOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64],
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('build no assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsMacOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64],
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/macos/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
isNot(contains('package:bar/bar.dart')),
);
});
for (final bool flutterTester in <bool>[false, true]) {
final bool isArm64 = native_assets_cli.ArchitectureImpl.current == ArchitectureImpl.arm64;
String testName = '';
if (flutterTester) {
testName += ' flutter tester';
@ -276,7 +76,7 @@ void main() {
}
for (final BuildMode buildMode in <BuildMode>[
BuildMode.debug,
BuildMode.release,
if (!flutterTester) BuildMode.release,
]) {
testUsingContext('build with assets $buildMode$testName', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
@ -289,8 +89,7 @@ void main() {
'-create',
'-output',
dylibPathBar,
'arm64/libbar.dylib',
'x64/libbar.dylib',
'${isArm64 ? 'arm64' : 'x64'}/libbar.dylib',
],
),
FakeCommand(
@ -312,8 +111,7 @@ void main() {
'-create',
'-output',
dylibPathBuz,
'arm64/libbuz.dylib',
'x64/libbuz.dylib',
'${isArm64 ? 'arm64' : 'x64'}/libbuz.dylib',
],
),
FakeCommand(
@ -323,9 +121,7 @@ void main() {
dylibPathBuz,
],
stdout: <String>[
'$dylibPathBuz (architecture x86_64):',
'@rpath/libbuz.dylib',
'$dylibPathBuz (architecture arm64):',
'$dylibPathBuz (architecture ${isArm64 ? 'arm64' : 'x86_64'}):',
'@rpath/libbuz.dylib',
].join('\n'),
),
@ -349,8 +145,7 @@ void main() {
'--force',
'--sign',
'-',
if (buildMode == BuildMode.debug)
'--timestamp=none',
if (buildMode == BuildMode.debug) '--timestamp=none',
signPathBar,
],
),
@ -361,7 +156,7 @@ void main() {
dylibPathBuz,
'-change',
'@rpath/libbar.dylib',
dylibPathBar,
dylibPathBar,
'-change',
'@rpath/libbuz.dylib',
signPathBuz,
@ -374,8 +169,7 @@ void main() {
'--force',
'--sign',
'-',
if (buildMode == BuildMode.debug)
'--timestamp=none',
if (buildMode == BuildMode.debug) '--timestamp=none',
signPathBuz,
],
),
@ -461,15 +255,27 @@ void main() {
if (const LocalPlatform().isWindows) {
return; // Backslashes in commands, but we will never run these commands on Windows.
}
if (flutterTester && !const LocalPlatform().isMacOS) {
// The [runFlutterSpecificDartBuild] will - when given
// `TargetPlatform.tester` - enable `flutter test` mode. That means if
// this test is run on linux, it's going to do a linux build.
// Though this test is mac-specific, so we skip that.
//
// Running the test in `!flutterTester` mode still works on linux as
// we explicitly tell it to do a mac build (instead of letting it
// choose the local build).
return;
}
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
onBuild: (native_assets_cli.Target target) =>
FakeNativeAssetsBuilderResult(
FakeFlutterNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
@ -486,26 +292,34 @@ void main() {
file: Uri.file('${target.architecture}/libbuz.dylib'),
),
],
),
),
);
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsMacOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64, DarwinArch.x86_64],
final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: buildMode.cliName,
kDarwinArchs: 'arm64 x86_64',
},
targetPlatform: flutterTester ? TargetPlatform.tester : TargetPlatform.darwin,
projectUri: projectUri,
buildMode: buildMode,
nativeAssetsYamlUri: flutterTester ? null : nonFlutterTesterAssetUri,
fileSystem: fileSystem,
flutterTester: flutterTester,
buildRunner: buildRunner,
);
final String expectedArchsBeingBuilt = flutterTester
? (isArm64 ? 'macos_arm64' : 'macos_x64')
: '[macos_arm64, macos_x64]';
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Building native assets for [macos_arm64, macos_x64] $buildMode.',
'Building native assets for [macos_arm64, macos_x64] done.',
'Building native assets for $expectedArchsBeingBuilt $buildMode.',
'Building native assets for $expectedArchsBeingBuilt $buildMode done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/macos/native_assets.yaml'),
flutterTester
? projectUri.resolve('build/native_assets/macos/native_assets.yaml')
: nonFlutterTesterAssetUri
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
@ -513,10 +327,10 @@ void main() {
'package:bar/bar.dart',
if (flutterTester)
// Tests run on host system, so the have the full path on the system.
'- ${projectUri.resolve('build/native_assets/macos/libbar.dylib').toFilePath()}'
projectUri.resolve('build/native_assets/macos/libbar.dylib').toFilePath()
else
// Apps are a bundle with the dylibs on their dlopen path.
'- bar.framework/bar',
'bar.framework/bar',
]),
);
expect(
@ -525,14 +339,14 @@ void main() {
'package:buz/buz.dart',
if (flutterTester)
// Tests run on host system, so the have the full path on the system.
'- ${projectUri.resolve('build/native_assets/macos/libbuz.dylib').toFilePath()}'
projectUri.resolve('build/native_assets/macos/libbuz.dylib').toFilePath()
else
// Apps are a bundle with the dylibs on their dlopen path.
'- buz.framework/buz',
'buz.framework/buz',
]),
);
// Multi arch.
expect(buildRunner.buildInvocations, 2);
expect(buildRunner.buildInvocations, flutterTester ? 1 : 2);
expect(
buildRunner.linkInvocations,
buildMode == BuildMode.release ? 2 : 0,
@ -541,82 +355,24 @@ void main() {
}
}
testUsingContext('Native assets dry run error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsMacOS(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: const FakeNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building (dry run) native assets failed. See the logs for more details.',
),
);
});
testUsingContext('Native assets build error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsMacOS(
darwinArchs: <DarwinArch>[DarwinArch.arm64],
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: const FakeNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building native assets failed. See the logs for more details.',
),
);
});
// This logic is mocked in the other tests to avoid having test order
// randomization causing issues with what processes are invoked.
// Exercise the parsing of the process output in this separate test.
testUsingContext('NativeAssetsBuildRunnerImpl.cCompilerConfig', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.list(
<FakeCommand>[
const FakeCommand(
command: <Pattern>['xcrun', 'clang', '--version'],
stdout: '''
testUsingContext('NativeAssetsBuildRunnerImpl.cCompilerConfig',
overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.list(
<FakeCommand>[
const FakeCommand(
command: <Pattern>['xcrun', 'clang', '--version'],
stdout: '''
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: arm64-apple-darwin22.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin''',
)
],
),
)
],
),
}, () async {
if (!const LocalPlatform().isMacOS) {
return;
@ -632,7 +388,7 @@ InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault
packageConfigFile,
logger: environment.logger,
);
final NativeAssetsBuildRunner runner = NativeAssetsBuildRunnerImpl(
final FlutterNativeAssetsBuildRunner runner = FlutterNativeAssetsBuildRunnerImpl(
projectUri,
packageConfigFile.path,
packageConfig,

View file

@ -0,0 +1,308 @@
// 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:file/file.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import 'package:package_config/package_config_types.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
import 'fake_native_assets_build_runner.dart';
void main() {
late FakeProcessManager processManager;
late Environment environment;
late Artifacts artifacts;
late FileSystem fileSystem;
late BufferLogger logger;
late Uri projectUri;
setUp(() {
processManager = FakeProcessManager.empty();
logger = BufferLogger.test();
artifacts = Artifacts.test();
fileSystem = MemoryFileSystem.test();
environment = Environment.test(
fileSystem.currentDirectory,
inputs: <String, String>{},
artifacts: artifacts,
processManager: processManager,
fileSystem: fileSystem,
logger: logger,
);
environment.buildDir.createSync(recursive: true);
projectUri = environment.projectDir.uri;
});
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
expect(
await runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
),
null,
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('build with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.windows_x64,
projectUri: projectUri,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: FakeFlutterNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run for multiple OSes with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[
TargetPlatform.windows_x64,
TargetPlatform.darwin,
TargetPlatform.ios,
],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('dry run with assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: FakeFlutterNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.windows,
architecture: ArchitectureImpl.x64,
file: Uri.file('bar.dll'),
),
],
),
);
final Uri? nativeAssetsYaml = await runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for windows.',
'Dry running native assets for windows done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/windows/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
contains('package:bar/bar.dart'),
);
expect(buildRunner.buildDryRunInvocations, 1);
});
testUsingContext('build with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.windows_x64,
projectUri: projectUri,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('build no assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.windows_x64,
projectUri: projectUri,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
);
expect(nativeAssetsYaml, nonFlutterTesterAssetUri);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
isNot(contains('package:bar/bar.dart')),
);
expect(
environment.projectDir.childDirectory('build').childDirectory('native_assets').childDirectory('windows'),
exists,
);
});
testUsingContext('Native assets dry run error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: const FakeFlutterNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building (dry run) native assets failed. See the logs for more details.',
),
);
});
testUsingContext('Native assets build error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.linux_x64,
projectUri: projectUri,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: const FakeFlutterNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building native assets failed. See the logs for more details.',
),
);
});
}

View file

@ -56,7 +56,7 @@ void main() {
globals.fs
.file(globals.fs.path.join('lib', 'main.dart'))
.createSync(recursive: true);
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner();
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner();
final HotRunner residentRunner = HotRunner(
<FlutterDevice>[
flutterDevice,

View file

@ -4,7 +4,6 @@
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
@ -15,9 +14,8 @@ import 'package:flutter_tools/src/dart/package_map.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
import 'package:flutter_tools/src/isolated/native_assets/windows/native_assets.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart'
hide Target;
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart' as native_assets_cli;
import 'package:package_config/package_config_types.dart';
import '../../../src/common.dart';
@ -50,187 +48,6 @@ void main() {
projectUri = environment.projectDir.uri;
});
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
expect(
await dryRunNativeAssetsWindows(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
),
null,
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('build with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await buildNativeAssetsWindows(
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run for multiple OSes with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await dryRunNativeAssetsMultipleOSes(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[
TargetPlatform.windows_x64,
],
buildRunner: FakeNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsWindows(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('dry run with assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: FakeNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.windows,
architecture: ArchitectureImpl.x64,
file: Uri.file('bar.dll'),
),
],
),
);
final Uri? nativeAssetsYaml = await dryRunNativeAssetsWindows(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for windows.',
'Dry running native assets for windows done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/windows/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
contains('package:bar/bar.dart'),
);
expect(buildRunner.buildDryRunInvocations, 1);
expect(buildRunner.linkDryRunInvocations, 0);
});
testUsingContext('build with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsWindows(
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('build no assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsWindows(
targetPlatform: TargetPlatform.windows_x64,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/windows/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
isNot(contains('package:bar/bar.dart')),
);
expect(
environment.projectDir.childDirectory('build').childDirectory('native_assets').childDirectory('windows'),
exists,
);
});
for (final bool flutterTester in <bool>[false, true]) {
String testName = '';
if (flutterTester) {
@ -238,23 +55,33 @@ void main() {
}
for (final BuildMode buildMode in <BuildMode>[
BuildMode.debug,
BuildMode.release,
if (!flutterTester) BuildMode.release,
]) {
if (flutterTester && !const LocalPlatform().isWindows) {
// When calling [runFlutterSpecificDartBuild] with the flutter tester
// target platform, it will perform a build for the local machine. That
// means e.g. running this test on MacOS will cause it to run a MacOS
// build - which in return requires a special [ProcessManager] that can
// simulate output of `otool` invocations.
continue;
}
testUsingContext('build with assets $buildMode$testName', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childDirectory('.dart_tool').childFile('package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
final File dylibAfterCompiling = fileSystem.file('bar.dll');
// The mock doesn't create the file, so create it here.
await dylibAfterCompiling.create();
final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: FakeNativeAssetsBuilderResult(
buildResult: FakeFlutterNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
@ -266,24 +93,35 @@ void main() {
],
),
);
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsWindows(
targetPlatform: TargetPlatform.windows_x64,
final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: buildMode.cliName,
},
targetPlatform: flutterTester
? TargetPlatform.tester
: TargetPlatform.windows_x64,
projectUri: projectUri,
buildMode: buildMode,
nativeAssetsYamlUri: flutterTester ? null : nonFlutterTesterAssetUri,
fileSystem: fileSystem,
flutterTester: flutterTester,
buildRunner: buildRunner,
);
final String expectedOS = flutterTester
? native_assets_cli.Target.current.toString()
: 'windows_x64';
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Building native assets for windows_x64 $buildMode.',
'Building native assets for windows_x64 done.',
'Building native assets for $expectedOS $buildMode.',
'Building native assets for $expectedOS $buildMode done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/windows/native_assets.yaml'),
final String expectedDirectory = flutterTester
? native_assets_cli.OSImpl.current.toString()
: 'windows';
expect(nativeAssetsYaml,
flutterTester
? projectUri.resolve('build/native_assets/$expectedDirectory/native_assets.yaml')
: nonFlutterTesterAssetUri
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
@ -291,80 +129,21 @@ void main() {
'package:bar/bar.dart',
if (flutterTester)
// Tests run on host system, so the have the full path on the system.
'- ${projectUri.resolve('build/native_assets/windows/bar.dll').toFilePath()}'
projectUri.resolve('build/native_assets/$expectedDirectory/bar.dll').toFilePath()
else
// Apps are a bundle with the dylibs on their dlopen path.
'- bar.dll',
'bar.dll',
]),
);
expect(buildRunner.buildInvocations, 1);
expect(
buildRunner.linkInvocations,
buildMode == BuildMode.release ? 1 :0,
buildMode == BuildMode.release ? 1 : 0,
);
});
}
}
testUsingContext('Native assets dry run error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => dryRunNativeAssetsWindows(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: const FakeNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building (dry run) native assets failed. See the logs for more details.',
),
);
});
testUsingContext('Native assets build error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => buildNativeAssetsWindows(
targetPlatform: TargetPlatform.windows_x64,
projectUri: projectUri,
buildMode: BuildMode.debug,
fileSystem: fileSystem,
yamlParentDirectory: environment.buildDir.uri,
buildRunner: FakeNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: const FakeNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building native assets failed. See the logs for more details.',
),
);
});
// This logic is mocked in the other tests to avoid having test order
// randomization causing issues with what processes are invoked.
// Exercise the parsing of the process output in this separate test.
@ -453,17 +232,14 @@ void main() {
fileSystem.directory(r'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\bin\Hostx64\x64');
await msvcBinDir.create(recursive: true);
final File packageConfigFile = fileSystem
.directory(projectUri)
.childDirectory('.dart_tool')
.childFile('package_config.json');
final File packageConfigFile = fileSystem.directory(projectUri).childDirectory('.dart_tool').childFile('package_config.json');
await packageConfigFile.parent.create();
await packageConfigFile.create();
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
packageConfigFile,
logger: environment.logger,
);
final NativeAssetsBuildRunner runner = NativeAssetsBuildRunnerImpl(
final FlutterNativeAssetsBuildRunner runner = FlutterNativeAssetsBuildRunnerImpl(
projectUri,
packageConfigFile.path,
packageConfig,