Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkgs/native_toolchain_c/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.18.1

- Fix [native_toolchain_c failing with MSSSMS installed](https://github.com/dart-lang/native/issues/3327) by requiring vswhere to only show output that includes the necessary build tools

## 0.18.0

- Made `CLinker.run` `Logger` argument optional. It now defaults to a logger
Expand Down
102 changes: 97 additions & 5 deletions pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,35 @@ final Tool vswhere = Tool(
),
);

/// Visual Studio.
/// A Visual Studio installation that ships the MSVC tools for x86/x64.
///
/// Resolved via [vswhere] requiring
/// `Microsoft.VisualStudio.Component.VC.Tools.x86.x64`, which filters out
/// installs that lack the C++ toolchain (e.g. SSMS) even when their
/// `installationVersion` would otherwise win `vswhere -latest`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final Tool visualStudioX64? I don't think we export this variable outside the package, so it would not be breaking anything?

And name Visual Studio (x64)? Or does changing the name break things?

///
/// https://visualstudio.microsoft.com/
final Tool visualStudio = Tool(
name: 'Visual Studio',
defaultResolver: VisualStudioResolver(),
);

/// The C/C++ Optimizing Compiler.
/// A Visual Studio installation that ships the MSVC tools for arm64.
///
/// Resolved via [vswhere] requiring
/// `Microsoft.VisualStudio.Component.VC.Tools.arm64`. May resolve a
/// different installation than [visualStudio] when not every VS instance on
/// the machine has the arm64 cross toolchain installed.
///
/// https://visualstudio.microsoft.com/
final Tool visualStudioArm64 = Tool(
name: 'Visual Studio',
defaultResolver: VisualStudioResolverArm64(),
);

/// The C/C++ Optimizing Compiler installation for targeting x86/x64.
///
/// Resolved relative to [visualStudio].
final Tool msvc = Tool(
name: 'MSVC',
defaultResolver: PathVersionResolver(
Expand All @@ -58,6 +78,22 @@ final Tool msvc = Tool(
),
);

/// The C/C++ Optimizing Compiler installation for targeting arm64.
///
/// Resolved relative to [visualStudioArm64], which may point at a different
/// VS installation than [msvc] when only some installs have the arm64
/// cross toolchain.
final Tool msvcArm64 = Tool(
name: 'MSVC',
defaultResolver: PathVersionResolver(
wrappedResolver: RelativeToolResolver(
toolName: 'MSVC',
wrappedResolver: visualStudioArm64.defaultResolver!,
relativePath: Uri(path: './VC/Tools/MSVC/*/'),
),
),
);

Tool vcvars(ToolInstance toolInstance) {
final tool = toolInstance.tool;
assert(tool == cl || tool == msvcLink || tool == lib);
Expand Down Expand Up @@ -241,7 +277,10 @@ Tool _msvcTool({
final targetArchName = _msvcArchNames[targetArchitecture]!;
ToolResolver resolver = RelativeToolResolver(
toolName: executableName,
wrappedResolver: msvc.defaultResolver!,
wrappedResolver: switch (targetArchitecture) {
.arm64 => msvcArm64.defaultResolver!,
_ => msvc.defaultResolver!,
},
relativePath: Uri(
path: 'bin/Host$hostArchName/$targetArchName/$executableName',
),
Expand All @@ -256,22 +295,75 @@ Tool _msvcTool({
return Tool(name: executableName, defaultResolver: resolver);
}

/// Resolves Visual Studio installations that ship the arm64 MSVC tools.
///
/// Same as [VisualStudioResolver] but requires the
/// `Microsoft.VisualStudio.Component.VC.Tools.arm64` component. On machines
/// with multiple VS installations these resolvers may pick different
/// installs.
class VisualStudioResolverArm64 extends VisualStudioResolver {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extending doesn't seem the right pattern if the x64 and arm64 are next to each other.

Can the VisualStudioResolver get a targetArchitecture param instead?

@override
Future<List<ToolInstance>> resolve(ToolResolvingContext context) async =>
resolveArch(context, 'arm64');
}

/// Resolves Visual Studio installations that ship the x86/x64 MSVC tools.
///
/// Runs [vswhere] with `-latest -requires
/// Microsoft.VisualStudio.Component.VC.Tools.x86.x64`. The `-requires`
/// filter is necessary because tools like SSMS share the VS installer and
/// can outrank Visual Studio under `-latest` when sorted by version alone
/// (see https://github.com/dart-lang/native/issues/3327).
class VisualStudioResolver implements ToolResolver {
@override
Future<List<ToolInstance>> resolve(ToolResolvingContext context) async {
Future<List<ToolInstance>> resolve(ToolResolvingContext context) async =>
resolveArch(context, 'x86.x64');

Future<List<ToolInstance>> resolveArch(
ToolResolvingContext context,
String pkgArchSuffix,
) async {
final vswhereInstances = await vswhere.defaultResolver!.resolve(context);
final logger = context.logger;

final result = <ToolInstance>[];
for (final vswhereInstance in vswhereInstances.take(1)) {
final vswhereResult = await runProcess(
executable: vswhereInstance.uri,
arguments: ['-format', 'json', '-utf8', '-latest', '-products', '*'],
arguments: [
'-format',
'json',
'-utf8',
'-latest',
'-products',
'*',
'-requires',
'Microsoft.VisualStudio.Component.VC.Tools.$pkgArchSuffix',
],
logger: logger,
);
final instances = parseVswhere(vswhereResult.stdout, logger);
result.addAll(instances);
}
if (result.isEmpty) {
logger?.warning(
'No Visual Studio installation found with the requested '
'Microsoft.VisualStudio.Component.VC.Tools.$pkgArchSuffix component.',
);
logger?.info('You can install the missing package via');
for (final vsWhere in vswhereInstances) {
final vsInstallerUri = vsWhere.uri
.resolve('../vs_installer.exe')
.toFilePath();
logger?.warning( // highlight command for user
'` $vsInstallerUri install --add ',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64`',
);
if (vswhereInstances.length > 1 && vswhereInstances.last != vsWhere) {
logger?.info('or');
}
}
}
return result;
}

Expand Down
2 changes: 1 addition & 1 deletion pkgs/native_toolchain_c/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: native_toolchain_c
description: >-
A library to invoke the native C compiler installed on the host machine.
version: 0.18.0
version: 0.18.1
repository: https://github.com/dart-lang/native/tree/main/pkgs/native_toolchain_c

topics:
Expand Down
50 changes: 38 additions & 12 deletions pkgs/native_toolchain_c/test/native_toolchain/msvc_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@OnPlatform({'windows': Timeout.factor(10)})
library;

import 'dart:convert';
import 'dart:io';

import 'package:native_toolchain_c/src/native_toolchain/msvc.dart';
Expand Down Expand Up @@ -37,19 +38,44 @@ void main() {
expect(instances.isNotEmpty, true);
});

test('cl', () async {
final instances = await cl.defaultResolver!.resolve(systemContext);
expect(instances.isNotEmpty, true);
});

test('clIA32', () async {
final instances = await clIA32.defaultResolver!.resolve(systemContext);
expect(instances.isNotEmpty, true);
});
test('parseVswhere handles mixed installations', () async {
final tempUri = await tempDirForTest();
final ssmsDir = Directory.fromUri(
tempUri.resolve('Microsoft SQL Server Management Studio 22/Release'),
);
final visualStudioDir = Directory.fromUri(
tempUri.resolve('Microsoft Visual Studio/18/Community'),
);
await ssmsDir.create(recursive: true);
await visualStudioDir.create(recursive: true);

final instances = VisualStudioResolver().parseVswhere(
jsonEncode([
{
'installationName': 'SSMS/22.5.0+11709.299',
'installationPath': ssmsDir.path,
'installationVersion': '22.5.11709.299',
'productId': 'Microsoft.VisualStudio.Product.Ssms',
'displayName': 'SQL Server Management Studio 22',
},
{
'installationName': 'VisualStudio/18.5.1+11716.220',
'installationPath': visualStudioDir.path,
'installationVersion': '18.5.11716.220',
'productId': 'Microsoft.VisualStudio.Product.Community',
'displayName': 'Visual Studio Community 2026',
},
{
'installationName': 'Incomplete entry',
'productId': 'Microsoft.VisualStudio.Product.Incomplete',
},
]),
);

test('clArm64', () async {
final instances = await clArm64.defaultResolver!.resolve(systemContext);
expect(instances.isNotEmpty, true);
expect(instances, hasLength(2));
expect(instances.first.uri, ssmsDir.uri);
expect(instances.last.uri, visualStudioDir.uri);
expect(instances.map((instance) => instance.version!.major), [22, 18]);
});

test('lib', () async {
Expand Down
Loading