diff --git a/.ci.yaml b/.ci.yaml index b685889056f..7c00866d0cd 100644 --- a/.ci.yaml +++ b/.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" diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index b084efeddf1..b99d5f6effa 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart @@ -47,6 +47,7 @@ class BuildInfo { this.packageConfig = PackageConfig.empty, this.initializeFromDill, this.assumeInitializeFromDillUpToDate = false, + this.buildNativeAssets = true, }) : extraFrontEndOptions = extraFrontEndOptions ?? const [], extraGenSnapshotOptions = extraGenSnapshotOptions ?? const [], fileSystemRoots = fileSystemRoots ?? const [], @@ -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); diff --git a/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart b/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart index 6e73882927f..c92c1d69495 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart @@ -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 diff --git a/packages/flutter_tools/lib/src/linux/build_linux.dart b/packages/flutter_tools/lib/src/linux/build_linux.dart index 410416aa630..1bcf07361fd 100644 --- a/packages/flutter_tools/lib/src/linux/build_linux.dart +++ b/packages/flutter_tools/lib/src/linux/build_linux.dart @@ -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: // - ::: (fatal) error: @@ -45,6 +46,7 @@ Future buildLinux( final List migrators = [ CmakeCustomCommandMigration(linuxProject, logger), + CmakeNativeAssetsMigration(linuxProject, 'linux', logger), ]; final ProjectMigration migration = ProjectMigration(migrators); diff --git a/packages/flutter_tools/lib/src/linux/native_assets.dart b/packages/flutter_tools/lib/src/linux/native_assets.dart new file mode 100644 index 00000000000..a9dcb334d87 --- /dev/null +++ b/packages/flutter_tools/lib/src/linux/native_assets.dart @@ -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 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 nativeAssetPaths = await dryRunNativeAssetsLinuxInternal( + fileSystem, + projectUri, + flutterTester, + buildRunner, + ); + final Uri nativeAssetsUri = await writeNativeAssetsYaml( + nativeAssetPaths, + buildUri_, + fileSystem, + ); + return nativeAssetsUri; +} + +Future> 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 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 assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath); + final Iterable 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 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([], yamlParentDirectory ?? buildUri_, fileSystem); + return (nativeAssetsYaml, []); + } + + 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 nativeAssets = result.assets; + final Set dependencies = result.dependencies.toSet(); + ensureNoLinkModeStatic(nativeAssets); + globals.logger.printTrace('Building native assets for $target done.'); + final Uri? absolutePath = flutterTester ? buildUri_ : null; + final Map 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 _assetTargetLocations( + List nativeAssets, + Uri? absolutePath, +) => + { + 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 _copyNativeAssetsLinux( + Uri buildUri, + Map 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 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 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(['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 binaryPaths = {}; + for (final String binary in [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], + ); +} diff --git a/packages/flutter_tools/lib/src/migrations/cmake_native_assets_migration.dart b/packages/flutter_tools/lib/src/migrations/cmake_native_assets_migration.dart new file mode 100644 index 00000000000..fe9ba86a035 --- /dev/null +++ b/packages/flutter_tools/lib/src/migrations/cmake_native_assets_migration.dart @@ -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); + } + } +} diff --git a/packages/flutter_tools/lib/src/native_assets.dart b/packages/flutter_tools/lib/src/native_assets.dart index 3253d291915..b269fd876ed 100644 --- a/packages/flutter_tools/lib/src/native_assets.dart +++ b/packages/flutter_tools/lib/src/native_assets.dart @@ -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 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 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 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 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; diff --git a/packages/flutter_tools/lib/src/test/test_compiler.dart b/packages/flutter_tools/lib/src/test/test_compiler.dart index 387795e521c..600478faa3e 100644 --- a/packages/flutter_tools/lib/src/test/test_compiler.dart +++ b/packages/flutter_tools/lib/src/test/test_compiler.dart @@ -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,28 +169,40 @@ class TestCompiler { } Uri? nativeAssetsYaml; - final Uri projectUri = FlutterProject.current().directory.uri; - final NativeAssetsBuildRunner buildRunner = NativeAssetsBuildRunnerImpl( - projectUri, - buildInfo.packageConfig, - globals.fs, - globals.logger, - ); - if (globals.platform.isMacOS) { - (nativeAssetsYaml, _) = await buildNativeAssetsMacOS( - buildMode: BuildMode.debug, - projectUri: projectUri, - flutterTester: true, - fileSystem: globals.fs, - buildRunner: buildRunner, - ); + if (!buildInfo.buildNativeAssets) { + nativeAssetsYaml = null; } else { - await ensureNoNativeAssetsOrOsIsSupported( + final Uri projectUri = FlutterProject.current().directory.uri; + final NativeAssetsBuildRunner buildRunner = NativeAssetsBuildRunnerImpl( projectUri, - const LocalPlatform().operatingSystem, + buildInfo.packageConfig, globals.fs, - buildRunner, + globals.logger, ); + if (globals.platform.isMacOS) { + (nativeAssetsYaml, _) = await buildNativeAssetsMacOS( + buildMode: buildInfo.mode, + projectUri: projectUri, + flutterTester: true, + fileSystem: globals.fs, + buildRunner: buildRunner, + ); + } else if (globals.platform.isLinux) { + (nativeAssetsYaml, _) = await buildNativeAssetsLinux( + buildMode: buildInfo.mode, + projectUri: projectUri, + flutterTester: true, + fileSystem: globals.fs, + buildRunner: buildRunner, + ); + } else { + await ensureNoNativeAssetsOrOsIsSupported( + projectUri, + const LocalPlatform().operatingSystem, + globals.fs, + buildRunner, + ); + } } final CompilerOutput? compilerOutput = await compiler!.recompile( diff --git a/packages/flutter_tools/templates/app_shared/linux.tmpl/CMakeLists.txt.tmpl b/packages/flutter_tools/templates/app_shared/linux.tmpl/CMakeLists.txt.tmpl index a4fc907bec3..580180401d1 100644 --- a/packages/flutter_tools/templates/app_shared/linux.tmpl/CMakeLists.txt.tmpl +++ b/packages/flutter_tools/templates/app_shared/linux.tmpl/CMakeLists.txt.tmpl @@ -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") diff --git a/packages/flutter_tools/test/general.shard/linux/native_assets_test.dart b/packages/flutter_tools/test/general.shard/linux/native_assets_test.dart new file mode 100644 index 00000000000..34d2fc552f8 --- /dev/null +++ b/packages/flutter_tools/test/general.shard/linux/native_assets_test.dart @@ -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: {}, + 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: { + 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: { + 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: { + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + await dryRunNativeAssetsMultipeOSes( + projectUri: projectUri, + fileSystem: fileSystem, + targetPlatforms: [ + 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: { + 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('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: { + 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('bar', projectUri), + ], + dryRunResult: FakeNativeAssetsBuilderResult( + assets: [ + 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: { + 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('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: { + 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('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 [false, true]) { + String testName = ''; + if (flutterTester) { + testName += ' flutter tester'; + } + testUsingContext('build with assets$testName', overrides: { + 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('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + assets: [ + 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([ + '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: { + 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('bar', projectUri), + ], + dryRunResult: FakeNativeAssetsBuilderResult( + assets: [ + 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: { + FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), + ProcessManager: () => FakeProcessManager.list( + [ + const FakeCommand( + command: ['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')); + }); +} diff --git a/packages/flutter_tools/test/general.shard/migrations/cmake_project_migration_test.dart b/packages/flutter_tools/test/general.shard/migrations/cmake_project_migration_test.dart index 36ee8d7824a..8689febb15a 100644 --- a/packages/flutter_tools/test/general.shard/migrations/cmake_project_migration_test.dart +++ b/packages/flutter_tools/test/general.shard/migrations/cmake_project_migration_test.dart @@ -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.')); + }); + }); }); } diff --git a/packages/flutter_tools/test/integration.shard/native_assets_test.dart b/packages/flutter_tools/test/integration.shard/native_assets_test.dart index e539e74a27a..c6824fb3577 100644 --- a/packages/flutter_tools/test/integration.shard/native_assets_test.dart +++ b/packages/flutter_tools/test/integration.shard/native_assets_test.dart @@ -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); } diff --git a/packages/flutter_tools/test/integration.shard/transition_test_utils.dart b/packages/flutter_tools/test/integration.shard/transition_test_utils.dart index 9c349176003..fa3a7f3b9e5 100644 --- a/packages/flutter_tools/test/integration.shard/transition_test_utils.dart +++ b/packages/flutter_tools/test/integration.shard/transition_test_utils.dart @@ -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 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( - [flutterBin, ...arguments], + [ + // 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 logs = [];