Skip to content

Commit 434db67

Browse files
authored
[native_toolchain_c] Support Clang on Windows (#1893)
1 parent 8882a55 commit 434db67

File tree

5 files changed

+137
-75
lines changed

5 files changed

+137
-75
lines changed

pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ class RunCBuilder {
224224
'${targetAndroidNdkApi!}',
225225
'--sysroot=${androidSysroot(toolInstance).toFilePath()}',
226226
],
227+
if (codeConfig.targetOS == OS.windows)
228+
'--target=${clangWindowsTargetFlags[architecture]!}',
227229
if (codeConfig.targetOS == OS.macOS)
228230
'--target=${appleClangMacosTargetFlags[architecture]!}',
229231
if (codeConfig.targetOS == OS.iOS)
@@ -244,7 +246,8 @@ class RunCBuilder {
244246
installName!.toFilePath(),
245247
],
246248
if (pic != null)
247-
if (toolInstance.tool.isClangLike) ...[
249+
if (toolInstance.tool.isClangLike &&
250+
codeConfig.targetOS != OS.windows) ...[
248251
if (pic!) ...[
249252
if (dynamicLibrary != null) '-fPIC',
250253
// Using PIC for static libraries allows them to be linked into
@@ -419,6 +422,12 @@ class RunCBuilder {
419422
},
420423
};
421424

425+
static const clangWindowsTargetFlags = {
426+
Architecture.arm64: 'arm64-pc-windows-msvc',
427+
Architecture.ia32: 'i386-pc-windows-msvc',
428+
Architecture.x64: 'x86_64-pc-windows-msvc',
429+
};
430+
422431
static const defaultCppLinkStdLib = {
423432
OS.android: 'c++_shared',
424433
OS.fuchsia: 'c++',

pkgs/native_toolchain_c/lib/src/native_toolchain/clang.dart

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:native_assets_cli/code_assets.dart';
6+
57
import '../tool/tool.dart';
68
import '../tool/tool_resolver.dart';
9+
import 'msvc.dart';
710

811
/// The Clang compiler.
912
///
@@ -14,10 +17,23 @@ final Tool clang = Tool(
1417
wrappedResolver: CliFilter(
1518
cliArguments: ['--version'],
1619
keepIf: ({required String stdout}) => !stdout.contains('Apple clang'),
17-
wrappedResolver: PathToolResolver(
18-
toolName: 'Clang',
19-
executableName: 'clang',
20-
),
20+
wrappedResolver: ToolResolvers([
21+
PathToolResolver(
22+
toolName: 'Clang',
23+
executableName: OS.current.executableFileName('clang'),
24+
),
25+
RelativeToolResolver(
26+
toolName: 'Clang',
27+
wrappedResolver: visualStudio.defaultResolver!,
28+
relativePath: Uri(path: './VC/Tools/Llvm/bin/clang.exe'),
29+
),
30+
InstallLocationResolver(
31+
toolName: 'Clang',
32+
paths: [
33+
'C:/Program Files/LLVM/bin/clang.exe',
34+
],
35+
),
36+
]),
2137
),
2238
),
2339
);
@@ -32,7 +48,7 @@ final Tool llvmAr = Tool(
3248
RelativeToolResolver(
3349
toolName: 'LLVM archiver',
3450
wrappedResolver: clang.defaultResolver!,
35-
relativePath: Uri.file('llvm-ar'),
51+
relativePath: Uri.file(OS.current.executableFileName('llvm-ar')),
3652
),
3753
]),
3854
),
@@ -48,7 +64,7 @@ final Tool lld = Tool(
4864
RelativeToolResolver(
4965
toolName: 'LLD',
5066
wrappedResolver: clang.defaultResolver!,
51-
relativePath: Uri.file('ld.lld'),
67+
relativePath: Uri.file(OS.current.executableFileName('ld.lld')),
5268
),
5369
]),
5470
),

pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart

Lines changed: 93 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,36 @@ library;
1111
import 'dart:io';
1212

1313
import 'package:native_toolchain_c/native_toolchain_c.dart';
14+
import 'package:native_toolchain_c/src/native_toolchain/clang.dart';
1415
import 'package:native_toolchain_c/src/native_toolchain/msvc.dart';
1516
import 'package:native_toolchain_c/src/utils/run_process.dart';
1617
import 'package:test/test.dart';
1718

1819
import '../helpers.dart';
1920

20-
void main() {
21+
void main() async {
2122
if (!Platform.isWindows) {
2223
// Avoid needing status files on Dart SDK CI.
2324
return;
2425
}
2526

27+
final compilers = {
28+
// Either provided to be MSVC or null which defaults to MSVC.
29+
msvc: () async => cCompiler,
30+
// Clang on Windows.
31+
clang: () async => CCompilerConfig(
32+
archiver:
33+
(await llvmAr.defaultResolver!.resolve(logger: logger)).first.uri,
34+
compiler:
35+
(await clang.defaultResolver!.resolve(logger: logger)).first.uri,
36+
linker:
37+
(await lld.defaultResolver!.resolve(logger: logger)).first.uri,
38+
)
39+
};
40+
2641
const targets = [
42+
// TODO(https://github.com/dart-lang/native/issues/170): Support arm64.
43+
// Architecture.arm64,
2744
Architecture.ia32,
2845
Architecture.x64,
2946
];
@@ -36,86 +53,95 @@ void main() {
3653
});
3754

3855
const dumpbinMachine = {
56+
Architecture.arm64: 'ARM64',
3957
Architecture.ia32: 'x86',
4058
Architecture.x64: 'x64',
4159
};
4260

4361
const optimizationLevels = OptimizationLevel.values;
4462
var selectOptimizationLevel = 0;
63+
var selectBuildMode = 0;
4564

4665
final dumpbinFileType = {
4766
DynamicLoadingBundled(): 'DLL',
4867
StaticLinking(): 'LIBRARY',
4968
};
5069

51-
for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
52-
for (final target in targets) {
53-
// Cycle through all optimization levels.
54-
final optimizationLevel = optimizationLevels[selectOptimizationLevel];
55-
selectOptimizationLevel =
56-
(selectOptimizationLevel + 1) % optimizationLevels.length;
57-
test('CBuilder $linkMode library $target $optimizationLevel', () async {
58-
final tempUri = await tempDirForTest();
59-
final tempUri2 = await tempDirForTest();
60-
final addCUri =
61-
packageUri.resolve('test/cbuilder/testfiles/add/src/add.c');
62-
const name = 'add';
63-
64-
final buildInputBuilder = BuildInputBuilder()
65-
..setupShared(
66-
packageName: name,
67-
packageRoot: tempUri,
68-
outputFile: tempUri.resolve('output.json'),
69-
outputDirectory: tempUri,
70-
outputDirectoryShared: tempUri2,
71-
)
72-
..config.setupBuild(
73-
linkingEnabled: false,
74-
dryRun: false,
75-
)
76-
..config.setupShared(buildAssetTypes: [CodeAsset.type])
77-
..config.setupCode(
78-
targetOS: OS.windows,
79-
targetArchitecture: target,
80-
linkModePreference: linkMode == DynamicLoadingBundled()
81-
? LinkModePreference.dynamic
82-
: LinkModePreference.static,
83-
cCompiler: cCompiler,
70+
for (final compiler in compilers.keys) {
71+
for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
72+
for (final target in targets) {
73+
// Cycle through all optimization levels.
74+
final optimizationLevel = optimizationLevels[selectOptimizationLevel];
75+
selectOptimizationLevel =
76+
(selectOptimizationLevel + 1) % optimizationLevels.length;
77+
final buildMode = BuildMode.values[selectBuildMode];
78+
selectBuildMode = (selectBuildMode + 1) % BuildMode.values.length;
79+
test(
80+
'CBuilder ${compiler.name} $linkMode library $target'
81+
' $optimizationLevel $buildMode', () async {
82+
final tempUri = await tempDirForTest();
83+
final tempUri2 = await tempDirForTest();
84+
final addCUri =
85+
packageUri.resolve('test/cbuilder/testfiles/add/src/add.c');
86+
const name = 'add';
87+
88+
final buildInputBuilder = BuildInputBuilder()
89+
..setupShared(
90+
packageName: name,
91+
packageRoot: tempUri,
92+
outputFile: tempUri.resolve('output.json'),
93+
outputDirectory: tempUri,
94+
outputDirectoryShared: tempUri2,
95+
)
96+
..config.setupBuild(
97+
linkingEnabled: false,
98+
dryRun: false,
99+
)
100+
..config.setupShared(buildAssetTypes: [CodeAsset.type])
101+
..config.setupCode(
102+
targetOS: OS.windows,
103+
targetArchitecture: target,
104+
linkModePreference: linkMode == DynamicLoadingBundled()
105+
? LinkModePreference.dynamic
106+
: LinkModePreference.static,
107+
cCompiler: await (compilers[compiler]!)(),
108+
);
109+
110+
final buildInput = BuildInput(buildInputBuilder.json);
111+
final buildOutput = BuildOutputBuilder();
112+
113+
final cbuilder = CBuilder.library(
114+
name: name,
115+
assetName: name,
116+
sources: [addCUri.toFilePath()],
117+
optimizationLevel: optimizationLevel,
118+
buildMode: buildMode,
119+
);
120+
await cbuilder.run(
121+
input: buildInput,
122+
output: buildOutput,
123+
logger: logger,
84124
);
85125

86-
final buildInput = BuildInput(buildInputBuilder.json);
87-
final buildOutput = BuildOutputBuilder();
88-
89-
final cbuilder = CBuilder.library(
90-
name: name,
91-
assetName: name,
92-
sources: [addCUri.toFilePath()],
93-
optimizationLevel: optimizationLevel,
94-
buildMode: BuildMode.release,
95-
);
96-
await cbuilder.run(
97-
input: buildInput,
98-
output: buildOutput,
99-
logger: logger,
100-
);
101-
102-
final libUri =
103-
tempUri.resolve(OS.windows.libraryFileName(name, linkMode));
104-
expect(await File.fromUri(libUri).exists(), true);
105-
final result = await runProcess(
106-
executable: dumpbinUri,
107-
arguments: ['/HEADERS', libUri.toFilePath()],
108-
logger: logger,
109-
);
110-
expect(result.exitCode, 0);
111-
final machine =
112-
result.stdout.split('\n').firstWhere((e) => e.contains('machine'));
113-
expect(machine, contains(dumpbinMachine[target]));
114-
final fileType = result.stdout
115-
.split('\n')
116-
.firstWhere((e) => e.contains('File Type'));
117-
expect(fileType, contains(dumpbinFileType[linkMode]));
118-
});
126+
final libUri =
127+
tempUri.resolve(OS.windows.libraryFileName(name, linkMode));
128+
expect(await File.fromUri(libUri).exists(), true);
129+
final result = await runProcess(
130+
executable: dumpbinUri,
131+
arguments: ['/HEADERS', libUri.toFilePath()],
132+
logger: logger,
133+
);
134+
expect(result.exitCode, 0);
135+
final machine = result.stdout
136+
.split('\n')
137+
.firstWhere((e) => e.contains('machine'));
138+
expect(machine, contains(dumpbinMachine[target]));
139+
final fileType = result.stdout
140+
.split('\n')
141+
.firstWhere((e) => e.contains('File Type'));
142+
expect(fileType, contains(dumpbinFileType[linkMode]));
143+
});
144+
}
119145
}
120146
}
121147
}

pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
#ifdef DEBUG
88
#include <stdio.h>
9+
10+
#if _WIN32
11+
#include <wchar.h>
12+
#endif
913
#endif
1014

1115
#if _WIN32
@@ -17,6 +21,9 @@
1721
FFI_EXPORT int32_t add(int32_t a, int32_t b) {
1822
#ifdef DEBUG
1923
printf("Adding %i and %i.\n", a, b);
24+
#if _WIN32
25+
wprintf("Adding %i and %i.\n", a, b);
26+
#endif
2027
#endif
2128
return a + b;
2229
}

pkgs/native_toolchain_c/test/native_toolchain/clang_test.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ import 'package:test/test.dart';
1616
import '../helpers.dart';
1717

1818
void main() {
19-
if (!Platform.isLinux) {
19+
if (Platform.isMacOS ||
20+
(Platform.isWindows &&
21+
Platform.environment['DART_HOOK_TESTING_C_COMPILER__CC']
22+
?.endsWith('cl.exe') ==
23+
true)) {
2024
// Avoid needing status files on Dart SDK CI.
2125
return;
2226
}

0 commit comments

Comments
 (0)