Wrap Windows build invocation in a batch script (#33443)
Invoking msbuild with runInShell makes handling path escaping more
error-prone, and substantially increases the chances of running into
maximum path limits. This replaces the direct call with a .bat wrapper
that calls vsvars64.bat then msbuild, and uses relative paths within the
script to keep command lengths short.
Fixes https://github.com/flutter/flutter/issues/32792
diff --git a/packages/flutter_tools/bin/vs_build.bat b/packages/flutter_tools/bin/vs_build.bat
new file mode 100644
index 0000000..474ccb9
--- /dev/null
+++ b/packages/flutter_tools/bin/vs_build.bat
@@ -0,0 +1,16 @@
+:: Copyright 2018 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.
+
+:: Calls vcvars64.bat to configure a command-line build environment, then builds
+:: a project with msbuild.
+@echo off
+
+set VCVARS=%~1
+set PROJECT=%~2
+set CONFIG=%~3
+
+call "%VCVARS%"
+if %errorlevel% neq 0 exit /b %errorlevel%
+
+msbuild "%PROJECT%" /p:Configuration=%CONFIG%
diff --git a/packages/flutter_tools/lib/src/windows/build_windows.dart b/packages/flutter_tools/lib/src/windows/build_windows.dart
index 776d864..986b595 100644
--- a/packages/flutter_tools/lib/src/windows/build_windows.dart
+++ b/packages/flutter_tools/lib/src/windows/build_windows.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file
import '../base/common.dart';
+import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/process_manager.dart';
@@ -28,12 +29,25 @@
throwToolExit('Unable to build: could not find vcvars64.bat');
}
+ final String buildScript = fs.path.join(
+ Cache.flutterRoot,
+ 'packages',
+ 'flutter_tools',
+ 'bin',
+ 'vs_build.bat',
+ );
+
final String configuration = buildInfo.isDebug ? 'Debug' : 'Release';
+ final String projectPath = windowsProject.vcprojFile.path;
+ // Run the script with a relative path to the project using the enclosing
+ // directory as the workingDirectory, to avoid hitting the limit on command
+ // lengths in batch scripts if the absolute path to the project is long.
final Process process = await processManager.start(<String>[
- vcvarsScript, '&&', 'msbuild',
- windowsProject.vcprojFile.path,
- '/p:Configuration=$configuration',
- ], runInShell: true);
+ buildScript,
+ vcvarsScript,
+ fs.path.basename(projectPath),
+ configuration,
+ ], workingDirectory: fs.path.dirname(projectPath));
final Status status = logger.startProgress(
'Building Windows application...',
timeout: null,
diff --git a/packages/flutter_tools/test/commands/build_windows_test.dart b/packages/flutter_tools/test/commands/build_windows_test.dart
index 9371b37..5733135 100644
--- a/packages/flutter_tools/test/commands/build_windows_test.dart
+++ b/packages/flutter_tools/test/commands/build_windows_test.dart
@@ -25,7 +25,7 @@
final MockPlatform windowsPlatform = MockPlatform()
..environment['PROGRAMFILES(X86)'] = r'C:\Program Files (x86)\';
final MockPlatform notWindowsPlatform = MockPlatform();
- const String projectPath = r'windows\Runner.vcxproj';
+ const String projectPath = r'C:\windows\Runner.vcxproj';
// A vcvars64.bat location that will be found by the lookup method.
const String vcvarsPath = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat';
@@ -90,12 +90,11 @@
fs.file('.packages').createSync();
when(mockProcessManager.start(<String>[
+ r'C:\packages\flutter_tools\bin\vs_build.bat',
vcvarsPath,
- '&&',
- 'msbuild',
- 'C:\\$projectPath',
- '/p:Configuration=Release',
- ], runInShell: true)).thenAnswer((Invocation invocation) async {
+ fs.path.basename(projectPath),
+ 'Release',
+ ], workingDirectory: fs.path.dirname(projectPath))).thenAnswer((Invocation invocation) async {
return mockProcess;
});