[native assets] Create NativeAssetsManifest.json instead of kernel embedding (#159322)

This PR introduces a `NativeAssetsManifest.json` next to the
`AssetManifest.bin` and `FontManifest.json`. This removes the need for
embedding the native assets mapping inside the kernel file and enables
decoupling native assets building and bundling from the kernel
compilation in flutter tools. This means `flutter run` no longer does a
dry run of `hook/build.dart` hooks.

(It also means all isolate groups will have the same native assets.
However, since Flutter does not support `Isolate.spawnUri` from kernel
files, this is not a regression.)

Implementation details:

* g3 is still using kernel embedding.
https://github.com/flutter/flutter/pull/142016 introduced an argument to
embed a `native_assets.yaml` inside `flutter attach` and `flutter run`
(the outer flutter process), but it is not used in `flutter assemble`
(the inner process when doing `flutter run`). So, those arguments need
to still be respected. However, all other logic related to embedding a
yaml encoding in the kernel file has been removed.
* All dry-run logic has been removed. 🎉 
* The `KernelSnapshot` target no longer depends on the
`InstallCodeAssets` target. Instead, the various OS-specific
"BundleAsset" targets now depend on the `InstallCodeAssets` target. The
`InstallCodeAssets` invokes the build hooks and produces the
`NativeAssetsManifest.json`. The various "BundleAsset" commands
synchronize the `NativeAssetsManifest.json` to the app bundle.
* `InstallCodeAssets` produces a `native_assets.json`, which is renamed
to `NativeAssetsManifest.json` in the various "Bundle" targets. This
means that all unit tests of the "Bundle" targets now need to create
this file. (Similar to how `app.dill` is expected to exist because
`KernelSnapshot` is a dependency of the "Bundle" targets.)
* Because dynamic libraries need to be code signed (at least on iOS and
MacOS), the bundling of the dylibs is _not_ migrated to reuse
`_updateDevFS` (which is used for ordinary assets). Only the 2nd and 3rd
invocation of `flutter assemble` from `xcodebuild` has access to the
code signing identity.

Relevant tests:

* test/integration.shard/isolated/native_assets_test.dart - runs
`flutter run` with native assets including hot restart and hot reload.

TODO:

* Undo engine-roll in this PR after engine has rolled in.

Issue:

* https://github.com/flutter/flutter/issues/154425

Related PRs:

* https://dart-review.googlesource.com/c/sdk/+/388161
* https://github.com/flutter/engine/pull/56727

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
Daco Harkes 2024-11-27 12:03:19 +01:00 committed by GitHub
parent c6260a86f6
commit 4aa2caef20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 197 additions and 954 deletions

View File

@ -218,7 +218,7 @@ EmbedFrameworks() {
fi
local native_assets_path="${project_path}/${FLUTTER_BUILD_DIR}/native_assets/macos/"
if [[ -d "$native_assets_path" ]]; then
RunCommand rsync -av --filter "- .DS_Store" --filter "- native_assets.yaml" "${native_assets_path}" "${xcode_frameworks_dir}"
RunCommand rsync -av --filter "- .DS_Store" --filter "- native_assets.yaml" --filter "- native_assets.json" "${native_assets_path}" "${xcode_frameworks_dir}"
# Iterate through all .frameworks in native assets directory.
for native_asset in "${native_assets_path}"*.framework; do

View File

@ -245,6 +245,8 @@ class Context {
extraArgs: <String>[
'--filter',
'- native_assets.yaml',
'--filter',
'- native_assets.json',
],
nativeAssetsPath,
xcodeFrameworksDir,

View File

@ -49,7 +49,6 @@ import 'src/globals.dart' as globals;
// Files in `isolated` are intentionally excluded from google3 tooling.
import 'src/isolated/build_targets.dart';
import 'src/isolated/mustache_template.dart';
import 'src/isolated/native_assets/native_assets.dart';
import 'src/isolated/native_assets/test/native_assets.dart';
import 'src/isolated/resident_web_runner.dart';
import 'src/native_assets.dart';
@ -181,7 +180,6 @@ List<FlutterCommand> generateCommands({
platform: globals.platform,
processInfo: globals.processInfo,
fileSystem: globals.fs,
nativeAssetsBuilder: const HotRunnerNativeAssetsBuilderImpl(),
),
BuildCommand(
artifacts: globals.artifacts!,
@ -244,7 +242,6 @@ List<FlutterCommand> generateCommands({
),
RunCommand(
verboseHelp: verboseHelp,
nativeAssetsBuilder: const HotRunnerNativeAssetsBuilderImpl(),
),
ScreenshotCommand(fs: globals.fs),
ShellCompletionCommand(),

View File

@ -7,6 +7,7 @@ import '../../base/build.dart';
import '../../base/deferred_component.dart';
import '../../base/file_system.dart';
import '../../build_info.dart';
import '../../devfs.dart';
import '../../globals.dart' as globals show xcode;
import '../../project.dart';
import '../build_system.dart';
@ -15,6 +16,7 @@ import '../exceptions.dart';
import 'assets.dart';
import 'common.dart';
import 'icon_tree_shaker.dart';
import 'native_assets.dart';
/// Prepares the asset bundle in the format expected by flutter.gradle.
///
@ -68,6 +70,10 @@ abstract class AndroidAssetBundle extends Target {
targetPlatform: TargetPlatform.android,
buildMode: buildMode,
flavor: environment.defines[kFlavor],
additionalContent: <String, DevFSContent>{
'NativeAssetsManifest.json':
DevFSFileContent(environment.buildDir.childFile('native_assets.json')),
},
);
environment.depFileService.writeToFile(
assetDepfile,
@ -78,6 +84,7 @@ abstract class AndroidAssetBundle extends Target {
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
InstallCodeAssets(),
];
}

View File

@ -22,6 +22,7 @@ import '../tools/scene_importer.dart';
import '../tools/shader_compiler.dart';
import 'common.dart';
import 'icon_tree_shaker.dart';
import 'native_assets.dart';
/// A helper function to copy an asset bundle into an [environment]'s output
/// directory.
@ -329,6 +330,7 @@ class CopyAssets extends Target {
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
InstallCodeAssets(),
];
@override
@ -363,6 +365,10 @@ class CopyAssets extends Target {
targetPlatform: TargetPlatform.android,
buildMode: buildMode,
flavor: environment.defines[kFlavor],
additionalContent: <String, DevFSContent>{
'NativeAssetsManifest.json':
DevFSFileContent(environment.buildDir.childFile('native_assets.json')),
},
);
environment.depFileService.writeToFile(
depfile,

View File

@ -3,16 +3,15 @@
// found in the LICENSE file.
import 'package:package_config/package_config.dart';
import 'package:yaml/yaml.dart';
import '../../artifacts.dart';
import '../../base/build.dart';
import '../../base/common.dart';
import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../build_info.dart';
import '../../compile.dart';
import '../../dart/package_map.dart';
import '../../devfs.dart';
import '../../globals.dart' as globals show xcode;
import '../build_system.dart';
import '../depfile.dart';
@ -81,6 +80,10 @@ class CopyFlutterBundle extends Target {
targetPlatform: TargetPlatform.android,
buildMode: buildMode,
flavor: flavor,
additionalContent: <String, DevFSContent>{
'NativeAssetsManifest.json':
DevFSFileContent(environment.buildDir.childFile('native_assets.json')),
},
);
environment.depFileService.writeToFile(
assetDepfile,
@ -91,6 +94,7 @@ class CopyFlutterBundle extends Target {
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
InstallCodeAssets(),
];
}
@ -122,11 +126,8 @@ class ReleaseCopyFlutterBundle extends CopyFlutterBundle {
/// even though it is not listed as an input. Pub inserts a timestamp into
/// the file which causes unnecessary rebuilds, so instead a subset of the contents
/// are used an input instead.
///
/// This kernel snapshot is concatenated with the [KernelSnapshotNativeAssets]
/// inside [KernelSnapshot] byte-wise to create the combined kernel snapshot.
class KernelSnapshotProgram extends Target {
const KernelSnapshotProgram();
class KernelSnapshot extends Target {
const KernelSnapshot();
@override
String get name => 'kernel_snapshot_program';
@ -143,7 +144,7 @@ class KernelSnapshotProgram extends Target {
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/${KernelSnapshotProgram.dillName}'),
Source.pattern('{BUILD_DIR}/${KernelSnapshot.dillName}'),
// TODO(mosuem): Should output resources.json. https://github.com/flutter/flutter/issues/146263
];
@ -160,7 +161,7 @@ class KernelSnapshotProgram extends Target {
DartPluginRegistrantTarget(),
];
static const String dillName = 'program.dill';
static const String dillName = 'app.dill';
@override
Future<void> build(Environment environment) async {
@ -276,150 +277,6 @@ class KernelSnapshotProgram extends Target {
}
}
/// Generate a kernel snapshot of the native assets mapping for resolving
/// `@Native` assets at runtime.
///
/// This kernel snapshot is concatenated to the [KernelSnapshotProgram]
/// inside [KernelSnapshot] to create the combined kernel snapshot.
class KernelSnapshotNativeAssets extends Target {
const KernelSnapshotNativeAssets();
@override
String get name => 'kernel_snapshot_native_assets';
@override
List<Source> get inputs => <Source>[
const Source.pattern('{BUILD_DIR}/${InstallCodeAssets.nativeAssetsFilename}'),
...const KernelSnapshotProgram().inputs,
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/${KernelSnapshotNativeAssets.dillName}'),
];
@override
List<String> get depfiles => const <String>[];
@override
List<Target> get dependencies => const <Target>[
InstallCodeAssets(),
];
static const String dillName = 'native_assets.dill';
@override
Future<void> build(Environment environment) async {
final File nativeAssetsFile = environment.buildDir.childFile(InstallCodeAssets.nativeAssetsFilename);
final File dillFile = environment.buildDir.childFile(dillName);
final YamlNode nativeAssetContents = loadYamlNode(await nativeAssetsFile.readAsString());
final Object? nativeAssetsInYaml = (nativeAssetContents as Map<Object?, Object?>)['native-assets'];
if (nativeAssetsInYaml is! Map || nativeAssetsInYaml.isEmpty) {
// Write an empty file to make concatenation a no-op.
// Write the file out to disk for caching.
await dillFile.writeAsBytes(<int>[]);
return;
}
final KernelCompiler compiler = KernelCompiler(
fileSystem: environment.fileSystem,
logger: environment.logger,
processManager: environment.processManager,
artifacts: environment.artifacts,
fileSystemRoots: <String>[],
);
final String? buildModeEnvironment = environment.defines[kBuildMode];
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, 'kernel_snapshot');
}
final String? targetPlatformEnvironment = environment.defines[kTargetPlatform];
if (targetPlatformEnvironment == null) {
throw MissingDefineException(kTargetPlatform, 'kernel_snapshot');
}
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final File packageConfigFile = findPackageConfigFileOrDefault(environment.projectDir);
final TargetPlatform targetPlatform = getTargetPlatformForName(targetPlatformEnvironment);
final String? frontendServerStarterPath = environment.defines[kFrontendServerStarterPath];
final String nativeAssets = nativeAssetsFile.path;
if (!await nativeAssetsFile.exists()) {
throwToolExit("$nativeAssets doesn't exist.");
}
environment.logger.printTrace('Embedding native assets mapping $nativeAssets in kernel.');
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
packageConfigFile,
logger: environment.logger,
);
final String dillPath = dillFile.path;
final CompilerOutput? output = await compiler.compile(
sdkRoot: environment.artifacts.getArtifactPath(
Artifact.flutterPatchedSdkPath,
platform: targetPlatform,
mode: buildMode,
),
aot: buildMode.isPrecompiled,
buildMode: buildMode,
trackWidgetCreation: false,
outputFilePath: dillPath,
packagesPath: packageConfigFile.path,
frontendServerStarterPath: frontendServerStarterPath,
packageConfig: packageConfig,
buildDir: environment.buildDir,
dartDefines: <String>[],
nativeAssets: nativeAssets,
);
if (output == null || output.errorCount != 0) {
throw Exception();
}
}
}
class KernelSnapshot extends Target {
const KernelSnapshot();
@override
String get name => 'kernel_snapshot';
@override
List<Target> get dependencies => const <Target>[
KernelSnapshotProgram(),
KernelSnapshotNativeAssets(),
];
@override
List<Source> get inputs => const <Source>[
Source.pattern('{BUILD_DIR}/${KernelSnapshotProgram.dillName}'),
Source.pattern('{BUILD_DIR}/${KernelSnapshotNativeAssets.dillName}'),
];
@override
List<Source> get outputs => <Source>[];
static const String dillName = 'app.dill';
@override
Future<void> build(Environment environment) async {
final File programDill = environment.buildDir.childFile(
KernelSnapshotProgram.dillName,
);
final File nativeAssetsDill = environment.buildDir.childFile(
KernelSnapshotNativeAssets.dillName,
);
final File dill = environment.buildDir.childFile(dillName);
await programDill.copy(dill.path);
await dill.writeAsBytes(
await nativeAssetsDill.readAsBytes(),
mode: FileMode.append,
);
}
}
/// Supports compiling a dart kernel file to an ELF binary.
abstract class AotElfBase extends Target {
const AotElfBase();

View File

@ -12,6 +12,7 @@ import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../base/process.dart';
import '../../build_info.dart';
import '../../devfs.dart';
import '../../globals.dart' as globals;
import '../../ios/mac.dart';
import '../../macos/xcode.dart';
@ -24,6 +25,7 @@ import '../tools/shader_compiler.dart';
import 'assets.dart';
import 'common.dart';
import 'icon_tree_shaker.dart';
import 'native_assets.dart';
/// Supports compiling a dart kernel file to an assembly file.
///
@ -464,6 +466,7 @@ abstract class IosAssetBundle extends Target {
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
InstallCodeAssets(),
];
@override
@ -551,6 +554,10 @@ abstract class IosAssetBundle extends Target {
flutterProject.ios.infoPlist,
flutterProject.ios.appFrameworkInfoPlist,
],
additionalContent: <String, DevFSContent>{
'NativeAssetsManifest.json':
DevFSFileContent(environment.buildDir.childFile('native_assets.json')),
},
flavor: environment.defines[kFlavor],
);
environment.depFileService.writeToFile(
@ -625,6 +632,7 @@ class ProfileIosApplicationBundle extends _IosAssetBundleWithDSYM {
@override
List<Target> get dependencies => const <Target>[
AotAssemblyProfile(),
InstallCodeAssets(),
];
}
@ -638,6 +646,7 @@ class ReleaseIosApplicationBundle extends _IosAssetBundleWithDSYM {
@override
List<Target> get dependencies => const <Target>[
AotAssemblyRelease(),
InstallCodeAssets(),
];
@override

View File

@ -15,6 +15,7 @@ import 'assets.dart';
import 'common.dart';
import 'desktop.dart';
import 'icon_tree_shaker.dart';
import 'native_assets.dart';
/// The only files/subdirectories we care out.
const List<String> _kLinuxArtifacts = <String>[
@ -99,6 +100,7 @@ abstract class BundleLinuxAssets extends Target {
@override
List<Target> get dependencies => <Target>[
const KernelSnapshot(),
const InstallCodeAssets(),
UnpackLinux(targetPlatform),
];
@ -140,6 +142,8 @@ abstract class BundleLinuxAssets extends Target {
buildMode: buildMode,
additionalContent: <String, DevFSContent>{
'version.json': DevFSStringContent(versionInfo),
'NativeAssetsManifest.json':
DevFSFileContent(environment.buildDir.childFile('native_assets.json')),
},
);
environment.depFileService.writeToFile(

View File

@ -10,6 +10,7 @@ import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../base/process.dart';
import '../../build_info.dart';
import '../../devfs.dart';
import '../../globals.dart' as globals show xcode;
import '../../reporting/reporting.dart';
import '../build_system.dart';
@ -18,6 +19,7 @@ import '../exceptions.dart';
import 'assets.dart';
import 'common.dart';
import 'icon_tree_shaker.dart';
import 'native_assets.dart';
/// Copy the macOS framework to the correct copy dir by invoking 'rsync'.
///
@ -489,6 +491,10 @@ abstract class MacOSBundleFlutterAssets extends Target {
targetPlatform: TargetPlatform.darwin,
buildMode: buildMode,
flavor: environment.defines[kFlavor],
additionalContent: <String, DevFSContent>{
'NativeAssetsManifest.json':
DevFSFileContent(environment.buildDir.childFile('native_assets.json')),
},
);
environment.depFileService.writeToFile(
assetDepfile,
@ -591,6 +597,7 @@ class DebugMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
KernelSnapshot(),
DebugMacOSFramework(),
DebugUnpackMacOS(),
InstallCodeAssets(),
];
@override
@ -620,6 +627,7 @@ class ProfileMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
@override
List<Target> get dependencies => const <Target>[
CompileMacOSFramework(),
InstallCodeAssets(),
ProfileUnpackMacOS(),
];
@ -647,6 +655,7 @@ class ReleaseMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
@override
List<Target> get dependencies => const <Target>[
CompileMacOSFramework(),
InstallCodeAssets(),
ReleaseUnpackMacOS(),
];

View File

@ -116,7 +116,7 @@ class DartBuildForNative extends DartBuild {
@override
List<Target> get dependencies => const <Target>[
KernelSnapshotProgram(),
KernelSnapshot(),
];
}
@ -125,11 +125,6 @@ class DartBuildForNative extends DartBuild {
/// The build mode and target architecture can be changed from the
/// native build project (Xcode etc.), so only `flutter assemble` has the
/// information about build-mode and target architecture.
/// Invocations of flutter_tools other than `flutter assemble` are dry runs.
///
/// This step needs to be consistent with the dry run invocations in `flutter
/// run`s so that the kernel mapping of asset id to dylib lines up after hot
/// restart.
class InstallCodeAssets extends Target {
const InstallCodeAssets();
@ -188,7 +183,7 @@ class InstallCodeAssets extends Target {
Source.pattern('{BUILD_DIR}/$nativeAssetsFilename'),
];
static const String nativeAssetsFilename = 'native_assets.yaml';
static const String nativeAssetsFilename = 'native_assets.json';
static const String depFilename = 'install_code_assets.d';
}

View File

@ -5,6 +5,7 @@
import '../../artifacts.dart';
import '../../base/file_system.dart';
import '../../build_info.dart';
import '../../devfs.dart';
import '../build_system.dart';
import '../depfile.dart';
import '../exceptions.dart';
@ -12,6 +13,7 @@ import 'assets.dart';
import 'common.dart';
import 'desktop.dart';
import 'icon_tree_shaker.dart';
import 'native_assets.dart';
/// The only files/subdirectories we care about.
const List<String> _kWindowsArtifacts = <String>[
@ -105,6 +107,7 @@ abstract class BundleWindowsAssets extends Target {
@override
List<Target> get dependencies => <Target>[
const KernelSnapshot(),
const InstallCodeAssets(),
UnpackWindows(targetPlatform),
];
@ -143,6 +146,10 @@ abstract class BundleWindowsAssets extends Target {
outputDirectory,
targetPlatform: targetPlatform,
buildMode: buildMode,
additionalContent: <String, DevFSContent>{
'NativeAssetsManifest.json':
DevFSFileContent(environment.buildDir.childFile('native_assets.json')),
},
);
environment.depFileService.writeToFile(
depfile,

View File

@ -68,7 +68,6 @@ class AttachCommand extends FlutterCommand {
required Platform platform,
required ProcessInfo processInfo,
required FileSystem fileSystem,
HotRunnerNativeAssetsBuilder? nativeAssetsBuilder,
}) : _hotRunnerFactory = hotRunnerFactory ?? HotRunnerFactory(),
_stdio = stdio,
_logger = logger,
@ -76,8 +75,7 @@ class AttachCommand extends FlutterCommand {
_signals = signals,
_platform = platform,
_processInfo = processInfo,
_fileSystem = fileSystem,
_nativeAssetsBuilder = nativeAssetsBuilder {
_fileSystem = fileSystem {
addBuildModeFlags(verboseHelp: verboseHelp, defaultToRelease: false, excludeRelease: true);
usesTargetOption();
usesPortOptions(verboseHelp: verboseHelp);
@ -149,7 +147,6 @@ class AttachCommand extends FlutterCommand {
final Platform _platform;
final ProcessInfo _processInfo;
final FileSystem _fileSystem;
final HotRunnerNativeAssetsBuilder? _nativeAssetsBuilder;
@override
final String name = 'attach';
@ -348,7 +345,6 @@ known, it can be explicitly provided to attach via the command-line, e.g.
device: device,
flutterProject: flutterProject,
usesIpv6: usesIpv6,
nativeAssetsBuilder: _nativeAssetsBuilder,
);
late AppInstance app;
try {
@ -381,7 +377,6 @@ known, it can be explicitly provided to attach via the command-line, e.g.
device: device,
flutterProject: flutterProject,
usesIpv6: usesIpv6,
nativeAssetsBuilder: _nativeAssetsBuilder,
);
final Completer<void> onAppStart = Completer<void>.sync();
TerminalHandler? terminalHandler;
@ -438,7 +433,6 @@ known, it can be explicitly provided to attach via the command-line, e.g.
required Device device,
required FlutterProject flutterProject,
required bool usesIpv6,
required HotRunnerNativeAssetsBuilder? nativeAssetsBuilder,
}) async {
final BuildInfo buildInfo = await getBuildInfo();
@ -475,7 +469,6 @@ known, it can be explicitly provided to attach via the command-line, e.g.
dillOutputPath: stringArg('output-dill'),
flutterProject: flutterProject,
nativeAssetsYamlFile: stringArg(FlutterOptions.kNativeAssetsYamlFile),
nativeAssetsBuilder: _nativeAssetsBuilder,
analytics: analytics,
)
: ColdRunner(
@ -507,7 +500,6 @@ class HotRunnerFactory {
bool stayResident = true,
FlutterProject? flutterProject,
String? nativeAssetsYamlFile,
required HotRunnerNativeAssetsBuilder? nativeAssetsBuilder,
required Analytics analytics,
}) => HotRunner(
devices,
@ -520,7 +512,6 @@ class HotRunnerFactory {
dillOutputPath: dillOutputPath,
stayResident: stayResident,
nativeAssetsYamlFile: nativeAssetsYamlFile,
nativeAssetsBuilder: nativeAssetsBuilder,
analytics: analytics,
);
}

View File

@ -286,6 +286,8 @@ class BuildIOSFrameworkCommand extends BuildFrameworkCommand {
'- .DS_Store',
'--filter',
'- native_assets.yaml',
'--filter',
'- native_assets.json',
nativeAssetsDirectory.path,
modeDirectory.path,
]);

View File

@ -118,6 +118,8 @@ class BuildMacOSFrameworkCommand extends BuildFrameworkCommand {
'- .DS_Store',
'--filter',
'- native_assets.yaml',
'--filter',
'- native_assets.json',
nativeAssetsDirectory.path,
modeDirectory.path,
]);

View File

@ -667,7 +667,6 @@ class AppDomain extends Domain {
String? isolateFilter,
bool machine = true,
String? userIdentifier,
required HotRunnerNativeAssetsBuilder? nativeAssetsBuilder,
}) async {
if (!await device.supportsRuntimeMode(options.buildInfo.mode)) {
throw Exception(
@ -717,7 +716,6 @@ class AppDomain extends Domain {
hostIsIde: true,
machine: machine,
analytics: globals.analytics,
nativeAssetsBuilder: nativeAssetsBuilder,
);
} else {
runner = ColdRunner(

View File

@ -349,9 +349,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
class RunCommand extends RunCommandBase {
RunCommand({
bool verboseHelp = false,
HotRunnerNativeAssetsBuilder? nativeAssetsBuilder,
}) : _nativeAssetsBuilder = nativeAssetsBuilder,
super(verboseHelp: verboseHelp) {
}) : super(verboseHelp: verboseHelp) {
requiresPubspecYaml();
usesFilesystemOptions(hide: !verboseHelp);
usesExtraDartFlagOptions(verboseHelp: verboseHelp);
@ -435,8 +433,6 @@ class RunCommand extends RunCommandBase {
);
}
final HotRunnerNativeAssetsBuilder? _nativeAssetsBuilder;
@override
final String name = 'run';
@ -695,7 +691,6 @@ class RunCommand extends RunCommandBase {
stayResident: stayResident,
analytics: globals.analytics,
nativeAssetsYamlFile: stringArg(FlutterOptions.kNativeAssetsYamlFile),
nativeAssetsBuilder: _nativeAssetsBuilder,
);
} else if (webMode) {
return webRunnerFactory!.createWebRunner(
@ -765,7 +760,6 @@ class RunCommand extends RunCommandBase {
packagesFilePath: globalResults![FlutterGlobalOptions.kPackagesOption] as String?,
dillOutputPath: stringArg('output-dill'),
userIdentifier: userIdentifier,
nativeAssetsBuilder: _nativeAssetsBuilder,
);
} on Exception catch (error) {
throwToolExit(error.toString());

View File

@ -419,7 +419,8 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
webUseWasm: useWasm,
);
String? testAssetDirectory;
final Uri? nativeAssetsJson = await nativeAssetsBuilder?.build(buildInfo);
String? testAssetPath;
if (buildTestAssets) {
await _buildTestAsset(
flavor: buildInfo.flavor,
@ -427,8 +428,19 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
buildMode: debuggingOptions.buildInfo.mode,
packageConfigPath: buildInfo.packageConfigPath,
);
testAssetDirectory = globals.fs.path.
join(flutterProject.directory.path, 'build', 'unit_test_assets');
}
if (buildTestAssets || nativeAssetsJson != null) {
testAssetPath = globals.fs.path
.join(flutterProject.directory.path, 'build', 'unit_test_assets');
}
if (nativeAssetsJson != null) {
final Directory testAssetDirectory = globals.fs.directory(testAssetPath);
if (!testAssetDirectory.existsSync()) {
await testAssetDirectory.create(recursive: true);
}
final File nativeAssetsManifest =
testAssetDirectory.childFile('NativeAssetsManifest.json');
await globals.fs.file(nativeAssetsJson).copy(nativeAssetsManifest.path);
}
final String? concurrencyString = stringArg('concurrency');
@ -585,7 +597,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
machine: machine,
updateGoldens: boolArg('update-goldens'),
concurrency: jobs,
testAssetDirectory: testAssetDirectory,
testAssetDirectory: testAssetPath,
flutterProject: flutterProject,
randomSeed: stringArg('test-randomize-ordering-seed'),
reporter: stringArg('reporter'),
@ -612,7 +624,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
machine: machine,
updateGoldens: boolArg('update-goldens'),
concurrency: jobs,
testAssetDirectory: testAssetDirectory,
testAssetDirectory: testAssetPath,
flutterProject: flutterProject,
web: isWeb,
randomSeed: stringArg('test-randomize-ordering-seed'),

View File

@ -17,11 +17,10 @@ import '../../base/platform.dart';
import '../../build_info.dart' as build_info;
import '../../build_system/exceptions.dart';
import '../../cache.dart';
import '../../convert.dart';
import '../../features.dart';
import '../../globals.dart' as globals;
import '../../macos/xcode.dart' as xcode;
import '../../resident_runner.dart';
import '../../run_hot.dart';
import 'android/native_assets.dart';
import 'ios/native_assets.dart';
import 'linux/native_assets.dart';
@ -129,67 +128,7 @@ Future<void> installCodeAssets({
final String? codesignIdentity = environmentDefines[build_info.kCodesignIdentity];
final Map<CodeAsset, KernelAsset> assetTargetLocations = assetTargetLocationsForOS(targetOS, dartBuildResult.codeAssets, flutterTester, buildUri);
await _copyNativeCodeAssetsForOS(targetOS, buildUri, buildMode, fileSystem, assetTargetLocations, codesignIdentity, flutterTester);
final KernelAssets kernelAssets = KernelAssets(assetTargetLocations.values.toList());
await _writeNativeAssetsYaml(kernelAssets, nativeAssetsFileUri, fileSystem);
}
Future<Uri?> runFlutterSpecificDartDryRunOnPlatforms({
required Uri projectUri,
required FileSystem fileSystem,
required FlutterNativeAssetsBuildRunner buildRunner,
required List<build_info.TargetPlatform> targetPlatforms,
}) async {
if (!await _nativeBuildRequired(buildRunner)) {
return null;
}
final Map<CodeAsset, KernelAsset> assetTargetLocations =
<CodeAsset, KernelAsset>{};
for (final build_info.TargetPlatform targetPlatform in targetPlatforms) {
// This dry-run functionality is only used in the `flutter run`
// implementation (not in `flutter build` or `flutter test`).
//
// Though we can end up with `flutterTester == true` if someone uses the
// `flutter-tester` device via `flutter run -d flutter-tester` (which mainly
// happens in tests)
final bool flutterTester =
targetPlatform == build_info.TargetPlatform.tester;
final OS targetOS = getNativeOSFromTargetPlatfrorm(targetPlatform);
if (targetOS != OS.macOS &&
targetOS != OS.windows &&
targetOS != OS.linux &&
targetOS != OS.android &&
targetOS != OS.iOS) {
await ensureNoNativeAssetsOrOsIsSupported(
projectUri,
targetPlatform.toString(),
fileSystem,
buildRunner,
);
}
final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
final DartBuildResult result = await _runDartDryRunBuild(
buildRunner: buildRunner,
projectUri: projectUri,
fileSystem: fileSystem,
targetOS: targetOS);
assetTargetLocations.addAll(assetTargetLocationsForOS(
targetOS, result.codeAssets, flutterTester, buildUri));
}
final Uri buildUri = targetPlatforms.length == 1
? nativeAssetsBuildUri(
projectUri, getNativeOSFromTargetPlatfrorm(targetPlatforms.single))
: _buildUriMultiple(projectUri);
final Uri nativeAssetsYamlUri = buildUri.resolve('native_assets.yaml');
await _writeNativeAssetsYaml(
KernelAssets(assetTargetLocations.values.toList()),
nativeAssetsYamlUri,
fileSystem,
);
return nativeAssetsYamlUri;
await _writeNativeAssetsJson(assetTargetLocations.values.toList(), nativeAssetsFileUri, fileSystem,);
}
/// Programmatic API to be used by Dart launchers to invoke native builds.
@ -199,23 +138,13 @@ Future<Uri?> runFlutterSpecificDartDryRunOnPlatforms({
abstract interface class FlutterNativeAssetsBuildRunner {
/// Whether the project has a `.dart_tools/package_config.json`.
///
/// If there is no package config, [packagesWithNativeAssets], [build], and
/// [buildDryRun] must not be invoked.
/// If there is no package config, [packagesWithNativeAssets], [build] and
/// [link] must not be invoked.
Future<bool> hasPackageConfig();
/// All packages in the transitive dependencies that have a `build.dart`.
Future<List<Package>> packagesWithNativeAssets();
/// Runs all [packagesWithNativeAssets] `build.dart` in dry run.
Future<BuildDryRunResult?> buildDryRun({
required List<String> supportedAssetTypes,
required BuildConfigCreator configCreator,
required BuildValidator buildValidator,
required bool includeParentEnvironment,
required OS targetOS,
required Uri workingDirectory,
});
/// Runs all [packagesWithNativeAssets] `build.dart`.
Future<BuildResult?> build({
required List<String> supportedAssetTypes,
@ -307,30 +236,6 @@ class FlutterNativeAssetsBuildRunnerImpl implements FlutterNativeAssetsBuildRunn
}
@override
Future<BuildDryRunResult?> buildDryRun({
required List<String> supportedAssetTypes,
required BuildConfigCreator configCreator,
required BuildValidator buildValidator,
required bool includeParentEnvironment,
required OS targetOS,
required Uri workingDirectory,
}) {
final PackageLayout packageLayout = PackageLayout.fromPackageConfig(
packageConfig,
Uri.file(packageConfigPath),
);
return _buildRunner.buildDryRun(
supportedAssetTypes: supportedAssetTypes,
configCreator: configCreator,
buildValidator: buildValidator,
includeParentEnvironment: includeParentEnvironment,
targetOS: targetOS,
workingDirectory: workingDirectory,
packageLayout: packageLayout,
linkingEnabled: false, // Dry run is only used in JIT mode.
);
}
@override
Future<BuildResult?> build({
required List<String> supportedAssetTypes,
@ -418,14 +323,14 @@ class FlutterNativeAssetsBuildRunnerImpl implements FlutterNativeAssetsBuildRunn
}();
}
Future<Uri> _writeNativeAssetsYaml(
KernelAssets assets,
Uri nativeAssetsYamlUri,
Future<Uri> _writeNativeAssetsJson(
List<KernelAsset> assets,
Uri nativeAssetsJsonUri,
FileSystem fileSystem,
) async {
globals.logger.printTrace('Writing native assets yaml to $nativeAssetsYamlUri.');
final String nativeAssetsDartContents = assets.toNativeAssetsFile();
final File nativeAssetsFile = fileSystem.file(nativeAssetsYamlUri);
globals.logger.printTrace('Writing native assets json to $nativeAssetsJsonUri.');
final String nativeAssetsDartContents = _toNativeAssetsJsonFile(assets);
final File nativeAssetsFile = fileSystem.file(nativeAssetsJsonUri);
final Directory parentDirectory = nativeAssetsFile.parent;
if (!await parentDirectory.exists()) {
await parentDirectory.create(recursive: true);
@ -435,6 +340,29 @@ Future<Uri> _writeNativeAssetsYaml(
return nativeAssetsFile.uri;
}
String _toNativeAssetsJsonFile(List<KernelAsset> kernelAssets) {
final Map<Target, List<KernelAsset>> assetsPerTarget = <Target, List<KernelAsset>>{};
for (final KernelAsset asset in kernelAssets) {
assetsPerTarget.putIfAbsent(asset.target, () => <KernelAsset>[]).add(asset);
}
const String formatVersionKey = 'format-version';
const String nativeAssetsKey = 'native-assets';
// See assets/native_assets.cc in the engine for the expected format.
final Map<String, Object> jsonContents = <String, Object>{
formatVersionKey: const <int>[1, 0, 0],
nativeAssetsKey: <String, Map<String, List<String>>>{
for (final MapEntry<Target, List<KernelAsset>> entry in assetsPerTarget.entries)
entry.key.toString(): <String, List<String>>{
for (final KernelAsset e in entry.value) e.id: e.path.toJson(),
}
},
};
return jsonEncode(jsonContents);
}
/// Select the native asset build mode for a given Flutter build mode.
BuildMode _nativeAssetsBuildMode(build_info.BuildMode buildMode) {
switch (buildMode) {
@ -518,54 +446,6 @@ Uri nativeAssetsBuildUri(Uri projectUri, OS os) {
return projectUri.resolve('$buildDir/native_assets/$os/');
}
/// Gets the native asset id to dylib mapping to embed in the kernel file.
///
/// Run hot compiles a kernel file that is pushed to the device after hot
/// restart. We need to embed the native assets mapping in order to access
/// native assets after hot restart.
class HotRunnerNativeAssetsBuilderImpl implements HotRunnerNativeAssetsBuilder {
const HotRunnerNativeAssetsBuilderImpl();
@override
Future<Uri?> dryRun({
required Uri projectUri,
required FileSystem fileSystem,
required List<FlutterDevice> flutterDevices,
required String packageConfigPath,
required PackageConfig packageConfig,
required Logger logger,
}) async {
final FlutterNativeAssetsBuildRunner buildRunner =
FlutterNativeAssetsBuildRunnerImpl(
projectUri,
packageConfigPath,
packageConfig,
fileSystem,
globals.logger,
);
// If `flutter run -d all` is used then we may have multiple OSes.
final List<build_info.TargetPlatform> targetPlatforms = flutterDevices
.map((FlutterDevice d) => d.targetPlatform)
.nonNulls
.toList();
return runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: buildRunner,
targetPlatforms: targetPlatforms,
);
}
}
/// With `flutter run -d all` we need a place to store the native assets
/// mapping for multiple OSes combined.
Uri _buildUriMultiple(Uri projectUri) {
final String buildDir = build_info.getBuildDirectory();
return projectUri.resolve('$buildDir/native_assets/multiple/');
}
Map<CodeAsset, KernelAsset> _assetTargetLocationsWindowsLinux(
List<CodeAsset> assets,
Uri? absolutePath,
@ -830,44 +710,6 @@ Future<DartBuildResult> _runDartBuild({
return DartBuildResult(codeAssets, dependencies.toList());
}
Future<DartBuildResult> _runDartDryRunBuild({
required FlutterNativeAssetsBuildRunner buildRunner,
required Uri projectUri,
required FileSystem fileSystem,
required OS targetOS,
}) async {
globals.logger.printTrace('Dry running native assets for $targetOS.');
final List<EncodedAsset> assets = <EncodedAsset>[];
final Set<Uri> dependencies = <Uri>{};
final BuildDryRunResult? buildResult = await buildRunner.buildDryRun(
supportedAssetTypes: <String>[CodeAsset.type],
configCreator: () => BuildConfigBuilder()
..setupCodeConfig(
targetArchitecture: null,
linkModePreference: LinkModePreference.dynamic,
),
buildValidator: (BuildConfig config, BuildOutput output) async => <String>[
...await validateCodeAssetBuildOutput(config, output),
],
workingDirectory: projectUri,
targetOS: targetOS,
includeParentEnvironment: true,
);
if (buildResult == null) {
_throwNativeAssetsBuildDryRunFailed();
}
assets.addAll(buildResult.encodedAssets);
final List<CodeAsset> codeAssets = assets
.where((EncodedAsset asset) => asset.type == CodeAsset.type)
.map<CodeAsset>(CodeAsset.fromEncoded)
.toList();
globals.logger.printTrace('Dry running native assets for $targetOS done.');
return DartBuildResult(
_expandCodeAssetsToAllArchitectures(codeAssets),
dependencies.toList());
}
List<Architecture> _architecturesForOS(build_info.TargetPlatform targetPlatform,
OS targetOS, Map<String, String> environmentDefines) {
switch (targetOS) {
@ -953,12 +795,6 @@ Future<void> _copyNativeCodeAssetsToBundleOnWindowsLinux(
}
}
Never _throwNativeAssetsBuildDryRunFailed() {
throwToolExit(
'Building (dry run) native assets failed. See the logs for more details.',
);
}
Never _throwNativeAssetsBuildFailed() {
throwToolExit(
'Building native assets failed. See the logs for more details.',
@ -1049,15 +885,6 @@ String? _emptyToNull(String? input) {
return input;
}
List<CodeAsset> _expandCodeAssetsToAllArchitectures(List<CodeAsset> codeAssets) {
assert(codeAssets.every((CodeAsset asset) => asset.architecture == null));
return <CodeAsset>[
for (final CodeAsset codeAsset in codeAssets)
for (final Architecture architecture in codeAsset.os.architectures)
codeAsset.copyWith(architecture: architecture),
];
}
extension OSArchitectures on OS {
Set<Architecture> get architectures => _osTargets[this]!;
}

View File

@ -52,11 +52,11 @@ Future<Uri?> testCompilerBuildNativeAssets(BuildInfo buildInfo) async {
}
// Only `flutter test` uses the
// `build/native_assets/<os>/native_assets.yaml` file which uses absolute
// `build/native_assets/<os>/native_assets.json` file which uses absolute
// paths to the shared libraries.
final OS targetOS = getNativeOSFromTargetPlatfrorm(TargetPlatform.tester);
final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
final Uri nativeAssetsFileUri = buildUri.resolve('native_assets.yaml');
final Uri nativeAssetsFileUri = buildUri.resolve('native_assets.json');
final Map<String, String> environmentDefines = <String, String>{
kBuildMode: buildInfo.mode.cliName,

View File

@ -90,13 +90,11 @@ class HotRunner extends ResidentRunner {
StopwatchFactory stopwatchFactory = const StopwatchFactory(),
ReloadSourcesHelper reloadSourcesHelper = defaultReloadSourcesHelper,
ReassembleHelper reassembleHelper = _defaultReassembleHelper,
HotRunnerNativeAssetsBuilder? nativeAssetsBuilder,
String? nativeAssetsYamlFile,
required Analytics analytics,
}) : _stopwatchFactory = stopwatchFactory,
_reloadSourcesHelper = reloadSourcesHelper,
_reassembleHelper = reassembleHelper,
_nativeAssetsBuilder = nativeAssetsBuilder,
_nativeAssetsYamlFile = nativeAssetsYamlFile,
_analytics = analytics,
super(
@ -131,7 +129,6 @@ class HotRunner extends ResidentRunner {
String? _sdkName;
bool? _emulator;
final HotRunnerNativeAssetsBuilder? _nativeAssetsBuilder;
final String? _nativeAssetsYamlFile;
String? flavor;
@ -378,20 +375,9 @@ class HotRunner extends ResidentRunner {
}) async {
await _calculateTargetPlatform();
final Uri? nativeAssetsYaml;
if (_nativeAssetsYamlFile != null) {
nativeAssetsYaml = globals.fs.path.toUri(_nativeAssetsYamlFile);
} else {
final Uri projectUri = Uri.directory(projectRootPath);
nativeAssetsYaml = await _nativeAssetsBuilder?.dryRun(
projectUri: projectUri,
fileSystem: fileSystem,
flutterDevices: flutterDevices,
logger: logger,
packageConfigPath: debuggingOptions.buildInfo.packageConfigPath,
packageConfig: debuggingOptions.buildInfo.packageConfig,
);
}
final Uri? nativeAssetsYaml = _nativeAssetsYamlFile != null
? globals.fs.path.toUri(_nativeAssetsYamlFile)
: null;
final Stopwatch appStartedTimer = Stopwatch()..start();
final File mainFile = globals.fs.file(mainPath);
@ -1708,16 +1694,3 @@ class ReasonForCancelling {
return '$message.\nTry performing a hot restart instead.';
}
}
/// An interface to enable overriding native assets build logic in other
/// build systems.
abstract class HotRunnerNativeAssetsBuilder {
Future<Uri?> dryRun({
required Uri projectUri,
required FileSystem fileSystem,
required List<FlutterDevice> flutterDevices,
required String packageConfigPath,
required PackageConfig packageConfig,
required Logger logger,
});
}

View File

@ -529,7 +529,6 @@ class FlutterPlatform extends PlatformPlugin {
flutterProject,
precompiledDillPath: precompiledDillPath,
testTimeRecorder: testTimeRecorder,
nativeAssetsBuilder: nativeAssetsBuilder,
);
final Uri uri = globals.fs.file(path).uri;
// Trigger a compilation to initialize the resident compiler.
@ -556,7 +555,6 @@ class FlutterPlatform extends PlatformPlugin {
debuggingOptions.buildInfo,
flutterProject,
testTimeRecorder: testTimeRecorder,
nativeAssetsBuilder: nativeAssetsBuilder,
);
mainDart = await compiler!.compile(globals.fs.file(mainDart).uri);

View File

@ -766,9 +766,6 @@ class SpawnPlugin extends PlatformPlugin {
rootTestIsolateSpawnerSourceFile: rootTestIsolateSpawnerSourceFile,
);
final Uri? nativeAssetsYaml = await nativeAssetsBuilder?.build(
debuggingOptions.buildInfo,
);
await _compileFile(
debuggingOptions: debuggingOptions,
@ -777,7 +774,6 @@ class SpawnPlugin extends PlatformPlugin {
sourceFile: childTestIsolateSpawnerSourceFile,
outputDillFile: childTestIsolateSpawnerDillFile,
testTimeRecorder: testTimeRecorder,
nativeAssetsYaml: nativeAssetsYaml,
);
await _compileFile(

View File

@ -15,7 +15,6 @@ import '../bundle.dart';
import '../compile.dart';
import '../flutter_plugins.dart';
import '../globals.dart' as globals;
import '../native_assets.dart';
import '../project.dart';
import 'test_time_recorder.dart';
@ -49,7 +48,6 @@ class TestCompiler {
this.flutterProject, {
String? precompiledDillPath,
this.testTimeRecorder,
TestCompilerNativeAssetsBuilder? nativeAssetsBuilder,
}) : testFilePath = precompiledDillPath ?? globals.fs.path.join(
flutterProject!.directory.path,
getBuildDirectory(),
@ -59,8 +57,7 @@ class TestCompiler {
dartDefines: buildInfo.dartDefines,
extraFrontEndOptions: buildInfo.extraFrontEndOptions,
)),
shouldCopyDillFile = precompiledDillPath == null,
_nativeAssetsBuilder = nativeAssetsBuilder {
shouldCopyDillFile = precompiledDillPath == null {
// Compiler maintains and updates single incremental dill file.
// Incremental compilation requests done for each test copy that file away
// for independent execution.
@ -81,8 +78,6 @@ class TestCompiler {
final String testFilePath;
final bool shouldCopyDillFile;
final TestTimeRecorder? testTimeRecorder;
final TestCompilerNativeAssetsBuilder? _nativeAssetsBuilder;
ResidentCompiler? compiler;
late File outputDill;
@ -169,8 +164,6 @@ class TestCompiler {
invalidatedRegistrantFiles.add(flutterProject!.dartPluginRegistrant.absolute.uri);
}
final Uri? nativeAssetsYaml = await _nativeAssetsBuilder?.build(buildInfo);
final CompilerOutput? compilerOutput = await compiler!.recompile(
request.mainUri,
<Uri>[request.mainUri, ...invalidatedRegistrantFiles],
@ -179,7 +172,6 @@ class TestCompiler {
projectRootPath: flutterProject?.directory.absolute.path,
checkDartPluginRegistry: true,
fs: globals.fs,
nativeAssetsYaml: nativeAssetsYaml,
);
final String? outputPath = compilerOutput?.outputFilename;

View File

@ -1258,7 +1258,6 @@ class FakeHotRunnerFactory extends Fake implements HotRunnerFactory {
FlutterProject? flutterProject,
Analytics? analytics,
String? nativeAssetsYamlFile,
HotRunnerNativeAssetsBuilder? nativeAssetsBuilder,
}) {
if (_artifactTester != null) {
for (final FlutterDevice device in devices) {

View File

@ -27,7 +27,6 @@ import 'package:flutter_tools/src/macos/macos_ipad_device.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/web/compile.dart';
import 'package:test/fake.dart';
@ -1611,7 +1610,6 @@ class CapturingAppDomain extends AppDomain {
String? isolateFilter,
bool machine = true,
String? userIdentifier,
required HotRunnerNativeAssetsBuilder? nativeAssetsBuilder,
}) async {
this.userIdentifier = userIdentifier;
enableDevTools = options.enableDevTools;

View File

@ -52,6 +52,7 @@ void main() {
// create pre-requisites.
environment.buildDir.childFile('app.dill')
.writeAsStringSync('abcd');
environment.buildDir.childFile('native_assets.json').createSync();
fileSystem
.file(artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: BuildMode.debug))
.createSync(recursive: true);
@ -96,6 +97,7 @@ void main() {
// create pre-requisites.
environment.buildDir.childFile('app.dill')
.writeAsStringSync('abcd');
environment.buildDir.childFile('native_assets.json').createSync();
fileSystem
.file(artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: BuildMode.debug))
.createSync(recursive: true);
@ -185,6 +187,7 @@ void main() {
));
environment.buildDir.createSync(recursive: true);
environment.buildDir.childFile('app.dill').createSync();
environment.buildDir.childFile('native_assets.json').createSync();
const AndroidAot androidAot = AndroidAot(TargetPlatform.android_arm64, BuildMode.release);
await androidAot.build(environment);
@ -223,6 +226,7 @@ void main() {
));
environment.buildDir.createSync(recursive: true);
environment.buildDir.childFile('app.dill').createSync();
environment.buildDir.childFile('native_assets.json').createSync();
const AndroidAot androidAot = AndroidAot(TargetPlatform.android_arm64, BuildMode.release);
await androidAot.build(environment);
@ -264,6 +268,7 @@ void main() {
));
environment.buildDir.createSync(recursive: true);
environment.buildDir.childFile('app.dill').createSync();
environment.buildDir.childFile('native_assets.json').createSync();
await const AndroidAot(TargetPlatform.android_arm64, BuildMode.release)
.build(environment);
@ -301,6 +306,7 @@ void main() {
));
environment.buildDir.createSync(recursive: true);
environment.buildDir.childFile('app.dill').createSync();
environment.buildDir.childFile('native_assets.json').createSync();
await const AndroidAot(TargetPlatform.android_arm64, BuildMode.release)
.build(environment);
@ -518,6 +524,7 @@ void main() {
// create pre-requisites.
environment.buildDir.childFile('app.dill')
.writeAsStringSync('abcd');
environment.buildDir.childFile('native_assets.json').createSync();
fileSystem
.file(artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: BuildMode.debug))
.createSync(recursive: true);

View File

@ -43,6 +43,7 @@ void main() {
},
);
fileSystem.file(environment.buildDir.childFile('app.dill')).createSync(recursive: true);
fileSystem.file(environment.buildDir.childFile('native_assets.json')).createSync(recursive: true);
fileSystem.file('packages/flutter_tools/lib/src/build_system/targets/assets.dart')
.createSync(recursive: true);
fileSystem.file('assets/foo/bar.png')

View File

@ -12,7 +12,6 @@ import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/exceptions.dart';
import 'package:flutter_tools/src/build_system/targets/common.dart';
import 'package:flutter_tools/src/build_system/targets/ios.dart';
import 'package:flutter_tools/src/build_system/targets/native_assets.dart';
import 'package:flutter_tools/src/compile.dart';
import '../../../src/common.dart';
@ -65,34 +64,14 @@ void main() {
iosEnvironment.buildDir.createSync(recursive: true);
});
testWithoutContext('KernelSnapshotProgram throws error if missing build mode', () async {
testWithoutContext('KernelSnapshot throws error if missing build mode', () async {
androidEnvironment.defines.remove(kBuildMode);
expect(
const KernelSnapshotProgram().build(androidEnvironment),
const KernelSnapshot().build(androidEnvironment),
throwsA(isA<MissingDefineException>()));
});
const String emptyNativeAssets = '''
format-version:
- 1
- 0
- 0
native-assets: {}
''';
const String nonEmptyNativeAssets = '''
format-version:
- 1
- 0
- 0
native-assets:
macos_arm64:
package:my_package/my_package_bindings_generated.dart:
- absolute
- my_package.framework/my_package
''';
testWithoutContext('KernelSnapshotProgram handles null result from kernel compilation', () async {
testWithoutContext('KernelSnapshot handles null result from kernel compilation', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
@ -119,7 +98,7 @@ native-assets:
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/program.dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot_program.d',
'--verbosity=error',
@ -127,11 +106,11 @@ native-assets:
], exitCode: 1),
]);
await expectLater(() => const KernelSnapshotProgram().build(androidEnvironment), throwsException);
await expectLater(() => const KernelSnapshot().build(androidEnvironment), throwsException);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('KernelSnapshotProgram does use track widget creation on profile builds', () async {
testWithoutContext('KernelSnapshot does use track widget creation on profile builds', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
@ -158,20 +137,20 @@ native-assets:
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/program.dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot_program.d',
'--verbosity=error',
'file:///lib/main.dart',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'),
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
]);
await const KernelSnapshotProgram().build(androidEnvironment);
await const KernelSnapshot().build(androidEnvironment);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('KernelSnapshotProgram correctly handles an empty string in ExtraFrontEndOptions', () async {
testWithoutContext('KernelSnapshot correctly handles an empty string in ExtraFrontEndOptions', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
@ -198,21 +177,21 @@ native-assets:
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/program.dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot_program.d',
'--verbosity=error',
'file:///lib/main.dart',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'),
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
]);
await const KernelSnapshotProgram()
await const KernelSnapshot()
.build(androidEnvironment..defines[kExtraFrontEndOptions] = '');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('KernelSnapshotProgram correctly forwards FrontendServerStarterPath', () async {
testWithoutContext('KernelSnapshot correctly forwards FrontendServerStarterPath', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
@ -239,21 +218,21 @@ native-assets:
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/program.dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot_program.d',
'--verbosity=error',
'file:///lib/main.dart',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'),
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
]);
await const KernelSnapshotProgram()
await const KernelSnapshot()
.build(androidEnvironment..defines[kFrontendServerStarterPath] = 'path/to/frontend_server_starter.dart');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('KernelSnapshotProgram correctly forwards ExtraFrontEndOptions', () async {
testWithoutContext('KernelSnapshot correctly forwards ExtraFrontEndOptions', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
@ -280,23 +259,23 @@ native-assets:
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/program.dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot_program.d',
'--verbosity=error',
'foo',
'bar',
'file:///lib/main.dart',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'),
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
]);
await const KernelSnapshotProgram()
await const KernelSnapshot()
.build(androidEnvironment..defines[kExtraFrontEndOptions] = 'foo,bar');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('KernelSnapshotProgram can disable track-widget-creation on debug builds', () async {
testWithoutContext('KernelSnapshot can disable track-widget-creation on debug builds', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
@ -320,25 +299,25 @@ native-assets:
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/program.dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot_program.d',
'--incremental',
'--initialize-from-dill',
'$build/program.dill',
'$build/app.dill',
'--verbosity=error',
'file:///lib/main.dart',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'),
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
]);
await const KernelSnapshotProgram().build(androidEnvironment
await const KernelSnapshot().build(androidEnvironment
..defines[kBuildMode] = BuildMode.debug.cliName
..defines[kTrackWidgetCreation] = 'false');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('KernelSnapshotProgram forces platform linking on debug for darwin target platforms', () async {
testWithoutContext('KernelSnapshot forces platform linking on debug for darwin target platforms', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
@ -360,18 +339,18 @@ native-assets:
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/program.dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot_program.d',
'--incremental',
'--initialize-from-dill',
'$build/program.dill',
'$build/app.dill',
'--verbosity=error',
'file:///lib/main.dart',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'),
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
]);
await const KernelSnapshotProgram().build(androidEnvironment
await const KernelSnapshot().build(androidEnvironment
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin)
..defines[kBuildMode] = BuildMode.debug.cliName
..defines[kTrackWidgetCreation] = 'false'
@ -380,7 +359,7 @@ native-assets:
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('KernelSnapshotProgram does use track widget creation on debug builds', () async {
testWithoutContext('KernelSnapshot does use track widget creation on debug builds', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
@ -415,92 +394,22 @@ native-assets:
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/program.dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot_program.d',
'--incremental',
'--initialize-from-dill',
'$build/program.dill',
'$build/app.dill',
'--verbosity=error',
'file:///lib/main.dart',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey /build/653e11a8e6908714056a57cd6b4f602a/program.dill 0\n'),
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey /build/653e11a8e6908714056a57cd6b4f602a/app.dill 0\n'),
]);
await const KernelSnapshotProgram().build(testEnvironment);
await const KernelSnapshot().build(testEnvironment);
expect(processManager, hasNoRemainingExpectations);
});
for (final BuildMode buildMode in <BuildMode>[BuildMode.debug, BuildMode.release]) {
for (final bool empty in <bool>[true, false]) {
final String testName = empty ? 'empty' : 'non empty';
testWithoutContext('KernelSnapshotNativeAssets ${buildMode.name} $testName', () async {
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
androidEnvironment.buildDir.childFile(InstallCodeAssets.nativeAssetsFilename)
..createSync(recursive: true)
..writeAsStringSync(empty ? emptyNativeAssets : nonEmptyNativeAssets);
final String build = androidEnvironment.buildDir.path;
final String flutterPatchedSdkPath = artifacts.getArtifactPath(
Artifact.flutterPatchedSdkPath,
platform: TargetPlatform.darwin,
mode: buildMode,
);
processManager.addCommands(<FakeCommand>[
if (!empty)
FakeCommand(command: <String>[
artifacts.getArtifactPath(Artifact.engineDartAotRuntime),
artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
'--sdk-root',
'$flutterPatchedSdkPath/',
'--target=flutter',
'--no-print-incremental-dependencies',
...buildModeOptions(buildMode, <String>[]),
'--no-link-platform',
if (buildMode == BuildMode.release) ...<String>['--aot', '--tfa'],
'--packages',
'/.dart_tool/package_config.json',
'--output-dill',
'$build/native_assets.dill',
'--native-assets',
'$build/${InstallCodeAssets.nativeAssetsFilename}',
'--verbosity=error',
'--native-assets-only',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
]);
await const KernelSnapshotNativeAssets().build(androidEnvironment
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin)
..defines[kBuildMode] = buildMode.cliName
);
expect(processManager, hasNoRemainingExpectations);
});
}
}
for (final bool empty in <bool>[true, false]) {
final String testName = empty ? 'empty' : 'non empty';
testWithoutContext('KernelSnapshot native assets $testName', () async {
const List<int> programDillBytes = <int>[1, 2, 3, 4];
androidEnvironment.buildDir.childFile('program.dill')
..createSync(recursive: true)
..writeAsBytesSync(programDillBytes);
final List<int> nativeAssetsDillBytes = empty ? <int>[] : <int>[5, 6, 7, 8];
androidEnvironment.buildDir.childFile('native_assets.dill')
..createSync(recursive: true)
..writeAsBytesSync(nativeAssetsDillBytes);
await const KernelSnapshot().build(androidEnvironment);
expect(
androidEnvironment.buildDir.childFile('app.dill').readAsBytesSync(),
equals(<int>[...programDillBytes, ...nativeAssetsDillBytes]),
);
});
}
testUsingContext('AotElfProfile Produces correct output directory', () async {
final String build = androidEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[
@ -520,6 +429,7 @@ native-assets:
]),
]);
androidEnvironment.buildDir.childFile('app.dill').createSync(recursive: true);
androidEnvironment.buildDir.childFile('native_assets.json').createSync();
await const AotElfProfile(TargetPlatform.android_arm).build(androidEnvironment);
@ -548,6 +458,7 @@ native-assets:
]),
]);
androidEnvironment.buildDir.childFile('app.dill').createSync(recursive: true);
androidEnvironment.buildDir.childFile('native_assets.json').createSync();
await const AotElfRelease(TargetPlatform.android_arm).build(androidEnvironment);

View File

@ -205,6 +205,7 @@ void main() {
.createSync(recursive: true);
// App kernel
environment.buildDir.childFile('app.dill').createSync(recursive: true);
environment.buildDir.childFile('native_assets.json').createSync();
// Stub framework
environment.buildDir
.childDirectory('App.framework')
@ -285,6 +286,7 @@ void main() {
fileSystem.file('shader.glsl').writeAsStringSync('test');
// App kernel
environment.buildDir.childFile('app.dill').createSync(recursive: true);
environment.buildDir.childFile('native_assets.json').createSync();
// Stub framework
environment.buildDir
.childDirectory('App.framework')
@ -360,6 +362,7 @@ void main() {
.childDirectory('App.framework')
.childFile('App')
.createSync(recursive: true);
environment.buildDir.childFile('native_assets.json').createSync();
// Input dSYM
environment.buildDir
@ -425,6 +428,7 @@ void main() {
.childDirectory('App.framework')
.childFile('App')
.createSync(recursive: true);
environment.buildDir.childFile('native_assets.json').createSync();
final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework');
final File frameworkDirectoryBinary = frameworkDirectory.childFile('App');

View File

@ -116,6 +116,7 @@ void main() {
// Create input files.
testEnvironment.buildDir.childFile('app.dill').createSync();
testEnvironment.buildDir.childFile('native_assets.json').createSync();
fileSystem.file('bundle.sksl').writeAsStringSync(json.encode(
<String, Object>{
'engineRevision': '2',
@ -169,6 +170,7 @@ void main() {
// Create input files.
testEnvironment.buildDir.childFile('app.so').createSync();
testEnvironment.buildDir.childFile('native_assets.json').createSync();
await const LinuxAotBundle(AotElfProfile(TargetPlatform.linux_x64)).build(testEnvironment);
await const ProfileBundleLinuxAssets(TargetPlatform.linux_x64).build(testEnvironment);
@ -208,6 +210,7 @@ void main() {
// Create input files.
testEnvironment.buildDir.childFile('app.so').createSync();
testEnvironment.buildDir.childFile('native_assets.json').createSync();
await const LinuxAotBundle(AotElfRelease(TargetPlatform.linux_x64)).build(testEnvironment);
await const ReleaseBundleLinuxAssets(TargetPlatform.linux_x64).build(testEnvironment);

View File

@ -418,6 +418,7 @@ void main() {
fileSystem.file(inputKernel)
..createSync(recursive: true)
..writeAsStringSync('testing');
environment.buildDir.childFile('native_assets.json').createSync();
await const DebugMacOSBundleFlutterAssets().build(environment);
@ -454,6 +455,9 @@ void main() {
.createSync(recursive: true);
fileSystem.file('${environment.buildDir.path}/App.framework/App')
.createSync(recursive: true);
fileSystem
.file('${environment.buildDir.path}/native_assets.json')
.createSync();
await const ProfileMacOSBundleFlutterAssets().build(environment..defines[kBuildMode] = 'profile');
@ -483,6 +487,9 @@ void main() {
.createSync(recursive: true);
fileSystem.file('${environment.buildDir.path}/App.framework.dSYM/Contents/Resources/DWARF/App')
.createSync(recursive: true);
fileSystem
.file('${environment.buildDir.path}/native_assets.json')
.createSync();
await const ReleaseMacOSBundleFlutterAssets()
.build(environment..defines[kBuildMode] = 'release');
@ -504,6 +511,9 @@ void main() {
final File inputFramework = fileSystem.file(fileSystem.path.join(environment.buildDir.path, 'App.framework', 'App'))
..createSync(recursive: true)
..writeAsStringSync('ABC');
fileSystem
.file(environment.buildDir.childFile('native_assets.json'))
.createSync();
await const ProfileMacOSBundleFlutterAssets().build(environment..defines[kBuildMode] = 'profile');
final File outputFramework = fileSystem.file(fileSystem.path.join(environment.outputDir.path, 'App.framework', 'App'));
@ -529,6 +539,9 @@ void main() {
.createSync(recursive: true);
fileSystem.file(fileSystem.path.join(environment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true);
fileSystem
.file(environment.buildDir.childFile('native_assets.json'))
.createSync();
await const ReleaseMacOSBundleFlutterAssets().build(environment);
expect(usage.events, contains(const TestUsageEvent('assemble', 'macos-archive', label: 'success')));

View File

@ -136,6 +136,7 @@ void main() {
);
environment.buildDir.childFile('app.dill').createSync(recursive: true);
environment.buildDir.childFile('native_assets.json').createSync(recursive: true);
// sksl bundle
fileSystem.file('bundle.sksl').writeAsStringSync(json.encode(
<String, Object>{
@ -173,6 +174,7 @@ void main() {
);
environment.buildDir.childFile('app.so').createSync(recursive: true);
environment.buildDir.childFile('native_assets.json').createSync(recursive: true);
await const WindowsAotBundle(AotElfProfile(TargetPlatform.windows_x64)).build(environment);
await const ProfileBundleWindowsAssets(TargetPlatform.windows_x64).build(environment);
@ -200,6 +202,7 @@ void main() {
);
environment.buildDir.childFile('app.so').createSync(recursive: true);
environment.buildDir.childFile('native_assets.json').createSync(recursive: true);
await const WindowsAotBundle(AotElfRelease(TargetPlatform.windows_x64)).build(environment);
await const ReleaseBundleWindowsAssets(TargetPlatform.windows_x64).build(environment);

View File

@ -116,7 +116,7 @@ void main() {
]),
);
expect(environment.buildDir.childFile('native_assets.yaml'), exists);
expect(environment.buildDir.childFile('native_assets.json'), exists);
expect(buildRunner.buildInvocations, 1);
expect(
buildRunner.linkInvocations,

View File

@ -104,7 +104,7 @@ void main() {
for (final bool isNativeAssetsEnabled in <bool>[true, false]) {
final String postFix = isNativeAssetsEnabled ? 'enabled' : 'disabled';
testUsingContext(
'Successful native_assets.yaml and native_assets.d creation with feature $postFix',
'Successful native_assets.json and native_assets.d creation with feature $postFix',
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,

View File

@ -2,12 +2,7 @@
// 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:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart' hide BuildMode;
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';
import 'package:native_assets_builder/native_assets_builder.dart';
import 'package:native_assets_cli/code_assets_builder.dart';
import 'package:package_config/package_config_types.dart';
@ -23,7 +18,6 @@ class FakeFlutterNativeAssetsBuildRunner
this.packagesWithNativeAssetsResult = const <Package>[],
this.onBuild,
this.onLink,
this.buildDryRunResult = const FakeFlutterNativeAssetsBuilderResult(),
this.buildResult = const FakeFlutterNativeAssetsBuilderResult(),
this.linkResult = const FakeFlutterNativeAssetsBuilderResult(),
CCompilerConfig? cCompilerConfigResult,
@ -36,14 +30,12 @@ class FakeFlutterNativeAssetsBuildRunner
final LinkResult? Function(LinkConfig)? onLink;
final BuildResult? buildResult;
final LinkResult? linkResult;
final BuildDryRunResult? buildDryRunResult;
final bool hasPackageConfigResult;
final List<Package> packagesWithNativeAssetsResult;
final CCompilerConfig cCompilerConfigResult;
final CCompilerConfig ndkCCompilerConfigResult;
int buildInvocations = 0;
int buildDryRunInvocations = 0;
int linkInvocations = 0;
int hasPackageConfigInvocations = 0;
int packagesWithNativeAssetsInvocations = 0;
@ -128,19 +120,6 @@ class FakeFlutterNativeAssetsBuildRunner
return result;
}
@override
Future<BuildDryRunResult?> buildDryRun({
required List<String> supportedAssetTypes,
required ConfigCreator configCreator,
required BuildValidator buildValidator,
required bool includeParentEnvironment,
required OS targetOS,
required Uri workingDirectory,
}) async {
buildDryRunInvocations++;
return buildDryRunResult;
}
@override
Future<bool> hasPackageConfig() async {
hasPackageConfigInvocations++;
@ -161,7 +140,7 @@ class FakeFlutterNativeAssetsBuildRunner
}
final class FakeFlutterNativeAssetsBuilderResult
implements BuildResult, BuildDryRunResult, LinkResult {
implements BuildResult, LinkResult {
const FakeFlutterNativeAssetsBuilderResult({
this.encodedAssets = const <EncodedAsset>[],
this.encodedAssetsForLinking = const <String, List<EncodedAsset>>{},
@ -197,30 +176,3 @@ final class FakeFlutterNativeAssetsBuilderResult
@override
final List<Uri> dependencies;
}
class FakeHotRunnerNativeAssetsBuilder implements HotRunnerNativeAssetsBuilder {
FakeHotRunnerNativeAssetsBuilder(this.buildRunner);
final FlutterNativeAssetsBuildRunner buildRunner;
@override
Future<Uri?> dryRun({
required Uri projectUri,
required FileSystem fileSystem,
required List<FlutterDevice> flutterDevices,
required String packageConfigPath,
required PackageConfig packageConfig,
required Logger logger,
}) {
final List<TargetPlatform> targetPlatforms = flutterDevices
.map((FlutterDevice d) => d.targetPlatform)
.nonNulls
.toList();
return runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
buildRunner: buildRunner,
targetPlatforms: targetPlatforms,
);
}
}

View File

@ -1,182 +0,0 @@
// 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/memory.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/resident_devtools_handler.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:native_assets_cli/code_assets_builder.dart';
import 'package:package_config/package_config.dart';
import 'package:unified_analytics/unified_analytics.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
import '../hot_shared.dart';
import 'fake_native_assets_build_runner.dart';
void main() {
group('native assets', () {
late TestHotRunnerConfig testingConfig;
late MemoryFileSystem fileSystem;
late FakeAnalytics fakeAnalytics;
setUp(() {
fileSystem = MemoryFileSystem.test();
testingConfig = TestHotRunnerConfig(
successfulHotRestartSetup: true,
);
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: fileSystem,
fakeFlutterVersion: FakeFlutterVersion(),
);
});
testUsingContext('native assets restart', () async {
final FakeDevice device = FakeDevice();
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device);
final List<FlutterDevice> devices = <FlutterDevice>[
fakeFlutterDevice,
];
fakeFlutterDevice.updateDevFSReportCallback = () async => UpdateFSReport(
success: true,
invalidatedSourcesCount: 6,
syncedBytes: 8,
scannedSourcesCount: 16,
compileDuration: const Duration(seconds: 16),
transferDuration: const Duration(seconds: 32),
);
(fakeFlutterDevice.devFS! as FakeDevFs).baseUri = Uri.parse('file:///base_uri');
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', fileSystem.currentDirectory.uri),
],
buildDryRunResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
codeAssets: <CodeAsset>[
CodeAsset(
package: 'bar',
name: 'bar.dart',
linkMode: DynamicLoadingBundled(),
os: OS.macOS,
architecture: Architecture.arm64,
file: Uri.file('bar.dylib'),
),
CodeAsset(
package: 'bar',
name: 'bar.dart',
linkMode: DynamicLoadingBundled(),
os: OS.macOS,
architecture: Architecture.x64,
file: Uri.file('bar.dylib'),
),
],
),
);
final HotRunner hotRunner = HotRunner(
devices,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
nativeAssetsBuilder: FakeHotRunnerNativeAssetsBuilder(buildRunner),
analytics: fakeAnalytics,
);
final OperationResult result = await hotRunner.restart(fullRestart: true);
expect(result.isOk, true);
// Hot restart does not require rerunning anything for native assets.
// The previous native assets mapping should be used.
expect(buildRunner.buildInvocations, 0);
expect(buildRunner.buildDryRunInvocations, 0);
expect(buildRunner.linkInvocations, 0);
expect(buildRunner.hasPackageConfigInvocations, 0);
expect(buildRunner.packagesWithNativeAssetsInvocations, 0);
}, overrides: <Type, Generator>{
HotRunnerConfig: () => testingConfig,
Artifacts: () => Artifacts.test(),
FileSystem: () => fileSystem,
Platform: () => FakePlatform(),
ProcessManager: () => FakeProcessManager.empty(),
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true, isMacOSEnabled: true),
});
testUsingContext('native assets run unsupported', () async {
final FakeDevice device = FakeDevice(targetPlatform: TargetPlatform.fuchsia_arm64);
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device);
final List<FlutterDevice> devices = <FlutterDevice>[
fakeFlutterDevice,
];
fakeFlutterDevice.updateDevFSReportCallback = () async => UpdateFSReport(
success: true,
invalidatedSourcesCount: 6,
syncedBytes: 8,
scannedSourcesCount: 16,
compileDuration: const Duration(seconds: 16),
transferDuration: const Duration(seconds: 32),
);
(fakeFlutterDevice.devFS! as FakeDevFs).baseUri = Uri.parse('file:///base_uri');
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', fileSystem.currentDirectory.uri),
],
buildDryRunResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
codeAssets: <CodeAsset>[
CodeAsset(
package: 'bar',
name: 'bar.dart',
linkMode: DynamicLoadingBundled(),
os: OS.macOS,
architecture: Architecture.arm64,
file: Uri.file('bar.dylib'),
),
CodeAsset(
package: 'bar',
name: 'bar.dart',
linkMode: DynamicLoadingBundled(),
os: OS.macOS,
architecture: Architecture.x64,
file: Uri.file('bar.dylib'),
),
],
),
);
final HotRunner hotRunner = HotRunner(
devices,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
nativeAssetsBuilder: FakeHotRunnerNativeAssetsBuilder(buildRunner),
analytics: fakeAnalytics,
);
expect(
() => hotRunner.run(),
throwsToolExit( message:
'Package(s) bar require the native assets feature. '
'This feature has not yet been implemented for `TargetPlatform.fuchsia_arm64`. '
'For more info see https://github.com/flutter/flutter/issues/129757.',
)
);
}, overrides: <Type, Generator>{
HotRunnerConfig: () => testingConfig,
Artifacts: () => Artifacts.test(),
FileSystem: () => fileSystem,
Platform: () => FakePlatform(),
ProcessManager: () => FakeProcessManager.empty(),
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true, isMacOSEnabled: true),
});
});
}

View File

@ -47,26 +47,6 @@ void main() {
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 {
@ -87,97 +67,6 @@ void main() {
);
});
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.fromAssets(
codeAssets: <CodeAsset>[
CodeAsset(
package: 'bar',
name: 'bar.dart',
linkMode: DynamicLoadingBundled(),
os: OS.windows,
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/${InstallCodeAssets.nativeAssetsFilename}'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
contains('package:bar/bar.dart'),
);
expect(buildRunner.buildDryRunInvocations, 1);
});
testUsingContext('Native assets: non-bundled libraries require no copying', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
@ -185,7 +74,7 @@ void main() {
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.json').uri;
await packageConfig.parent.create();
await packageConfig.create();
@ -302,33 +191,6 @@ 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(
() => runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: null,
),
),
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(),

View File

@ -17,7 +17,6 @@ import '../../src/fake_vm_services.dart';
import '../../src/fakes.dart';
import '../../src/testbed.dart';
import '../resident_runner_helpers.dart';
import 'fake_native_assets_build_runner.dart';
void main() {
late Testbed testbed;
@ -55,8 +54,7 @@ void main() {
]);
globals.fs
.file(globals.fs.path.join('lib', 'main.dart'))
.createSync(recursive: true);
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner();
.createSync(recursive: true);
final HotRunner residentRunner = HotRunner(
<FlutterDevice>[
flutterDevice,
@ -71,7 +69,6 @@ void main() {
)),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
nativeAssetsBuilder: FakeHotRunnerNativeAssetsBuilder(buildRunner),
analytics: FakeAnalytics(),
nativeAssetsYamlFile: 'foo.yaml',
);
@ -79,12 +76,6 @@ void main() {
final int result = await residentRunner.run();
expect(result, 0);
expect(buildRunner.buildInvocations, 0);
expect(buildRunner.buildDryRunInvocations, 0);
expect(buildRunner.linkInvocations, 0);
expect(buildRunner.hasPackageConfigInvocations, 0);
expect(buildRunner.packagesWithNativeAssetsInvocations, 0);
expect(residentCompiler.recompileCalled, true);
expect(residentCompiler.receivedNativeAssetsYaml, globals.fs.path.toUri('foo.yaml'));
}),

View File

@ -145,7 +145,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/$expectedDirectory/bar.dll').toFilePath()
projectUri
.resolve('build/native_assets/$expectedDirectory/bar.dll')
.toFilePath()
.replaceAll(r'\', r'\\') // Undo JSON string escaping.
else
// Apps are a bundle with the dylibs on their dlopen path.
'bar.dll',