blob: 5a5d173521e454729fcbce1eb97acc4429291ff6 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. 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:xml/xml.dart' as xml;
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/platform.dart';
import '../base/process_manager.dart';
import '../globals.dart';
/// Returns the path to an installed vcvars64.bat script if found, or null.
Future<String> findVcvars() async {
final String vswherePath = fs.path.join(
platform.environment['PROGRAMFILES(X86)'],
'Microsoft Visual Studio',
'Installer',
'vswhere.exe',
);
// The "Desktop development with C++" workload. This is a coarse check, since
// it doesn't validate that the specific pieces are available, but should be
// a reasonable first-pass approximation.
// In the future, a set of more targetted checks will be used to provide
// clear validation feedback (e.g., VS is installed, but missing component X).
const String requiredComponent = 'Microsoft.VisualStudio.Workload.NativeDesktop';
const String visualStudioInstallMessage =
'Ensure that you have Visual Studio 2017 or later installed, including '
'the "Desktop development with C++" workload.';
if (!fs.file(vswherePath).existsSync()) {
printError(
'Unable to locate Visual Studio: vswhere.exe not found\n'
'$visualStudioInstallMessage',
emphasis: true,
);
return null;
}
final ProcessResult whereResult = await processManager.run(<String>[
vswherePath,
'-latest',
'-requires', requiredComponent,
'-property', 'installationPath',
]);
if (whereResult.exitCode != 0) {
printError(
'Unable to locate Visual Studio:\n'
'${whereResult.stdout}\n'
'$visualStudioInstallMessage',
emphasis: true,
);
return null;
}
final String visualStudioPath = whereResult.stdout.trim();
if (visualStudioPath.isEmpty) {
printError(
'No suitable Visual Studio found. $visualStudioInstallMessage\n',
emphasis: true,
);
return null;
}
final String vcvarsPath =
fs.path.join(visualStudioPath, 'VC', 'Auxiliary', 'Build', 'vcvars64.bat');
if (!fs.file(vcvarsPath).existsSync()) {
printError(
'vcvars64.bat does not exist at $vcvarsPath.\n',
emphasis: true,
);
return null;
}
return vcvarsPath;
}
/// Writes a property sheet (.props) file to expose all of the key/value
/// pairs in [variables] as enivornment variables.
void writePropertySheet(File propertySheetFile, Map<String, String> variables) {
final xml.XmlBuilder builder = xml.XmlBuilder();
builder.processing('xml', 'version="1.0" encoding="utf-8"');
builder.element('Project', nest: () {
builder.attribute('ToolsVersion', '4.0');
builder.attribute(
'xmlns', 'http://schemas.microsoft.com/developer/msbuild/2003');
builder.element('ImportGroup', nest: () {
builder.attribute('Label', 'PropertySheets');
});
_addUserMacros(builder, variables);
builder.element('PropertyGroup');
builder.element('ItemDefinitionGroup');
_addItemGroup(builder, variables);
});
propertySheetFile.createSync(recursive: true);
propertySheetFile.writeAsStringSync(
builder.build().toXmlString(pretty: true, indent: ' '));
}
/// Adds the UserMacros PropertyGroup that defines [variables] to [builder].
void _addUserMacros(xml.XmlBuilder builder, Map<String, String> variables) {
builder.element('PropertyGroup', nest: () {
builder.attribute('Label', 'UserMacros');
for (final MapEntry<String, String> variable in variables.entries) {
builder.element(variable.key, nest: () {
builder.text(variable.value);
});
}
});
}
/// Adds the ItemGroup to expose the given [variables] as environment variables
/// to [builder].
void _addItemGroup(xml.XmlBuilder builder, Map<String, String> variables) {
builder.element('ItemGroup', nest: () {
for (final String name in variables.keys) {
builder.element('BuildMacro', nest: () {
builder.attribute('Include', name);
builder.element('Value', nest: () {
builder.text('\$($name)');
});
builder.element('EnvironmentVariable', nest: () {
builder.text('true');
});
});
}
});
}