diff --git a/Build.cmd b/Build.cmd index 675fdf83f6a..29c8a52538c 100644 --- a/Build.cmd +++ b/Build.cmd @@ -1,2 +1,2 @@ @echo off -powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0eng\common\Build.ps1""" -restore -build %*" +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0eng\common\build.ps1" -restore -build %* diff --git a/Documentation/ArcadeSdk.md b/Documentation/ArcadeSdk.md index 20278df7cbb..39eb25f5f96 100644 --- a/Documentation/ArcadeSdk.md +++ b/Documentation/ArcadeSdk.md @@ -119,6 +119,32 @@ Since the default scripts pass along additional arguments, you could restore, bu You should feel free to create more repo specific scripts as appropriate to meet common dev scenarios for your repo. +#### Passing arguments on Windows + +The `.cmd` wrappers invoke PowerShell with `-File`, so everything after the script path reaches the +target script as a literal argument. Arguments are not re-parsed as PowerShell source, which means +`;` and spaces are safe: + +``` +build.cmd -projects "src\A\A.csproj;src\B\B.csproj" /p:Foo=Bar +build.cmd -projects "C:\my dir\A.csproj" +``` + +Two consequences of arguments arriving as literal strings: + +- Boolean parameters (`-warnAsError`, `-nodeReuse`, `-msbuildMultiThreaded`) accept `$true`, `true`, + `1`, `$false`, `false` and `0`. Anything else is an error rather than a silently flipped value. +- Switch parameters cannot be negated. `-ci:$false` does not work through a `.cmd` wrapper; omit the + switch instead, or call `eng\common\build.ps1` directly. + +If your repo has its own `eng/build.ps1` and you switch your own `.cmd` wrappers to `-File`, remove +the `[bool]` type constraint from any parameter that can be passed on the command line. A `[bool]` +parameter cannot bind a string, and under `-File` every argument is a string, so `-warnAsError $false` +fails with `Cannot convert value "System.String" to type "System.Boolean"`. No value works, `0` +included. The parameter block binds before `eng/common/tools.ps1` is dot-sourced, so this cannot be +fixed inside `tools.ps1`. Drop the constraint at the parameter, and let `tools.ps1` normalize the +value afterwards, the way `eng/common/build.ps1` does. + ### /eng/common/* The Arcade SDK requires bootstrapper scripts to be present in the repo. @@ -137,6 +163,9 @@ By default, Arcade builds solutions in the root of the repo. Overriding the def Example: `build.cmd -projects MyProject.proj` + A semi-colon delimited list works too, quoted so that `cmd` passes it as one argument: + `build.cmd -projects "src\A\A.csproj;src\B\B.csproj"` + See [source code](https://github.com/dotnet/arcade/blob/440b2dae3a206b28f6aba727b7818873358fcc0a/eng/common/build.ps1#L53) - Provide a list of projects or solutions in `eng/Build.props`. diff --git a/Restore.cmd b/Restore.cmd index f9b8e80f15e..62594161156 100644 --- a/Restore.cmd +++ b/Restore.cmd @@ -1,2 +1,2 @@ @echo off -powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0eng\common\Build.ps1""" -restore %*" \ No newline at end of file +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0eng\common\build.ps1" -restore %* \ No newline at end of file diff --git a/Test.cmd b/Test.cmd index 802b44d7726..6a4c038c298 100644 --- a/Test.cmd +++ b/Test.cmd @@ -1,2 +1,2 @@ @echo off -powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0eng\common\Build.ps1""" -test %*" \ No newline at end of file +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0eng\common\build.ps1" -test %* \ No newline at end of file diff --git a/eng/common/CIBuild.cmd b/eng/common/CIBuild.cmd index ac1f72bf94e..a9d264c3e7a 100644 --- a/eng/common/CIBuild.cmd +++ b/eng/common/CIBuild.cmd @@ -1,2 +1,2 @@ @echo off -powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Build.ps1""" -restore -build -test -sign -pack -publish -ci %*" +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" -restore -build -test -sign -pack -publish -ci %* diff --git a/eng/common/build.cmd b/eng/common/build.cmd index 99daf368aba..c64ee69a7d9 100644 --- a/eng/common/build.cmd +++ b/eng/common/build.cmd @@ -1,3 +1,3 @@ @echo off -powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0build.ps1""" %*" +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* exit /b %ErrorLevel% diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index fee2f839919..0b228fa70bb 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -5,10 +5,10 @@ Param( [string] $projects, [string][Alias('v')]$verbosity = "minimal", [string] $msbuildEngine = $null, - [bool] $warnAsError = $true, + $warnAsError = $true, [string] $warnNotAsError = '', - [bool] $nodeReuse = $true, - [bool][Alias('mt')]$msbuildMultiThreaded = $false, + $nodeReuse = $true, + [Alias('mt')]$msbuildMultiThreaded = $false, [switch] $buildCheck = $false, [switch][Alias('r')]$restore, [switch] $deployDeps, diff --git a/eng/common/dotnet-install.cmd b/eng/common/dotnet-install.cmd index b1c2642e76f..aa6bac761da 100644 --- a/eng/common/dotnet-install.cmd +++ b/eng/common/dotnet-install.cmd @@ -1,2 +1,2 @@ @echo off -powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0dotnet-install.ps1""" %*" \ No newline at end of file +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0dotnet-install.ps1" %* \ No newline at end of file diff --git a/eng/common/dotnet.cmd b/eng/common/dotnet.cmd index 527fa4bb38f..e7ee8f5832a 100644 --- a/eng/common/dotnet.cmd +++ b/eng/common/dotnet.cmd @@ -3,5 +3,5 @@ :: This script is used to install the .NET SDK. :: It will also invoke the SDK with any provided arguments. -powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0dotnet.ps1""" %*" +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0dotnet.ps1" %* exit /b %ErrorLevel% diff --git a/eng/common/init-tools-native.cmd b/eng/common/init-tools-native.cmd index 438cd548c45..e742c8e16c6 100644 --- a/eng/common/init-tools-native.cmd +++ b/eng/common/init-tools-native.cmd @@ -1,3 +1,3 @@ @echo off -powershell -NoProfile -NoLogo -ExecutionPolicy ByPass -command "& """%~dp0init-tools-native.ps1""" %*" +powershell -NoProfile -NoLogo -ExecutionPolicy ByPass -File "%~dp0init-tools-native.ps1" %* exit /b %ErrorLevel% \ No newline at end of file diff --git a/eng/common/msbuild.ps1 b/eng/common/msbuild.ps1 index b6dfb570ea5..555e8d72d11 100644 --- a/eng/common/msbuild.ps1 +++ b/eng/common/msbuild.ps1 @@ -1,9 +1,9 @@ [CmdletBinding(PositionalBinding=$false)] Param( [string] $verbosity = 'minimal', - [bool] $warnAsError = $true, - [bool] $nodeReuse = $true, - [bool][Alias('mt')]$msbuildMultiThreaded = $false, + $warnAsError = $true, + $nodeReuse = $true, + [Alias('mt')]$msbuildMultiThreaded = $false, [switch] $ci, [switch] $prepareMachine, [switch] $excludePrereleaseVS, diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index e84033dad90..8712bb528e5 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -1,6 +1,28 @@ # Initialize variables if they aren't already defined. # These may be defined as parameters of the importing script, or set after importing this script. +# Wrapper scripts (build.cmd, CIBuild.cmd, ...) invoke PowerShell with -File, which passes every +# argument to the script as a literal string. '-warnAsError $false' therefore arrives as the text +# '$false', which does not bind to [bool]. Accept the spellings a caller can reasonably produce and +# fail on anything else, so that a typo is an error rather than a silently flipped value. +function ParseBooleanArgument([string] $name, $value) { + if ($value -is [bool]) { + return $value + } + + $text = "$value".Trim() + + if ($text -in '$true', 'true', '1') { + return $true + } + + if ($text -in '$false', 'false', '0') { + return $false + } + + throw "Invalid value '$value' for -$name. Expected one of: `$true, true, 1, `$false, false, 0." +} + # CI mode - set to true on CI server for PR validation build or official build. [bool]$ci = if (Test-Path variable:ci) { $ci } else { $false } @@ -29,14 +51,14 @@ [string]$verbosity = if (Test-Path variable:verbosity) { $verbosity } else { 'minimal' } # Set to true to reuse msbuild nodes. Recommended to not reuse on CI. -[bool]$nodeReuse = if (Test-Path variable:nodeReuse) { $nodeReuse } else { !$ci } +[bool]$nodeReuse = if ((Test-Path variable:nodeReuse) -and ($null -ne $nodeReuse)) { ParseBooleanArgument 'nodeReuse' $nodeReuse } else { !$ci } # Set to true to build with MSBuild's multi-threaded mode (-mt). Opt-in for now, so off unless it was # explicitly requested. It's intended to become the default for local builds once it has proven out. -[bool]$msbuildMultiThreaded = if (Test-Path variable:msbuildMultiThreaded) { $msbuildMultiThreaded } else { $false } +[bool]$msbuildMultiThreaded = if ((Test-Path variable:msbuildMultiThreaded) -and ($null -ne $msbuildMultiThreaded)) { ParseBooleanArgument 'msbuildMultiThreaded' $msbuildMultiThreaded } else { $false } # Configures warning treatment in msbuild. -[bool]$warnAsError = if (Test-Path variable:warnAsError) { $warnAsError } else { $true } +[bool]$warnAsError = if ((Test-Path variable:warnAsError) -and ($null -ne $warnAsError)) { ParseBooleanArgument 'warnAsError' $warnAsError } else { $true } # Specifies semi-colon delimited list of warning codes that should not be treated as errors. # Defaults to NuGet Audit warning codes NU1901-NU1904.