diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart index 76afc334d..246d7c95d 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart @@ -224,6 +224,8 @@ class RunCBuilder { '${targetAndroidNdkApi!}', '--sysroot=${androidSysroot(toolInstance).toFilePath()}', ], + if (codeConfig.targetOS == OS.windows) + '--target=${clangWindowsTargetFlags[architecture]!}', if (codeConfig.targetOS == OS.macOS) '--target=${appleClangMacosTargetFlags[architecture]!}', if (codeConfig.targetOS == OS.iOS) @@ -244,7 +246,8 @@ class RunCBuilder { installName!.toFilePath(), ], if (pic != null) - if (toolInstance.tool.isClangLike) ...[ + if (toolInstance.tool.isClangLike && + codeConfig.targetOS != OS.windows) ...[ if (pic!) ...[ if (dynamicLibrary != null) '-fPIC', // Using PIC for static libraries allows them to be linked into @@ -419,6 +422,12 @@ class RunCBuilder { }, }; + static const clangWindowsTargetFlags = { + Architecture.arm64: 'arm64-pc-windows-msvc', + Architecture.ia32: 'i386-pc-windows-msvc', + Architecture.x64: 'x86_64-pc-windows-msvc', + }; + static const defaultCppLinkStdLib = { OS.android: 'c++_shared', OS.fuchsia: 'c++', diff --git a/pkgs/native_toolchain_c/lib/src/native_toolchain/clang.dart b/pkgs/native_toolchain_c/lib/src/native_toolchain/clang.dart index 910e3b33c..0b31639e8 100644 --- a/pkgs/native_toolchain_c/lib/src/native_toolchain/clang.dart +++ b/pkgs/native_toolchain_c/lib/src/native_toolchain/clang.dart @@ -2,8 +2,11 @@ // for details. 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/code_assets.dart'; + import '../tool/tool.dart'; import '../tool/tool_resolver.dart'; +import 'msvc.dart'; /// The Clang compiler. /// @@ -14,10 +17,23 @@ final Tool clang = Tool( wrappedResolver: CliFilter( cliArguments: ['--version'], keepIf: ({required String stdout}) => !stdout.contains('Apple clang'), - wrappedResolver: PathToolResolver( - toolName: 'Clang', - executableName: 'clang', - ), + wrappedResolver: ToolResolvers([ + PathToolResolver( + toolName: 'Clang', + executableName: OS.current.executableFileName('clang'), + ), + RelativeToolResolver( + toolName: 'Clang', + wrappedResolver: visualStudio.defaultResolver!, + relativePath: Uri(path: './VC/Tools/Llvm/bin/clang.exe'), + ), + InstallLocationResolver( + toolName: 'Clang', + paths: [ + 'C:/Program Files/LLVM/bin/clang.exe', + ], + ), + ]), ), ), ); @@ -32,7 +48,7 @@ final Tool llvmAr = Tool( RelativeToolResolver( toolName: 'LLVM archiver', wrappedResolver: clang.defaultResolver!, - relativePath: Uri.file('llvm-ar'), + relativePath: Uri.file(OS.current.executableFileName('llvm-ar')), ), ]), ), @@ -48,7 +64,7 @@ final Tool lld = Tool( RelativeToolResolver( toolName: 'LLD', wrappedResolver: clang.defaultResolver!, - relativePath: Uri.file('ld.lld'), + relativePath: Uri.file(OS.current.executableFileName('ld.lld')), ), ]), ), diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart index f3f875401..01aeb6203 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart @@ -11,19 +11,36 @@ library; import 'dart:io'; import 'package:native_toolchain_c/native_toolchain_c.dart'; +import 'package:native_toolchain_c/src/native_toolchain/clang.dart'; import 'package:native_toolchain_c/src/native_toolchain/msvc.dart'; import 'package:native_toolchain_c/src/utils/run_process.dart'; import 'package:test/test.dart'; import '../helpers.dart'; -void main() { +void main() async { if (!Platform.isWindows) { // Avoid needing status files on Dart SDK CI. return; } + final compilers = { + // Either provided to be MSVC or null which defaults to MSVC. + msvc: () async => cCompiler, + // Clang on Windows. + clang: () async => CCompilerConfig( + archiver: + (await llvmAr.defaultResolver!.resolve(logger: logger)).first.uri, + compiler: + (await clang.defaultResolver!.resolve(logger: logger)).first.uri, + linker: + (await lld.defaultResolver!.resolve(logger: logger)).first.uri, + ) + }; + const targets = [ + // TODO(https://github.com/dart-lang/native/issues/170): Support arm64. + // Architecture.arm64, Architecture.ia32, Architecture.x64, ]; @@ -36,86 +53,95 @@ void main() { }); const dumpbinMachine = { + Architecture.arm64: 'ARM64', Architecture.ia32: 'x86', Architecture.x64: 'x64', }; const optimizationLevels = OptimizationLevel.values; var selectOptimizationLevel = 0; + var selectBuildMode = 0; final dumpbinFileType = { DynamicLoadingBundled(): 'DLL', StaticLinking(): 'LIBRARY', }; - for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) { - for (final target in targets) { - // Cycle through all optimization levels. - final optimizationLevel = optimizationLevels[selectOptimizationLevel]; - selectOptimizationLevel = - (selectOptimizationLevel + 1) % optimizationLevels.length; - test('CBuilder $linkMode library $target $optimizationLevel', () async { - final tempUri = await tempDirForTest(); - final tempUri2 = await tempDirForTest(); - final addCUri = - packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); - const name = 'add'; - - final buildInputBuilder = BuildInputBuilder() - ..setupShared( - packageName: name, - packageRoot: tempUri, - outputFile: tempUri.resolve('output.json'), - outputDirectory: tempUri, - outputDirectoryShared: tempUri2, - ) - ..config.setupBuild( - linkingEnabled: false, - dryRun: false, - ) - ..config.setupShared(buildAssetTypes: [CodeAsset.type]) - ..config.setupCode( - targetOS: OS.windows, - targetArchitecture: target, - linkModePreference: linkMode == DynamicLoadingBundled() - ? LinkModePreference.dynamic - : LinkModePreference.static, - cCompiler: cCompiler, + for (final compiler in compilers.keys) { + for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) { + for (final target in targets) { + // Cycle through all optimization levels. + final optimizationLevel = optimizationLevels[selectOptimizationLevel]; + selectOptimizationLevel = + (selectOptimizationLevel + 1) % optimizationLevels.length; + final buildMode = BuildMode.values[selectBuildMode]; + selectBuildMode = (selectBuildMode + 1) % BuildMode.values.length; + test( + 'CBuilder ${compiler.name} $linkMode library $target' + ' $optimizationLevel $buildMode', () async { + final tempUri = await tempDirForTest(); + final tempUri2 = await tempDirForTest(); + final addCUri = + packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); + const name = 'add'; + + final buildInputBuilder = BuildInputBuilder() + ..setupShared( + packageName: name, + packageRoot: tempUri, + outputFile: tempUri.resolve('output.json'), + outputDirectory: tempUri, + outputDirectoryShared: tempUri2, + ) + ..config.setupBuild( + linkingEnabled: false, + dryRun: false, + ) + ..config.setupShared(buildAssetTypes: [CodeAsset.type]) + ..config.setupCode( + targetOS: OS.windows, + targetArchitecture: target, + linkModePreference: linkMode == DynamicLoadingBundled() + ? LinkModePreference.dynamic + : LinkModePreference.static, + cCompiler: await (compilers[compiler]!)(), + ); + + final buildInput = BuildInput(buildInputBuilder.json); + final buildOutput = BuildOutputBuilder(); + + final cbuilder = CBuilder.library( + name: name, + assetName: name, + sources: [addCUri.toFilePath()], + optimizationLevel: optimizationLevel, + buildMode: buildMode, + ); + await cbuilder.run( + input: buildInput, + output: buildOutput, + logger: logger, ); - final buildInput = BuildInput(buildInputBuilder.json); - final buildOutput = BuildOutputBuilder(); - - final cbuilder = CBuilder.library( - name: name, - assetName: name, - sources: [addCUri.toFilePath()], - optimizationLevel: optimizationLevel, - buildMode: BuildMode.release, - ); - await cbuilder.run( - input: buildInput, - output: buildOutput, - logger: logger, - ); - - final libUri = - tempUri.resolve(OS.windows.libraryFileName(name, linkMode)); - expect(await File.fromUri(libUri).exists(), true); - final result = await runProcess( - executable: dumpbinUri, - arguments: ['/HEADERS', libUri.toFilePath()], - logger: logger, - ); - expect(result.exitCode, 0); - final machine = - result.stdout.split('\n').firstWhere((e) => e.contains('machine')); - expect(machine, contains(dumpbinMachine[target])); - final fileType = result.stdout - .split('\n') - .firstWhere((e) => e.contains('File Type')); - expect(fileType, contains(dumpbinFileType[linkMode])); - }); + final libUri = + tempUri.resolve(OS.windows.libraryFileName(name, linkMode)); + expect(await File.fromUri(libUri).exists(), true); + final result = await runProcess( + executable: dumpbinUri, + arguments: ['/HEADERS', libUri.toFilePath()], + logger: logger, + ); + expect(result.exitCode, 0); + final machine = result.stdout + .split('\n') + .firstWhere((e) => e.contains('machine')); + expect(machine, contains(dumpbinMachine[target])); + final fileType = result.stdout + .split('\n') + .firstWhere((e) => e.contains('File Type')); + expect(fileType, contains(dumpbinFileType[linkMode])); + }); + } } } } diff --git a/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c b/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c index 6a16ccab6..3c0fab5f1 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c +++ b/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c @@ -6,6 +6,10 @@ #ifdef DEBUG #include <stdio.h> + +#if _WIN32 +#include <wchar.h> +#endif #endif #if _WIN32 @@ -17,6 +21,9 @@ FFI_EXPORT int32_t add(int32_t a, int32_t b) { #ifdef DEBUG printf("Adding %i and %i.\n", a, b); +#if _WIN32 + wprintf("Adding %i and %i.\n", a, b); +#endif #endif return a + b; } diff --git a/pkgs/native_toolchain_c/test/native_toolchain/clang_test.dart b/pkgs/native_toolchain_c/test/native_toolchain/clang_test.dart index 88be30c8e..cfe499ccc 100644 --- a/pkgs/native_toolchain_c/test/native_toolchain/clang_test.dart +++ b/pkgs/native_toolchain_c/test/native_toolchain/clang_test.dart @@ -16,7 +16,11 @@ import 'package:test/test.dart'; import '../helpers.dart'; void main() { - if (!Platform.isLinux) { + if (Platform.isMacOS || + (Platform.isWindows && + Platform.environment['DART_HOOK_TESTING_C_COMPILER__CC'] + ?.endsWith('cl.exe') == + true)) { // Avoid needing status files on Dart SDK CI. return; }