• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import '../base/context.dart';
6import '../base/file_system.dart';
7import '../base/io.dart';
8import '../base/platform.dart';
9import '../base/process_manager.dart';
10import '../convert.dart';
11
12VisualStudio get visualStudio => context.get<VisualStudio>();
13
14/// Encapsulates information about the installed copy of Visual Studio, if any.
15class VisualStudio {
16  /// True if a sufficiently recent version of Visual Studio is installed.
17  ///
18  /// Versions older than 2017 Update 2 won't be detected, so error messages to
19  /// users should take into account that [false] may mean that the user may
20  /// have an old version rather than no installation at all.
21  bool get isInstalled => _bestVisualStudioDetails != null;
22
23  /// True if there is a version of Visual Studio with all the components
24  /// necessary to build the project.
25  bool get hasNecessaryComponents => _usableVisualStudioDetails != null;
26
27  /// The name of the Visual Studio install.
28  ///
29  /// For instance: "Visual Studio Community 2017".
30  String get displayName => _bestVisualStudioDetails[_displayNameKey];
31
32  /// The user-friendly version number of the Visual Studio install.
33  ///
34  /// For instance: "15.4.0".
35  String get displayVersion =>
36      _bestVisualStudioDetails[_catalogKey][_catalogDisplayVersionKey];
37
38  /// The directory where Visual Studio is installed.
39  String get installLocation => _bestVisualStudioDetails[_installationPathKey];
40
41  /// The full version of the Visual Studio install.
42  ///
43  /// For instance: "15.4.27004.2002".
44  String get fullVersion => _bestVisualStudioDetails[_fullVersionKey];
45
46  /// The name of the recommended Visual Studio installer workload.
47  String get workloadDescription => 'Desktop development with C++';
48
49  /// The names of the components within the workload that must be installed.
50  ///
51  /// If there is an existing Visual Studio installation, the major version
52  /// should be provided here, as the descriptions of some componets differ
53  /// from version to version.
54  List<String> necessaryComponentDescriptions([int visualStudioMajorVersion]) {
55    return _requiredComponents(visualStudioMajorVersion).values.toList();
56  }
57
58  /// The path to vcvars64.bat, or null if no Visual Studio installation has
59  /// the components necessary to build.
60  String get vcvarsPath {
61    final Map<String, dynamic> details = _usableVisualStudioDetails;
62    if (details == null) {
63      return null;
64    }
65    return fs.path.join(
66      _usableVisualStudioDetails[_installationPathKey],
67      'VC',
68      'Auxiliary',
69      'Build',
70      'vcvars64.bat',
71    );
72  }
73
74  /// The path to vswhere.exe.
75  ///
76  /// vswhere should be installed for VS 2017 Update 2 and later; if it's not
77  /// present then there isn't a new enough installation of VS. This path is
78  /// not user-controllable, unlike the install location of Visual Studio
79  /// itself.
80  final String _vswherePath = fs.path.join(
81    platform.environment['PROGRAMFILES(X86)'],
82    'Microsoft Visual Studio',
83    'Installer',
84    'vswhere.exe',
85  );
86
87  /// Components for use with vswhere requriements.
88  ///
89  /// Maps from component IDs to description in the installer UI.
90  /// See https://docs.microsoft.com/en-us/visualstudio/install/workload-and-component-ids
91  Map<String, String> _requiredComponents([int visualStudioMajorVersion]) {
92    // The description of the C++ toolchain required by the template. The
93    // component name is significantly different in different versions.
94    // Default to the latest known description, but use a specific string
95    // if a known older version is requested.
96    String cppToolchainDescription = 'MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.21)';
97    if (visualStudioMajorVersion == 15) {
98      cppToolchainDescription = 'VC++ 2017 version 15.9 v14.16 latest v141 tools';
99    }
100
101    return <String, String>{
102      // The MSBuild tool and related command-line toolchain.
103      'Microsoft.Component.MSBuild': 'MSBuild',
104      // The C++ toolchain required by the template.
105      'Microsoft.VisualStudio.Component.VC.Tools.x86.x64': cppToolchainDescription,
106      // The Windows SDK version used by the template.
107      'Microsoft.VisualStudio.Component.Windows10SDK.17763':
108          'Windows 10 SDK (10.0.17763.0)',
109    };
110  }
111
112  // Keys in a VS details dictionary returned from vswhere.
113
114  /// The root directory of the Visual Studio installation.
115  static const String _installationPathKey = 'installationPath';
116
117  /// The user-friendly name of the installation.
118  static const String _displayNameKey = 'displayName';
119
120  /// The complete version.
121  static const String _fullVersionKey = 'installationVersion';
122
123  /// The 'catalog' entry containing more details.
124  static const String _catalogKey = 'catalog';
125
126  /// The user-friendly version.
127  ///
128  /// This key is under the 'catalog' entry.
129  static const String _catalogDisplayVersionKey = 'productDisplayVersion';
130
131  /// Returns the details dictionary for the newest version of Visual Studio
132  /// that includes all of [requiredComponents], if there is one.
133  Map<String, dynamic> _visualStudioDetails({Iterable<String> requiredComponents}) {
134    final List<String> requirementArguments = requiredComponents == null
135        ? <String>[]
136        : <String>['-requires', ...requiredComponents];
137    try {
138      final ProcessResult whereResult = processManager.runSync(<String>[
139        _vswherePath,
140        '-format', 'json',
141        '-utf8',
142        '-latest',
143        ...?requirementArguments,
144      ]);
145      if (whereResult.exitCode == 0) {
146        final List<Map<String, dynamic>> installations = json.decode(whereResult.stdout)
147            .cast<Map<String, dynamic>>();
148        if (installations.isNotEmpty) {
149          return installations[0];
150        }
151      }
152    } on ArgumentError {
153      // Thrown if vswhere doesnt' exist; ignore and return null below.
154    } on ProcessException {
155      // Ignored, return null below.
156    }
157    return null;
158  }
159
160  /// Returns the details dictionary for the latest version of Visual Studio
161  /// that has all required components.
162  Map<String, dynamic> _cachedUsableVisualStudioDetails;
163  Map<String, dynamic> get _usableVisualStudioDetails {
164    _cachedUsableVisualStudioDetails ??=
165        _visualStudioDetails(requiredComponents: _requiredComponents().keys);
166    return _cachedUsableVisualStudioDetails;
167  }
168
169  /// Returns the details dictionary of the latest version of Visual Studio,
170  /// regardless of components.
171  Map<String, dynamic> _cachedAnyVisualStudioDetails;
172  Map<String, dynamic> get _anyVisualStudioDetails {
173    _cachedAnyVisualStudioDetails ??= _visualStudioDetails();
174    return _cachedAnyVisualStudioDetails;
175  }
176
177  /// Returns the details dictionary of the best available version of Visual
178  /// Studio. If there's a version that has all the required components, that
179  /// will be returned, otherwise returs the lastest installed version (if any).
180  Map<String, dynamic> get _bestVisualStudioDetails {
181    if (_usableVisualStudioDetails != null) {
182      return _usableVisualStudioDetails;
183    }
184    return _anyVisualStudioDetails;
185  }
186}
187