blob: 4287f5f3dd39258d3fd311851995058b6fec9e76 [file] [log] [blame]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// 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 'dart:convert';
import 'dart:io';
import 'package:code_assets/code_assets.dart';
import 'package:glob/glob.dart';
import 'package:logging/logging.dart';
import '../tool/tool.dart';
import '../tool/tool_instance.dart';
import '../tool/tool_resolver.dart';
import '../utils/run_process.dart';
import '../utils/sem_version.dart';
/// The Visual Studio Locator.
///
/// https://github.com/microsoft/vswhere
final Tool vswhere = Tool(
name: 'Visual Studio Locator',
defaultResolver: CliVersionResolver(
arguments: [],
wrappedResolver: ToolResolvers([
PathToolResolver(
toolName: 'Visual Studio Locator',
executableName: 'vswhere.exe',
),
InstallLocationResolver(
toolName: 'Visual Studio Locator',
paths: [
'C:/Program Files \\(x86\\)/Microsoft Visual Studio/Installer/vswhere.exe',
'C:/Program Files/Microsoft Visual Studio/Installer/vswhere.exe',
],
),
]),
),
);
/// Visual Studio.
///
/// https://visualstudio.microsoft.com/
final Tool visualStudio = Tool(
name: 'Visual Studio',
defaultResolver: VisualStudioResolver(),
);
/// The C/C++ Optimizing Compiler.
final Tool msvc = Tool(
name: 'MSVC',
defaultResolver: PathVersionResolver(
wrappedResolver: RelativeToolResolver(
toolName: 'MSVC',
wrappedResolver: visualStudio.defaultResolver!,
relativePath: Uri(path: './VC/Tools/MSVC/*/'),
),
),
);
Tool vcvars(ToolInstance toolInstance) {
final tool = toolInstance.tool;
assert(tool == cl || tool == msvcLink || tool == lib);
final vcDir = toolInstance.uri.resolve('../../../../../../');
final String fileName;
if (toolInstance.uri.toFilePath().contains('\\x86\\')) {
fileName = 'vcvars32.bat';
} else if (toolInstance.uri.toFilePath().contains('\\arm64\\')) {
// TODO(https://github.com/dart-lang/native/issues/170): Support native
// windows-arm64 MSVC toolchain.
// vcvarsarm64 only works on native windows-arm64. In case of cross
// compilation, it's better to stick to cross toolchain, which works under
// emulation on windows-arm64.
fileName = 'vcvarsamd64_arm64.bat';
} else {
fileName = 'vcvars64.bat';
}
final batchScript = vcDir.resolve('Auxiliary/Build/$fileName');
return Tool(
name: fileName,
defaultResolver: InstallLocationResolver(
toolName: fileName,
paths: [Glob.quote(batchScript.toFilePath().replaceAll('\\', '/'))],
),
);
}
final Tool vcvars64 = Tool(
name: 'vcvars64.bat',
defaultResolver: RelativeToolResolver(
toolName: 'vcvars64.bat',
wrappedResolver: visualStudio.defaultResolver!,
relativePath: Uri(path: './VC/Auxiliary/Build/vcvars64.bat'),
),
);
final Tool vcvars32 = Tool(
name: 'vcvars32.bat',
defaultResolver: RelativeToolResolver(
toolName: 'vcvars32.bat',
wrappedResolver: visualStudio.defaultResolver!,
relativePath: Uri(path: './VC/Auxiliary/Build/vcvars32.bat'),
),
);
final Tool vcvarsarm64 = Tool(
// TODO(https://github.com/dart-lang/native/issues/170): Support native
// windows-arm64 MSVC toolchain.
// vcvarsarm64 only works on native windows-arm64. In case of cross
// compilation, it's better to stick to cross toolchain, which works under
// emulation on windows-arm64.
name: 'vcvarsamd64_arm64.bat',
defaultResolver: RelativeToolResolver(
toolName: 'vcvarsamd64_arm64.bat',
wrappedResolver: visualStudio.defaultResolver!,
relativePath: Uri(path: './VC/Auxiliary/Build/vcvarsamd64_arm64.bat'),
),
);
final Tool vcvarsall = Tool(
name: 'vcvarsall.bat',
defaultResolver: RelativeToolResolver(
toolName: 'vcvars32.bat',
wrappedResolver: visualStudio.defaultResolver!,
relativePath: Uri(path: './VC/Auxiliary/Build/vcvarsall.bat'),
),
);
final Tool vsDevCmd = Tool(
name: 'VsDevCmd.bat',
defaultResolver: RelativeToolResolver(
toolName: 'VsDevCmd.bat',
wrappedResolver: visualStudio.defaultResolver!,
relativePath: Uri(path: './Common7/Tools/VsDevCmd.bat'),
),
);
/// The C/C++ Optimizing Compiler main executable.
///
/// For targeting [Architecture.x64].
final Tool cl = _msvcTool(
name: 'cl',
versionArguments: [],
targetArchitecture: Architecture.x64,
hostArchitecture: Architecture.current,
);
/// The C/C++ Optimizing Compiler main executable.
///
/// For targeting [Architecture.ia32].
final Tool clIA32 = _msvcTool(
name: 'cl',
versionArguments: [],
targetArchitecture: Architecture.ia32,
hostArchitecture: Architecture.current,
);
/// The C/C++ Optimizing Compiler main executable.
///
/// For targeting [Architecture.arm64].
final Tool clArm64 = _msvcTool(
name: 'cl',
versionArguments: [],
targetArchitecture: Architecture.arm64,
hostArchitecture: Architecture.current,
);
final Tool lib = _msvcTool(
name: 'lib',
targetArchitecture: Architecture.x64,
hostArchitecture: Architecture.current,
// https://github.com/dart-lang/native/issues/18
resolveVersion: false,
);
final Tool libIA32 = _msvcTool(
name: 'lib',
targetArchitecture: Architecture.ia32,
hostArchitecture: Architecture.current,
// https://github.com/dart-lang/native/issues/18
resolveVersion: false,
);
final Tool libArm64 = _msvcTool(
name: 'lib',
targetArchitecture: Architecture.arm64,
hostArchitecture: Architecture.current,
// https://github.com/dart-lang/native/issues/18
resolveVersion: false,
);
final Tool msvcLink = _msvcTool(
name: 'link',
versionArguments: ['/help'],
versionExitCode: 1100,
targetArchitecture: Architecture.x64,
hostArchitecture: Architecture.current,
);
final Tool linkIA32 = _msvcTool(
name: 'link',
versionArguments: ['/help'],
versionExitCode: 1100,
targetArchitecture: Architecture.ia32,
hostArchitecture: Architecture.current,
);
final Tool linkArm64 = _msvcTool(
name: 'link',
versionArguments: ['/help'],
versionExitCode: 1100,
targetArchitecture: Architecture.arm64,
hostArchitecture: Architecture.current,
);
final Tool dumpbin = _msvcTool(
name: 'dumpbin',
targetArchitecture: Architecture.x64,
hostArchitecture: Architecture.current,
);
const _msvcArchNames = {
Architecture.ia32: 'x86',
Architecture.x64: 'x64',
Architecture.arm64: 'arm64',
};
Tool _msvcTool({
required String name,
required Architecture targetArchitecture,
required Architecture hostArchitecture,
List<String> versionArguments = const ['--version'],
int versionExitCode = 0,
bool resolveVersion = true,
}) {
final executableName = OS.windows.executableFileName(name);
if (OS.current != OS.windows) {
return Tool(name: executableName, defaultResolver: ToolResolvers([]));
}
final hostArchName = _msvcArchNames[hostArchitecture]!;
final targetArchName = _msvcArchNames[targetArchitecture]!;
ToolResolver resolver = RelativeToolResolver(
toolName: executableName,
wrappedResolver: msvc.defaultResolver!,
relativePath: Uri(
path: 'bin/Host$hostArchName/$targetArchName/$executableName',
),
);
if (resolveVersion) {
resolver = CliVersionResolver(
expectedExitCode: versionExitCode,
arguments: versionArguments,
wrappedResolver: resolver,
);
}
return Tool(name: executableName, defaultResolver: resolver);
}
class VisualStudioResolver implements ToolResolver {
@override
Future<List<ToolInstance>> resolve(ToolResolvingContext context) 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', '*'],
logger: logger,
);
final instances = parseVswhere(vswhereResult.stdout, logger);
result.addAll(instances);
}
return result;
}
List<ToolInstance> parseVswhere(String vswhereStdout, [Logger? logger]) {
final result = <ToolInstance>[];
final toolInfos = json.decode(vswhereStdout) as List;
for (final toolInfo in toolInfos) {
final toolInfoParsed = toolInfo as Map<String, Object?>;
if (toolInfoParsed['installationPath'] != null &&
toolInfoParsed['installationVersion'] != null) {
final dir = Directory(toolInfoParsed['installationPath'] as String);
assert(dir.existsSync());
final uri = dir.uri;
final version = versionFromString(
toolInfoParsed['installationVersion'] as String,
);
final instance = ToolInstance(
tool: visualStudio,
uri: uri,
version: version,
);
logger?.fine('Found $instance.');
result.add(instance);
}
}
return result;
}
}