| // Copyright 2014 The Flutter 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:file/memory.dart'; |
| import 'package:file_testing/file_testing.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/base/platform.dart'; |
| import 'package:flutter_tools/src/cache.dart'; |
| import 'package:flutter_tools/src/commands/build_windows.dart'; |
| import 'package:flutter_tools/src/features.dart'; |
| import 'package:flutter_tools/src/reporting/reporting.dart'; |
| import 'package:flutter_tools/src/windows/visual_studio.dart'; |
| import 'package:test/fake.dart'; |
| |
| import '../../src/common.dart'; |
| import '../../src/context.dart'; |
| import '../../src/fakes.dart'; |
| import '../../src/test_flutter_command_runner.dart'; |
| |
| const String flutterRoot = r'C:\flutter'; |
| const String buildFilePath = r'windows\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 _defaultGenerator = 'Visual Studio 16 2019'; |
| |
| final Platform windowsPlatform = FakePlatform( |
| operatingSystem: 'windows', |
| environment: <String, String>{ |
| 'PROGRAMFILES(X86)': r'C:\Program Files (x86)\', |
| 'FLUTTER_ROOT': flutterRoot, |
| 'USERPROFILE': '/', |
| } |
| ); |
| final Platform notWindowsPlatform = FakePlatform( |
| environment: <String, String>{ |
| 'FLUTTER_ROOT': flutterRoot, |
| } |
| ); |
| |
| void main() { |
| late FileSystem fileSystem; |
| late ProcessManager processManager; |
| late TestUsage usage; |
| |
| setUpAll(() { |
| Cache.disableLocking(); |
| Cache.flutterRoot = ''; |
| }); |
| |
| setUp(() { |
| fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); |
| Cache.flutterRoot = flutterRoot; |
| usage = TestUsage(); |
| }); |
| |
| // Creates the mock files necessary to look like a Flutter project. |
| void setUpMockCoreProjectFiles() { |
| fileSystem.file('pubspec.yaml').createSync(); |
| fileSystem.file('.packages').createSync(); |
| fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true); |
| } |
| |
| // Creates the mock files necessary to run a build. |
| void setUpMockProjectFilesForBuild() { |
| fileSystem.file(buildFilePath).createSync(recursive: true); |
| setUpMockCoreProjectFiles(); |
| } |
| |
| // Returns the command matching the build_windows call to generate CMake |
| // files. |
| FakeCommand cmakeGenerationCommand({ |
| void Function()? onRun, |
| String generator = _defaultGenerator, |
| }) { |
| return FakeCommand( |
| command: <String>[ |
| _cmakePath, |
| '-S', |
| fileSystem.path.absolute(fileSystem.path.dirname(buildFilePath)), |
| '-B', |
| r'build\windows', |
| '-G', |
| generator, |
| ], |
| onRun: onRun, |
| ); |
| } |
| |
| // Returns the command matching the build_windows call to build. |
| FakeCommand buildCommand(String buildMode, { |
| bool verbose = false, |
| void Function()? onRun, |
| String stdout = '', |
| }) { |
| return FakeCommand( |
| command: <String>[ |
| _cmakePath, |
| '--build', |
| r'build\windows', |
| '--config', |
| buildMode, |
| ...<String>['--target', 'INSTALL'], |
| if (verbose) |
| '--verbose', |
| ], |
| environment: <String, String>{ |
| if (verbose) |
| 'VERBOSE_SCRIPT_LOGGING': 'true', |
| }, |
| onRun: onRun, |
| stdout: stdout, |
| ); |
| } |
| |
| testUsingContext('Windows build fails when there is no cmake path', () async { |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = FakeVisualStudio(cmakePath: null); |
| setUpMockProjectFilesForBuild(); |
| |
| expect(createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub'] |
| ), throwsToolExit()); |
| }, overrides: <Type, Generator>{ |
| Platform: () => windowsPlatform, |
| FileSystem: () => fileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build fails when there is no windows project', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockCoreProjectFiles(); |
| |
| expect(createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub'] |
| ), throwsToolExit(message: 'No Windows desktop project configured. See ' |
| 'https://docs.flutter.dev/desktop#add-desktop-support-to-an-existing-flutter-app ' |
| 'to learn about adding Windows support to a project.')); |
| }, overrides: <Type, Generator>{ |
| Platform: () => windowsPlatform, |
| FileSystem: () => fileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build fails on non windows platform', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| expect(createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub'] |
| ), throwsToolExit(message: '"build windows" only supported on Windows hosts.')); |
| }, overrides: <Type, Generator>{ |
| Platform: () => notWindowsPlatform, |
| FileSystem: () => fileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build fails when feature is disabled', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| expect(createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub'] |
| ), throwsToolExit(message: '"build windows" is not currently supported. To enable, run "flutter config --enable-windows-desktop".')); |
| }, overrides: <Type, Generator>{ |
| Platform: () => windowsPlatform, |
| FileSystem: () => fileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| FeatureFlags: () => TestFeatureFlags(), |
| }); |
| |
| testUsingContext('Windows build does not spew stdout to status logger', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release', |
| stdout: 'STDOUT STUFF', |
| ), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub'] |
| ); |
| expect(testLogger.statusText, isNot(contains('STDOUT STUFF'))); |
| expect(testLogger.traceText, contains('STDOUT STUFF')); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build extracts errors from stdout', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| // This contains a mix of routine build output and various types of errors |
| // (compile error, link error, warning treated as an error) from MSBuild, |
| // edited down for compactness. For instance, where similar lines are |
| // repeated in actual output, one or two representative lines are chosen |
| // to be included here. |
| const String stdout = r''' |
| Microsoft (R) Build Engine version 16.6.0+5ff7b0c9e for .NET Framework |
| Copyright (C) Microsoft Corporation. All rights reserved. |
| |
| Checking Build System |
| Generating C:/foo/windows/flutter/ephemeral/flutter_windows.dll, [etc], _phony_ |
| Building Custom Rule C:/foo/windows/flutter/CMakeLists.txt |
| standard_codec.cc |
| Generating Code... |
| flutter_wrapper_plugin.vcxproj -> C:\foo\build\windows\flutter\Debug\flutter_wrapper_plugin.lib |
| C:\foo\windows\runner\main.cpp(18): error C2220: the following warning is treated as an error [C:\foo\build\windows\runner\test.vcxproj] |
| C:\foo\windows\runner\main.cpp(18): warning C4706: assignment within conditional expression [C:\foo\build\windows\runner\test.vcxproj] |
| main.obj : error LNK2019: unresolved external symbol "void __cdecl Bar(void)" (?Bar@@YAXXZ) referenced in function wWinMain [C:\foo\build\windows\runner\test.vcxproj] |
| C:\foo\build\windows\runner\Debug\test.exe : fatal error LNK1120: 1 unresolved externals [C:\foo\build\windows\runner\test.vcxproj] |
| Building Custom Rule C:/foo/windows/runner/CMakeLists.txt |
| flutter_window.cpp |
| main.cpp |
| C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier [C:\foo\build\windows\runner\test.vcxproj] |
| -- Install configuration: "Debug" |
| -- Installing: C:/foo/build/windows/runner/Debug/data/icudtl.dat |
| '''; |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release', |
| stdout: stdout, |
| ), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub'] |
| ); |
| // Just the warnings and errors should be surfaced. |
| expect(testLogger.errorText, r''' |
| C:\foo\windows\runner\main.cpp(18): error C2220: the following warning is treated as an error [C:\foo\build\windows\runner\test.vcxproj] |
| C:\foo\windows\runner\main.cpp(18): warning C4706: assignment within conditional expression [C:\foo\build\windows\runner\test.vcxproj] |
| main.obj : error LNK2019: unresolved external symbol "void __cdecl Bar(void)" (?Bar@@YAXXZ) referenced in function wWinMain [C:\foo\build\windows\runner\test.vcxproj] |
| C:\foo\build\windows\runner\Debug\test.exe : fatal error LNK1120: 1 unresolved externals [C:\foo\build\windows\runner\test.vcxproj] |
| C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier [C:\foo\build\windows\runner\test.vcxproj] |
| '''); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows verbose build sets VERBOSE_SCRIPT_LOGGING', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release', |
| verbose: true, |
| stdout: 'STDOUT STUFF', |
| ), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub', '-v'] |
| ); |
| expect(testLogger.statusText, contains('STDOUT STUFF')); |
| expect(testLogger.traceText, isNot(contains('STDOUT STUFF'))); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build works around CMake generation bug', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(displayVersion: '17.1.0'); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| fileSystem.file(fileSystem.path.join('lib', 'other.dart')) |
| .createSync(recursive: true); |
| fileSystem.file(fileSystem.path.join('foo', 'bar.sksl.json')) |
| .createSync(recursive: true); |
| |
| // Relevant portions of an incorrectly generated project, with some |
| // irrelevant details removed for length. |
| const String fakeBadProjectContent = r''' |
| <?xml version="1.0" encoding="utf-8"?> |
| <Project DefaultTargets="Build" ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
| <ItemGroup> |
| <CustomBuild Include="somepath\build\windows\CMakeFiles\8b570225f626c250e12bc1ede88babae\flutter_windows.dll.rule"> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Debug |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Debug |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Debug |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Profile |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Profile |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Profile |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Release |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Release |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Generating some files</Message> |
| <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">setlocal |
| "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64 Release |
| endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone |
| :cmErrorLevel |
| exit /b %1 |
| :cmDone |
| if %errorlevel% neq 0 goto :VCEnd</Command> |
| </CustomBuild> |
| </ItemGroup> |
| </Project> |
| '''; |
| final File assembleProject = fileSystem.currentDirectory |
| .childDirectory('build') |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childFile('flutter_assemble.vcxproj'); |
| assembleProject.createSync(recursive: true); |
| assembleProject.writeAsStringSync(fakeBadProjectContent); |
| |
| await createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub'] |
| ); |
| |
| final List<String> projectLines = assembleProject.readAsLinesSync(); |
| |
| const String commandBase = r'"C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" ' |
| r'-E env FOO=bar C:/src/flutter/packages/flutter_tools/bin/tool_backend.bat windows-x64'; |
| // The duplicate commands will still be present, but with the order matching |
| // the condition order (cycling through the configurations), rather than |
| // three copies of Debug, then three copies of Profile, then three copies |
| // of Release. |
| expect(projectLines, containsAllInOrder(<String>[ |
| '$commandBase Debug\r', |
| '$commandBase Profile\r', |
| '$commandBase Release\r', |
| '$commandBase Debug\r', |
| '$commandBase Profile\r', |
| '$commandBase Release\r', |
| '$commandBase Debug\r', |
| '$commandBase Profile\r', |
| '$commandBase Release\r', |
| ])); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build invokes build and writes generated files', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| fileSystem.file(fileSystem.path.join('lib', 'other.dart')) |
| .createSync(recursive: true); |
| fileSystem.file(fileSystem.path.join('foo', 'bar.sksl.json')) |
| .createSync(recursive: true); |
| |
| await createTestCommandRunner(command).run( |
| const <String>[ |
| 'windows', |
| '--no-pub', |
| '--track-widget-creation', |
| '--obfuscate', |
| '--tree-shake-icons', |
| '--enable-experiment=non-nullable', |
| r'--split-debug-info=C:\foo\', |
| '--dart-define=foo=a', |
| '--dart-define=bar=b', |
| r'--bundle-sksl-path=foo\bar.sksl.json', |
| r'--target=lib\other.dart', |
| ] |
| ); |
| |
| final File cmakeConfig = fileSystem.currentDirectory |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childDirectory('ephemeral') |
| .childFile('generated_config.cmake'); |
| |
| expect(cmakeConfig, exists); |
| |
| final List<String> configLines = cmakeConfig.readAsLinesSync(); |
| |
| // Backslashes are escaped in the file, which is why this uses both raw |
| // strings and double backslashes. |
| expect(configLines, containsAll(<String>[ |
| r'file(TO_CMAKE_PATH "C:\\flutter" FLUTTER_ROOT)', |
| r'file(TO_CMAKE_PATH "C:\\" PROJECT_DIR)', |
| r'set(FLUTTER_VERSION "1.0.0" PARENT_SCOPE)', |
| r'set(FLUTTER_VERSION_MAJOR 1 PARENT_SCOPE)', |
| r'set(FLUTTER_VERSION_MINOR 0 PARENT_SCOPE)', |
| r'set(FLUTTER_VERSION_PATCH 0 PARENT_SCOPE)', |
| r'set(FLUTTER_VERSION_BUILD 0 PARENT_SCOPE)', |
| r' "DART_DEFINES=Zm9vPWE=,YmFyPWI="', |
| r' "DART_OBFUSCATION=true"', |
| r' "EXTRA_FRONT_END_OPTIONS=--enable-experiment=non-nullable"', |
| r' "EXTRA_GEN_SNAPSHOT_OPTIONS=--enable-experiment=non-nullable"', |
| r' "SPLIT_DEBUG_INFO=C:\\foo\\"', |
| r' "TRACK_WIDGET_CREATION=true"', |
| r' "TREE_SHAKE_ICONS=true"', |
| r' "FLUTTER_ROOT=C:\\flutter"', |
| r' "PROJECT_DIR=C:\\"', |
| r' "FLUTTER_TARGET=lib\\other.dart"', |
| r' "BUNDLE_SKSL_PATH=foo\\bar.sksl.json"', |
| ])); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows profile build passes Profile configuration', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Profile'), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>['windows', '--profile', '--no-pub'] |
| ); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| 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("Windows build uses pubspec's version", () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| fileSystem.file('pubspec.yaml') |
| ..createSync() |
| ..writeAsStringSync('version: 1.2.3+4'); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>[ |
| 'windows', |
| '--no-pub', |
| ] |
| ); |
| |
| final File cmakeConfig = fileSystem.currentDirectory |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childDirectory('ephemeral') |
| .childFile('generated_config.cmake'); |
| |
| expect(cmakeConfig, exists); |
| |
| final List<String> configLines = cmakeConfig.readAsLinesSync(); |
| |
| expect(configLines, containsAll(<String>[ |
| 'set(FLUTTER_VERSION "1.2.3+4" PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MAJOR 1 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MINOR 2 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_PATCH 3 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_BUILD 4 PARENT_SCOPE)', |
| ])); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build uses build-name and build-number', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>[ |
| 'windows', |
| '--no-pub', |
| '--build-name=1.2.3', |
| '--build-number=4', |
| ] |
| ); |
| |
| final File cmakeConfig = fileSystem.currentDirectory |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childDirectory('ephemeral') |
| .childFile('generated_config.cmake'); |
| |
| expect(cmakeConfig, exists); |
| |
| final List<String> configLines = cmakeConfig.readAsLinesSync(); |
| |
| expect(configLines, containsAll(<String>[ |
| 'set(FLUTTER_VERSION "1.2.3+4" PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MAJOR 1 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MINOR 2 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_PATCH 3 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_BUILD 4 PARENT_SCOPE)', |
| ])); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build build-name overrides pubspec', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| fileSystem.file('pubspec.yaml') |
| ..createSync() |
| ..writeAsStringSync('version: 9.9.9+9'); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>[ |
| 'windows', |
| '--no-pub', |
| '--build-name=1.2.3', |
| ] |
| ); |
| |
| final File cmakeConfig = fileSystem.currentDirectory |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childDirectory('ephemeral') |
| .childFile('generated_config.cmake'); |
| |
| expect(cmakeConfig, exists); |
| |
| final List<String> configLines = cmakeConfig.readAsLinesSync(); |
| |
| expect(configLines, containsAll(<String>[ |
| 'set(FLUTTER_VERSION "1.2.3" PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MAJOR 1 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MINOR 2 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_PATCH 3 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_BUILD 0 PARENT_SCOPE)', |
| ])); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build build-number overrides pubspec', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| fileSystem.file('pubspec.yaml') |
| ..createSync() |
| ..writeAsStringSync('version: 1.2.3+9'); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>[ |
| 'windows', |
| '--no-pub', |
| '--build-number=4', |
| ] |
| ); |
| |
| final File cmakeConfig = fileSystem.currentDirectory |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childDirectory('ephemeral') |
| .childFile('generated_config.cmake'); |
| |
| expect(cmakeConfig, exists); |
| |
| final List<String> configLines = cmakeConfig.readAsLinesSync(); |
| |
| expect(configLines, containsAll(<String>[ |
| 'set(FLUTTER_VERSION "1.2.3+4" PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MAJOR 1 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MINOR 2 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_PATCH 3 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_BUILD 4 PARENT_SCOPE)', |
| ])); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build build-name and build-number override pubspec', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| fileSystem.file('pubspec.yaml') |
| ..createSync() |
| ..writeAsStringSync('version: 9.9.9+9'); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>[ |
| 'windows', |
| '--no-pub', |
| '--build-name=1.2.3', |
| '--build-number=4', |
| ] |
| ); |
| |
| final File cmakeConfig = fileSystem.currentDirectory |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childDirectory('ephemeral') |
| .childFile('generated_config.cmake'); |
| |
| expect(cmakeConfig, exists); |
| |
| final List<String> configLines = cmakeConfig.readAsLinesSync(); |
| |
| expect(configLines, containsAll(<String>[ |
| 'set(FLUTTER_VERSION "1.2.3+4" PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MAJOR 1 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MINOR 2 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_PATCH 3 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_BUILD 4 PARENT_SCOPE)', |
| ])); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build warns on non-numeric build-number', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>[ |
| 'windows', |
| '--no-pub', |
| '--build-name=1.2.3', |
| '--build-number=hello', |
| ] |
| ); |
| |
| final File cmakeConfig = fileSystem.currentDirectory |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childDirectory('ephemeral') |
| .childFile('generated_config.cmake'); |
| |
| expect(cmakeConfig, exists); |
| |
| final List<String> configLines = cmakeConfig.readAsLinesSync(); |
| |
| expect(configLines, containsAll(<String>[ |
| 'set(FLUTTER_VERSION "1.2.3+hello" PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MAJOR 1 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MINOR 2 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_PATCH 3 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_BUILD 0 PARENT_SCOPE)', |
| ])); |
| |
| expect(testLogger.warningText, contains( |
| 'Warning: build identifier hello in version 1.2.3+hello is not numeric and ' |
| 'cannot be converted into a Windows build version number. Defaulting to 0.\n' |
| 'This may cause issues with Windows installers.' |
| )); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| |
| testUsingContext('Windows build warns on complex build-number', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release'), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>[ |
| 'windows', |
| '--no-pub', |
| '--build-name=1.2.3', |
| '--build-number=4.5', |
| ] |
| ); |
| |
| final File cmakeConfig = fileSystem.currentDirectory |
| .childDirectory('windows') |
| .childDirectory('flutter') |
| .childDirectory('ephemeral') |
| .childFile('generated_config.cmake'); |
| |
| expect(cmakeConfig, exists); |
| |
| final List<String> configLines = cmakeConfig.readAsLinesSync(); |
| |
| expect(configLines, containsAll(<String>[ |
| 'set(FLUTTER_VERSION "1.2.3+4.5" PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MAJOR 1 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_MINOR 2 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_PATCH 3 PARENT_SCOPE)', |
| 'set(FLUTTER_VERSION_BUILD 0 PARENT_SCOPE)', |
| ])); |
| |
| expect(testLogger.warningText, contains( |
| 'Warning: build identifier 4.5 in version 1.2.3+4.5 is not numeric and ' |
| 'cannot be converted into a Windows build version number. Defaulting to 0.\n' |
| 'This may cause issues with Windows installers.' |
| )); |
| }, 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>{ |
| FeatureFlags: () => TestFeatureFlags(), |
| Platform: () => windowsPlatform, |
| }); |
| |
| testUsingContext('Not hidden when enabled and on Windows host', () { |
| expect(BuildWindowsCommand().hidden, false); |
| }, overrides: <Type, Generator>{ |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| Platform: () => windowsPlatform, |
| }); |
| |
| testUsingContext('Performs code size analysis and sends analytics', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| setUpMockProjectFilesForBuild(); |
| |
| fileSystem.file(r'build\windows\runner\Release\app.so') |
| ..createSync(recursive: true) |
| ..writeAsBytesSync(List<int>.generate(10000, (int index) => 0)); |
| |
| processManager = FakeProcessManager.list(<FakeCommand>[ |
| cmakeGenerationCommand(), |
| buildCommand('Release', onRun: () { |
| fileSystem.file(r'build\flutter_size_01\snapshot.windows-x64.json') |
| ..createSync(recursive: true) |
| ..writeAsStringSync(''' |
| [ |
| { |
| "l": "dart:_internal", |
| "c": "SubListIterable", |
| "n": "[Optimized] skip", |
| "s": 2400 |
| } |
| ]'''); |
| fileSystem.file(r'build\flutter_size_01\trace.windows-x64.json') |
| ..createSync(recursive: true) |
| ..writeAsStringSync('{}'); |
| }), |
| ]); |
| |
| await createTestCommandRunner(command).run( |
| const <String>['windows', '--no-pub', '--analyze-size'] |
| ); |
| |
| expect(testLogger.statusText, contains('A summary of your Windows bundle analysis can be found at')); |
| expect(testLogger.statusText, contains('flutter pub global activate devtools; flutter pub global run devtools --appSizeBase=')); |
| expect(usage.events, contains( |
| const TestUsageEvent('code-size-analysis', 'windows'), |
| )); |
| }, overrides: <Type, Generator>{ |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| FileSystem: () => fileSystem, |
| ProcessManager: () => processManager, |
| Platform: () => windowsPlatform, |
| FileSystemUtils: () => FileSystemUtils(fileSystem: fileSystem, platform: windowsPlatform), |
| Usage: () => usage, |
| }); |
| |
| // Confirms that running for Windows in a directory with a |
| // bad character (' in this case) throws the desired error message |
| // If the issue https://github.com/flutter/flutter/issues/104802 ever |
| // is resolved on the VS side, we can allow these paths again |
| testUsingContext('Test bad path characters', () async { |
| final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); |
| final BuildWindowsCommand command = BuildWindowsCommand() |
| ..visualStudioOverride = fakeVisualStudio; |
| fileSystem.currentDirectory = fileSystem.directory("test_'path") |
| ..createSync(); |
| final String absPath = fileSystem.currentDirectory.absolute.path; |
| setUpMockCoreProjectFiles(); |
| |
| expect( |
| createTestCommandRunner(command).run(const <String>['windows', '--no-pub']), |
| throwsToolExit( |
| message: |
| 'Path $absPath contains invalid characters in "\'#!\$^&*=|,;<>?". ' |
| 'Please rename your directory so as to not include any of these characters ' |
| 'and retry.', |
| ), |
| ); |
| }, overrides: <Type, Generator>{ |
| Platform: () => windowsPlatform, |
| FileSystem: () => fileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), |
| }); |
| } |
| |
| class FakeVisualStudio extends Fake implements VisualStudio { |
| FakeVisualStudio({ |
| this.cmakePath = _cmakePath, |
| this.cmakeGenerator = 'Visual Studio 16 2019', |
| this.displayVersion = '17.0.0' |
| }); |
| |
| @override |
| final String? cmakePath; |
| |
| @override |
| final String cmakeGenerator; |
| |
| @override |
| final String displayVersion; |
| } |