|
| 1 | +# Measurement for mcpp#693, round 5. TEMPORARY: this branch is never merged. |
| 2 | +# |
| 3 | +# Question: |
| 4 | +# R1 In which encodings do the MSVC tools read a response file whose content |
| 5 | +# is not ASCII? mcpp's compile, link and archive edges on Windows pass |
| 6 | +# their arguments through response files that Ninja writes with the bytes |
| 7 | +# of build.ninja, which are UTF-8 under the code page mcpp declares. The |
| 8 | +# earlier rounds produced no response file with non-ASCII content. |
| 9 | +# |
| 10 | +# Each case writes one response file in one encoding and runs the tool on it. |
| 11 | +# Directories: an ASCII control, a name inside code page 1252 (U+00E9) and a |
| 12 | +# name outside it (U+6D4B U+8BD5). Encodings: UTF-8 without a BOM, UTF-8 with |
| 13 | +# a BOM, UTF-16LE with a BOM, and code page 1252. The last case runs the same |
| 14 | +# UTF-8-with-BOM content through Ninja's `rspfile_content`, as mcpp would write |
| 15 | +# it. |
| 16 | +# |
| 17 | +# Every observation is one `READING <id>: ...` line. Source is ASCII only. |
| 18 | + |
| 19 | +$ErrorActionPreference = 'Continue' |
| 20 | +$ProgressPreference = 'SilentlyContinue' |
| 21 | +[System.Text.Encoding]::RegisterProvider([System.Text.CodePagesEncodingProvider]::Instance) |
| 22 | + |
| 23 | +function Reading([string]$id, [string]$text) { |
| 24 | + $line = "READING ${id}: $text" |
| 25 | + Write-Host $line |
| 26 | + if ($env:GITHUB_STEP_SUMMARY) { |
| 27 | + Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY -Value (" " + $line) -Encoding utf8 |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +function Run([string]$exe, [string[]]$argv, [string]$cwd) { |
| 32 | + $psi = [System.Diagnostics.ProcessStartInfo]::new() |
| 33 | + $psi.FileName = $exe |
| 34 | + foreach ($a in $argv) { [void]$psi.ArgumentList.Add($a) } |
| 35 | + $psi.WorkingDirectory = $cwd |
| 36 | + $psi.UseShellExecute = $false |
| 37 | + $psi.RedirectStandardOutput = $true |
| 38 | + $psi.RedirectStandardError = $true |
| 39 | + $p = [System.Diagnostics.Process]::Start($psi) |
| 40 | + $o = $p.StandardOutput.ReadToEndAsync(); $e = $p.StandardError.ReadToEndAsync() |
| 41 | + $p.WaitForExit() |
| 42 | + return @{ code = $p.ExitCode; out = ($o.Result + $e.Result) } |
| 43 | +} |
| 44 | + |
| 45 | +function FirstLine([string]$s) { |
| 46 | + $t = ($s -replace "`r", '' -split "`n" | Where-Object { $_ -match '\S' } | Select-Object -First 1) |
| 47 | + if ($null -eq $t) { return '(no output)' } |
| 48 | + if ($t.Length -gt 150) { $t = $t.Substring(0, 150) + '...' } |
| 49 | + return $t |
| 50 | +} |
| 51 | + |
| 52 | +$Utf8 = [System.Text.UTF8Encoding]::new($false) |
| 53 | +$Ansi = [System.Text.Encoding]::GetEncoding(1252) |
| 54 | +function Bytes([string]$text, [string]$enc) { |
| 55 | + switch ($enc) { |
| 56 | + 'u8' { return $Utf8.GetBytes($text) } |
| 57 | + 'u8bom' { return [byte[]](@(0xEF, 0xBB, 0xBF) + $Utf8.GetBytes($text)) } |
| 58 | + 'u16' { return [byte[]](@(0xFF, 0xFE) + [System.Text.Encoding]::Unicode.GetBytes($text)) } |
| 59 | + 'ansi' { return $Ansi.GetBytes($text) } |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +# --------------------------------------------------------------------------- |
| 64 | +# The Visual Studio environment |
| 65 | + |
| 66 | +$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe' |
| 67 | +$vs = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath |
| 68 | +Import-Module (Join-Path $vs 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll') |
| 69 | +Enter-VsDevShell -VsInstallPath $vs -SkipAutomaticLocation -DevCmdArguments '-arch=x64 -host_arch=x64' | Out-Null |
| 70 | +$acp = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage').ACP |
| 71 | +$cl = (Get-Command cl.exe).Source |
| 72 | +$ninja = Get-Command ninja.exe -ErrorAction SilentlyContinue |
| 73 | +Reading 'env' "acp=$acp cl=$cl ninja=$(if ($ninja) { $ninja.Source } else { 'absent' })" |
| 74 | +$ver = Run $cl @() $env:TEMP |
| 75 | +Reading 'env.cl' (FirstLine $ver.out) |
| 76 | + |
| 77 | +$Root = 'C:\m' |
| 78 | +New-Item -ItemType Directory -Force -Path (Join-Path $Root 'a') | Out-Null |
| 79 | +$A = Join-Path $Root 'a' |
| 80 | +[System.IO.File]::WriteAllText((Join-Path $A 't.c'), "#include `"h.h`"`nint main(void) { return H_OK - 1; }`n") |
| 81 | +[System.IO.File]::WriteAllText((Join-Path $A 't0.c'), "int main(void) { return 0; }`n") |
| 82 | +$r = Run $cl @('/nologo', '/c', 't0.c', '/Fot0.obj') $A |
| 83 | +Reading 'setup.t0' "exit=$($r.code)" |
| 84 | + |
| 85 | +$Dirs = [ordered]@{ |
| 86 | + ascii = 'plain' |
| 87 | + cafe = 'caf' + [char]0x00E9 |
| 88 | + cjk = [string][char]0x6D4B + [char]0x8BD5 |
| 89 | +} |
| 90 | +foreach ($k in $Dirs.Keys) { |
| 91 | + $d = Join-Path $Root $Dirs[$k] |
| 92 | + New-Item -ItemType Directory -Force -Path (Join-Path $d 'inc'), (Join-Path $d 'src') | Out-Null |
| 93 | + [System.IO.File]::WriteAllText((Join-Path $d 'inc\h.h'), "#define H_OK 1`n") |
| 94 | + [System.IO.File]::WriteAllText((Join-Path $d 'src\s.c'), "int s(void) { return 0; }`n") |
| 95 | +} |
| 96 | + |
| 97 | +# --------------------------------------------------------------------------- |
| 98 | +# R1: one response file per tool, directory and encoding |
| 99 | + |
| 100 | +foreach ($k in $Dirs.Keys) { |
| 101 | + $d = Join-Path $Root $Dirs[$k] |
| 102 | + foreach ($enc in @('u8', 'u8bom', 'u16', 'ansi')) { |
| 103 | + if ($enc -eq 'ansi' -and $k -eq 'cjk') { continue } # not representable |
| 104 | + $cases = [ordered]@{ |
| 105 | + 'cl-include' = @{ exe = 'cl.exe'; text = "/nologo /c /I`"$d\inc`" /Fo`"$A\t-$k-$enc.obj`" `"$A\t.c`"`r`n"; out = "$A\t-$k-$enc.obj" } |
| 106 | + 'cl-source' = @{ exe = 'cl.exe'; text = "/nologo /c `"$d\src\s.c`" /Fo`"$A\s-$k-$enc.obj`"`r`n"; out = "$A\s-$k-$enc.obj" } |
| 107 | + 'link-out' = @{ exe = 'link.exe'; text = "/nologo /OUT:`"$d\o-$enc.exe`" `"$A\t0.obj`"`r`n"; out = "$d\o-$enc.exe" } |
| 108 | + 'lib-out' = @{ exe = 'lib.exe'; text = "/nologo /OUT:`"$d\l-$enc.lib`" `"$A\t0.obj`"`r`n"; out = "$d\l-$enc.lib" } |
| 109 | + } |
| 110 | + foreach ($c in $cases.Keys) { |
| 111 | + $rsp = Join-Path $A "$c-$k-$enc.rsp" |
| 112 | + [System.IO.File]::WriteAllBytes($rsp, (Bytes $cases[$c].text $enc)) |
| 113 | + $r = Run $cases[$c].exe @("@$rsp") $A |
| 114 | + $made = Test-Path -LiteralPath $cases[$c].out |
| 115 | + $note = if ($r.code -eq 0) { '' } else { ' | ' + (FirstLine $r.out) } |
| 116 | + Reading "R1.$c.$k.$enc" "exit=$($r.code) output=$made$note" |
| 117 | + } |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +# --------------------------------------------------------------------------- |
| 122 | +# R1n: the same UTF-8-with-BOM content, written by Ninja from `rspfile_content` |
| 123 | + |
| 124 | +if (-not $ninja) { |
| 125 | + $zip = Join-Path $env:TEMP 'ninja.zip' |
| 126 | + Invoke-WebRequest -Uri 'https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-win.zip' -OutFile $zip -UseBasicParsing |
| 127 | + Expand-Archive -LiteralPath $zip -DestinationPath (Join-Path $env:TEMP 'ninja') -Force |
| 128 | + $ninjaExe = Join-Path $env:TEMP 'ninja\ninja.exe' |
| 129 | +} else { $ninjaExe = $ninja.Source } |
| 130 | +$wcp = Run $ninjaExe @('-t', 'wincodepage') $A |
| 131 | +Reading 'R1n.wincodepage' (FirstLine $wcp.out) |
| 132 | +foreach ($k in $Dirs.Keys) { |
| 133 | + foreach ($bom in @('none', 'bom')) { |
| 134 | + $d = Join-Path $Root $Dirs[$k] |
| 135 | + $b = Join-Path $Root "n-$k-$bom" |
| 136 | + New-Item -ItemType Directory -Force -Path $b | Out-Null |
| 137 | + $prefix = if ($bom -eq 'bom') { [string][char]0xFEFF } else { '' } |
| 138 | + $text = "ninja_required_version = 1.11`n" + |
| 139 | + "rule cc`n command = cl.exe @`$out.rsp`n rspfile = `$out.rsp`n" + |
| 140 | + " rspfile_content = $prefix/nologo /c /I`"$($d -replace ':', '$$:')\inc`" `$in /Fo`$out`n" + |
| 141 | + "build t.obj: cc $($A -replace ':', '$$:')\t.c`n" |
| 142 | + [System.IO.File]::WriteAllBytes((Join-Path $b 'build.ninja'), $Utf8.GetBytes($text)) |
| 143 | + $r = Run $ninjaExe @('-C', $b) $Root |
| 144 | + $note = if ($r.code -eq 0) { '' } else { ' | ' + (FirstLine ($r.out -replace '(?m)^\[.*\]\s*', '')) } |
| 145 | + Reading "R1n.$k.$bom" "exit=$($r.code) output=$(Test-Path -LiteralPath (Join-Path $b 't.obj'))$note" |
| 146 | + } |
| 147 | +} |
0 commit comments