Fix duplicate work in native assets release builds (#158980)
In release builds linking of native assets is enabled. The build step is only a temprary step, it's output is given to the link step which then returns all final assets (effectively a map-reduce system). Assets that aren't sent to a specific linker could be conceptually viewed as sent to a linker that will emit it's input as-is. => The code currently took output of build & link step and therefore accumulated assets that aren't explicitly sent to a linker twice. => This led to performing work twice for those (e.g. copying them twice) This PR changes this such that if linking mode is enabled, we only rely on the output of the link phase. That in return means many tests that mock the native asset builds need to be updated to mock the output of the link phase.
This commit is contained in:
parent
eaa99f28b1
commit
256359194e
@ -750,9 +750,10 @@ Future<DartBuildResult> _runDartBuild({
|
||||
if (buildResult == null) {
|
||||
_throwNativeAssetsBuildFailed();
|
||||
}
|
||||
assets.addAll(buildResult.encodedAssets);
|
||||
dependencies.addAll(buildResult.dependencies);
|
||||
if (linkingEnabled) {
|
||||
if (!linkingEnabled) {
|
||||
assets.addAll(buildResult.encodedAssets);
|
||||
} else {
|
||||
final LinkResult? linkResult = await buildRunner.link(
|
||||
supportedAssetTypes: <String>[CodeAsset.type],
|
||||
configCreator: () => LinkConfigBuilder()
|
||||
|
@ -67,21 +67,26 @@ void main() {
|
||||
final File dylibAfterCompiling = fileSystem.file('libbar.so');
|
||||
// The mock doesn't create the file, so create it here.
|
||||
await dylibAfterCompiling.create();
|
||||
|
||||
final List<CodeAsset> codeAssets = <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'bar',
|
||||
name: 'bar.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: OS.android,
|
||||
architecture: Architecture.arm64,
|
||||
file: Uri.file('libbar.so'),
|
||||
),
|
||||
];
|
||||
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
buildResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'bar',
|
||||
name: 'bar.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: OS.android,
|
||||
architecture: Architecture.arm64,
|
||||
file: Uri.file('libbar.so'),
|
||||
),
|
||||
],
|
||||
codeAssets: codeAssets,
|
||||
),
|
||||
linkResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: codeAssets,
|
||||
),
|
||||
);
|
||||
await runFlutterSpecificDartBuild(
|
||||
|
@ -190,23 +190,27 @@ void main() {
|
||||
() async {
|
||||
await createPackageConfig(iosEnvironment);
|
||||
|
||||
final List<CodeAsset> codeAssets = <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'foo',
|
||||
name: 'foo.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: OS.iOS,
|
||||
architecture: Architecture.arm64,
|
||||
file: Uri.file('foo.framework/foo'),
|
||||
),
|
||||
];
|
||||
final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[Package('foo', iosEnvironment.buildDir.uri)],
|
||||
buildResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'foo',
|
||||
name: 'foo.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: OS.iOS,
|
||||
architecture: Architecture.arm64,
|
||||
file: Uri.file('foo.framework/foo'),
|
||||
),
|
||||
],
|
||||
codeAssets: codeAssets,
|
||||
dependencies: <Uri>[
|
||||
Uri.file('src/foo.c'),
|
||||
],
|
||||
),
|
||||
linkResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: codeAssets,
|
||||
),
|
||||
);
|
||||
await NativeAssets(buildRunner: buildRunner).build(iosEnvironment);
|
||||
|
||||
@ -252,26 +256,30 @@ void main() {
|
||||
await createPackageConfig(androidEnvironment);
|
||||
await fileSystem.file('libfoo.so').create();
|
||||
|
||||
final List<CodeAsset> codeAssets = <CodeAsset>[
|
||||
if (hasAssets)
|
||||
CodeAsset(
|
||||
package: 'foo',
|
||||
name: 'foo.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: OS.android,
|
||||
architecture: Architecture.arm64,
|
||||
file: Uri.file('libfoo.so'),
|
||||
),
|
||||
];
|
||||
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('foo', androidEnvironment.buildDir.uri)
|
||||
],
|
||||
buildResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: <CodeAsset>[
|
||||
if (hasAssets)
|
||||
CodeAsset(
|
||||
package: 'foo',
|
||||
name: 'foo.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: OS.android,
|
||||
architecture: Architecture.arm64,
|
||||
file: Uri.file('libfoo.so'),
|
||||
),
|
||||
],
|
||||
codeAssets: codeAssets,
|
||||
dependencies: <Uri>[
|
||||
Uri.file('src/foo.c'),
|
||||
],
|
||||
),
|
||||
linkResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: codeAssets,
|
||||
),
|
||||
);
|
||||
await NativeAssets(buildRunner: buildRunner).build(androidEnvironment);
|
||||
expect(
|
||||
|
@ -12,6 +12,8 @@ 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';
|
||||
|
||||
export 'package:native_assets_cli/code_assets_builder.dart' show CodeAsset, DynamicLoadingBundled;
|
||||
|
||||
/// Mocks all logic instead of using `package:native_assets_builder`, which
|
||||
/// relies on doing process calls to `pub` and the local file system.
|
||||
class FakeFlutterNativeAssetsBuildRunner
|
||||
@ -20,6 +22,7 @@ class FakeFlutterNativeAssetsBuildRunner
|
||||
this.hasPackageConfigResult = true,
|
||||
this.packagesWithNativeAssetsResult = const <Package>[],
|
||||
this.onBuild,
|
||||
this.onLink,
|
||||
this.buildDryRunResult = const FakeFlutterNativeAssetsBuilderResult(),
|
||||
this.buildResult = const FakeFlutterNativeAssetsBuilderResult(),
|
||||
this.linkResult = const FakeFlutterNativeAssetsBuilderResult(),
|
||||
@ -30,6 +33,7 @@ class FakeFlutterNativeAssetsBuildRunner
|
||||
ndkCCompilerConfigResult ?? CCompilerConfig();
|
||||
|
||||
final BuildResult? Function(BuildConfig)? onBuild;
|
||||
final LinkResult? Function(LinkConfig)? onLink;
|
||||
final BuildResult? buildResult;
|
||||
final LinkResult? linkResult;
|
||||
final BuildDryRunResult? buildDryRunResult;
|
||||
@ -99,11 +103,29 @@ class FakeFlutterNativeAssetsBuildRunner
|
||||
required Uri workingDirectory,
|
||||
required BuildResult buildResult,
|
||||
}) async {
|
||||
for (final Package _ in packagesWithNativeAssetsResult) {
|
||||
LinkResult? result = linkResult;
|
||||
for (final Package package in packagesWithNativeAssetsResult) {
|
||||
final LinkConfigBuilder configBuilder = configCreator()
|
||||
..setupHookConfig(
|
||||
packageRoot: package.root,
|
||||
packageName: package.name,
|
||||
targetOS: targetOS,
|
||||
supportedAssetTypes: supportedAssetTypes,
|
||||
buildMode: buildMode,
|
||||
)
|
||||
..setupLinkRunConfig(
|
||||
outputDirectory: Uri.parse('build-out-dir'),
|
||||
outputDirectoryShared: Uri.parse('build-out-dir-shared'),
|
||||
recordedUsesFile: null,
|
||||
);
|
||||
final LinkConfig buildConfig = LinkConfig(configBuilder.json);
|
||||
if (onLink != null) {
|
||||
result = onLink!(buildConfig);
|
||||
}
|
||||
lastBuildMode = buildMode;
|
||||
linkInvocations++;
|
||||
}
|
||||
return linkResult;
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -148,12 +170,22 @@ final class FakeFlutterNativeAssetsBuilderResult
|
||||
|
||||
factory FakeFlutterNativeAssetsBuilderResult.fromAssets({
|
||||
List<CodeAsset> codeAssets = const <CodeAsset>[],
|
||||
Map<String, List<CodeAsset>> codeAssetsForLinking = const <String, List<CodeAsset>>{},
|
||||
List<Uri> dependencies = const <Uri>[],
|
||||
}) {
|
||||
return FakeFlutterNativeAssetsBuilderResult(
|
||||
encodedAssets: <EncodedAsset>[
|
||||
for (final CodeAsset codeAsset in codeAssets) codeAsset.encode(),
|
||||
], dependencies: dependencies);
|
||||
],
|
||||
encodedAssetsForLinking: <String, List<EncodedAsset>>{
|
||||
for (final String linkerName in codeAssetsForLinking.keys)
|
||||
linkerName: <EncodedAsset>[
|
||||
for (final CodeAsset codeAsset in codeAssetsForLinking[linkerName]!)
|
||||
codeAsset.encode(),
|
||||
],
|
||||
},
|
||||
dependencies: dependencies,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -162,31 +162,35 @@ void main() {
|
||||
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
|
||||
await packageConfig.parent.create();
|
||||
await packageConfig.create();
|
||||
|
||||
List<CodeAsset> codeAssets(OS targetOS, CodeConfig codeConfig) => <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'bar',
|
||||
name: 'bar.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: targetOS,
|
||||
architecture: codeConfig.targetArchitecture,
|
||||
file: Uri.file('${codeConfig.targetArchitecture}/libbar.dylib'),
|
||||
),
|
||||
CodeAsset(
|
||||
package: 'buz',
|
||||
name: 'buz.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: targetOS,
|
||||
architecture: codeConfig.targetArchitecture,
|
||||
file: Uri.file('${codeConfig.targetArchitecture}/libbuz.dylib'),
|
||||
),
|
||||
];
|
||||
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
onBuild: (BuildConfig config) =>
|
||||
FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'bar',
|
||||
name: 'bar.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: config.targetOS,
|
||||
architecture: config.codeConfig.targetArchitecture,
|
||||
file: Uri.file('${config.codeConfig.targetArchitecture}/libbar.dylib'),
|
||||
),
|
||||
CodeAsset(
|
||||
package: 'buz',
|
||||
name: 'buz.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: config.targetOS,
|
||||
architecture: config.codeConfig.targetArchitecture,
|
||||
file: Uri.file('${config.codeConfig.targetArchitecture}/libbuz.dylib'),
|
||||
),
|
||||
],
|
||||
),
|
||||
FakeFlutterNativeAssetsBuilderResult.fromAssets(codeAssets: codeAssets(config.targetOS, config.codeConfig)),
|
||||
onLink: (LinkConfig config) =>
|
||||
buildMode == BuildMode.debug
|
||||
? null
|
||||
: FakeFlutterNativeAssetsBuilderResult.fromAssets(codeAssets: codeAssets(config.targetOS, config.codeConfig)),
|
||||
);
|
||||
await runFlutterSpecificDartBuild(
|
||||
environmentDefines: <String, String>{
|
||||
|
@ -268,31 +268,34 @@ void main() {
|
||||
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
|
||||
await packageConfig.parent.create();
|
||||
await packageConfig.create();
|
||||
|
||||
List<CodeAsset> codeAssets(OS targetOS, CodeConfig codeConfig) => <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'bar',
|
||||
name: 'bar.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: targetOS,
|
||||
architecture: codeConfig.targetArchitecture,
|
||||
file: Uri.file('${codeConfig.targetArchitecture}/libbar.dylib'),
|
||||
),
|
||||
CodeAsset(
|
||||
package: 'buz',
|
||||
name: 'buz.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: targetOS,
|
||||
architecture: codeConfig.targetArchitecture,
|
||||
file: Uri.file('${codeConfig.targetArchitecture}/libbuz.dylib'),
|
||||
),
|
||||
];
|
||||
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
onBuild: (BuildConfig config) =>
|
||||
FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'bar',
|
||||
name: 'bar.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: config.targetOS,
|
||||
architecture: config.codeConfig.targetArchitecture,
|
||||
file: Uri.file('${config.codeConfig.targetArchitecture}/libbar.dylib'),
|
||||
),
|
||||
CodeAsset(
|
||||
package: 'buz',
|
||||
name: 'buz.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: config.targetOS,
|
||||
architecture: config.codeConfig.targetArchitecture,
|
||||
file: Uri.file('${config.codeConfig.targetArchitecture}/libbuz.dylib'),
|
||||
),
|
||||
],
|
||||
),
|
||||
FakeFlutterNativeAssetsBuilderResult.fromAssets(codeAssets: codeAssets(config.targetOS, config.codeConfig)),
|
||||
onLink: (LinkConfig config) => buildMode == BuildMode.debug
|
||||
? null
|
||||
: FakeFlutterNativeAssetsBuilderResult.fromAssets(codeAssets: codeAssets(config.targetOS, config.codeConfig)),
|
||||
);
|
||||
final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild(
|
||||
environmentDefines: <String, String>{
|
||||
|
@ -300,4 +300,67 @@ void main() {
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('Native assets: no duplicate assets with linking', 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 File directSoFile = environment.projectDir.childFile('direct.so');
|
||||
directSoFile.writeAsBytesSync(<int>[]);
|
||||
final File linkableAFile = environment.projectDir.childFile('linkable.a');
|
||||
linkableAFile.writeAsBytesSync(<int>[]);
|
||||
final File linkedSoFile = environment.projectDir.childFile('linked.so');
|
||||
linkedSoFile.writeAsBytesSync(<int>[]);
|
||||
|
||||
CodeAsset makeCodeAsset(String name, Uri file, LinkMode linkMode)
|
||||
=> CodeAsset(
|
||||
package: 'bar',
|
||||
name: name,
|
||||
linkMode: linkMode,
|
||||
os: OS.linux,
|
||||
architecture: Architecture.x64,
|
||||
file: file,
|
||||
);
|
||||
|
||||
final (DartBuildResult result, _) = await runFlutterSpecificDartBuild(
|
||||
environmentDefines: <String, String>{
|
||||
// Release mode means the dart build has linking enabled.
|
||||
kBuildMode: BuildMode.release.cliName,
|
||||
},
|
||||
targetPlatform: TargetPlatform.linux_x64,
|
||||
projectUri: projectUri,
|
||||
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: FakeFlutterNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
buildResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: <CodeAsset>[
|
||||
makeCodeAsset('direct', directSoFile.uri, DynamicLoadingBundled()),
|
||||
],
|
||||
codeAssetsForLinking: <String, List<CodeAsset>>{
|
||||
'package:bar' : <CodeAsset>[
|
||||
makeCodeAsset('linkable', linkableAFile.uri, StaticLinking()),
|
||||
],
|
||||
},
|
||||
),
|
||||
linkResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: <CodeAsset>[
|
||||
makeCodeAsset('direct', directSoFile.uri, DynamicLoadingBundled()),
|
||||
makeCodeAsset('linked', linkedSoFile.uri, DynamicLoadingBundled()),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
result.codeAssets.map((CodeAsset c) => c.file!.toString()).toList()..sort(),
|
||||
<String>[directSoFile.uri.toString(), linkedSoFile.uri.toString()]);
|
||||
});
|
||||
}
|
||||
|
@ -77,21 +77,25 @@ void main() {
|
||||
final File dylibAfterCompiling = fileSystem.file('bar.dll');
|
||||
// The mock doesn't create the file, so create it here.
|
||||
await dylibAfterCompiling.create();
|
||||
|
||||
final List<CodeAsset> codeAssets = <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'bar',
|
||||
name: 'bar.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: OS.windows,
|
||||
architecture: Architecture.x64,
|
||||
file: dylibAfterCompiling.uri,
|
||||
),
|
||||
];
|
||||
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
buildResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
|
||||
codeAssets: <CodeAsset>[
|
||||
CodeAsset(
|
||||
package: 'bar',
|
||||
name: 'bar.dart',
|
||||
linkMode: DynamicLoadingBundled(),
|
||||
os: OS.windows,
|
||||
architecture: Architecture.x64,
|
||||
file: dylibAfterCompiling.uri,
|
||||
),
|
||||
],
|
||||
buildResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(codeAssets: codeAssets),
|
||||
linkResult: buildMode == BuildMode.debug
|
||||
? null
|
||||
: FakeFlutterNativeAssetsBuilderResult.fromAssets(codeAssets: codeAssets,
|
||||
),
|
||||
);
|
||||
final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild(
|
||||
|
Loading…
x
Reference in New Issue
Block a user