Unverified Commit ff4a0f67 authored by Daco Harkes's avatar Daco Harkes Committed by GitHub

Native assets support for Windows (#134203)

Support for FFI calls with `@Native external` functions through Native assets on Windows. 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 Windows.

Mainly follows the design of https://github.com/flutter/flutter/pull/134031.

Specifically for Windows in this PR is the logic for finding the compiler `cl.exe` and environment variables that contain the paths to the Windows headers `vcvars.bat` based on `vswhere.exe`.
parent 3e7c388e
...@@ -16,6 +16,7 @@ import '../../linux/native_assets.dart'; ...@@ -16,6 +16,7 @@ import '../../linux/native_assets.dart';
import '../../macos/native_assets.dart'; import '../../macos/native_assets.dart';
import '../../macos/xcode.dart'; import '../../macos/xcode.dart';
import '../../native_assets.dart'; import '../../native_assets.dart';
import '../../windows/native_assets.dart';
import '../build_system.dart'; import '../build_system.dart';
import '../depfile.dart'; import '../depfile.dart';
import '../exceptions.dart'; import '../exceptions.dart';
...@@ -134,6 +135,20 @@ class NativeAssets extends Target { ...@@ -134,6 +135,20 @@ class NativeAssets extends Target {
fileSystem: fileSystem, fileSystem: fileSystem,
buildRunner: buildRunner, buildRunner: buildRunner,
); );
case TargetPlatform.windows_x64:
final String? environmentBuildMode = environment.defines[kBuildMode];
if (environmentBuildMode == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
(_, dependencies) = await buildNativeAssetsWindows(
targetPlatform: targetPlatform,
buildMode: buildMode,
projectUri: projectUri,
yamlParentDirectory: environment.buildDir.uri,
fileSystem: fileSystem,
buildRunner: buildRunner,
);
case TargetPlatform.tester: case TargetPlatform.tester:
if (const LocalPlatform().isMacOS) { if (const LocalPlatform().isMacOS) {
(_, dependencies) = await buildNativeAssetsMacOS( (_, dependencies) = await buildNativeAssetsMacOS(
...@@ -154,6 +169,15 @@ class NativeAssets extends Target { ...@@ -154,6 +169,15 @@ class NativeAssets extends Target {
buildRunner: buildRunner, buildRunner: buildRunner,
flutterTester: true, flutterTester: true,
); );
} else if (const LocalPlatform().isWindows) {
(_, dependencies) = await buildNativeAssetsWindows(
buildMode: BuildMode.debug,
projectUri: projectUri,
yamlParentDirectory: environment.buildDir.uri,
fileSystem: fileSystem,
buildRunner: buildRunner,
flutterTester: true,
);
} else { } else {
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757 // TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
// Write the file we claim to have in the [outputs]. // Write the file we claim to have in the [outputs].
...@@ -168,7 +192,6 @@ class NativeAssets extends Target { ...@@ -168,7 +192,6 @@ class NativeAssets extends Target {
case TargetPlatform.fuchsia_arm64: case TargetPlatform.fuchsia_arm64:
case TargetPlatform.fuchsia_x64: case TargetPlatform.fuchsia_x64:
case TargetPlatform.web_javascript: case TargetPlatform.web_javascript:
case TargetPlatform.windows_x64:
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757 // TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
// Write the file we claim to have in the [outputs]. // Write the file we claim to have in the [outputs].
await writeNativeAssetsYaml(<Asset>[], environment.buildDir.uri, fileSystem); await writeNativeAssetsYaml(<Asset>[], environment.buildDir.uri, fileSystem);
......
...@@ -27,7 +27,7 @@ Future<Uri?> dryRunNativeAssetsIOS({ ...@@ -27,7 +27,7 @@ Future<Uri?> dryRunNativeAssetsIOS({
return null; return null;
} }
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, OS.iOS); final Uri buildUri = nativeAssetsBuildUri(projectUri, OS.iOS);
final Iterable<Asset> assetTargetLocations = await dryRunNativeAssetsIOSInternal( final Iterable<Asset> assetTargetLocations = await dryRunNativeAssetsIOSInternal(
fileSystem, fileSystem,
projectUri, projectUri,
...@@ -35,7 +35,7 @@ Future<Uri?> dryRunNativeAssetsIOS({ ...@@ -35,7 +35,7 @@ Future<Uri?> dryRunNativeAssetsIOS({
); );
final Uri nativeAssetsUri = await writeNativeAssetsYaml( final Uri nativeAssetsUri = await writeNativeAssetsYaml(
assetTargetLocations, assetTargetLocations,
buildUri_, buildUri,
fileSystem, fileSystem,
); );
return nativeAssetsUri; return nativeAssetsUri;
...@@ -46,17 +46,17 @@ Future<Iterable<Asset>> dryRunNativeAssetsIOSInternal( ...@@ -46,17 +46,17 @@ Future<Iterable<Asset>> dryRunNativeAssetsIOSInternal(
Uri projectUri, Uri projectUri,
NativeAssetsBuildRunner buildRunner, NativeAssetsBuildRunner buildRunner,
) async { ) async {
const OS targetOs = OS.iOS; const OS targetOS = OS.iOS;
globals.logger.printTrace('Dry running native assets for $targetOs.'); globals.logger.printTrace('Dry running native assets for $targetOS.');
final List<Asset> nativeAssets = (await buildRunner.dryRun( final List<Asset> nativeAssets = (await buildRunner.dryRun(
linkModePreference: LinkModePreference.dynamic, linkModePreference: LinkModePreference.dynamic,
targetOs: targetOs, targetOS: targetOS,
workingDirectory: projectUri, workingDirectory: projectUri,
includeParentEnvironment: true, includeParentEnvironment: true,
)) ))
.assets; .assets;
ensureNoLinkModeStatic(nativeAssets); ensureNoLinkModeStatic(nativeAssets);
globals.logger.printTrace('Dry running native assets for $targetOs done.'); globals.logger.printTrace('Dry running native assets for $targetOS done.');
final Iterable<Asset> assetTargetLocations = _assetTargetLocations(nativeAssets).values; final Iterable<Asset> assetTargetLocations = _assetTargetLocations(nativeAssets).values;
return assetTargetLocations; return assetTargetLocations;
} }
...@@ -80,8 +80,8 @@ Future<List<Uri>> buildNativeAssetsIOS({ ...@@ -80,8 +80,8 @@ Future<List<Uri>> buildNativeAssetsIOS({
final List<Target> targets = darwinArchs.map(_getNativeTarget).toList(); final List<Target> targets = darwinArchs.map(_getNativeTarget).toList();
final native_assets_cli.BuildMode buildModeCli = nativeAssetsBuildMode(buildMode); final native_assets_cli.BuildMode buildModeCli = nativeAssetsBuildMode(buildMode);
const OS targetOs = OS.iOS; const OS targetOS = OS.iOS;
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs); final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
final IOSSdk iosSdk = _getIOSSdk(environmentType); final IOSSdk iosSdk = _getIOSSdk(environmentType);
globals.logger.printTrace('Building native assets for $targets $buildModeCli.'); globals.logger.printTrace('Building native assets for $targets $buildModeCli.');
...@@ -104,7 +104,7 @@ Future<List<Uri>> buildNativeAssetsIOS({ ...@@ -104,7 +104,7 @@ Future<List<Uri>> buildNativeAssetsIOS({
globals.logger.printTrace('Building native assets for $targets done.'); globals.logger.printTrace('Building native assets for $targets done.');
final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets); final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets);
await copyNativeAssetsMacOSHost( await copyNativeAssetsMacOSHost(
buildUri_, buildUri,
fatAssetTargetLocations, fatAssetTargetLocations,
codesignIdentity, codesignIdentity,
buildMode, buildMode,
......
...@@ -2,9 +2,7 @@ ...@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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' hide BuildMode;
import 'package:native_assets_cli/native_assets_cli.dart' as native_assets_cli;
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
...@@ -22,24 +20,14 @@ Future<Uri?> dryRunNativeAssetsLinux({ ...@@ -22,24 +20,14 @@ Future<Uri?> dryRunNativeAssetsLinux({
required Uri projectUri, required Uri projectUri,
bool flutterTester = false, bool flutterTester = false,
required FileSystem fileSystem, required FileSystem fileSystem,
}) async { }) {
if (!await nativeBuildRequired(buildRunner)) { return dryRunNativeAssetsSingleArchitecture(
return null; buildRunner: buildRunner,
} projectUri: projectUri,
flutterTester: flutterTester,
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, OS.linux); fileSystem: fileSystem,
final Iterable<Asset> nativeAssetPaths = await dryRunNativeAssetsLinuxInternal( os: OS.linux,
fileSystem,
projectUri,
flutterTester,
buildRunner,
);
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
nativeAssetPaths,
buildUri_,
fileSystem,
); );
return nativeAssetsUri;
} }
Future<Iterable<Asset>> dryRunNativeAssetsLinuxInternal( Future<Iterable<Asset>> dryRunNativeAssetsLinuxInternal(
...@@ -47,33 +35,16 @@ Future<Iterable<Asset>> dryRunNativeAssetsLinuxInternal( ...@@ -47,33 +35,16 @@ Future<Iterable<Asset>> dryRunNativeAssetsLinuxInternal(
Uri projectUri, Uri projectUri,
bool flutterTester, bool flutterTester,
NativeAssetsBuildRunner buildRunner, NativeAssetsBuildRunner buildRunner,
) async { ) {
const OS targetOs = OS.linux; return dryRunNativeAssetsSingleArchitectureInternal(
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs); fileSystem,
projectUri,
globals.logger.printTrace('Dry running native assets for $targetOs.'); flutterTester,
final List<Asset> nativeAssets = (await buildRunner.dryRun( buildRunner,
linkModePreference: LinkModePreference.dynamic, OS.linux,
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({ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsLinux({
required NativeAssetsBuildRunner buildRunner, required NativeAssetsBuildRunner buildRunner,
TargetPlatform? targetPlatform, TargetPlatform? targetPlatform,
...@@ -82,127 +53,16 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsLinux({ ...@@ -82,127 +53,16 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsLinux({
bool flutterTester = false, bool flutterTester = false,
Uri? yamlParentDirectory, Uri? yamlParentDirectory,
required FileSystem fileSystem, required FileSystem fileSystem,
}) async { }) {
const OS targetOs = OS.linux; return buildNativeAssetsSingleArchitecture(
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs); buildRunner: buildRunner,
final Directory buildDir = fileSystem.directory(buildUri_); targetPlatform: targetPlatform,
if (!await buildDir.exists()) { projectUri: projectUri,
// CMake requires the folder to exist to do copying. buildMode: buildMode,
await buildDir.create(recursive: true); flutterTester: flutterTester,
} yamlParentDirectory: yamlParentDirectory,
if (!await nativeBuildRequired(buildRunner)) { fileSystem: fileSystem,
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. /// Flutter expects `clang++` to be on the path on Linux hosts.
......
...@@ -27,9 +27,9 @@ Future<Uri?> dryRunNativeAssetsMacOS({ ...@@ -27,9 +27,9 @@ Future<Uri?> dryRunNativeAssetsMacOS({
return null; return null;
} }
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, OS.macOS); final Uri buildUri = nativeAssetsBuildUri(projectUri, OS.macOS);
final Iterable<Asset> nativeAssetPaths = await dryRunNativeAssetsMacOSInternal(fileSystem, projectUri, flutterTester, buildRunner); final Iterable<Asset> nativeAssetPaths = await dryRunNativeAssetsMacOSInternal(fileSystem, projectUri, flutterTester, buildRunner);
final Uri nativeAssetsUri = await writeNativeAssetsYaml(nativeAssetPaths, buildUri_, fileSystem); final Uri nativeAssetsUri = await writeNativeAssetsYaml(nativeAssetPaths, buildUri, fileSystem);
return nativeAssetsUri; return nativeAssetsUri;
} }
...@@ -39,20 +39,20 @@ Future<Iterable<Asset>> dryRunNativeAssetsMacOSInternal( ...@@ -39,20 +39,20 @@ Future<Iterable<Asset>> dryRunNativeAssetsMacOSInternal(
bool flutterTester, bool flutterTester,
NativeAssetsBuildRunner buildRunner, NativeAssetsBuildRunner buildRunner,
) async { ) async {
const OS targetOs = OS.macOS; const OS targetOS = OS.macOS;
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs); final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
globals.logger.printTrace('Dry running native assets for $targetOs.'); globals.logger.printTrace('Dry running native assets for $targetOS.');
final List<Asset> nativeAssets = (await buildRunner.dryRun( final List<Asset> nativeAssets = (await buildRunner.dryRun(
linkModePreference: LinkModePreference.dynamic, linkModePreference: LinkModePreference.dynamic,
targetOs: targetOs, targetOS: targetOS,
workingDirectory: projectUri, workingDirectory: projectUri,
includeParentEnvironment: true, includeParentEnvironment: true,
)) ))
.assets; .assets;
ensureNoLinkModeStatic(nativeAssets); ensureNoLinkModeStatic(nativeAssets);
globals.logger.printTrace('Dry running native assets for $targetOs done.'); globals.logger.printTrace('Dry running native assets for $targetOS done.');
final Uri? absolutePath = flutterTester ? buildUri_ : null; final Uri? absolutePath = flutterTester ? buildUri : null;
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath); final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath);
final Iterable<Asset> nativeAssetPaths = assetTargetLocations.values; final Iterable<Asset> nativeAssetPaths = assetTargetLocations.values;
return nativeAssetPaths; return nativeAssetPaths;
...@@ -75,10 +75,10 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsMacOS({ ...@@ -75,10 +75,10 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsMacOS({
Uri? yamlParentDirectory, Uri? yamlParentDirectory,
required FileSystem fileSystem, required FileSystem fileSystem,
}) async { }) async {
const OS targetOs = OS.macOS; const OS targetOS = OS.macOS;
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs); final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
if (!await nativeBuildRequired(buildRunner)) { if (!await nativeBuildRequired(buildRunner)) {
final Uri nativeAssetsYaml = await writeNativeAssetsYaml(<Asset>[], yamlParentDirectory ?? buildUri_, fileSystem); final Uri nativeAssetsYaml = await writeNativeAssetsYaml(<Asset>[], yamlParentDirectory ?? buildUri, fileSystem);
return (nativeAssetsYaml, <Uri>[]); return (nativeAssetsYaml, <Uri>[]);
} }
...@@ -102,11 +102,11 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsMacOS({ ...@@ -102,11 +102,11 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsMacOS({
} }
ensureNoLinkModeStatic(nativeAssets); ensureNoLinkModeStatic(nativeAssets);
globals.logger.printTrace('Building native assets for $targets done.'); globals.logger.printTrace('Building native assets for $targets done.');
final Uri? absolutePath = flutterTester ? buildUri_ : null; final Uri? absolutePath = flutterTester ? buildUri : null;
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath); final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath);
final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets, absolutePath); final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets, absolutePath);
await copyNativeAssetsMacOSHost(buildUri_, fatAssetTargetLocations, codesignIdentity, buildMode, fileSystem); await copyNativeAssetsMacOSHost(buildUri, fatAssetTargetLocations, codesignIdentity, buildMode, fileSystem);
final Uri nativeAssetsUri = await writeNativeAssetsYaml(assetTargetLocations.values, yamlParentDirectory ?? buildUri_, fileSystem); final Uri nativeAssetsUri = await writeNativeAssetsYaml(assetTargetLocations.values, yamlParentDirectory ?? buildUri, fileSystem);
return (nativeAssetsUri, dependencies.toList()); return (nativeAssetsUri, dependencies.toList());
} }
......
...@@ -41,8 +41,8 @@ class CmakeNativeAssetsMigration extends ProjectMigrator { ...@@ -41,8 +41,8 @@ class CmakeNativeAssetsMigration extends ProjectMigrator {
# Copy the native assets provided by the build.dart from all packages. # Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "\${PROJECT_BUILD_DIR}native_assets/$os/") set(NATIVE_ASSETS_DIR "\${PROJECT_BUILD_DIR}native_assets/$os/")
install(DIRECTORY "\${NATIVE_ASSETS_DIR}" install(DIRECTORY "\${NATIVE_ASSETS_DIR}"
DESTINATION "\${INSTALL_BUNDLE_LIB_DIR}" DESTINATION "\${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
'''; ''';
// Insert the new command after the bundled libraries loop. // Insert the new command after the bundled libraries loop.
......
...@@ -20,6 +20,7 @@ import '../linux/native_assets.dart'; ...@@ -20,6 +20,7 @@ import '../linux/native_assets.dart';
import '../macos/native_assets.dart'; import '../macos/native_assets.dart';
import '../native_assets.dart'; import '../native_assets.dart';
import '../project.dart'; import '../project.dart';
import '../windows/native_assets.dart';
import 'test_time_recorder.dart'; import 'test_time_recorder.dart';
/// A request to the [TestCompiler] for recompilation. /// A request to the [TestCompiler] for recompilation.
...@@ -195,6 +196,14 @@ class TestCompiler { ...@@ -195,6 +196,14 @@ class TestCompiler {
fileSystem: globals.fs, fileSystem: globals.fs,
buildRunner: buildRunner, buildRunner: buildRunner,
); );
} else if (globals.platform.isWindows) {
(nativeAssetsYaml, _) = await buildNativeAssetsWindows(
buildMode: buildInfo.mode,
projectUri: projectUri,
flutterTester: true,
fileSystem: globals.fs,
buildRunner: buildRunner,
);
} else { } else {
await ensureNoNativeAssetsOrOsIsSupported( await ensureNoNativeAssetsOrOsIsSupported(
projectUri, projectUri,
......
...@@ -18,6 +18,7 @@ import '../convert.dart'; ...@@ -18,6 +18,7 @@ import '../convert.dart';
import '../flutter_plugins.dart'; import '../flutter_plugins.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../migrations/cmake_custom_command_migration.dart'; import '../migrations/cmake_custom_command_migration.dart';
import '../migrations/cmake_native_assets_migration.dart';
import 'migrations/build_architecture_migration.dart'; import 'migrations/build_architecture_migration.dart';
import 'migrations/show_window_migration.dart'; import 'migrations/show_window_migration.dart';
import 'migrations/version_migration.dart'; import 'migrations/version_migration.dart';
...@@ -61,6 +62,7 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, { ...@@ -61,6 +62,7 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {
final List<ProjectMigrator> migrators = <ProjectMigrator>[ final List<ProjectMigrator> migrators = <ProjectMigrator>[
CmakeCustomCommandMigration(windowsProject, globals.logger), CmakeCustomCommandMigration(windowsProject, globals.logger),
CmakeNativeAssetsMigration(windowsProject, 'windows', globals.logger),
VersionMigration(windowsProject, globals.logger), VersionMigration(windowsProject, globals.logger),
ShowWindowMigration(windowsProject, globals.logger), ShowWindowMigration(windowsProject, globals.logger),
BuildArchitectureMigration(windowsProject, buildDirectory, globals.logger), BuildArchitectureMigration(windowsProject, buildDirectory, globals.logger),
......
// 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_cli/native_assets_cli.dart' hide BuildMode;
import '../base/file_system.dart';
import '../build_info.dart';
import '../globals.dart' as globals;
import '../native_assets.dart';
import 'visual_studio.dart';
/// Dry run the native builds.
///
/// This does not build native assets, it only simulates what the final paths
/// of all assets will be so that this can be embedded in the kernel file.
Future<Uri?> dryRunNativeAssetsWindows({
required NativeAssetsBuildRunner buildRunner,
required Uri projectUri,
bool flutterTester = false,
required FileSystem fileSystem,
}) {
return dryRunNativeAssetsSingleArchitecture(
buildRunner: buildRunner,
projectUri: projectUri,
flutterTester: flutterTester,
fileSystem: fileSystem,
os: OS.windows,
);
}
Future<Iterable<Asset>> dryRunNativeAssetsWindowsInternal(
FileSystem fileSystem,
Uri projectUri,
bool flutterTester,
NativeAssetsBuildRunner buildRunner,
) {
return dryRunNativeAssetsSingleArchitectureInternal(
fileSystem,
projectUri,
flutterTester,
buildRunner,
OS.windows,
);
}
Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)>
buildNativeAssetsWindows({
required NativeAssetsBuildRunner buildRunner,
TargetPlatform? targetPlatform,
required Uri projectUri,
required BuildMode buildMode,
bool flutterTester = false,
Uri? yamlParentDirectory,
required FileSystem fileSystem,
}) {
return buildNativeAssetsSingleArchitecture(
buildRunner: buildRunner,
targetPlatform: targetPlatform,
projectUri: projectUri,
buildMode: buildMode,
flutterTester: flutterTester,
yamlParentDirectory: yamlParentDirectory,
fileSystem: fileSystem,
);
}
Future<CCompilerConfig> cCompilerConfigWindows() async {
final VisualStudio visualStudio = VisualStudio(
fileSystem: globals.fs,
platform: globals.platform,
logger: globals.logger,
processManager: globals.processManager,
);
return CCompilerConfig(
cc: _toOptionalFileUri(visualStudio.clPath),
ld: _toOptionalFileUri(visualStudio.linkPath),
ar: _toOptionalFileUri(visualStudio.libPath),
envScript: _toOptionalFileUri(visualStudio.vcvarsPath),
envScriptArgs: <String>[],
);
}
Uri? _toOptionalFileUri(String? string) {
if (string == null) {
return null;
}
return Uri.file(string);
}
...@@ -184,6 +184,60 @@ class VisualStudio { ...@@ -184,6 +184,60 @@ class VisualStudio {
} }
} }
/// The path to cl.exe, or null if no Visual Studio installation has
/// the components necessary to build.
String? get clPath {
return _getMsvcBinPath('cl.exe');
}
/// The path to lib.exe, or null if no Visual Studio installation has
/// the components necessary to build.
String? get libPath {
return _getMsvcBinPath('lib.exe');
}
/// The path to link.exe, or null if no Visual Studio installation has
/// the components necessary to build.
String? get linkPath {
return _getMsvcBinPath('link.exe');
}
String? _getMsvcBinPath(String executable) {
final VswhereDetails? details = _bestVisualStudioDetails;
if (details == null || !details.isUsable || details.installationPath == null || details.msvcVersion == null) {
return null;
}
return _fileSystem.path.joinAll(<String>[
details.installationPath!,
'VC',
'Tools',
'MSVC',
details.msvcVersion!,
'bin',
'Hostx64',
'x64',
executable,
]);
}
/// The path to vcvars64.exe, or null if no Visual Studio installation has
/// the components necessary to build.
String? get vcvarsPath {
final VswhereDetails? details = _bestVisualStudioDetails;
if (details == null || !details.isUsable || details.installationPath == null) {
return null;
}
return _fileSystem.path.joinAll(<String>[
details.installationPath!,
'VC',
'Auxiliary',
'Build',
'vcvars64.bat',
]);
}
/// The major version of the Visual Studio install, as an integer. /// The major version of the Visual Studio install, as an integer.
int? get _majorVersion => fullVersion != null ? int.tryParse(fullVersion!.split('.')[0]) : null; int? get _majorVersion => fullVersion != null ? int.tryParse(fullVersion!.split('.')[0]) : null;
...@@ -301,7 +355,12 @@ class VisualStudio { ...@@ -301,7 +355,12 @@ class VisualStudio {
if (whereResult.exitCode == 0) { if (whereResult.exitCode == 0) {
final List<Map<String, dynamic>>? installations = _tryDecodeVswhereJson(whereResult.stdout); final List<Map<String, dynamic>>? installations = _tryDecodeVswhereJson(whereResult.stdout);
if (installations != null && installations.isNotEmpty) { if (installations != null && installations.isNotEmpty) {
return VswhereDetails.fromJson(validateRequirements, installations[0]); final String? msvcVersion = _findMsvcVersion(installations);
return VswhereDetails.fromJson(
validateRequirements,
installations[0],
msvcVersion,
);
} }
} }
} on ArgumentError { } on ArgumentError {
...@@ -312,6 +371,28 @@ class VisualStudio { ...@@ -312,6 +371,28 @@ class VisualStudio {
return null; return null;
} }
String? _findMsvcVersion(List<Map<String, dynamic>> installations) {
final String? installationPath = installations[0]['installationPath'] as String?;
String? msvcVersion;
if (installationPath != null) {
final Directory installationDir = _fileSystem.directory(installationPath);
final Directory msvcDir = installationDir
.childDirectory('VC')
.childDirectory('Tools')
.childDirectory('MSVC');
if (msvcDir.existsSync()) {
final Iterable<Directory> msvcVersionDirs = msvcDir.listSync().whereType<Directory>();
if (msvcVersionDirs.isEmpty) {
return null;
}
msvcVersion = msvcVersionDirs.last.uri.pathSegments
.where((String e) => e.isNotEmpty)
.last;
}
}
return msvcVersion;
}
List<Map<String, dynamic>>? _tryDecodeVswhereJson(String vswhereJson) { List<Map<String, dynamic>>? _tryDecodeVswhereJson(String vswhereJson) {
List<dynamic>? result; List<dynamic>? result;
FormatException? originalError; FormatException? originalError;
...@@ -443,12 +524,14 @@ class VswhereDetails { ...@@ -443,12 +524,14 @@ class VswhereDetails {
required this.isRebootRequired, required this.isRebootRequired,
required this.isPrerelease, required this.isPrerelease,
required this.catalogDisplayVersion, required this.catalogDisplayVersion,
required this.msvcVersion,
}); });
/// Create a `VswhereDetails` from the JSON output of vswhere.exe. /// Create a `VswhereDetails` from the JSON output of vswhere.exe.
factory VswhereDetails.fromJson( factory VswhereDetails.fromJson(
bool meetsRequirements, bool meetsRequirements,
Map<String, dynamic> details Map<String, dynamic> details,
String? msvcVersion,
) { ) {
final Map<String, dynamic>? catalog = details['catalog'] as Map<String, dynamic>?; final Map<String, dynamic>? catalog = details['catalog'] as Map<String, dynamic>?;
...@@ -467,6 +550,8 @@ class VswhereDetails { ...@@ -467,6 +550,8 @@ class VswhereDetails {
// contain replacement characters. // contain replacement characters.
displayName: details['displayName'] as String?, displayName: details['displayName'] as String?,
catalogDisplayVersion: catalog == null ? null : catalog['productDisplayVersion'] as String?, catalogDisplayVersion: catalog == null ? null : catalog['productDisplayVersion'] as String?,
msvcVersion: msvcVersion,
); );
} }
...@@ -511,6 +596,9 @@ class VswhereDetails { ...@@ -511,6 +596,9 @@ class VswhereDetails {
/// The user-friendly version. /// The user-friendly version.
final String? catalogDisplayVersion; final String? catalogDisplayVersion;
/// The MSVC versions.
final String? msvcVersion;
/// Checks if the Visual Studio installation can be used by Flutter. /// Checks if the Visual Studio installation can be used by Flutter.
/// ///
/// Returns false if the installation has issues the user must resolve. /// Returns false if the installation has issues the user must resolve.
......
...@@ -91,6 +91,12 @@ if(PLUGIN_BUNDLED_LIBRARIES) ...@@ -91,6 +91,12 @@ if(PLUGIN_BUNDLED_LIBRARIES)
COMPONENT Runtime) COMPONENT Runtime)
endif() endif()
# Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/")
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 # Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install. # from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets") set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
......
...@@ -10,7 +10,7 @@ dependencies: ...@@ -10,7 +10,7 @@ dependencies:
cli_config: ^0.1.1 cli_config: ^0.1.1
logging: ^1.1.1 logging: ^1.1.1
native_assets_cli: ^0.2.0 native_assets_cli: ^0.2.0
native_toolchain_c: ^0.2.0 native_toolchain_c: ^0.2.3
dev_dependencies: dev_dependencies:
ffi: ^2.0.2 ffi: ^2.0.2
......
...@@ -49,7 +49,7 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner { ...@@ -49,7 +49,7 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner {
Future<native_assets_builder.DryRunResult> dryRun({ Future<native_assets_builder.DryRunResult> dryRun({
required bool includeParentEnvironment, required bool includeParentEnvironment,
required LinkModePreference linkModePreference, required LinkModePreference linkModePreference,
required OS targetOs, required OS targetOS,
required Uri workingDirectory, required Uri workingDirectory,
}) async { }) async {
dryRunInvocations++; dryRunInvocations++;
......
...@@ -142,6 +142,13 @@ void main() { ...@@ -142,6 +142,13 @@ void main() {
), ),
), ),
); );
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for ios.',
'Dry running native assets for ios done.',
]),
);
expect( expect(
nativeAssetsYaml, nativeAssetsYaml,
projectUri.resolve('build/native_assets/ios/native_assets.yaml'), projectUri.resolve('build/native_assets/ios/native_assets.yaml'),
...@@ -259,13 +266,20 @@ void main() { ...@@ -259,13 +266,20 @@ void main() {
Asset( Asset(
id: 'package:bar/bar.dart', id: 'package:bar/bar.dart',
linkMode: LinkMode.dynamic, linkMode: LinkMode.dynamic,
target: native_assets_cli.Target.macOSArm64, target: native_assets_cli.Target.iOSArm64,
path: AssetAbsolutePath(Uri.file('bar.dylib')), path: AssetAbsolutePath(Uri.file('bar.dylib')),
), ),
], ],
), ),
), ),
); );
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Building native assets for [ios_arm64] debug.',
'Building native assets for [ios_arm64] done.',
]),
);
expect( expect(
environment.buildDir.childFile('native_assets.yaml'), environment.buildDir.childFile('native_assets.yaml'),
exists, exists,
......
...@@ -180,6 +180,13 @@ void main() { ...@@ -180,6 +180,13 @@ void main() {
), ),
), ),
); );
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for linux.',
'Dry running native assets for linux done.',
]),
);
expect( expect(
nativeAssetsYaml, nativeAssetsYaml,
projectUri.resolve('build/native_assets/linux/native_assets.yaml'), projectUri.resolve('build/native_assets/linux/native_assets.yaml'),
...@@ -222,6 +229,7 @@ void main() { ...@@ -222,6 +229,7 @@ void main() {
await packageConfig.parent.create(); await packageConfig.parent.create();
await packageConfig.create(); await packageConfig.create();
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux( final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux(
targetPlatform: TargetPlatform.linux_x64,
projectUri: projectUri, projectUri: projectUri,
buildMode: BuildMode.debug, buildMode: BuildMode.debug,
fileSystem: fileSystem, fileSystem: fileSystem,
...@@ -257,16 +265,14 @@ void main() { ...@@ -257,16 +265,14 @@ void main() {
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(), ProcessManager: () => FakeProcessManager.empty(),
}, () async { }, () async {
if (const LocalPlatform().isWindows) { final File packageConfig = environment.projectDir.childDirectory('.dart_tool').childFile('package_config.json');
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.parent.create();
await packageConfig.create(); await packageConfig.create();
final File dylibAfterCompiling = fileSystem.file('libbar.so'); final File dylibAfterCompiling = fileSystem.file('libbar.so');
// The mock doesn't create the file, so create it here. // The mock doesn't create the file, so create it here.
await dylibAfterCompiling.create(); await dylibAfterCompiling.create();
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux( final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux(
targetPlatform: TargetPlatform.linux_x64,
projectUri: projectUri, projectUri: projectUri,
buildMode: BuildMode.debug, buildMode: BuildMode.debug,
fileSystem: fileSystem, fileSystem: fileSystem,
...@@ -287,6 +293,13 @@ void main() { ...@@ -287,6 +293,13 @@ void main() {
), ),
), ),
); );
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Building native assets for linux_x64 debug.',
'Building native assets for linux_x64 done.',
]),
);
expect( expect(
nativeAssetsYaml, nativeAssetsYaml,
projectUri.resolve('build/native_assets/linux/native_assets.yaml'), projectUri.resolve('build/native_assets/linux/native_assets.yaml'),
...@@ -297,7 +310,7 @@ void main() { ...@@ -297,7 +310,7 @@ void main() {
'package:bar/bar.dart', 'package:bar/bar.dart',
if (flutterTester) if (flutterTester)
// Tests run on host system, so the have the full path on the system. // Tests run on host system, so the have the full path on the system.
'- ${projectUri.resolve('/build/native_assets/linux/libbar.so').toFilePath()}' '- ${projectUri.resolve('build/native_assets/linux/libbar.so').toFilePath()}'
else else
// Apps are a bundle with the dylibs on their dlopen path. // Apps are a bundle with the dylibs on their dlopen path.
'- libbar.so', '- libbar.so',
...@@ -365,7 +378,6 @@ void main() { ...@@ -365,7 +378,6 @@ void main() {
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
}, () async { }, () async {
if (!const LocalPlatform().isLinux) { if (!const LocalPlatform().isLinux) {
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
return; return;
} }
......
...@@ -161,6 +161,13 @@ void main() { ...@@ -161,6 +161,13 @@ void main() {
), ),
), ),
); );
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for macos.',
'Dry running native assets for macos done.',
]),
);
expect( expect(
nativeAssetsYaml, nativeAssetsYaml,
projectUri.resolve('build/native_assets/macos/native_assets.yaml'), projectUri.resolve('build/native_assets/macos/native_assets.yaml'),
...@@ -291,6 +298,13 @@ void main() { ...@@ -291,6 +298,13 @@ void main() {
), ),
), ),
); );
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Building native assets for [macos_arm64] debug.',
'Building native assets for [macos_arm64] done.',
]),
);
expect( expect(
nativeAssetsYaml, nativeAssetsYaml,
projectUri.resolve('build/native_assets/macos/native_assets.yaml'), projectUri.resolve('build/native_assets/macos/native_assets.yaml'),
...@@ -301,7 +315,7 @@ void main() { ...@@ -301,7 +315,7 @@ void main() {
'package:bar/bar.dart', 'package:bar/bar.dart',
if (flutterTester) if (flutterTester)
// Tests run on host system, so the have the full path on the system. // Tests run on host system, so the have the full path on the system.
'- ${projectUri.resolve('/build/native_assets/macos/bar.dylib').toFilePath()}' '- ${projectUri.resolve('build/native_assets/macos/bar.dylib').toFilePath()}'
else else
// Apps are a bundle with the dylibs on their dlopen path. // Apps are a bundle with the dylibs on their dlopen path.
'- bar.dylib', '- bar.dylib',
...@@ -370,7 +384,6 @@ InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault ...@@ -370,7 +384,6 @@ InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault
), ),
}, () async { }, () async {
if (!const LocalPlatform().isMacOS) { if (!const LocalPlatform().isMacOS) {
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
return; return;
} }
......
...@@ -230,9 +230,9 @@ install(DIRECTORY "${NATIVE_ASSETS_DIR}" ...@@ -230,9 +230,9 @@ install(DIRECTORY "${NATIVE_ASSETS_DIR}"
expect(testLogger.statusText, isEmpty); expect(testLogger.statusText, isEmpty);
}); });
// TODO(dacoharkes): Add test for Windows when adding Windows support. https://github.com/flutter/flutter/issues/129757 for (final String os in <String>['linux', 'windows']) {
testWithoutContext('is migrated to copy native assets', () { testWithoutContext('is migrated to copy native assets', () {
managedCmakeFile.writeAsStringSync(r''' managedCmakeFile.writeAsStringSync(r'''
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}" install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
...@@ -249,38 +249,40 @@ install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" ...@@ -249,38 +249,40 @@ install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
'''); ''');
final CmakeNativeAssetsMigration cmakeProjectMigration = CmakeNativeAssetsMigration( final CmakeNativeAssetsMigration cmakeProjectMigration = CmakeNativeAssetsMigration(
mockCmakeProject, mockCmakeProject,
'linux', os,
testLogger, testLogger,
); );
cmakeProjectMigration.migrate(); cmakeProjectMigration.migrate();
expect(managedCmakeFile.readAsStringSync(), r''' expect(managedCmakeFile.readAsStringSync(), '''
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) foreach(bundled_library \${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}" install(FILES "\${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" DESTINATION "\${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
endforeach(bundled_library) endforeach(bundled_library)
# Copy the native assets provided by the build.dart from all packages. # Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") set(NATIVE_ASSETS_DIR "\${PROJECT_BUILD_DIR}native_assets/$os/")
install(DIRECTORY "${NATIVE_ASSETS_DIR}" install(DIRECTORY "\${NATIVE_ASSETS_DIR}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" DESTINATION "\${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
# Fully re-copy the assets directory on each build to avoid having stale files # Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install. # from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets") set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE " install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") file(REMOVE_RECURSE \\"\${INSTALL_BUNDLE_DATA_DIR}/\${FLUTTER_ASSET_DIR_NAME}\\")
" COMPONENT Runtime) " COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" install(DIRECTORY "\${PROJECT_BUILD_DIR}/\${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) DESTINATION "\${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
'''); ''');
expect(testLogger.statusText, contains('CMake missing install() NATIVE_ASSETS_DIR command, updating.')); expect(testLogger.statusText,
}); contains('CMake missing install() NATIVE_ASSETS_DIR command, updating.'));
});
}
}); });
}); });
} }
......
...@@ -58,8 +58,8 @@ const String packageName = 'package_with_native_assets'; ...@@ -58,8 +58,8 @@ const String packageName = 'package_with_native_assets';
const String exampleAppName = '${packageName}_example'; const String exampleAppName = '${packageName}_example';
void main() { void main() {
if (!platform.isMacOS && !platform.isLinux) { if (!platform.isMacOS && !platform.isLinux && !platform.isWindows) {
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757 // TODO(dacoharkes): Implement Fuchsia. https://github.com/flutter/flutter/issues/129757
return; return;
} }
...@@ -146,9 +146,10 @@ void main() { ...@@ -146,9 +146,10 @@ void main() {
if (device == 'macos') { if (device == 'macos') {
expectDylibIsBundledMacOS(exampleDirectory, buildMode); expectDylibIsBundledMacOS(exampleDirectory, buildMode);
} } else if (device == 'linux') {
if (device == 'linux') {
expectDylibIsBundledLinux(exampleDirectory, buildMode); expectDylibIsBundledLinux(exampleDirectory, buildMode);
} else if (device == 'windows') {
expectDylibIsBundledWindows(exampleDirectory, buildMode);
} }
if (device == hostOs) { if (device == hostOs) {
expectCCompilerIsConfigured(exampleDirectory); expectCCompilerIsConfigured(exampleDirectory);
...@@ -205,6 +206,8 @@ void main() { ...@@ -205,6 +206,8 @@ void main() {
expectDylibIsBundledIos(exampleDirectory, buildMode); expectDylibIsBundledIos(exampleDirectory, buildMode);
} else if (buildSubcommand == 'linux') { } else if (buildSubcommand == 'linux') {
expectDylibIsBundledLinux(exampleDirectory, buildMode); expectDylibIsBundledLinux(exampleDirectory, buildMode);
} else if (buildSubcommand == 'windows') {
expectDylibIsBundledWindows(exampleDirectory, buildMode);
} }
expectCCompilerIsConfigured(exampleDirectory); expectCCompilerIsConfigured(exampleDirectory);
}); });
...@@ -239,11 +242,15 @@ void main() { ...@@ -239,11 +242,15 @@ void main() {
'build', 'build',
buildSubcommand, buildSubcommand,
if (buildSubcommand == 'ios') '--no-codesign', if (buildSubcommand == 'ios') '--no-codesign',
if (buildSubcommand == 'windows') '-v' // Requires verbose mode for error.
], ],
workingDirectory: exampleDirectory.path, workingDirectory: exampleDirectory.path,
); );
expect(result.exitCode, isNot(0)); expect(result.exitCode, isNot(0));
expect(result.stderr, contains('link mode set to static, but this is not yet supported')); expect(
(result.stdout as String) + (result.stderr as String),
contains('link mode set to static, but this is not yet supported'),
);
}); });
}); });
} }
...@@ -301,14 +308,36 @@ void expectDylibIsBundledIos(Directory appDirectory, String buildMode) { ...@@ -301,14 +308,36 @@ void expectDylibIsBundledIos(Directory appDirectory, String buildMode) {
void expectDylibIsBundledLinux(Directory appDirectory, String buildMode) { void expectDylibIsBundledLinux(Directory appDirectory, String buildMode) {
// Linux does not support cross compilation, so always only check current architecture. // Linux does not support cross compilation, so always only check current architecture.
final String architecture = Architecture.current.dartPlatform; final String architecture = Architecture.current.dartPlatform;
final Directory appBundle = appDirectory.childDirectory('build/$hostOs/$architecture/$buildMode/bundle/'); final Directory appBundle = appDirectory
.childDirectory('build')
.childDirectory(hostOs)
.childDirectory(architecture)
.childDirectory(buildMode)
.childDirectory('bundle');
expect(appBundle, exists); expect(appBundle, exists);
final Directory dylibsFolder = appBundle.childDirectory('lib/'); final Directory dylibsFolder = appBundle.childDirectory('lib');
expect(dylibsFolder, exists); expect(dylibsFolder, exists);
final File dylib = dylibsFolder.childFile(OS.linux.dylibFileName(packageName)); final File dylib = dylibsFolder.childFile(OS.linux.dylibFileName(packageName));
expect(dylib, exists); expect(dylib, exists);
} }
/// Checks that dylibs are bundled.
///
/// Sample path: build\windows\x64\runner\Debug\my_package_example.exe
void expectDylibIsBundledWindows(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')
.childDirectory(hostOs)
.childDirectory(architecture)
.childDirectory('runner')
.childDirectory(buildMode.upperCaseFirst());
expect(appBundle, exists);
final File dylib = appBundle.childFile(OS.windows.dylibFileName(packageName));
expect(dylib, exists);
}
/// For `flutter build` we can't easily test whether running the app works. /// For `flutter build` we can't easily test whether running the app works.
/// Check that we have the dylibs in the app. /// Check that we have the dylibs in the app.
void expectDylibIsBundledWithFrameworks(Directory appDirectory, String buildMode, String os) { void expectDylibIsBundledWithFrameworks(Directory appDirectory, String buildMode, String os) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment