mirror of
https://github.com/flutter/flutter
synced 2024-10-02 14:34:22 +00:00
Reland "Native assets support for Linux" (#135097)
Reland of #134031. (Reverted in #135069.) Contains the fix for b/301051367 together with cl/567233346. Support for FFI calls with `@Native external` functions through Native assets on Linux. This enables bundling native code without any build-system boilerplate code. For more info see: * https://github.com/flutter/flutter/issues/129757 ### Implementation details for Linux. Mainly follows the design of https://github.com/flutter/flutter/pull/130494. Some differences are: * Linux does not support cross compiling or compiling for multiple architectures, so this has not been implemented. * Linux has no add2app. The assets copying is done in the install-phase of the CMake build of a flutter app. CMake requires the native assets folder to exist, so we create it also when the feature is disabled or there are no assets. ### Tests This PR adds new tests to cover the various use cases. * packages/flutter_tools/test/general.shard/linux/native_assets_test.dart * Unit tests the Linux-specific part of building native assets. It also extends various existing tests: * packages/flutter_tools/test/integration.shard/native_assets_test.dart * Runs (incl hot reload/hot restart), builds, builds frameworks for Linux and flutter-tester.
This commit is contained in:
parent
0377e80de8
commit
2def951924
24
.ci.yaml
24
.ci.yaml
|
@ -952,8 +952,10 @@ targets:
|
|||
{"dependency": "android_sdk", "version": "version:33v6"},
|
||||
{"dependency": "chrome_and_driver", "version": "version:117.0"},
|
||||
{"dependency": "clang", "version": "git_revision:5d5aba78dbbee75508f01bcaa69aedb2ab79065a"},
|
||||
{"dependency": "open_jdk", "version": "version:11"},
|
||||
{"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"}
|
||||
{"dependency": "cmake", "version": "build_id:8787856497187628321"},
|
||||
{"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"},
|
||||
{"dependency": "ninja", "version": "version:1.9.0"},
|
||||
{"dependency": "open_jdk", "version": "version:11"}
|
||||
]
|
||||
shard: tool_integration_tests
|
||||
subshard: "1_4"
|
||||
|
@ -976,8 +978,10 @@ targets:
|
|||
{"dependency": "android_sdk", "version": "version:33v6"},
|
||||
{"dependency": "chrome_and_driver", "version": "version:117.0"},
|
||||
{"dependency": "clang", "version": "git_revision:5d5aba78dbbee75508f01bcaa69aedb2ab79065a"},
|
||||
{"dependency": "open_jdk", "version": "version:11"},
|
||||
{"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"}
|
||||
{"dependency": "cmake", "version": "build_id:8787856497187628321"},
|
||||
{"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"},
|
||||
{"dependency": "ninja", "version": "version:1.9.0"},
|
||||
{"dependency": "open_jdk", "version": "version:11"}
|
||||
]
|
||||
shard: tool_integration_tests
|
||||
subshard: "2_4"
|
||||
|
@ -1000,8 +1004,10 @@ targets:
|
|||
{"dependency": "android_sdk", "version": "version:33v6"},
|
||||
{"dependency": "chrome_and_driver", "version": "version:117.0"},
|
||||
{"dependency": "clang", "version": "git_revision:5d5aba78dbbee75508f01bcaa69aedb2ab79065a"},
|
||||
{"dependency": "open_jdk", "version": "version:11"},
|
||||
{"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"}
|
||||
{"dependency": "cmake", "version": "build_id:8787856497187628321"},
|
||||
{"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"},
|
||||
{"dependency": "ninja", "version": "version:1.9.0"},
|
||||
{"dependency": "open_jdk", "version": "version:11"}
|
||||
]
|
||||
shard: tool_integration_tests
|
||||
subshard: "3_4"
|
||||
|
@ -1024,8 +1030,10 @@ targets:
|
|||
{"dependency": "android_sdk", "version": "version:33v6"},
|
||||
{"dependency": "chrome_and_driver", "version": "version:117.0"},
|
||||
{"dependency": "clang", "version": "git_revision:5d5aba78dbbee75508f01bcaa69aedb2ab79065a"},
|
||||
{"dependency": "open_jdk", "version": "version:11"},
|
||||
{"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"}
|
||||
{"dependency": "cmake", "version": "build_id:8787856497187628321"},
|
||||
{"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"},
|
||||
{"dependency": "ninja", "version": "version:1.9.0"},
|
||||
{"dependency": "open_jdk", "version": "version:11"}
|
||||
]
|
||||
shard: tool_integration_tests
|
||||
subshard: "4_4"
|
||||
|
|
|
@ -47,6 +47,7 @@ class BuildInfo {
|
|||
this.packageConfig = PackageConfig.empty,
|
||||
this.initializeFromDill,
|
||||
this.assumeInitializeFromDillUpToDate = false,
|
||||
this.buildNativeAssets = true,
|
||||
}) : extraFrontEndOptions = extraFrontEndOptions ?? const <String>[],
|
||||
extraGenSnapshotOptions = extraGenSnapshotOptions ?? const <String>[],
|
||||
fileSystemRoots = fileSystemRoots ?? const <String>[],
|
||||
|
@ -188,6 +189,9 @@ class BuildInfo {
|
|||
/// and skips the check and potential invalidation of files.
|
||||
final bool assumeInitializeFromDillUpToDate;
|
||||
|
||||
/// If set, builds native assets with `build.dart` from all packages.
|
||||
final bool buildNativeAssets;
|
||||
|
||||
static const BuildInfo debug = BuildInfo(BuildMode.debug, null, trackWidgetCreation: true, treeShakeIcons: false);
|
||||
static const BuildInfo profile = BuildInfo(BuildMode.profile, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
|
||||
static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
|
||||
|
|
|
@ -12,6 +12,7 @@ import '../../base/platform.dart';
|
|||
import '../../build_info.dart';
|
||||
import '../../dart/package_map.dart';
|
||||
import '../../ios/native_assets.dart';
|
||||
import '../../linux/native_assets.dart';
|
||||
import '../../macos/native_assets.dart';
|
||||
import '../../macos/xcode.dart';
|
||||
import '../../native_assets.dart';
|
||||
|
@ -118,6 +119,21 @@ class NativeAssets extends Target {
|
|||
fileSystem: fileSystem,
|
||||
buildRunner: buildRunner,
|
||||
);
|
||||
case TargetPlatform.linux_arm64:
|
||||
case TargetPlatform.linux_x64:
|
||||
final String? environmentBuildMode = environment.defines[kBuildMode];
|
||||
if (environmentBuildMode == null) {
|
||||
throw MissingDefineException(kBuildMode, name);
|
||||
}
|
||||
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
|
||||
(_, dependencies) = await buildNativeAssetsLinux(
|
||||
targetPlatform: targetPlatform,
|
||||
buildMode: buildMode,
|
||||
projectUri: projectUri,
|
||||
yamlParentDirectory: environment.buildDir.uri,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: buildRunner,
|
||||
);
|
||||
case TargetPlatform.tester:
|
||||
if (const LocalPlatform().isMacOS) {
|
||||
(_, dependencies) = await buildNativeAssetsMacOS(
|
||||
|
@ -129,6 +145,15 @@ class NativeAssets extends Target {
|
|||
buildRunner: buildRunner,
|
||||
flutterTester: true,
|
||||
);
|
||||
} else if (const LocalPlatform().isLinux) {
|
||||
(_, dependencies) = await buildNativeAssetsLinux(
|
||||
buildMode: BuildMode.debug,
|
||||
projectUri: projectUri,
|
||||
yamlParentDirectory: environment.buildDir.uri,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: buildRunner,
|
||||
flutterTester: true,
|
||||
);
|
||||
} else {
|
||||
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
|
||||
// Write the file we claim to have in the [outputs].
|
||||
|
@ -142,8 +167,6 @@ class NativeAssets extends Target {
|
|||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia_arm64:
|
||||
case TargetPlatform.fuchsia_x64:
|
||||
case TargetPlatform.linux_arm64:
|
||||
case TargetPlatform.linux_x64:
|
||||
case TargetPlatform.web_javascript:
|
||||
case TargetPlatform.windows_x64:
|
||||
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
|
||||
|
|
|
@ -17,6 +17,7 @@ import '../convert.dart';
|
|||
import '../flutter_plugins.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../migrations/cmake_custom_command_migration.dart';
|
||||
import '../migrations/cmake_native_assets_migration.dart';
|
||||
|
||||
// Matches the following error and warning patterns:
|
||||
// - <file path>:<line>:<column>: (fatal) error: <error...>
|
||||
|
@ -45,6 +46,7 @@ Future<void> buildLinux(
|
|||
|
||||
final List<ProjectMigrator> migrators = <ProjectMigrator>[
|
||||
CmakeCustomCommandMigration(linuxProject, logger),
|
||||
CmakeNativeAssetsMigration(linuxProject, 'linux', logger),
|
||||
];
|
||||
|
||||
final ProjectMigration migration = ProjectMigration(migrators);
|
||||
|
|
238
packages/flutter_tools/lib/src/linux/native_assets.dart
Normal file
238
packages/flutter_tools/lib/src/linux/native_assets.dart
Normal file
|
@ -0,0 +1,238 @@
|
|||
// 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:native_assets_builder/native_assets_builder.dart' show BuildResult;
|
||||
import 'package:native_assets_cli/native_assets_cli.dart' hide BuildMode;
|
||||
import 'package:native_assets_cli/native_assets_cli.dart' as native_assets_cli;
|
||||
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../build_info.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../native_assets.dart';
|
||||
|
||||
/// Dry run the native builds.
|
||||
///
|
||||
/// This does not build native assets, it only simulates what the final paths
|
||||
/// of all assets will be so that this can be embedded in the kernel file.
|
||||
Future<Uri?> dryRunNativeAssetsLinux({
|
||||
required NativeAssetsBuildRunner buildRunner,
|
||||
required Uri projectUri,
|
||||
bool flutterTester = false,
|
||||
required FileSystem fileSystem,
|
||||
}) async {
|
||||
if (await hasNoPackageConfig(buildRunner) || await isDisabledAndNoNativeAssets(buildRunner)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, OS.linux);
|
||||
final Iterable<Asset> nativeAssetPaths = await dryRunNativeAssetsLinuxInternal(
|
||||
fileSystem,
|
||||
projectUri,
|
||||
flutterTester,
|
||||
buildRunner,
|
||||
);
|
||||
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
|
||||
nativeAssetPaths,
|
||||
buildUri_,
|
||||
fileSystem,
|
||||
);
|
||||
return nativeAssetsUri;
|
||||
}
|
||||
|
||||
Future<Iterable<Asset>> dryRunNativeAssetsLinuxInternal(
|
||||
FileSystem fileSystem,
|
||||
Uri projectUri,
|
||||
bool flutterTester,
|
||||
NativeAssetsBuildRunner buildRunner,
|
||||
) async {
|
||||
const OS targetOs = OS.linux;
|
||||
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs);
|
||||
|
||||
globals.logger.printTrace('Dry running native assets for $targetOs.');
|
||||
final List<Asset> nativeAssets = (await buildRunner.dryRun(
|
||||
linkModePreference: LinkModePreference.dynamic,
|
||||
targetOs: targetOs,
|
||||
workingDirectory: projectUri,
|
||||
includeParentEnvironment: true,
|
||||
))
|
||||
.assets;
|
||||
ensureNoLinkModeStatic(nativeAssets);
|
||||
globals.logger.printTrace('Dry running native assets for $targetOs done.');
|
||||
final Uri? absolutePath = flutterTester ? buildUri_ : null;
|
||||
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath);
|
||||
final Iterable<Asset> nativeAssetPaths = assetTargetLocations.values;
|
||||
return nativeAssetPaths;
|
||||
}
|
||||
|
||||
/// Builds native assets.
|
||||
///
|
||||
/// If [targetPlatform] is omitted, the current target architecture is used.
|
||||
///
|
||||
/// If [flutterTester] is true, absolute paths are emitted in the native
|
||||
/// assets mapping. This can be used for JIT mode without sandbox on the host.
|
||||
/// This is used in `flutter test` and `flutter run -d flutter-tester`.
|
||||
Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsLinux({
|
||||
required NativeAssetsBuildRunner buildRunner,
|
||||
TargetPlatform? targetPlatform,
|
||||
required Uri projectUri,
|
||||
required BuildMode buildMode,
|
||||
bool flutterTester = false,
|
||||
Uri? yamlParentDirectory,
|
||||
required FileSystem fileSystem,
|
||||
}) async {
|
||||
const OS targetOs = OS.linux;
|
||||
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs);
|
||||
final Directory buildDir = fileSystem.directory(buildUri_);
|
||||
if (!await buildDir.exists()) {
|
||||
// CMake requires the folder to exist to do copying.
|
||||
await buildDir.create(recursive: true);
|
||||
}
|
||||
if (await hasNoPackageConfig(buildRunner) || await isDisabledAndNoNativeAssets(buildRunner)) {
|
||||
final Uri nativeAssetsYaml = await writeNativeAssetsYaml(<Asset>[], yamlParentDirectory ?? buildUri_, fileSystem);
|
||||
return (nativeAssetsYaml, <Uri>[]);
|
||||
}
|
||||
|
||||
final Target target = targetPlatform != null ? _getNativeTarget(targetPlatform) : Target.current;
|
||||
final native_assets_cli.BuildMode buildModeCli = nativeAssetsBuildMode(buildMode);
|
||||
|
||||
globals.logger.printTrace('Building native assets for $target $buildModeCli.');
|
||||
final BuildResult result = await buildRunner.build(
|
||||
linkModePreference: LinkModePreference.dynamic,
|
||||
target: target,
|
||||
buildMode: buildModeCli,
|
||||
workingDirectory: projectUri,
|
||||
includeParentEnvironment: true,
|
||||
cCompilerConfig: await buildRunner.cCompilerConfig,
|
||||
);
|
||||
final List<Asset> nativeAssets = result.assets;
|
||||
final Set<Uri> dependencies = result.dependencies.toSet();
|
||||
ensureNoLinkModeStatic(nativeAssets);
|
||||
globals.logger.printTrace('Building native assets for $target done.');
|
||||
final Uri? absolutePath = flutterTester ? buildUri_ : null;
|
||||
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath);
|
||||
await _copyNativeAssetsLinux(
|
||||
buildUri_,
|
||||
assetTargetLocations,
|
||||
buildMode,
|
||||
fileSystem,
|
||||
);
|
||||
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
|
||||
assetTargetLocations.values,
|
||||
yamlParentDirectory ?? buildUri_,
|
||||
fileSystem,
|
||||
);
|
||||
return (nativeAssetsUri, dependencies.toList());
|
||||
}
|
||||
|
||||
Map<Asset, Asset> _assetTargetLocations(
|
||||
List<Asset> nativeAssets,
|
||||
Uri? absolutePath,
|
||||
) =>
|
||||
<Asset, Asset>{
|
||||
for (final Asset asset in nativeAssets) asset: _targetLocationLinux(asset, absolutePath),
|
||||
};
|
||||
|
||||
Asset _targetLocationLinux(Asset asset, Uri? absolutePath) {
|
||||
final AssetPath path = asset.path;
|
||||
switch (path) {
|
||||
case AssetSystemPath _:
|
||||
case AssetInExecutable _:
|
||||
case AssetInProcess _:
|
||||
return asset;
|
||||
case AssetAbsolutePath _:
|
||||
final String fileName = path.uri.pathSegments.last;
|
||||
Uri uri;
|
||||
if (absolutePath != null) {
|
||||
// Flutter tester needs full host paths.
|
||||
uri = absolutePath.resolve(fileName);
|
||||
} else {
|
||||
// Flutter Desktop needs "absolute" paths inside the app.
|
||||
// "relative" in the context of native assets would be relative to the
|
||||
// kernel or aot snapshot.
|
||||
uri = Uri(path: fileName);
|
||||
}
|
||||
return asset.copyWith(path: AssetAbsolutePath(uri));
|
||||
}
|
||||
throw Exception('Unsupported asset path type ${path.runtimeType} in asset $asset');
|
||||
}
|
||||
|
||||
/// Extract the [Target] from a [TargetPlatform].
|
||||
Target _getNativeTarget(TargetPlatform targetPlatform) {
|
||||
switch (targetPlatform) {
|
||||
case TargetPlatform.linux_x64:
|
||||
return Target.linuxX64;
|
||||
case TargetPlatform.linux_arm64:
|
||||
return Target.linuxArm64;
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.ios:
|
||||
case TargetPlatform.darwin:
|
||||
case TargetPlatform.windows_x64:
|
||||
case TargetPlatform.fuchsia_arm64:
|
||||
case TargetPlatform.fuchsia_x64:
|
||||
case TargetPlatform.tester:
|
||||
case TargetPlatform.web_javascript:
|
||||
case TargetPlatform.android_arm:
|
||||
case TargetPlatform.android_arm64:
|
||||
case TargetPlatform.android_x64:
|
||||
case TargetPlatform.android_x86:
|
||||
throw Exception('Unknown targetPlatform: $targetPlatform.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _copyNativeAssetsLinux(
|
||||
Uri buildUri,
|
||||
Map<Asset, Asset> assetTargetLocations,
|
||||
BuildMode buildMode,
|
||||
FileSystem fileSystem,
|
||||
) async {
|
||||
if (assetTargetLocations.isNotEmpty) {
|
||||
globals.logger.printTrace('Copying native assets to ${buildUri.toFilePath()}.');
|
||||
final Directory buildDir = fileSystem.directory(buildUri.toFilePath());
|
||||
if (!buildDir.existsSync()) {
|
||||
buildDir.createSync(recursive: true);
|
||||
}
|
||||
for (final MapEntry<Asset, Asset> assetMapping in assetTargetLocations.entries) {
|
||||
final Uri source = (assetMapping.key.path as AssetAbsolutePath).uri;
|
||||
final Uri target = (assetMapping.value.path as AssetAbsolutePath).uri;
|
||||
final Uri targetUri = buildUri.resolveUri(target);
|
||||
final String targetFullPath = targetUri.toFilePath();
|
||||
await fileSystem.file(source).copy(targetFullPath);
|
||||
}
|
||||
globals.logger.printTrace('Copying native assets done.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Flutter expects `clang++` to be on the path on Linux hosts.
|
||||
///
|
||||
/// Search for the accompanying `clang`, `ar`, and `ld`.
|
||||
Future<CCompilerConfig> cCompilerConfigLinux() async {
|
||||
const String kClangPlusPlusBinary = 'clang++';
|
||||
const String kClangBinary = 'clang';
|
||||
const String kArBinary = 'llvm-ar';
|
||||
const String kLdBinary = 'ld.lld';
|
||||
|
||||
final ProcessResult whichResult = await globals.processManager.run(<String>['which', kClangPlusPlusBinary]);
|
||||
if (whichResult.exitCode != 0) {
|
||||
throwToolExit('Failed to find $kClangPlusPlusBinary on PATH.');
|
||||
}
|
||||
File clangPpFile = globals.fs.file((whichResult.stdout as String).trim());
|
||||
clangPpFile = globals.fs.file(await clangPpFile.resolveSymbolicLinks());
|
||||
|
||||
final Directory clangDir = clangPpFile.parent;
|
||||
final Map<String, Uri> binaryPaths = <String, Uri>{};
|
||||
for (final String binary in <String>[kClangBinary, kArBinary, kLdBinary]) {
|
||||
final File binaryFile = clangDir.childFile(binary);
|
||||
if (!await binaryFile.exists()) {
|
||||
throwToolExit("Failed to find $binary relative to $clangPpFile: $binaryFile doesn't exist.");
|
||||
}
|
||||
binaryPaths[binary] = binaryFile.uri;
|
||||
}
|
||||
return CCompilerConfig(
|
||||
ar: binaryPaths[kArBinary],
|
||||
cc: binaryPaths[kClangBinary],
|
||||
ld: binaryPaths[kLdBinary],
|
||||
);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// 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 '../base/file_system.dart';
|
||||
import '../base/project_migrator.dart';
|
||||
import '../cmake_project.dart';
|
||||
|
||||
/// Adds the snippet to the CMake file that copies the native assets.
|
||||
///
|
||||
/// ```cmake
|
||||
/// # Copy the native assets provided by the build.dart from all packages.
|
||||
/// set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
|
||||
/// install(DIRECTORY "${NATIVE_ASSETS_DIR}"
|
||||
/// DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
/// COMPONENT Runtime)
|
||||
/// ```
|
||||
class CmakeNativeAssetsMigration extends ProjectMigrator {
|
||||
CmakeNativeAssetsMigration(CmakeBasedProject project, this.os, super.logger)
|
||||
: _cmakeFile = project.managedCmakeFile;
|
||||
|
||||
final File _cmakeFile;
|
||||
final String os;
|
||||
|
||||
@override
|
||||
void migrate() {
|
||||
if (!_cmakeFile.existsSync()) {
|
||||
logger.printTrace('CMake project not found, skipping install() NATIVE_ASSETS_DIR migration.');
|
||||
return;
|
||||
}
|
||||
|
||||
final String originalProjectContents = _cmakeFile.readAsStringSync();
|
||||
|
||||
if (originalProjectContents.contains('set(NATIVE_ASSETS_DIR')) {
|
||||
// Command is already present.
|
||||
return;
|
||||
}
|
||||
|
||||
final String copyNativeAssetsCommand = '''
|
||||
|
||||
# Copy the native assets provided by the build.dart from all packages.
|
||||
set(NATIVE_ASSETS_DIR "\${PROJECT_BUILD_DIR}native_assets/$os/")
|
||||
install(DIRECTORY "\${NATIVE_ASSETS_DIR}"
|
||||
DESTINATION "\${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
''';
|
||||
|
||||
// Insert the new command after the bundled libraries loop.
|
||||
const String bundleLibrariesCommandEnd = r'''
|
||||
endforeach(bundled_library)
|
||||
''';
|
||||
|
||||
String newProjectContents = originalProjectContents;
|
||||
|
||||
newProjectContents = originalProjectContents.replaceFirst(
|
||||
bundleLibrariesCommandEnd,
|
||||
'$bundleLibrariesCommandEnd$copyNativeAssetsCommand',
|
||||
);
|
||||
|
||||
if (originalProjectContents != newProjectContents) {
|
||||
logger.printStatus('CMake missing install() NATIVE_ASSETS_DIR command, updating.');
|
||||
_cmakeFile.writeAsStringSync(newProjectContents);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import 'cache.dart';
|
|||
import 'features.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'ios/native_assets.dart';
|
||||
import 'linux/native_assets.dart';
|
||||
import 'macos/native_assets.dart';
|
||||
import 'macos/native_assets_host.dart';
|
||||
import 'resident_runner.dart';
|
||||
|
@ -168,6 +169,9 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner {
|
|||
if (globals.platform.isMacOS || globals.platform.isIOS) {
|
||||
return cCompilerConfigMacOS();
|
||||
}
|
||||
if (globals.platform.isLinux) {
|
||||
return cCompilerConfigLinux();
|
||||
}
|
||||
throwToolExit(
|
||||
'Native assets feature not yet implemented for Linux, Windows and Android.',
|
||||
);
|
||||
|
@ -333,6 +337,13 @@ Future<Uri?> dryRunNativeAssets({
|
|||
fileSystem: fileSystem,
|
||||
buildRunner: buildRunner,
|
||||
);
|
||||
} else if (const LocalPlatform().isLinux) {
|
||||
nativeAssetsYaml = await dryRunNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
flutterTester: true,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: buildRunner,
|
||||
);
|
||||
} else {
|
||||
await ensureNoNativeAssetsOrOsIsSupported(
|
||||
projectUri,
|
||||
|
@ -342,6 +353,13 @@ Future<Uri?> dryRunNativeAssets({
|
|||
);
|
||||
nativeAssetsYaml = null;
|
||||
}
|
||||
case build_info.TargetPlatform.linux_arm64:
|
||||
case build_info.TargetPlatform.linux_x64:
|
||||
nativeAssetsYaml = await dryRunNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: buildRunner,
|
||||
);
|
||||
case build_info.TargetPlatform.android_arm:
|
||||
case build_info.TargetPlatform.android_arm64:
|
||||
case build_info.TargetPlatform.android_x64:
|
||||
|
@ -349,8 +367,6 @@ Future<Uri?> dryRunNativeAssets({
|
|||
case build_info.TargetPlatform.android:
|
||||
case build_info.TargetPlatform.fuchsia_arm64:
|
||||
case build_info.TargetPlatform.fuchsia_x64:
|
||||
case build_info.TargetPlatform.linux_arm64:
|
||||
case build_info.TargetPlatform.linux_x64:
|
||||
case build_info.TargetPlatform.web_javascript:
|
||||
case build_info.TargetPlatform.windows_x64:
|
||||
await ensureNoNativeAssetsOrOsIsSupported(
|
||||
|
@ -382,7 +398,12 @@ Future<Uri?> dryRunNativeAssetsMultipeOSes({
|
|||
if (targetPlatforms.contains(build_info.TargetPlatform.darwin) ||
|
||||
(targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.macOS))
|
||||
...await dryRunNativeAssetsMacOSInternal(fileSystem, projectUri, false, buildRunner),
|
||||
if (targetPlatforms.contains(build_info.TargetPlatform.ios)) ...await dryRunNativeAssetsIOSInternal(fileSystem, projectUri, buildRunner)
|
||||
if (targetPlatforms.contains(build_info.TargetPlatform.linux_arm64) ||
|
||||
targetPlatforms.contains(build_info.TargetPlatform.linux_x64) ||
|
||||
(targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.linux))
|
||||
...await dryRunNativeAssetsLinuxInternal(fileSystem, projectUri, false, buildRunner),
|
||||
if (targetPlatforms.contains(build_info.TargetPlatform.ios))
|
||||
...await dryRunNativeAssetsIOSInternal(fileSystem, projectUri, buildRunner)
|
||||
];
|
||||
final Uri nativeAssetsUri = await writeNativeAssetsYaml(nativeAssetPaths, buildUri_, fileSystem);
|
||||
return nativeAssetsUri;
|
||||
|
|
|
@ -16,6 +16,7 @@ import '../bundle.dart';
|
|||
import '../compile.dart';
|
||||
import '../flutter_plugins.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../linux/native_assets.dart';
|
||||
import '../macos/native_assets.dart';
|
||||
import '../native_assets.dart';
|
||||
import '../project.dart';
|
||||
|
@ -168,6 +169,9 @@ class TestCompiler {
|
|||
}
|
||||
|
||||
Uri? nativeAssetsYaml;
|
||||
if (!buildInfo.buildNativeAssets) {
|
||||
nativeAssetsYaml = null;
|
||||
} else {
|
||||
final Uri projectUri = FlutterProject.current().directory.uri;
|
||||
final NativeAssetsBuildRunner buildRunner = NativeAssetsBuildRunnerImpl(
|
||||
projectUri,
|
||||
|
@ -177,7 +181,15 @@ class TestCompiler {
|
|||
);
|
||||
if (globals.platform.isMacOS) {
|
||||
(nativeAssetsYaml, _) = await buildNativeAssetsMacOS(
|
||||
buildMode: BuildMode.debug,
|
||||
buildMode: buildInfo.mode,
|
||||
projectUri: projectUri,
|
||||
flutterTester: true,
|
||||
fileSystem: globals.fs,
|
||||
buildRunner: buildRunner,
|
||||
);
|
||||
} else if (globals.platform.isLinux) {
|
||||
(nativeAssetsYaml, _) = await buildNativeAssetsLinux(
|
||||
buildMode: buildInfo.mode,
|
||||
projectUri: projectUri,
|
||||
flutterTester: true,
|
||||
fileSystem: globals.fs,
|
||||
|
@ -191,6 +203,7 @@ class TestCompiler {
|
|||
buildRunner,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final CompilerOutput? compilerOutput = await compiler!.recompile(
|
||||
request.mainUri,
|
||||
|
|
|
@ -127,6 +127,12 @@ foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
|
|||
COMPONENT Runtime)
|
||||
endforeach(bundled_library)
|
||||
|
||||
# Copy the native assets provided by the build.dart from all packages.
|
||||
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
|
||||
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
|
|
|
@ -0,0 +1,374 @@
|
|||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:file_testing/file_testing.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||
import 'package:flutter_tools/src/dart/package_map.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/linux/native_assets.dart';
|
||||
import 'package:flutter_tools/src/native_assets.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart' hide BuildMode, Target;
|
||||
import 'package:native_assets_cli/native_assets_cli.dart' as native_assets_cli;
|
||||
import 'package:package_config/package_config_types.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/fakes.dart';
|
||||
import '../fake_native_assets_build_runner.dart';
|
||||
|
||||
void main() {
|
||||
late FakeProcessManager processManager;
|
||||
late Environment environment;
|
||||
late Artifacts artifacts;
|
||||
late FileSystem fileSystem;
|
||||
late BufferLogger logger;
|
||||
late Uri projectUri;
|
||||
|
||||
setUp(() {
|
||||
processManager = FakeProcessManager.empty();
|
||||
logger = BufferLogger.test();
|
||||
artifacts = Artifacts.test();
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
inputs: <String, String>{},
|
||||
artifacts: artifacts,
|
||||
processManager: processManager,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
);
|
||||
environment.buildDir.createSync(recursive: true);
|
||||
projectUri = environment.projectDir.uri;
|
||||
});
|
||||
|
||||
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
expect(
|
||||
await dryRunNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
hasPackageConfigResult: false,
|
||||
),
|
||||
),
|
||||
null,
|
||||
);
|
||||
expect(
|
||||
(globals.logger as BufferLogger).traceText,
|
||||
contains('No package config found. Skipping native assets compilation.'),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('build with no package config', overrides: <Type, Generator>{
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
await buildNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
buildMode: BuildMode.debug,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
hasPackageConfigResult: false,
|
||||
),
|
||||
);
|
||||
expect(
|
||||
(globals.logger as BufferLogger).traceText,
|
||||
contains('No package config found. Skipping native assets compilation.'),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('dry run for multiple OSes with no package config', overrides: <Type, Generator>{
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
await dryRunNativeAssetsMultipeOSes(
|
||||
projectUri: projectUri,
|
||||
fileSystem: fileSystem,
|
||||
targetPlatforms: <TargetPlatform>[
|
||||
TargetPlatform.darwin,
|
||||
TargetPlatform.ios,
|
||||
],
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
hasPackageConfigResult: false,
|
||||
),
|
||||
);
|
||||
expect(
|
||||
(globals.logger as BufferLogger).traceText,
|
||||
contains('No package config found. Skipping native assets compilation.'),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||
await packageConfig.parent.create();
|
||||
await packageConfig.create();
|
||||
expect(
|
||||
() => dryRunNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
),
|
||||
),
|
||||
throwsToolExit(
|
||||
message: 'Package(s) bar require the native assets feature to be enabled. '
|
||||
'Enable using `flutter config --enable-native-assets`.',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('dry run with assets', overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||
await packageConfig.parent.create();
|
||||
await packageConfig.create();
|
||||
final Uri? nativeAssetsYaml = await dryRunNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
dryRunResult: FakeNativeAssetsBuilderResult(
|
||||
assets: <Asset>[
|
||||
Asset(
|
||||
id: 'package:bar/bar.dart',
|
||||
linkMode: LinkMode.dynamic,
|
||||
target: native_assets_cli.Target.linuxX64,
|
||||
path: AssetAbsolutePath(Uri.file('libbar.so')),
|
||||
),
|
||||
Asset(
|
||||
id: 'package:bar/bar.dart',
|
||||
linkMode: LinkMode.dynamic,
|
||||
target: native_assets_cli.Target.linuxArm64,
|
||||
path: AssetAbsolutePath(Uri.file('libbar.so')),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
nativeAssetsYaml,
|
||||
projectUri.resolve('build/native_assets/linux/native_assets.yaml'),
|
||||
);
|
||||
expect(
|
||||
await fileSystem.file(nativeAssetsYaml).readAsString(),
|
||||
contains('package:bar/bar.dart'),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('build with assets but not enabled', overrides: <Type, Generator>{
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||
await packageConfig.parent.create();
|
||||
await packageConfig.create();
|
||||
expect(
|
||||
() => buildNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
buildMode: BuildMode.debug,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
),
|
||||
),
|
||||
throwsToolExit(
|
||||
message: 'Package(s) bar require the native assets feature to be enabled. '
|
||||
'Enable using `flutter config --enable-native-assets`.',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('build no assets', overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||
await packageConfig.parent.create();
|
||||
await packageConfig.create();
|
||||
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
buildMode: BuildMode.debug,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
),
|
||||
);
|
||||
expect(
|
||||
nativeAssetsYaml,
|
||||
projectUri.resolve('build/native_assets/linux/native_assets.yaml'),
|
||||
);
|
||||
expect(
|
||||
await fileSystem.file(nativeAssetsYaml).readAsString(),
|
||||
isNot(contains('package:bar/bar.dart')),
|
||||
);
|
||||
expect(
|
||||
environment.projectDir
|
||||
.childDirectory('build')
|
||||
.childDirectory('native_assets')
|
||||
.childDirectory('linux'),
|
||||
exists,
|
||||
);
|
||||
});
|
||||
|
||||
for (final bool flutterTester in <bool>[false, true]) {
|
||||
String testName = '';
|
||||
if (flutterTester) {
|
||||
testName += ' flutter tester';
|
||||
}
|
||||
testUsingContext('build with assets$testName', overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
if (const LocalPlatform().isWindows) {
|
||||
return; // Backslashes in commands, but we will never run these commands on Windows.
|
||||
}
|
||||
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||
await packageConfig.parent.create();
|
||||
await packageConfig.create();
|
||||
final File dylibAfterCompiling = fileSystem.file('libbar.so');
|
||||
// The mock doesn't create the file, so create it here.
|
||||
await dylibAfterCompiling.create();
|
||||
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
buildMode: BuildMode.debug,
|
||||
fileSystem: fileSystem,
|
||||
flutterTester: flutterTester,
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
buildResult: FakeNativeAssetsBuilderResult(
|
||||
assets: <Asset>[
|
||||
Asset(
|
||||
id: 'package:bar/bar.dart',
|
||||
linkMode: LinkMode.dynamic,
|
||||
target: native_assets_cli.Target.linuxX64,
|
||||
path: AssetAbsolutePath(dylibAfterCompiling.uri),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
nativeAssetsYaml,
|
||||
projectUri.resolve('build/native_assets/linux/native_assets.yaml'),
|
||||
);
|
||||
expect(
|
||||
await fileSystem.file(nativeAssetsYaml).readAsString(),
|
||||
stringContainsInOrder(<String>[
|
||||
'package:bar/bar.dart',
|
||||
if (flutterTester)
|
||||
// Tests run on host system, so the have the full path on the system.
|
||||
'- ${projectUri.resolve('/build/native_assets/linux/libbar.so').toFilePath()}'
|
||||
else
|
||||
// Apps are a bundle with the dylibs on their dlopen path.
|
||||
'- libbar.so',
|
||||
]),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
testUsingContext('static libs not supported', overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
}, () async {
|
||||
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||
await packageConfig.parent.create();
|
||||
await packageConfig.create();
|
||||
expect(
|
||||
() => dryRunNativeAssetsLinux(
|
||||
projectUri: projectUri,
|
||||
fileSystem: fileSystem,
|
||||
buildRunner: FakeNativeAssetsBuildRunner(
|
||||
packagesWithNativeAssetsResult: <Package>[
|
||||
Package('bar', projectUri),
|
||||
],
|
||||
dryRunResult: FakeNativeAssetsBuilderResult(
|
||||
assets: <Asset>[
|
||||
Asset(
|
||||
id: 'package:bar/bar.dart',
|
||||
linkMode: LinkMode.static,
|
||||
target: native_assets_cli.Target.macOSArm64,
|
||||
path: AssetAbsolutePath(Uri.file('bar.a')),
|
||||
),
|
||||
Asset(
|
||||
id: 'package:bar/bar.dart',
|
||||
linkMode: LinkMode.static,
|
||||
target: native_assets_cli.Target.macOSX64,
|
||||
path: AssetAbsolutePath(Uri.file('bar.a')),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
throwsToolExit(
|
||||
message: 'Native asset(s) package:bar/bar.dart have their link mode set to '
|
||||
'static, but this is not yet supported. '
|
||||
'For more info see https://github.com/dart-lang/sdk/issues/49418.',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
// This logic is mocked in the other tests to avoid having test order
|
||||
// randomization causing issues with what processes are invoked.
|
||||
// Exercise the parsing of the process output in this separate test.
|
||||
testUsingContext('NativeAssetsBuildRunnerImpl.cCompilerConfig', overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||
ProcessManager: () => FakeProcessManager.list(
|
||||
<FakeCommand>[
|
||||
const FakeCommand(
|
||||
command: <Pattern>['which', 'clang++'],
|
||||
stdout: '''
|
||||
/some/path/to/clang++
|
||||
''', // Newline at the end of the string.
|
||||
)
|
||||
],
|
||||
),
|
||||
FileSystem: () => fileSystem,
|
||||
}, () async {
|
||||
if (!const LocalPlatform().isLinux) {
|
||||
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
|
||||
return;
|
||||
}
|
||||
|
||||
await fileSystem.directory('/some/path/to/').create(recursive: true);
|
||||
await fileSystem.file('/some/path/to/clang++').create();
|
||||
await fileSystem.file('/some/path/to/clang').create();
|
||||
await fileSystem.file('/some/path/to/llvm-ar').create();
|
||||
await fileSystem.file('/some/path/to/ld.lld').create();
|
||||
|
||||
final File packagesFile = fileSystem
|
||||
.directory(projectUri)
|
||||
.childDirectory('.dart_tool')
|
||||
.childFile('package_config.json');
|
||||
await packagesFile.parent.create();
|
||||
await packagesFile.create();
|
||||
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
||||
packagesFile,
|
||||
logger: environment.logger,
|
||||
);
|
||||
final NativeAssetsBuildRunner runner =
|
||||
NativeAssetsBuildRunnerImpl(projectUri, packageConfig, fileSystem, logger);
|
||||
final CCompilerConfig result = await runner.cCompilerConfig;
|
||||
expect(result.cc, Uri.file('/some/path/to/clang'));
|
||||
});
|
||||
}
|
|
@ -8,6 +8,7 @@ import 'package:flutter_tools/src/base/logger.dart';
|
|||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/cmake_project.dart';
|
||||
import 'package:flutter_tools/src/migrations/cmake_custom_command_migration.dart';
|
||||
import 'package:flutter_tools/src/migrations/cmake_native_assets_migration.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
|
@ -155,6 +156,132 @@ add_custom_command(
|
|||
expect(testLogger.statusText, contains('add_custom_command() missing VERBATIM or FLUTTER_TARGET_PLATFORM, updating.'));
|
||||
});
|
||||
});
|
||||
|
||||
group('migrate add install() NATIVE_ASSETS_DIR command', () {
|
||||
late MemoryFileSystem memoryFileSystem;
|
||||
late BufferLogger testLogger;
|
||||
late FakeCmakeProject mockCmakeProject;
|
||||
late File managedCmakeFile;
|
||||
|
||||
setUp(() {
|
||||
memoryFileSystem = MemoryFileSystem.test();
|
||||
managedCmakeFile = memoryFileSystem.file('CMakeLists.txtx');
|
||||
|
||||
testLogger = BufferLogger(
|
||||
terminal: Terminal.test(),
|
||||
outputPreferences: OutputPreferences.test(),
|
||||
);
|
||||
|
||||
mockCmakeProject = FakeCmakeProject(managedCmakeFile);
|
||||
});
|
||||
|
||||
testWithoutContext('skipped if files are missing', () {
|
||||
final CmakeNativeAssetsMigration cmakeProjectMigration = CmakeNativeAssetsMigration(
|
||||
mockCmakeProject,
|
||||
'linux',
|
||||
testLogger,
|
||||
);
|
||||
cmakeProjectMigration.migrate();
|
||||
expect(managedCmakeFile.existsSync(), isFalse);
|
||||
|
||||
expect(testLogger.traceText, contains('CMake project not found, skipping install() NATIVE_ASSETS_DIR migration.'));
|
||||
expect(testLogger.statusText, isEmpty);
|
||||
});
|
||||
|
||||
testWithoutContext('skipped if nothing to migrate', () {
|
||||
const String contents = 'Nothing to migrate';
|
||||
managedCmakeFile.writeAsStringSync(contents);
|
||||
final DateTime projectLastModified = managedCmakeFile.lastModifiedSync();
|
||||
|
||||
final CmakeNativeAssetsMigration cmakeProjectMigration = CmakeNativeAssetsMigration(
|
||||
mockCmakeProject,
|
||||
'linux',
|
||||
testLogger,
|
||||
);
|
||||
cmakeProjectMigration.migrate();
|
||||
|
||||
expect(managedCmakeFile.lastModifiedSync(), projectLastModified);
|
||||
expect(managedCmakeFile.readAsStringSync(), contents);
|
||||
|
||||
expect(testLogger.statusText, isEmpty);
|
||||
});
|
||||
|
||||
testWithoutContext('skipped if already migrated', () {
|
||||
const String contents = r'''
|
||||
# Copy the native assets provided by the build.dart from all packages.
|
||||
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
|
||||
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
''';
|
||||
managedCmakeFile.writeAsStringSync(contents);
|
||||
final DateTime projectLastModified = managedCmakeFile.lastModifiedSync();
|
||||
|
||||
final CmakeNativeAssetsMigration cmakeProjectMigration = CmakeNativeAssetsMigration(
|
||||
mockCmakeProject,
|
||||
'linux',
|
||||
testLogger,
|
||||
);
|
||||
cmakeProjectMigration.migrate();
|
||||
|
||||
expect(managedCmakeFile.lastModifiedSync(), projectLastModified);
|
||||
expect(managedCmakeFile.readAsStringSync(), contents);
|
||||
|
||||
expect(testLogger.statusText, isEmpty);
|
||||
});
|
||||
|
||||
// TODO(dacoharkes): Add test for Windows when adding Windows support. https://github.com/flutter/flutter/issues/129757
|
||||
testWithoutContext('is migrated to copy native assets', () {
|
||||
managedCmakeFile.writeAsStringSync(r'''
|
||||
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
|
||||
install(FILES "${bundled_library}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endforeach(bundled_library)
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||
" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||
''');
|
||||
|
||||
final CmakeNativeAssetsMigration cmakeProjectMigration = CmakeNativeAssetsMigration(
|
||||
mockCmakeProject,
|
||||
'linux',
|
||||
testLogger,
|
||||
);
|
||||
cmakeProjectMigration.migrate();
|
||||
|
||||
expect(managedCmakeFile.readAsStringSync(), r'''
|
||||
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
|
||||
install(FILES "${bundled_library}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endforeach(bundled_library)
|
||||
|
||||
# Copy the native assets provided by the build.dart from all packages.
|
||||
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
|
||||
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||
" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||
''');
|
||||
|
||||
expect(testLogger.statusText, contains('CMake missing install() NATIVE_ASSETS_DIR command, updating.'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file_testing/file_testing.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import 'test_utils.dart' show fileSystem, platform;
|
||||
|
@ -56,10 +57,8 @@ const String packageName = 'package_with_native_assets';
|
|||
|
||||
const String exampleAppName = '${packageName}_example';
|
||||
|
||||
const String dylibName = 'lib$packageName.dylib';
|
||||
|
||||
void main() {
|
||||
if (!platform.isMacOS) {
|
||||
if (!platform.isMacOS && !platform.isLinux) {
|
||||
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
|
||||
return;
|
||||
}
|
||||
|
@ -148,6 +147,9 @@ void main() {
|
|||
if (device == 'macos') {
|
||||
expectDylibIsBundledMacOS(exampleDirectory, buildMode);
|
||||
}
|
||||
if (device == 'linux') {
|
||||
expectDylibIsBundledLinux(exampleDirectory, buildMode);
|
||||
}
|
||||
if (device == hostOs) {
|
||||
expectCCompilerIsConfigured(exampleDirectory);
|
||||
}
|
||||
|
@ -201,6 +203,8 @@ void main() {
|
|||
expectDylibIsBundledMacOS(exampleDirectory, buildMode);
|
||||
} else if (buildSubcommand == 'ios') {
|
||||
expectDylibIsBundledIos(exampleDirectory, buildMode);
|
||||
} else if (buildSubcommand == 'linux') {
|
||||
expectDylibIsBundledLinux(exampleDirectory, buildMode);
|
||||
}
|
||||
expectCCompilerIsConfigured(exampleDirectory);
|
||||
});
|
||||
|
@ -278,7 +282,7 @@ void expectDylibIsBundledMacOS(Directory appDirectory, String buildMode) {
|
|||
expect(appBundle, exists);
|
||||
final Directory dylibsFolder = appBundle.childDirectory('Contents/Frameworks');
|
||||
expect(dylibsFolder, exists);
|
||||
final File dylib = dylibsFolder.childFile(dylibName);
|
||||
final File dylib = dylibsFolder.childFile(OS.macOS.dylibFileName(packageName));
|
||||
expect(dylib, exists);
|
||||
}
|
||||
|
||||
|
@ -287,7 +291,21 @@ void expectDylibIsBundledIos(Directory appDirectory, String buildMode) {
|
|||
expect(appBundle, exists);
|
||||
final Directory dylibsFolder = appBundle.childDirectory('Frameworks');
|
||||
expect(dylibsFolder, exists);
|
||||
final File dylib = dylibsFolder.childFile(dylibName);
|
||||
final File dylib = dylibsFolder.childFile(OS.iOS.dylibFileName(packageName));
|
||||
expect(dylib, exists);
|
||||
}
|
||||
|
||||
/// Checks that dylibs are bundled.
|
||||
///
|
||||
/// Sample path: build/linux/x64/release/bundle/lib/libmy_package.so
|
||||
void expectDylibIsBundledLinux(Directory appDirectory, String buildMode) {
|
||||
// Linux does not support cross compilation, so always only check current architecture.
|
||||
final String architecture = Architecture.current.dartPlatform;
|
||||
final Directory appBundle = appDirectory.childDirectory('build/$hostOs/$architecture/$buildMode/bundle/');
|
||||
expect(appBundle, exists);
|
||||
final Directory dylibsFolder = appBundle.childDirectory('lib/');
|
||||
expect(dylibsFolder, exists);
|
||||
final File dylib = dylibsFolder.childFile(OS.linux.dylibFileName(packageName));
|
||||
expect(dylib, exists);
|
||||
}
|
||||
|
||||
|
@ -296,7 +314,7 @@ void expectDylibIsBundledIos(Directory appDirectory, String buildMode) {
|
|||
void expectDylibIsBundledWithFrameworks(Directory appDirectory, String buildMode, String os) {
|
||||
final Directory frameworksFolder = appDirectory.childDirectory('build/$os/framework/${buildMode.upperCaseFirst()}');
|
||||
expect(frameworksFolder, exists);
|
||||
final File dylib = frameworksFolder.childFile(dylibName);
|
||||
final File dylib = frameworksFolder.childFile(OS.macOS.dylibFileName(packageName));
|
||||
expect(dylib, exists);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
|
@ -177,9 +178,15 @@ Future<ProcessTestResult> runFlutter(
|
|||
minutes:
|
||||
10), // must be less than test timeout of 15 minutes! See ../../dart_test.yaml.
|
||||
}) async {
|
||||
const LocalPlatform platform = LocalPlatform();
|
||||
final Stopwatch clock = Stopwatch()..start();
|
||||
final Process process = await processManager.start(
|
||||
<String>[flutterBin, ...arguments],
|
||||
<String>[
|
||||
// In a container with no X display, use the virtual framebuffer.
|
||||
if (platform.isLinux && (platform.environment['DISPLAY'] ?? '').isEmpty) '/usr/bin/xvfb-run',
|
||||
flutterBin,
|
||||
...arguments,
|
||||
],
|
||||
workingDirectory: workingDirectory,
|
||||
);
|
||||
final List<LogLine> logs = <LogLine>[];
|
||||
|
|
Loading…
Reference in a new issue