Unverified Commit 7e05d103 authored by stuartmorgan's avatar stuartmorgan Committed by GitHub

Add support for Visual Studio 2022 (#93426)

parent 9cc47c2b
......@@ -20,11 +20,6 @@ import '../migrations/cmake_custom_command_migration.dart';
import 'install_manifest.dart';
import 'visual_studio.dart';
// From https://cmake.org/cmake/help/v3.14/manual/cmake-generators.7.html#visual-studio-generators
// This may need to become a getter on VisualStudio in the future to support
// future major versions of Visual Studio.
const String _cmakeVisualStudioGeneratorIdentifier = 'Visual Studio 16 2019';
/// Update the string when non-backwards compatible changes are made to the UWP template.
const int kCurrentUwpTemplateVersion = 0;
......@@ -61,7 +56,8 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {
processManager: globals.processManager,
);
final String? cmakePath = visualStudio.cmakePath;
if (cmakePath == null) {
final String? cmakeGenerator = visualStudio.cmakeGenerator;
if (cmakePath == null || cmakeGenerator == null) {
throwToolExit('Unable to find suitable Visual Studio toolchain. '
'Please run `flutter doctor` for more details.');
}
......@@ -72,7 +68,12 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {
'Building Windows application...',
);
try {
await _runCmakeGeneration(cmakePath, buildDirectory, windowsProject.cmakeFile.parent);
await _runCmakeGeneration(
cmakePath: cmakePath,
generator: cmakeGenerator,
buildDir: buildDirectory,
sourceDir: windowsProject.cmakeFile.parent,
);
await _runBuild(cmakePath, buildDirectory, buildModeName);
} finally {
status.cancel();
......@@ -152,7 +153,8 @@ Future<void> buildWindowsUwp(WindowsUwpProject windowsProject, BuildInfo buildIn
processManager: globals.processManager,
);
final String? cmakePath = visualStudio.cmakePath;
if (cmakePath == null) {
final String? cmakeGenerator = visualStudio.cmakeGenerator;
if (cmakePath == null || cmakeGenerator == null) {
throwToolExit('Unable to find suitable Visual Studio toolchain. '
'Please run `flutter doctor` for more details.');
}
......@@ -165,7 +167,12 @@ Future<void> buildWindowsUwp(WindowsUwpProject windowsProject, BuildInfo buildIn
// The Cmake re-entrant build does not work for UWP, so the flutter build is
// run in advance.
await _runFlutterBuild(buildDirectory, buildInfo, target);
await _runCmakeGeneration(cmakePath, buildDirectory, windowsProject.cmakeFile.parent);
await _runCmakeGeneration(
cmakePath: cmakePath,
generator: cmakeGenerator,
buildDir: buildDirectory,
sourceDir: windowsProject.cmakeFile.parent,
);
await _runBuild(cmakePath, buildDirectory, buildModeName, install: false);
} finally {
status.cancel();
......@@ -232,7 +239,12 @@ Future<void> _runFlutterBuild(Directory buildDirectory, BuildInfo buildInfo, Str
}
}
Future<void> _runCmakeGeneration(String cmakePath, Directory buildDir, Directory sourceDir) async {
Future<void> _runCmakeGeneration({
required String cmakePath,
required String generator,
required Directory buildDir,
required Directory sourceDir,
}) async {
final Stopwatch sw = Stopwatch()..start();
await buildDir.create(recursive: true);
......@@ -246,7 +258,7 @@ Future<void> _runCmakeGeneration(String cmakePath, Directory buildDir, Directory
'-B',
buildDir.path,
'-G',
_cmakeVisualStudioGeneratorIdentifier,
generator,
],
trace: true,
);
......
......@@ -167,6 +167,19 @@ class VisualStudio {
]);
}
/// The generator string to pass to CMake to select this Visual Studio
/// version.
String? get cmakeGenerator {
// From https://cmake.org/cmake/help/v3.22/manual/cmake-generators.7.html#visual-studio-generators
switch (_majorVersion) {
case 17:
return 'Visual Studio 17 2022';
case 16:
default:
return 'Visual Studio 16 2019';
}
}
/// The major version of the Visual Studio install, as an integer.
int? get _majorVersion => fullVersion != null ? int.tryParse(fullVersion!.split('.')[0]) : null;
......
......@@ -25,7 +25,8 @@ const String flutterRoot = r'C:\flutter';
const String buildFilePath = r'C:\windows\CMakeLists.txt';
const String buildUwpFilePath = r'C:\winuwp\CMakeLists.txt';
const String visualStudioPath = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community';
const String cmakePath = visualStudioPath + r'\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe';
const String _cmakePath = visualStudioPath + r'\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe';
const String _defaultGenerator = 'Visual Studio 16 2019';
final Platform windowsPlatform = FakePlatform(
operatingSystem: 'windows',
......@@ -45,7 +46,6 @@ void main() {
FileSystem fileSystem;
ProcessManager processManager;
FakeVisualStudio fakeVisualStudio;
TestUsage usage;
setUpAll(() {
......@@ -56,7 +56,6 @@ void main() {
setUp(() {
fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
Cache.flutterRoot = flutterRoot;
fakeVisualStudio = FakeVisualStudio();
usage = TestUsage();
});
......@@ -83,10 +82,14 @@ void main() {
// Returns the command matching the build_windows call to generate CMake
// files.
FakeCommand cmakeGenerationCommand({void Function() onRun, bool winuwp = false}) {
FakeCommand cmakeGenerationCommand({
void Function() onRun,
bool winuwp = false,
String generator = _defaultGenerator,
}) {
return FakeCommand(
command: <String>[
cmakePath,
_cmakePath,
'-S',
fileSystem.path.dirname(winuwp ? buildUwpFilePath : buildFilePath),
'-B',
......@@ -95,7 +98,7 @@ void main() {
else
r'build\windows',
'-G',
'Visual Studio 16 2019',
generator,
],
onRun: onRun,
);
......@@ -110,7 +113,7 @@ void main() {
}) {
return FakeCommand(
command: <String>[
cmakePath,
_cmakePath,
'--build',
if (winuwp)
r'build\winuwp'
......@@ -132,9 +135,9 @@ void main() {
);
}
testUsingContext('Windows build fails when there is no vcvars64.bat', () async {
testUsingContext('Windows build fails when there is no cmake path', () async {
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
..visualStudioOverride = FakeVisualStudio(cmakePath: null);
setUpMockProjectFilesForBuild();
expect(createTestCommandRunner(command).run(
......@@ -148,7 +151,7 @@ void main() {
});
testUsingContext('Windows build fails when there is no windows project', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockCoreProjectFiles();
......@@ -166,7 +169,7 @@ void main() {
});
testUsingContext('Windows build fails on non windows platform', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -182,7 +185,7 @@ void main() {
});
testUsingContext('Windows build fails when feature is disabled', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -198,7 +201,7 @@ void main() {
});
testUsingContext('Windows build does not spew stdout to status logger', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -223,7 +226,7 @@ void main() {
});
testUsingContext('Windows build extracts errors from stdout', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -281,7 +284,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows verbose build sets VERBOSE_SCRIPT_LOGGING', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -307,7 +310,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows build invokes build and writes generated files', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -372,7 +375,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows profile build passes Profile configuration', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -392,6 +395,29 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
});
testUsingContext('Windows build passes correct generator', () async {
const String generator = 'A different generator';
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(
cmakeGenerator: generator);
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
cmakeGenerationCommand(generator: generator),
buildCommand('Release'),
]);
await createTestCommandRunner(command).run(
const <String>['windows', '--release', '--no-pub']
);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Platform: () => windowsPlatform,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
});
testUsingContext('hidden when not enabled on Windows host', () {
expect(BuildWindowsCommand().hidden, true);
}, overrides: <Type, Generator>{
......@@ -407,7 +433,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Performs code size analysis and sends analytics', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -455,7 +481,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows build fails when there is no windows project', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsUwpCommand command = BuildWindowsUwpCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockCoreProjectFiles();
......@@ -473,7 +499,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows build fails on non windows platform', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsUwpCommand command = BuildWindowsUwpCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockUwpFilesForBuild(0);
......@@ -489,7 +515,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows UWP uild fails on non windows platform', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsUwpCommand command = BuildWindowsUwpCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -505,7 +531,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows UWP build fails when the project version is out of date', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsUwpCommand command = BuildWindowsUwpCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockUwpFilesForBuild(-1);
......@@ -522,7 +548,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows UWP build fails when feature is disabled', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsUwpCommand command = BuildWindowsUwpCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockProjectFilesForBuild();
......@@ -540,7 +566,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
});
testUsingContext('Windows UWP build completes successfully', () async {
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath);
final FakeVisualStudio fakeVisualStudio = FakeVisualStudio();
final BuildWindowsUwpCommand command = BuildWindowsUwpCommand()
..visualStudioOverride = fakeVisualStudio;
setUpMockUwpFilesForBuild(0);
......@@ -575,8 +601,14 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
}
class FakeVisualStudio extends Fake implements VisualStudio {
FakeVisualStudio([this.cmakePath]);
FakeVisualStudio({
this.cmakePath = _cmakePath,
this.cmakeGenerator = 'Visual Studio 16 2019',
});
@override
final String cmakePath;
@override
final String cmakeGenerator;
}
......@@ -39,6 +39,20 @@ const Map<String, dynamic> _defaultResponse = <String, dynamic>{
},
};
// A minimum version of a response where a VS 2022 installation was found.
const Map<String, dynamic> _vs2022Response = <String, dynamic>{
'installationPath': visualStudioPath,
'displayName': 'Visual Studio Community 2022',
'installationVersion': '17.0.31903.59',
'isRebootRequired': false,
'isComplete': true,
'isLaunchable': true,
'isPrerelease': false,
'catalog': <String, dynamic>{
'productDisplayVersion': '17.0.0',
},
};
// A minimum version of a response where a Build Tools installation was found.
const Map<String, dynamic> _defaultBuildToolsResponse = <String, dynamic>{
'installationPath': visualStudioPath,
......@@ -732,6 +746,7 @@ void main() {
expect(visualStudio.isAtLeastMinimumVersion, true);
expect(visualStudio.hasNecessaryComponents, true);
expect(visualStudio.cmakePath, equals(cmakePath));
expect(visualStudio.cmakeGenerator, equals('Visual Studio 16 2019'));
});
testWithoutContext('Everything returns good values when Build Tools is present with all components', () {
......@@ -755,6 +770,23 @@ void main() {
expect(visualStudio.cmakePath, equals(cmakePath));
});
testWithoutContext('properties return the right value for Visual Studio 2022', () {
final VisualStudioFixture fixture = setUpVisualStudio();
final VisualStudio visualStudio = fixture.visualStudio;
setMockCompatibleVisualStudioInstallation(
_vs2022Response,
fixture.fileSystem,
fixture.processManager,
);
expect(visualStudio.isInstalled, true);
expect(visualStudio.isAtLeastMinimumVersion, true);
expect(visualStudio.hasNecessaryComponents, true);
expect(visualStudio.cmakePath, equals(cmakePath));
expect(visualStudio.cmakeGenerator, equals('Visual Studio 17 2022'));
});
testWithoutContext('Metadata is for compatible version when latest is missing components', () {
final VisualStudioFixture fixture = setUpVisualStudio();
final VisualStudio visualStudio = fixture.visualStudio;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment