mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
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`.
This commit is contained in:
parent
3e7c388e91
commit
ff4a0f676f
@ -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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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({
|
|||||||
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({
|
|||||||
);
|
);
|
||||||
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(
|
|||||||
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({
|
|||||||
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({
|
|||||||
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 @@
|
|||||||
// 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({
|
|||||||
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(
|
|||||||
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({
|
|||||||
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({
|
|||||||
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(
|
|||||||
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({
|
|||||||
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({
|
|||||||
}
|
}
|
||||||
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 {
|
|||||||
# 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.
|
||||||
|
@ -23,6 +23,7 @@ import 'linux/native_assets.dart';
|
|||||||
import 'macos/native_assets.dart';
|
import 'macos/native_assets.dart';
|
||||||
import 'macos/native_assets_host.dart';
|
import 'macos/native_assets_host.dart';
|
||||||
import 'resident_runner.dart';
|
import 'resident_runner.dart';
|
||||||
|
import 'windows/native_assets.dart';
|
||||||
|
|
||||||
/// Programmatic API to be used by Dart launchers to invoke native builds.
|
/// Programmatic API to be used by Dart launchers to invoke native builds.
|
||||||
///
|
///
|
||||||
@ -42,7 +43,7 @@ abstract class NativeAssetsBuildRunner {
|
|||||||
Future<DryRunResult> dryRun({
|
Future<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,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,10 +101,8 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> hasPackageConfig() {
|
Future<bool> hasPackageConfig() {
|
||||||
final File packageConfigJson = fileSystem
|
final File packageConfigJson =
|
||||||
.directory(projectUri.toFilePath())
|
fileSystem.directory(projectUri.toFilePath()).childDirectory('.dart_tool').childFile('package_config.json');
|
||||||
.childDirectory('.dart_tool')
|
|
||||||
.childFile('package_config.json');
|
|
||||||
return packageConfigJson.exists();
|
return packageConfigJson.exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +119,7 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner {
|
|||||||
Future<DryRunResult> dryRun({
|
Future<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,
|
||||||
}) {
|
}) {
|
||||||
final PackageLayout packageLayout = PackageLayout.fromPackageConfig(
|
final PackageLayout packageLayout = PackageLayout.fromPackageConfig(
|
||||||
@ -130,7 +129,7 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner {
|
|||||||
return _buildRunner.dryRun(
|
return _buildRunner.dryRun(
|
||||||
includeParentEnvironment: includeParentEnvironment,
|
includeParentEnvironment: includeParentEnvironment,
|
||||||
linkModePreference: linkModePreference,
|
linkModePreference: linkModePreference,
|
||||||
targetOs: targetOs,
|
targetOs: targetOS,
|
||||||
workingDirectory: workingDirectory,
|
workingDirectory: workingDirectory,
|
||||||
packageLayout: packageLayout,
|
packageLayout: packageLayout,
|
||||||
);
|
);
|
||||||
@ -172,8 +171,11 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner {
|
|||||||
if (globals.platform.isLinux) {
|
if (globals.platform.isLinux) {
|
||||||
return cCompilerConfigLinux();
|
return cCompilerConfigLinux();
|
||||||
}
|
}
|
||||||
|
if (globals.platform.isWindows) {
|
||||||
|
return cCompilerConfigWindows();
|
||||||
|
}
|
||||||
throwToolExit(
|
throwToolExit(
|
||||||
'Native assets feature not yet implemented for Linux, Windows and Android.',
|
'Native assets feature not yet implemented for Android.',
|
||||||
);
|
);
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
@ -343,6 +345,13 @@ Future<Uri?> dryRunNativeAssets({
|
|||||||
fileSystem: fileSystem,
|
fileSystem: fileSystem,
|
||||||
buildRunner: buildRunner,
|
buildRunner: buildRunner,
|
||||||
);
|
);
|
||||||
|
} else if (const LocalPlatform().isWindows) {
|
||||||
|
nativeAssetsYaml = await dryRunNativeAssetsWindows(
|
||||||
|
projectUri: projectUri,
|
||||||
|
flutterTester: true,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: buildRunner,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await nativeBuildRequired(buildRunner);
|
await nativeBuildRequired(buildRunner);
|
||||||
nativeAssetsYaml = null;
|
nativeAssetsYaml = null;
|
||||||
@ -354,6 +363,12 @@ Future<Uri?> dryRunNativeAssets({
|
|||||||
fileSystem: fileSystem,
|
fileSystem: fileSystem,
|
||||||
buildRunner: buildRunner,
|
buildRunner: buildRunner,
|
||||||
);
|
);
|
||||||
|
case build_info.TargetPlatform.windows_x64:
|
||||||
|
nativeAssetsYaml = await dryRunNativeAssetsWindows(
|
||||||
|
projectUri: projectUri,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: buildRunner,
|
||||||
|
);
|
||||||
case build_info.TargetPlatform.android_arm:
|
case build_info.TargetPlatform.android_arm:
|
||||||
case build_info.TargetPlatform.android_arm64:
|
case build_info.TargetPlatform.android_arm64:
|
||||||
case build_info.TargetPlatform.android_x64:
|
case build_info.TargetPlatform.android_x64:
|
||||||
@ -362,7 +377,6 @@ Future<Uri?> dryRunNativeAssets({
|
|||||||
case build_info.TargetPlatform.fuchsia_arm64:
|
case build_info.TargetPlatform.fuchsia_arm64:
|
||||||
case build_info.TargetPlatform.fuchsia_x64:
|
case build_info.TargetPlatform.fuchsia_x64:
|
||||||
case build_info.TargetPlatform.web_javascript:
|
case build_info.TargetPlatform.web_javascript:
|
||||||
case build_info.TargetPlatform.windows_x64:
|
|
||||||
await ensureNoNativeAssetsOrOsIsSupported(
|
await ensureNoNativeAssetsOrOsIsSupported(
|
||||||
projectUri,
|
projectUri,
|
||||||
targetPlatform.toString(),
|
targetPlatform.toString(),
|
||||||
@ -387,19 +401,41 @@ Future<Uri?> dryRunNativeAssetsMultipeOSes({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Uri buildUri_ = buildUriMultiple(projectUri);
|
final Uri buildUri = buildUriMultiple(projectUri);
|
||||||
final Iterable<Asset> nativeAssetPaths = <Asset>[
|
final Iterable<Asset> nativeAssetPaths = <Asset>[
|
||||||
if (targetPlatforms.contains(build_info.TargetPlatform.darwin) ||
|
if (targetPlatforms.contains(build_info.TargetPlatform.darwin) ||
|
||||||
(targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.macOS))
|
(targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.macOS))
|
||||||
...await dryRunNativeAssetsMacOSInternal(fileSystem, projectUri, false, buildRunner),
|
...await dryRunNativeAssetsMacOSInternal(
|
||||||
|
fileSystem,
|
||||||
|
projectUri,
|
||||||
|
false,
|
||||||
|
buildRunner,
|
||||||
|
),
|
||||||
if (targetPlatforms.contains(build_info.TargetPlatform.linux_arm64) ||
|
if (targetPlatforms.contains(build_info.TargetPlatform.linux_arm64) ||
|
||||||
targetPlatforms.contains(build_info.TargetPlatform.linux_x64) ||
|
targetPlatforms.contains(build_info.TargetPlatform.linux_x64) ||
|
||||||
(targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.linux))
|
(targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.linux))
|
||||||
...await dryRunNativeAssetsLinuxInternal(fileSystem, projectUri, false, buildRunner),
|
...await dryRunNativeAssetsLinuxInternal(
|
||||||
|
fileSystem,
|
||||||
|
projectUri,
|
||||||
|
false,
|
||||||
|
buildRunner,
|
||||||
|
),
|
||||||
|
if (targetPlatforms.contains(build_info.TargetPlatform.windows_x64) ||
|
||||||
|
(targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.windows))
|
||||||
|
...await dryRunNativeAssetsWindowsInternal(
|
||||||
|
fileSystem,
|
||||||
|
projectUri,
|
||||||
|
false,
|
||||||
|
buildRunner,
|
||||||
|
),
|
||||||
if (targetPlatforms.contains(build_info.TargetPlatform.ios))
|
if (targetPlatforms.contains(build_info.TargetPlatform.ios))
|
||||||
...await dryRunNativeAssetsIOSInternal(fileSystem, projectUri, buildRunner)
|
...await dryRunNativeAssetsIOSInternal(
|
||||||
|
fileSystem,
|
||||||
|
projectUri,
|
||||||
|
buildRunner,
|
||||||
|
)
|
||||||
];
|
];
|
||||||
final Uri nativeAssetsUri = await writeNativeAssetsYaml(nativeAssetPaths, buildUri_, fileSystem);
|
final Uri nativeAssetsUri = await writeNativeAssetsYaml(nativeAssetPaths, buildUri, fileSystem);
|
||||||
return nativeAssetsUri;
|
return nativeAssetsUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,3 +445,213 @@ Uri buildUriMultiple(Uri projectUri) {
|
|||||||
final String buildDir = build_info.getBuildDirectory();
|
final String buildDir = build_info.getBuildDirectory();
|
||||||
return projectUri.resolve('$buildDir/native_assets/multiple/');
|
return projectUri.resolve('$buildDir/native_assets/multiple/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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?> dryRunNativeAssetsSingleArchitecture({
|
||||||
|
required NativeAssetsBuildRunner buildRunner,
|
||||||
|
required Uri projectUri,
|
||||||
|
bool flutterTester = false,
|
||||||
|
required FileSystem fileSystem,
|
||||||
|
required OS os,
|
||||||
|
}) async {
|
||||||
|
if (!await nativeBuildRequired(buildRunner)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Uri buildUri = nativeAssetsBuildUri(projectUri, os);
|
||||||
|
final Iterable<Asset> nativeAssetPaths = await dryRunNativeAssetsSingleArchitectureInternal(
|
||||||
|
fileSystem,
|
||||||
|
projectUri,
|
||||||
|
flutterTester,
|
||||||
|
buildRunner,
|
||||||
|
os,
|
||||||
|
);
|
||||||
|
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
|
||||||
|
nativeAssetPaths,
|
||||||
|
buildUri,
|
||||||
|
fileSystem,
|
||||||
|
);
|
||||||
|
return nativeAssetsUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Iterable<Asset>> dryRunNativeAssetsSingleArchitectureInternal(
|
||||||
|
FileSystem fileSystem,
|
||||||
|
Uri projectUri,
|
||||||
|
bool flutterTester,
|
||||||
|
NativeAssetsBuildRunner buildRunner,
|
||||||
|
OS targetOS,
|
||||||
|
) async {
|
||||||
|
final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
|
||||||
|
|
||||||
|
globals.logger.printTrace('Dry running native assets for $targetOS.');
|
||||||
|
final List<Asset> nativeAssets = (await buildRunner.dryRun(
|
||||||
|
linkModePreference: LinkModePreference.dynamic,
|
||||||
|
targetOS: targetOS,
|
||||||
|
workingDirectory: projectUri,
|
||||||
|
includeParentEnvironment: true,
|
||||||
|
))
|
||||||
|
.assets;
|
||||||
|
ensureNoLinkModeStatic(nativeAssets);
|
||||||
|
globals.logger.printTrace('Dry running native assets for $targetOS done.');
|
||||||
|
final Uri? absolutePath = flutterTester ? buildUri : null;
|
||||||
|
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocationsSingleArchitecture(
|
||||||
|
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)> buildNativeAssetsSingleArchitecture({
|
||||||
|
required NativeAssetsBuildRunner buildRunner,
|
||||||
|
build_info.TargetPlatform? targetPlatform,
|
||||||
|
required Uri projectUri,
|
||||||
|
required build_info.BuildMode buildMode,
|
||||||
|
bool flutterTester = false,
|
||||||
|
Uri? yamlParentDirectory,
|
||||||
|
required FileSystem fileSystem,
|
||||||
|
}) async {
|
||||||
|
final Target target = targetPlatform != null ? _getNativeTarget(targetPlatform) : Target.current;
|
||||||
|
final OS targetOS = target.os;
|
||||||
|
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 nativeBuildRequired(buildRunner)) {
|
||||||
|
final Uri nativeAssetsYaml = await writeNativeAssetsYaml(
|
||||||
|
<Asset>[],
|
||||||
|
yamlParentDirectory ?? buildUri,
|
||||||
|
fileSystem,
|
||||||
|
);
|
||||||
|
return (nativeAssetsYaml, <Uri>[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
final 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 = _assetTargetLocationsSingleArchitecture(nativeAssets, absolutePath);
|
||||||
|
await _copyNativeAssetsSingleArchitecture(
|
||||||
|
buildUri,
|
||||||
|
assetTargetLocations,
|
||||||
|
buildMode,
|
||||||
|
fileSystem,
|
||||||
|
);
|
||||||
|
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
|
||||||
|
assetTargetLocations.values,
|
||||||
|
yamlParentDirectory ?? buildUri,
|
||||||
|
fileSystem,
|
||||||
|
);
|
||||||
|
return (nativeAssetsUri, dependencies.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Asset, Asset> _assetTargetLocationsSingleArchitecture(
|
||||||
|
List<Asset> nativeAssets,
|
||||||
|
Uri? absolutePath,
|
||||||
|
) {
|
||||||
|
return <Asset, Asset>{
|
||||||
|
for (final Asset asset in nativeAssets)
|
||||||
|
asset: _targetLocationSingleArchitecture(
|
||||||
|
asset,
|
||||||
|
absolutePath,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Asset _targetLocationSingleArchitecture(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].
|
||||||
|
///
|
||||||
|
/// Does not cover MacOS, iOS, and Android as these pass the architecture
|
||||||
|
/// in other enums.
|
||||||
|
Target _getNativeTarget(build_info.TargetPlatform targetPlatform) {
|
||||||
|
switch (targetPlatform) {
|
||||||
|
case build_info.TargetPlatform.linux_x64:
|
||||||
|
return Target.linuxX64;
|
||||||
|
case build_info.TargetPlatform.linux_arm64:
|
||||||
|
return Target.linuxArm64;
|
||||||
|
case build_info.TargetPlatform.windows_x64:
|
||||||
|
return Target.windowsX64;
|
||||||
|
case build_info.TargetPlatform.android:
|
||||||
|
case build_info.TargetPlatform.ios:
|
||||||
|
case build_info.TargetPlatform.darwin:
|
||||||
|
case build_info.TargetPlatform.fuchsia_arm64:
|
||||||
|
case build_info.TargetPlatform.fuchsia_x64:
|
||||||
|
case build_info.TargetPlatform.tester:
|
||||||
|
case build_info.TargetPlatform.web_javascript:
|
||||||
|
case build_info.TargetPlatform.android_arm:
|
||||||
|
case build_info.TargetPlatform.android_arm64:
|
||||||
|
case build_info.TargetPlatform.android_x64:
|
||||||
|
case build_info.TargetPlatform.android_x86:
|
||||||
|
throw Exception('Unknown targetPlatform: $targetPlatform.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _copyNativeAssetsSingleArchitecture(
|
||||||
|
Uri buildUri,
|
||||||
|
Map<Asset, Asset> assetTargetLocations,
|
||||||
|
build_info.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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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 {
|
|||||||
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';
|
|||||||
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, {
|
|||||||
|
|
||||||
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),
|
||||||
|
91
packages/flutter_tools/lib/src/windows/native_assets.dart
Normal file
91
packages/flutter_tools/lib/src/windows/native_assets.dart
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// 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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
/// 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)
|
|||||||
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:
|
|||||||
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 {
|
|||||||
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() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
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() {
|
|||||||
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() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
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() {
|
|||||||
'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() {
|
|||||||
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() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
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() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
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() {
|
|||||||
'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
|
|||||||
),
|
),
|
||||||
}, () 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}"
|
|||||||
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}"
|
|||||||
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.'));
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,439 @@
|
|||||||
|
// 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/native_assets.dart';
|
||||||
|
import 'package:flutter_tools/src/windows/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(style: FileSystemStyle.windows);
|
||||||
|
environment = Environment.test(
|
||||||
|
fileSystem.currentDirectory,
|
||||||
|
inputs: <String, String>{},
|
||||||
|
artifacts: artifacts,
|
||||||
|
processManager: processManager,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
logger: logger,
|
||||||
|
);
|
||||||
|
environment.buildDir.createSync(recursive: true);
|
||||||
|
projectUri = environment.projectDir.uri;
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
expect(
|
||||||
|
await dryRunNativeAssetsWindows(
|
||||||
|
projectUri: projectUri,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
hasPackageConfigResult: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(globals.logger as BufferLogger).traceText,
|
||||||
|
contains('No package config found. Skipping native assets compilation.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('build with no package config', overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
await buildNativeAssetsWindows(
|
||||||
|
projectUri: projectUri,
|
||||||
|
buildMode: BuildMode.debug,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
hasPackageConfigResult: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(globals.logger as BufferLogger).traceText,
|
||||||
|
contains('No package config found. Skipping native assets compilation.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('dry run for multiple OSes with no package config', overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
await dryRunNativeAssetsMultipeOSes(
|
||||||
|
projectUri: projectUri,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
targetPlatforms: <TargetPlatform>[
|
||||||
|
TargetPlatform.windows_x64,
|
||||||
|
],
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
hasPackageConfigResult: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(globals.logger as BufferLogger).traceText,
|
||||||
|
contains('No package config found. Skipping native assets compilation.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||||
|
await packageConfig.parent.create();
|
||||||
|
await packageConfig.create();
|
||||||
|
expect(
|
||||||
|
() => dryRunNativeAssetsWindows(
|
||||||
|
projectUri: projectUri,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
packagesWithNativeAssetsResult: <Package>[
|
||||||
|
Package('bar', projectUri),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
throwsToolExit(
|
||||||
|
message: 'Package(s) bar require the native assets feature to be enabled. '
|
||||||
|
'Enable using `flutter config --enable-native-assets`.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('dry run with assets', overrides: <Type, Generator>{
|
||||||
|
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||||
|
await packageConfig.parent.create();
|
||||||
|
await packageConfig.create();
|
||||||
|
final Uri? nativeAssetsYaml = await dryRunNativeAssetsWindows(
|
||||||
|
projectUri: projectUri,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
packagesWithNativeAssetsResult: <Package>[
|
||||||
|
Package('bar', projectUri),
|
||||||
|
],
|
||||||
|
dryRunResult: FakeNativeAssetsBuilderResult(
|
||||||
|
assets: <Asset>[
|
||||||
|
Asset(
|
||||||
|
id: 'package:bar/bar.dart',
|
||||||
|
linkMode: LinkMode.dynamic,
|
||||||
|
target: native_assets_cli.Target.windowsX64,
|
||||||
|
path: AssetAbsolutePath(Uri.file('bar.dll')),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(globals.logger as BufferLogger).traceText,
|
||||||
|
stringContainsInOrder(<String>[
|
||||||
|
'Dry running native assets for windows.',
|
||||||
|
'Dry running native assets for windows done.',
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
nativeAssetsYaml,
|
||||||
|
projectUri.resolve('build/native_assets/windows/native_assets.yaml'),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
await fileSystem.file(nativeAssetsYaml).readAsString(),
|
||||||
|
contains('package:bar/bar.dart'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('build with assets but not enabled', overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||||
|
await packageConfig.parent.create();
|
||||||
|
await packageConfig.create();
|
||||||
|
expect(
|
||||||
|
() => buildNativeAssetsWindows(
|
||||||
|
projectUri: projectUri,
|
||||||
|
buildMode: BuildMode.debug,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
packagesWithNativeAssetsResult: <Package>[
|
||||||
|
Package('bar', projectUri),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
throwsToolExit(
|
||||||
|
message: 'Package(s) bar require the native assets feature to be enabled. '
|
||||||
|
'Enable using `flutter config --enable-native-assets`.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('build no assets', overrides: <Type, Generator>{
|
||||||
|
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||||
|
await packageConfig.parent.create();
|
||||||
|
await packageConfig.create();
|
||||||
|
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsWindows(
|
||||||
|
targetPlatform: TargetPlatform.windows_x64,
|
||||||
|
projectUri: projectUri,
|
||||||
|
buildMode: BuildMode.debug,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
packagesWithNativeAssetsResult: <Package>[
|
||||||
|
Package('bar', projectUri),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
nativeAssetsYaml,
|
||||||
|
projectUri.resolve('build/native_assets/windows/native_assets.yaml'),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
await fileSystem.file(nativeAssetsYaml).readAsString(),
|
||||||
|
isNot(contains('package:bar/bar.dart')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
environment.projectDir.childDirectory('build').childDirectory('native_assets').childDirectory('windows'),
|
||||||
|
exists,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (final bool flutterTester in <bool>[false, true]) {
|
||||||
|
String testName = '';
|
||||||
|
if (flutterTester) {
|
||||||
|
testName += ' flutter tester';
|
||||||
|
}
|
||||||
|
testUsingContext('build with assets$testName', overrides: <Type, Generator>{
|
||||||
|
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
final File packageConfig = environment.projectDir.childDirectory('.dart_tool').childFile('package_config.json');
|
||||||
|
await packageConfig.parent.create();
|
||||||
|
await packageConfig.create();
|
||||||
|
final File dylibAfterCompiling = fileSystem.file('bar.dll');
|
||||||
|
// The mock doesn't create the file, so create it here.
|
||||||
|
await dylibAfterCompiling.create();
|
||||||
|
final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsWindows(
|
||||||
|
targetPlatform: TargetPlatform.windows_x64,
|
||||||
|
projectUri: projectUri,
|
||||||
|
buildMode: BuildMode.debug,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
flutterTester: flutterTester,
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
packagesWithNativeAssetsResult: <Package>[
|
||||||
|
Package('bar', projectUri),
|
||||||
|
],
|
||||||
|
buildResult: FakeNativeAssetsBuilderResult(
|
||||||
|
assets: <Asset>[
|
||||||
|
Asset(
|
||||||
|
id: 'package:bar/bar.dart',
|
||||||
|
linkMode: LinkMode.dynamic,
|
||||||
|
target: native_assets_cli.Target.windowsX64,
|
||||||
|
path: AssetAbsolutePath(dylibAfterCompiling.uri),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(globals.logger as BufferLogger).traceText,
|
||||||
|
stringContainsInOrder(<String>[
|
||||||
|
'Building native assets for windows_x64 debug.',
|
||||||
|
'Building native assets for windows_x64 done.',
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
nativeAssetsYaml,
|
||||||
|
projectUri.resolve('build/native_assets/windows/native_assets.yaml'),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
await fileSystem.file(nativeAssetsYaml).readAsString(),
|
||||||
|
stringContainsInOrder(<String>[
|
||||||
|
'package:bar/bar.dart',
|
||||||
|
if (flutterTester)
|
||||||
|
// Tests run on host system, so the have the full path on the system.
|
||||||
|
'- ${projectUri.resolve('build/native_assets/windows/bar.dll').toFilePath()}'
|
||||||
|
else
|
||||||
|
// Apps are a bundle with the dylibs on their dlopen path.
|
||||||
|
'- bar.dll',
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testUsingContext('static libs not supported', overrides: <Type, Generator>{
|
||||||
|
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||||
|
ProcessManager: () => FakeProcessManager.empty(),
|
||||||
|
}, () async {
|
||||||
|
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
|
||||||
|
await packageConfig.parent.create();
|
||||||
|
await packageConfig.create();
|
||||||
|
expect(
|
||||||
|
() => dryRunNativeAssetsWindows(
|
||||||
|
projectUri: projectUri,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
buildRunner: FakeNativeAssetsBuildRunner(
|
||||||
|
packagesWithNativeAssetsResult: <Package>[
|
||||||
|
Package('bar', projectUri),
|
||||||
|
],
|
||||||
|
dryRunResult: FakeNativeAssetsBuilderResult(
|
||||||
|
assets: <Asset>[
|
||||||
|
Asset(
|
||||||
|
id: 'package:bar/bar.dart',
|
||||||
|
linkMode: LinkMode.static,
|
||||||
|
target: native_assets_cli.Target.windowsX64,
|
||||||
|
path: AssetAbsolutePath(Uri.file(OS.windows.staticlibFileName('bar'))),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
throwsToolExit(
|
||||||
|
message: 'Native asset(s) package:bar/bar.dart have their link mode set to '
|
||||||
|
'static, but this is not yet supported. '
|
||||||
|
'For more info see https://github.com/dart-lang/sdk/issues/49418.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// This logic is mocked in the other tests to avoid having test order
|
||||||
|
// randomization causing issues with what processes are invoked.
|
||||||
|
// Exercise the parsing of the process output in this separate test.
|
||||||
|
testUsingContext('NativeAssetsBuildRunnerImpl.cCompilerConfig', overrides: <Type, Generator>{
|
||||||
|
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||||
|
ProcessManager: () => FakeProcessManager.list(
|
||||||
|
<FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <Pattern>[
|
||||||
|
RegExp(r'(.*)vswhere.exe'),
|
||||||
|
'-format',
|
||||||
|
'json',
|
||||||
|
'-products',
|
||||||
|
'*',
|
||||||
|
'-utf8',
|
||||||
|
'-latest',
|
||||||
|
'-version',
|
||||||
|
'16',
|
||||||
|
'-requires',
|
||||||
|
'Microsoft.VisualStudio.Workload.NativeDesktop',
|
||||||
|
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
|
||||||
|
'Microsoft.VisualStudio.Component.VC.CMake.Project',
|
||||||
|
],
|
||||||
|
stdout: r'''
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"instanceId": "491ec752",
|
||||||
|
"installDate": "2023-04-21T08:17:11Z",
|
||||||
|
"installationName": "VisualStudio/17.5.4+33530.505",
|
||||||
|
"installationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community",
|
||||||
|
"installationVersion": "17.5.33530.505",
|
||||||
|
"productId": "Microsoft.VisualStudio.Product.Community",
|
||||||
|
"productPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Common7\\IDE\\devenv.exe",
|
||||||
|
"state": 4294967295,
|
||||||
|
"isComplete": true,
|
||||||
|
"isLaunchable": true,
|
||||||
|
"isPrerelease": false,
|
||||||
|
"isRebootRequired": false,
|
||||||
|
"displayName": "Visual Studio Community 2022",
|
||||||
|
"description": "Powerful IDE, free for students, open-source contributors, and individuals",
|
||||||
|
"channelId": "VisualStudio.17.Release",
|
||||||
|
"channelUri": "https://aka.ms/vs/17/release/channel",
|
||||||
|
"enginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service",
|
||||||
|
"installedChannelId": "VisualStudio.17.Release",
|
||||||
|
"installedChannelUri": "https://aka.ms/vs/17/release/channel",
|
||||||
|
"releaseNotes": "https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.5#17.5.4",
|
||||||
|
"thirdPartyNotices": "https://go.microsoft.com/fwlink/?LinkId=661288",
|
||||||
|
"updateDate": "2023-04-21T08:17:11.2249473Z",
|
||||||
|
"catalog": {
|
||||||
|
"buildBranch": "d17.5",
|
||||||
|
"buildVersion": "17.5.33530.505",
|
||||||
|
"id": "VisualStudio/17.5.4+33530.505",
|
||||||
|
"localBuild": "build-lab",
|
||||||
|
"manifestName": "VisualStudio",
|
||||||
|
"manifestType": "installer",
|
||||||
|
"productDisplayVersion": "17.5.4",
|
||||||
|
"productLine": "Dev17",
|
||||||
|
"productLineVersion": "2022",
|
||||||
|
"productMilestone": "RTW",
|
||||||
|
"productMilestoneIsPreRelease": "False",
|
||||||
|
"productName": "Visual Studio",
|
||||||
|
"productPatchVersion": "4",
|
||||||
|
"productPreReleaseMilestoneSuffix": "1.0",
|
||||||
|
"productSemanticVersion": "17.5.4+33530.505",
|
||||||
|
"requiredEngineVersion": "3.5.2150.18781"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"campaignId": "2060:abb99c5d1ecc4013acf2e1814b10b690",
|
||||||
|
"channelManifestId": "VisualStudio.17.Release/17.5.4+33530.505",
|
||||||
|
"nickname": "",
|
||||||
|
"setupEngineFilePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\setup.exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
''', // Newline at the end of the string.
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
}, () async {
|
||||||
|
if (!const LocalPlatform().isWindows) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Directory msvcBinDir =
|
||||||
|
fileSystem.directory(r'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\bin\Hostx64\x64');
|
||||||
|
await msvcBinDir.create(recursive: true);
|
||||||
|
|
||||||
|
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?.toFilePath(), msvcBinDir.childFile('cl.exe').uri.toFilePath());
|
||||||
|
expect(result.ar?.toFilePath(), msvcBinDir.childFile('lib.exe').uri.toFilePath());
|
||||||
|
expect(result.ld?.toFilePath(), msvcBinDir.childFile('link.exe').uri.toFilePath());
|
||||||
|
expect(result.envScript, isNotNull);
|
||||||
|
expect(result.envScriptArgs, isNotNull);
|
||||||
|
});
|
||||||
|
}
|
@ -11,12 +11,16 @@ import 'package:flutter_tools/src/convert.dart';
|
|||||||
import 'package:flutter_tools/src/windows/visual_studio.dart';
|
import 'package:flutter_tools/src/windows/visual_studio.dart';
|
||||||
|
|
||||||
import '../../src/common.dart';
|
import '../../src/common.dart';
|
||||||
import '../../src/fake_process_manager.dart';
|
import '../../src/context.dart';
|
||||||
|
|
||||||
const String programFilesPath = r'C:\Program Files (x86)';
|
const String programFilesPath = r'C:\Program Files (x86)';
|
||||||
const String visualStudioPath = programFilesPath + r'\Microsoft Visual Studio\2017\Community';
|
const String visualStudioPath = programFilesPath + r'\Microsoft Visual Studio\2017\Community';
|
||||||
const String cmakePath = visualStudioPath + r'\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe';
|
const String cmakePath = visualStudioPath + r'\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe';
|
||||||
const String vswherePath = programFilesPath + r'\Microsoft Visual Studio\Installer\vswhere.exe';
|
const String vswherePath = programFilesPath + r'\Microsoft Visual Studio\Installer\vswhere.exe';
|
||||||
|
const String clPath = visualStudioPath + r'\VC\Tools\MSVC\14.35.32215\bin\Hostx64\x64\cl.exe';
|
||||||
|
const String libPath = visualStudioPath + r'\VC\Tools\MSVC\14.35.32215\bin\Hostx64\x64\lib.exe';
|
||||||
|
const String linkPath = visualStudioPath + r'\VC\Tools\MSVC\14.35.32215\bin\Hostx64\x64\link.exe';
|
||||||
|
const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat';
|
||||||
|
|
||||||
final Platform windowsPlatform = FakePlatform(
|
final Platform windowsPlatform = FakePlatform(
|
||||||
operatingSystem: 'windows',
|
operatingSystem: 'windows',
|
||||||
@ -140,6 +144,7 @@ void setMockVswhereResponse(
|
|||||||
]) {
|
]) {
|
||||||
fileSystem.file(vswherePath).createSync(recursive: true);
|
fileSystem.file(vswherePath).createSync(recursive: true);
|
||||||
fileSystem.file(cmakePath).createSync(recursive: true);
|
fileSystem.file(cmakePath).createSync(recursive: true);
|
||||||
|
fileSystem.file(clPath).createSync(recursive: true);
|
||||||
final String finalResponse = responseOverride
|
final String finalResponse = responseOverride
|
||||||
?? (response != null ? json.encode(<Map<String, dynamic>>[response]) : '[]');
|
?? (response != null ? json.encode(<Map<String, dynamic>>[response]) : '[]');
|
||||||
final List<String> requirementArguments = requiredComponents == null
|
final List<String> requirementArguments = requiredComponents == null
|
||||||
@ -300,11 +305,11 @@ void setMockSdkRegResponse(
|
|||||||
const String registryKey = r'InstallationFolder';
|
const String registryKey = r'InstallationFolder';
|
||||||
const String installationPath = r'C:\Program Files (x86)\Windows Kits\10\';
|
const String installationPath = r'C:\Program Files (x86)\Windows Kits\10\';
|
||||||
final String stdout = registryPresent
|
final String stdout = registryPresent
|
||||||
? '''
|
? '''
|
||||||
$registryPath
|
$registryPath
|
||||||
$registryKey REG_SZ $installationPath
|
$registryKey REG_SZ $installationPath
|
||||||
'''
|
'''
|
||||||
: '''
|
: '''
|
||||||
|
|
||||||
ERROR: The system was unable to find the specified registry key or value.
|
ERROR: The system was unable to find the specified registry key or value.
|
||||||
''';
|
''';
|
||||||
@ -776,6 +781,10 @@ void main() {
|
|||||||
expect(visualStudio.hasNecessaryComponents, true);
|
expect(visualStudio.hasNecessaryComponents, true);
|
||||||
expect(visualStudio.cmakePath, equals(cmakePath));
|
expect(visualStudio.cmakePath, equals(cmakePath));
|
||||||
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
||||||
|
expect(visualStudio.clPath, equals(clPath));
|
||||||
|
expect(visualStudio.libPath, equals(libPath));
|
||||||
|
expect(visualStudio.linkPath, equals(linkPath));
|
||||||
|
expect(visualStudio.vcvarsPath, equals(vcvarsPath));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('Everything returns good values when Build Tools is present with all components', () {
|
testWithoutContext('Everything returns good values when Build Tools is present with all components', () {
|
||||||
@ -814,6 +823,10 @@ void main() {
|
|||||||
expect(visualStudio.hasNecessaryComponents, true);
|
expect(visualStudio.hasNecessaryComponents, true);
|
||||||
expect(visualStudio.cmakePath, equals(cmakePath));
|
expect(visualStudio.cmakePath, equals(cmakePath));
|
||||||
expect(visualStudio.cmakeGenerator, equals('Visual Studio 17 2022'));
|
expect(visualStudio.cmakeGenerator, equals('Visual Studio 17 2022'));
|
||||||
|
expect(visualStudio.clPath, equals(clPath));
|
||||||
|
expect(visualStudio.libPath, equals(libPath));
|
||||||
|
expect(visualStudio.linkPath, equals(linkPath));
|
||||||
|
expect(visualStudio.vcvarsPath, equals(vcvarsPath));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('Metadata is for compatible version when latest is missing components', () {
|
testWithoutContext('Metadata is for compatible version when latest is missing components', () {
|
||||||
@ -905,8 +918,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('Ignores unicode replacement char in unused properties', () {
|
testWithoutContext('Ignores unicode replacement char in unused properties', () {
|
||||||
final Map<String, dynamic> response = Map<String, dynamic>.of(_defaultResponse)
|
final Map<String, dynamic> response = Map<String, dynamic>.of(_defaultResponse)..['unused'] = 'Bad UTF8 \u{FFFD}';
|
||||||
..['unused'] = 'Bad UTF8 \u{FFFD}';
|
|
||||||
|
|
||||||
setMockCompatibleVisualStudioInstallation(
|
setMockCompatibleVisualStudioInstallation(
|
||||||
response,
|
response,
|
||||||
@ -919,6 +931,10 @@ void main() {
|
|||||||
expect(visualStudio.hasNecessaryComponents, true);
|
expect(visualStudio.hasNecessaryComponents, true);
|
||||||
expect(visualStudio.cmakePath, equals(cmakePath));
|
expect(visualStudio.cmakePath, equals(cmakePath));
|
||||||
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
||||||
|
expect(visualStudio.clPath, equals(clPath));
|
||||||
|
expect(visualStudio.libPath, equals(libPath));
|
||||||
|
expect(visualStudio.linkPath, equals(linkPath));
|
||||||
|
expect(visualStudio.vcvarsPath, equals(vcvarsPath));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('Throws ToolExit on bad UTF-8 in installationPath', () {
|
testWithoutContext('Throws ToolExit on bad UTF-8 in installationPath', () {
|
||||||
@ -953,13 +969,16 @@ void main() {
|
|||||||
expect(visualStudio.cmakePath, equals(cmakePath));
|
expect(visualStudio.cmakePath, equals(cmakePath));
|
||||||
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
||||||
expect(visualStudio.displayName, equals('\u{FFFD}'));
|
expect(visualStudio.displayName, equals('\u{FFFD}'));
|
||||||
|
expect(visualStudio.clPath, equals(clPath));
|
||||||
|
expect(visualStudio.libPath, equals(libPath));
|
||||||
|
expect(visualStudio.linkPath, equals(linkPath));
|
||||||
|
expect(visualStudio.vcvarsPath, equals(vcvarsPath));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext("Ignores bad UTF-8 in catalog's productDisplayVersion", () {
|
testWithoutContext("Ignores bad UTF-8 in catalog's productDisplayVersion", () {
|
||||||
final Map<String, dynamic> catalog = Map<String, dynamic>.of(_defaultResponse['catalog'] as Map<String, dynamic>)
|
final Map<String, dynamic> catalog = Map<String, dynamic>.of(_defaultResponse['catalog'] as Map<String, dynamic>)
|
||||||
..['productDisplayVersion'] = '\u{FFFD}';
|
..['productDisplayVersion'] = '\u{FFFD}';
|
||||||
final Map<String, dynamic> response = Map<String, dynamic>.of(_defaultResponse)
|
final Map<String, dynamic> response = Map<String, dynamic>.of(_defaultResponse)..['catalog'] = catalog;
|
||||||
..['catalog'] = catalog;
|
|
||||||
|
|
||||||
setMockCompatibleVisualStudioInstallation(response, fixture.fileSystem, fixture.processManager);
|
setMockCompatibleVisualStudioInstallation(response, fixture.fileSystem, fixture.processManager);
|
||||||
|
|
||||||
@ -969,6 +988,10 @@ void main() {
|
|||||||
expect(visualStudio.cmakePath, equals(cmakePath));
|
expect(visualStudio.cmakePath, equals(cmakePath));
|
||||||
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
||||||
expect(visualStudio.displayVersion, equals('\u{FFFD}'));
|
expect(visualStudio.displayVersion, equals('\u{FFFD}'));
|
||||||
|
expect(visualStudio.clPath, equals(clPath));
|
||||||
|
expect(visualStudio.libPath, equals(libPath));
|
||||||
|
expect(visualStudio.linkPath, equals(linkPath));
|
||||||
|
expect(visualStudio.vcvarsPath, equals(vcvarsPath));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('Ignores malformed JSON in description property', () {
|
testWithoutContext('Ignores malformed JSON in description property', () {
|
||||||
@ -987,101 +1010,109 @@ void main() {
|
|||||||
expect(visualStudio.cmakePath, equals(cmakePath));
|
expect(visualStudio.cmakePath, equals(cmakePath));
|
||||||
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
|
||||||
expect(visualStudio.displayVersion, equals('16.2.5'));
|
expect(visualStudio.displayVersion, equals('16.2.5'));
|
||||||
|
expect(visualStudio.clPath, equals(clPath));
|
||||||
|
expect(visualStudio.libPath, equals(libPath));
|
||||||
|
expect(visualStudio.linkPath, equals(linkPath));
|
||||||
|
expect(visualStudio.vcvarsPath, equals(vcvarsPath));
|
||||||
|
|
||||||
expect(fixture.logger.warningText, isEmpty);
|
expect(fixture.logger.warningText, isEmpty);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group(VswhereDetails, () {
|
group(VswhereDetails, () {
|
||||||
test('Accepts empty JSON', () {
|
test('Accepts empty JSON', () {
|
||||||
const bool meetsRequirements = true;
|
const bool meetsRequirements = true;
|
||||||
final Map<String, dynamic> json = <String, dynamic>{};
|
final Map<String, dynamic> json = <String, dynamic>{};
|
||||||
|
const String msvcVersion = '';
|
||||||
|
|
||||||
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json);
|
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json, msvcVersion);
|
||||||
|
|
||||||
expect(result.installationPath, null);
|
expect(result.installationPath, null);
|
||||||
expect(result.displayName, null);
|
expect(result.displayName, null);
|
||||||
expect(result.fullVersion, null);
|
expect(result.fullVersion, null);
|
||||||
expect(result.isComplete, null);
|
expect(result.isComplete, null);
|
||||||
expect(result.isLaunchable, null);
|
expect(result.isLaunchable, null);
|
||||||
expect(result.isRebootRequired, null);
|
expect(result.isRebootRequired, null);
|
||||||
expect(result.isPrerelease, null);
|
expect(result.isPrerelease, null);
|
||||||
expect(result.catalogDisplayVersion, null);
|
expect(result.catalogDisplayVersion, null);
|
||||||
expect(result.isUsable, isTrue);
|
expect(result.isUsable, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Ignores unknown JSON properties', () {
|
test('Ignores unknown JSON properties', () {
|
||||||
const bool meetsRequirements = true;
|
const bool meetsRequirements = true;
|
||||||
final Map<String, dynamic> json = <String, dynamic>{
|
final Map<String, dynamic> json = <String, dynamic>{
|
||||||
'hello': 'world',
|
'hello': 'world',
|
||||||
};
|
};
|
||||||
|
const String msvcVersion = '';
|
||||||
|
|
||||||
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json);
|
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json, msvcVersion);
|
||||||
|
|
||||||
expect(result.installationPath, null);
|
expect(result.installationPath, null);
|
||||||
expect(result.displayName, null);
|
expect(result.displayName, null);
|
||||||
expect(result.fullVersion, null);
|
expect(result.fullVersion, null);
|
||||||
expect(result.isComplete, null);
|
expect(result.isComplete, null);
|
||||||
expect(result.isLaunchable, null);
|
expect(result.isLaunchable, null);
|
||||||
expect(result.isRebootRequired, null);
|
expect(result.isRebootRequired, null);
|
||||||
expect(result.isPrerelease, null);
|
expect(result.isPrerelease, null);
|
||||||
expect(result.catalogDisplayVersion, null);
|
expect(result.catalogDisplayVersion, null);
|
||||||
expect(result.isUsable, isTrue);
|
expect(result.isUsable, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Accepts JSON', () {
|
test('Accepts JSON', () {
|
||||||
const bool meetsRequirements = true;
|
const bool meetsRequirements = true;
|
||||||
|
const String msvcVersion = '';
|
||||||
|
|
||||||
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, _defaultResponse);
|
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, _defaultResponse, msvcVersion);
|
||||||
|
|
||||||
expect(result.installationPath, visualStudioPath);
|
expect(result.installationPath, visualStudioPath);
|
||||||
expect(result.displayName, 'Visual Studio Community 2019');
|
expect(result.displayName, 'Visual Studio Community 2019');
|
||||||
expect(result.fullVersion, '16.2.29306.81');
|
expect(result.fullVersion, '16.2.29306.81');
|
||||||
expect(result.isComplete, true);
|
expect(result.isComplete, true);
|
||||||
expect(result.isLaunchable, true);
|
expect(result.isLaunchable, true);
|
||||||
expect(result.isRebootRequired, false);
|
expect(result.isRebootRequired, false);
|
||||||
expect(result.isPrerelease, false);
|
expect(result.isPrerelease, false);
|
||||||
expect(result.catalogDisplayVersion, '16.2.5');
|
expect(result.catalogDisplayVersion, '16.2.5');
|
||||||
expect(result.isUsable, isTrue);
|
expect(result.isUsable, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Installation that does not satisfy requirements is not usable', () {
|
test('Installation that does not satisfy requirements is not usable', () {
|
||||||
const bool meetsRequirements = false;
|
const bool meetsRequirements = false;
|
||||||
|
const String msvcVersion = '';
|
||||||
|
|
||||||
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, _defaultResponse);
|
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, _defaultResponse, msvcVersion);
|
||||||
|
|
||||||
expect(result.isUsable, isFalse);
|
expect(result.isUsable, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Incomplete installation is not usable', () {
|
test('Incomplete installation is not usable', () {
|
||||||
const bool meetsRequirements = true;
|
const bool meetsRequirements = true;
|
||||||
final Map<String, dynamic> json = Map<String, dynamic>.of(_defaultResponse)
|
final Map<String, dynamic> json = Map<String, dynamic>.of(_defaultResponse)..['isComplete'] = false;
|
||||||
..['isComplete'] = false;
|
const String msvcVersion = '';
|
||||||
|
|
||||||
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json);
|
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json, msvcVersion);
|
||||||
|
|
||||||
expect(result.isUsable, isFalse);
|
expect(result.isUsable, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unlaunchable installation is not usable', () {
|
test('Unlaunchable installation is not usable', () {
|
||||||
const bool meetsRequirements = true;
|
const bool meetsRequirements = true;
|
||||||
final Map<String, dynamic> json = Map<String, dynamic>.of(_defaultResponse)
|
final Map<String, dynamic> json = Map<String, dynamic>.of(_defaultResponse)..['isLaunchable'] = false;
|
||||||
..['isLaunchable'] = false;
|
const String msvcVersion = '';
|
||||||
|
|
||||||
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json);
|
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json, msvcVersion);
|
||||||
|
|
||||||
expect(result.isUsable, isFalse);
|
expect(result.isUsable, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Installation that requires reboot is not usable', () {
|
test('Installation that requires reboot is not usable', () {
|
||||||
const bool meetsRequirements = true;
|
const bool meetsRequirements = true;
|
||||||
final Map<String, dynamic> json = Map<String, dynamic>.of(_defaultResponse)
|
final Map<String, dynamic> json = Map<String, dynamic>.of(_defaultResponse)..['isRebootRequired'] = true;
|
||||||
..['isRebootRequired'] = true;
|
const String msvcVersion = '';
|
||||||
|
|
||||||
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json);
|
final VswhereDetails result = VswhereDetails.fromJson(meetsRequirements, json, msvcVersion);
|
||||||
|
|
||||||
expect(result.isUsable, isFalse);
|
expect(result.isUsable, isFalse);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
|||||||
|
|
||||||
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() {
|
|||||||
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() {
|
|||||||
'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) {
|
|||||||
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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user