Skip to content

Commit ff4a0f6

Browse files
authored
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: * flutter/flutter#129757 ### Implementation details for Windows. Mainly follows the design of flutter/flutter#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`.
1 parent 3e7c388 commit ff4a0f6

20 files changed

+1208
-343
lines changed

packages/flutter_tools/lib/src/build_system/targets/native_assets.dart

+24-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import '../../linux/native_assets.dart';
1616
import '../../macos/native_assets.dart';
1717
import '../../macos/xcode.dart';
1818
import '../../native_assets.dart';
19+
import '../../windows/native_assets.dart';
1920
import '../build_system.dart';
2021
import '../depfile.dart';
2122
import '../exceptions.dart';
@@ -134,6 +135,20 @@ class NativeAssets extends Target {
134135
fileSystem: fileSystem,
135136
buildRunner: buildRunner,
136137
);
138+
case TargetPlatform.windows_x64:
139+
final String? environmentBuildMode = environment.defines[kBuildMode];
140+
if (environmentBuildMode == null) {
141+
throw MissingDefineException(kBuildMode, name);
142+
}
143+
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
144+
(_, dependencies) = await buildNativeAssetsWindows(
145+
targetPlatform: targetPlatform,
146+
buildMode: buildMode,
147+
projectUri: projectUri,
148+
yamlParentDirectory: environment.buildDir.uri,
149+
fileSystem: fileSystem,
150+
buildRunner: buildRunner,
151+
);
137152
case TargetPlatform.tester:
138153
if (const LocalPlatform().isMacOS) {
139154
(_, dependencies) = await buildNativeAssetsMacOS(
@@ -154,6 +169,15 @@ class NativeAssets extends Target {
154169
buildRunner: buildRunner,
155170
flutterTester: true,
156171
);
172+
} else if (const LocalPlatform().isWindows) {
173+
(_, dependencies) = await buildNativeAssetsWindows(
174+
buildMode: BuildMode.debug,
175+
projectUri: projectUri,
176+
yamlParentDirectory: environment.buildDir.uri,
177+
fileSystem: fileSystem,
178+
buildRunner: buildRunner,
179+
flutterTester: true,
180+
);
157181
} else {
158182
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
159183
// Write the file we claim to have in the [outputs].
@@ -168,7 +192,6 @@ class NativeAssets extends Target {
168192
case TargetPlatform.fuchsia_arm64:
169193
case TargetPlatform.fuchsia_x64:
170194
case TargetPlatform.web_javascript:
171-
case TargetPlatform.windows_x64:
172195
// TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
173196
// Write the file we claim to have in the [outputs].
174197
await writeNativeAssetsYaml(<Asset>[], environment.buildDir.uri, fileSystem);

packages/flutter_tools/lib/src/ios/native_assets.dart

+9-9
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ Future<Uri?> dryRunNativeAssetsIOS({
2727
return null;
2828
}
2929

30-
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, OS.iOS);
30+
final Uri buildUri = nativeAssetsBuildUri(projectUri, OS.iOS);
3131
final Iterable<Asset> assetTargetLocations = await dryRunNativeAssetsIOSInternal(
3232
fileSystem,
3333
projectUri,
3434
buildRunner,
3535
);
3636
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
3737
assetTargetLocations,
38-
buildUri_,
38+
buildUri,
3939
fileSystem,
4040
);
4141
return nativeAssetsUri;
@@ -46,17 +46,17 @@ Future<Iterable<Asset>> dryRunNativeAssetsIOSInternal(
4646
Uri projectUri,
4747
NativeAssetsBuildRunner buildRunner,
4848
) async {
49-
const OS targetOs = OS.iOS;
50-
globals.logger.printTrace('Dry running native assets for $targetOs.');
49+
const OS targetOS = OS.iOS;
50+
globals.logger.printTrace('Dry running native assets for $targetOS.');
5151
final List<Asset> nativeAssets = (await buildRunner.dryRun(
5252
linkModePreference: LinkModePreference.dynamic,
53-
targetOs: targetOs,
53+
targetOS: targetOS,
5454
workingDirectory: projectUri,
5555
includeParentEnvironment: true,
5656
))
5757
.assets;
5858
ensureNoLinkModeStatic(nativeAssets);
59-
globals.logger.printTrace('Dry running native assets for $targetOs done.');
59+
globals.logger.printTrace('Dry running native assets for $targetOS done.');
6060
final Iterable<Asset> assetTargetLocations = _assetTargetLocations(nativeAssets).values;
6161
return assetTargetLocations;
6262
}
@@ -80,8 +80,8 @@ Future<List<Uri>> buildNativeAssetsIOS({
8080
final List<Target> targets = darwinArchs.map(_getNativeTarget).toList();
8181
final native_assets_cli.BuildMode buildModeCli = nativeAssetsBuildMode(buildMode);
8282

83-
const OS targetOs = OS.iOS;
84-
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs);
83+
const OS targetOS = OS.iOS;
84+
final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
8585
final IOSSdk iosSdk = _getIOSSdk(environmentType);
8686

8787
globals.logger.printTrace('Building native assets for $targets $buildModeCli.');
@@ -104,7 +104,7 @@ Future<List<Uri>> buildNativeAssetsIOS({
104104
globals.logger.printTrace('Building native assets for $targets done.');
105105
final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets);
106106
await copyNativeAssetsMacOSHost(
107-
buildUri_,
107+
buildUri,
108108
fatAssetTargetLocations,
109109
codesignIdentity,
110110
buildMode,

packages/flutter_tools/lib/src/linux/native_assets.dart

+24-164
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'package:native_assets_builder/native_assets_builder.dart' show BuildResult;
65
import 'package:native_assets_cli/native_assets_cli.dart' hide BuildMode;
7-
import 'package:native_assets_cli/native_assets_cli.dart' as native_assets_cli;
86

97
import '../base/common.dart';
108
import '../base/file_system.dart';
@@ -22,58 +20,31 @@ Future<Uri?> dryRunNativeAssetsLinux({
2220
required Uri projectUri,
2321
bool flutterTester = false,
2422
required FileSystem fileSystem,
25-
}) async {
26-
if (!await nativeBuildRequired(buildRunner)) {
27-
return null;
28-
}
29-
30-
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, OS.linux);
31-
final Iterable<Asset> nativeAssetPaths = await dryRunNativeAssetsLinuxInternal(
32-
fileSystem,
33-
projectUri,
34-
flutterTester,
35-
buildRunner,
36-
);
37-
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
38-
nativeAssetPaths,
39-
buildUri_,
40-
fileSystem,
23+
}) {
24+
return dryRunNativeAssetsSingleArchitecture(
25+
buildRunner: buildRunner,
26+
projectUri: projectUri,
27+
flutterTester: flutterTester,
28+
fileSystem: fileSystem,
29+
os: OS.linux,
4130
);
42-
return nativeAssetsUri;
4331
}
4432

4533
Future<Iterable<Asset>> dryRunNativeAssetsLinuxInternal(
4634
FileSystem fileSystem,
4735
Uri projectUri,
4836
bool flutterTester,
4937
NativeAssetsBuildRunner buildRunner,
50-
) async {
51-
const OS targetOs = OS.linux;
52-
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs);
53-
54-
globals.logger.printTrace('Dry running native assets for $targetOs.');
55-
final List<Asset> nativeAssets = (await buildRunner.dryRun(
56-
linkModePreference: LinkModePreference.dynamic,
57-
targetOs: targetOs,
58-
workingDirectory: projectUri,
59-
includeParentEnvironment: true,
60-
))
61-
.assets;
62-
ensureNoLinkModeStatic(nativeAssets);
63-
globals.logger.printTrace('Dry running native assets for $targetOs done.');
64-
final Uri? absolutePath = flutterTester ? buildUri_ : null;
65-
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath);
66-
final Iterable<Asset> nativeAssetPaths = assetTargetLocations.values;
67-
return nativeAssetPaths;
38+
) {
39+
return dryRunNativeAssetsSingleArchitectureInternal(
40+
fileSystem,
41+
projectUri,
42+
flutterTester,
43+
buildRunner,
44+
OS.linux,
45+
);
6846
}
6947

70-
/// Builds native assets.
71-
///
72-
/// If [targetPlatform] is omitted, the current target architecture is used.
73-
///
74-
/// If [flutterTester] is true, absolute paths are emitted in the native
75-
/// assets mapping. This can be used for JIT mode without sandbox on the host.
76-
/// This is used in `flutter test` and `flutter run -d flutter-tester`.
7748
Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsLinux({
7849
required NativeAssetsBuildRunner buildRunner,
7950
TargetPlatform? targetPlatform,
@@ -82,127 +53,16 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsLinux({
8253
bool flutterTester = false,
8354
Uri? yamlParentDirectory,
8455
required FileSystem fileSystem,
85-
}) async {
86-
const OS targetOs = OS.linux;
87-
final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOs);
88-
final Directory buildDir = fileSystem.directory(buildUri_);
89-
if (!await buildDir.exists()) {
90-
// CMake requires the folder to exist to do copying.
91-
await buildDir.create(recursive: true);
92-
}
93-
if (!await nativeBuildRequired(buildRunner)) {
94-
final Uri nativeAssetsYaml = await writeNativeAssetsYaml(<Asset>[], yamlParentDirectory ?? buildUri_, fileSystem);
95-
return (nativeAssetsYaml, <Uri>[]);
96-
}
97-
98-
final Target target = targetPlatform != null ? _getNativeTarget(targetPlatform) : Target.current;
99-
final native_assets_cli.BuildMode buildModeCli = nativeAssetsBuildMode(buildMode);
100-
101-
globals.logger.printTrace('Building native assets for $target $buildModeCli.');
102-
final BuildResult result = await buildRunner.build(
103-
linkModePreference: LinkModePreference.dynamic,
104-
target: target,
105-
buildMode: buildModeCli,
106-
workingDirectory: projectUri,
107-
includeParentEnvironment: true,
108-
cCompilerConfig: await buildRunner.cCompilerConfig,
109-
);
110-
final List<Asset> nativeAssets = result.assets;
111-
final Set<Uri> dependencies = result.dependencies.toSet();
112-
ensureNoLinkModeStatic(nativeAssets);
113-
globals.logger.printTrace('Building native assets for $target done.');
114-
final Uri? absolutePath = flutterTester ? buildUri_ : null;
115-
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath);
116-
await _copyNativeAssetsLinux(
117-
buildUri_,
118-
assetTargetLocations,
119-
buildMode,
120-
fileSystem,
121-
);
122-
final Uri nativeAssetsUri = await writeNativeAssetsYaml(
123-
assetTargetLocations.values,
124-
yamlParentDirectory ?? buildUri_,
125-
fileSystem,
56+
}) {
57+
return buildNativeAssetsSingleArchitecture(
58+
buildRunner: buildRunner,
59+
targetPlatform: targetPlatform,
60+
projectUri: projectUri,
61+
buildMode: buildMode,
62+
flutterTester: flutterTester,
63+
yamlParentDirectory: yamlParentDirectory,
64+
fileSystem: fileSystem,
12665
);
127-
return (nativeAssetsUri, dependencies.toList());
128-
}
129-
130-
Map<Asset, Asset> _assetTargetLocations(
131-
List<Asset> nativeAssets,
132-
Uri? absolutePath,
133-
) =>
134-
<Asset, Asset>{
135-
for (final Asset asset in nativeAssets) asset: _targetLocationLinux(asset, absolutePath),
136-
};
137-
138-
Asset _targetLocationLinux(Asset asset, Uri? absolutePath) {
139-
final AssetPath path = asset.path;
140-
switch (path) {
141-
case AssetSystemPath _:
142-
case AssetInExecutable _:
143-
case AssetInProcess _:
144-
return asset;
145-
case AssetAbsolutePath _:
146-
final String fileName = path.uri.pathSegments.last;
147-
Uri uri;
148-
if (absolutePath != null) {
149-
// Flutter tester needs full host paths.
150-
uri = absolutePath.resolve(fileName);
151-
} else {
152-
// Flutter Desktop needs "absolute" paths inside the app.
153-
// "relative" in the context of native assets would be relative to the
154-
// kernel or aot snapshot.
155-
uri = Uri(path: fileName);
156-
}
157-
return asset.copyWith(path: AssetAbsolutePath(uri));
158-
}
159-
throw Exception('Unsupported asset path type ${path.runtimeType} in asset $asset');
160-
}
161-
162-
/// Extract the [Target] from a [TargetPlatform].
163-
Target _getNativeTarget(TargetPlatform targetPlatform) {
164-
switch (targetPlatform) {
165-
case TargetPlatform.linux_x64:
166-
return Target.linuxX64;
167-
case TargetPlatform.linux_arm64:
168-
return Target.linuxArm64;
169-
case TargetPlatform.android:
170-
case TargetPlatform.ios:
171-
case TargetPlatform.darwin:
172-
case TargetPlatform.windows_x64:
173-
case TargetPlatform.fuchsia_arm64:
174-
case TargetPlatform.fuchsia_x64:
175-
case TargetPlatform.tester:
176-
case TargetPlatform.web_javascript:
177-
case TargetPlatform.android_arm:
178-
case TargetPlatform.android_arm64:
179-
case TargetPlatform.android_x64:
180-
case TargetPlatform.android_x86:
181-
throw Exception('Unknown targetPlatform: $targetPlatform.');
182-
}
183-
}
184-
185-
Future<void> _copyNativeAssetsLinux(
186-
Uri buildUri,
187-
Map<Asset, Asset> assetTargetLocations,
188-
BuildMode buildMode,
189-
FileSystem fileSystem,
190-
) async {
191-
if (assetTargetLocations.isNotEmpty) {
192-
globals.logger.printTrace('Copying native assets to ${buildUri.toFilePath()}.');
193-
final Directory buildDir = fileSystem.directory(buildUri.toFilePath());
194-
if (!buildDir.existsSync()) {
195-
buildDir.createSync(recursive: true);
196-
}
197-
for (final MapEntry<Asset, Asset> assetMapping in assetTargetLocations.entries) {
198-
final Uri source = (assetMapping.key.path as AssetAbsolutePath).uri;
199-
final Uri target = (assetMapping.value.path as AssetAbsolutePath).uri;
200-
final Uri targetUri = buildUri.resolveUri(target);
201-
final String targetFullPath = targetUri.toFilePath();
202-
await fileSystem.file(source).copy(targetFullPath);
203-
}
204-
globals.logger.printTrace('Copying native assets done.');
205-
}
20666
}
20767

20868
/// Flutter expects `clang++` to be on the path on Linux hosts.

0 commit comments

Comments
 (0)