| 1 | name: CI Windows MSVC |
| 2 | |
| 3 | on: |
| 4 | push: |
| 5 | branches: |
| 6 | - master |
| 7 | paths-ignore: |
| 8 | - '**.md' |
| 9 | - '**.yml' |
| 10 | - '!**.bat' |
| 11 | - '!**/windows_ci_msvc.yml' |
| 12 | - 'cmd/tools/**' |
| 13 | - '!cmd/tools/builders/**.v' |
| 14 | pull_request: |
| 15 | paths-ignore: |
| 16 | - '**.md' |
| 17 | - '**.yml' |
| 18 | - '!**.bat' |
| 19 | - '!**/windows_ci_msvc.yml' |
| 20 | - '!**/windows-install-sqlite.bat' |
| 21 | - 'cmd/tools/**' |
| 22 | - '!cmd/tools/builders/**.v' |
| 23 | |
| 24 | concurrency: |
| 25 | group: windows-${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && github.sha || github.ref }} |
| 26 | cancel-in-progress: true |
| 27 | |
| 28 | jobs: |
| 29 | msvc-windows: |
| 30 | runs-on: windows-2022 |
| 31 | timeout-minutes: 70 |
| 32 | env: |
| 33 | VFLAGS: -cc msvc |
| 34 | VTEST_SHOW_LONGEST_BY_RUNTIME: 3 |
| 35 | VTEST_SHOW_LONGEST_BY_COMPTIME: 3 |
| 36 | VTEST_SHOW_LONGEST_BY_TOTALTIME: 3 |
| 37 | steps: |
| 38 | - uses: actions/checkout@v6 |
| 39 | - uses: ilammy/msvc-dev-cmd@v1 |
| 40 | with: |
| 41 | arch: x64 |
| 42 | - name: Verify MSVC tools |
| 43 | run: | |
| 44 | where.exe cl.exe |
| 45 | where.exe link.exe |
| 46 | where.exe dumpbin.exe |
| 47 | - name: Build |
| 48 | run: | |
| 49 | echo %VFLAGS% |
| 50 | echo $VFLAGS |
| 51 | .\makev.bat -msvc |
| 52 | .\v.exe symlink |
| 53 | - name: backend x64 regressions |
| 54 | run: | |
| 55 | function Assert-LastExit([string] $label) { |
| 56 | if ($LASTEXITCODE -ne 0) { |
| 57 | throw "$label failed with exit code $LASTEXITCODE" |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | $env:VJOBS = '1' |
| 62 | .\v.exe test ` |
| 63 | vlib/v2/abi ` |
| 64 | vlib/v2/gen/x64/x64_abi_test.v ` |
| 65 | vlib/v2/gen/x64/x64_backend_diagnostics_test.v ` |
| 66 | vlib/v2/gen/x64/x64_object_format_test.v ` |
| 67 | vlib/v2/gen/x64/x64_pe_linker_test.v ` |
| 68 | vlib/v2/ssa/tree_sumtype_lowering_test.v |
| 69 | Assert-LastExit 'stable V2 x64 unit tests' |
| 70 | .\v.exe test -run-only test_module_qualified_alias_array_field_index_has_alias_element_type,test_module_storage_legacy_direct_import_nested_module_selector_uses_declared_leaf_module,test_module_storage_flat_direct_import_nested_module_selector_uses_declared_leaf_module vlib/v2/ssa/module_storage_test.v |
| 71 | Assert-LastExit 'targeted vlib/v2/ssa/module_storage_test.v regressions' |
| 72 | .\v.exe test -run-only test_x64_macos_windows_math_log_20_stdout_exact_bytes,test_x64_macos_windows_generic_sumtype_repeated_base_specialization_stdout_exact_bytes,test_x64_macos_windows_formatted_int_interpolation_stdout_exact_bytes,test_x64_macos_windows_formatted_int_width_100_interpolation_stdout_exact_bytes,test_x64_macos_windows_formatted_f64_interpolation_stdout_exact_bytes,test_x64_macos_windows_formatted_string_return_lifetime_stdout_exact_bytes,test_x64_macos_windows_spectral_reduced_formatted_stdout_exact_bytes,test_x64_macos_windows_bits_len32_stdout_exact_bytes,test_x64_macos_windows_rand_u32n_interface_result_stdout_exact_bytes,test_x64_macos_windows_rand_intn_range_interface_result_stdout_exact_bytes,test_x64_macos_windows_textscanner_embedded_parser_stdout_exact_bytes,test_x64_macos_windows_struct_positional_side_effect_stdout_exact_bytes,test_x64_macos_windows_struct_named_side_effect_stdout_exact_bytes,test_x64_macos_windows_unrelated_same_shape_struct_stdout_exact_bytes,test_x64_macos_windows_named_init_embedded_value_stdout_exact_bytes,test_x64_macos_windows_get_raw_line_example_stdin_stdout_exact_bytes,test_x64_macos_windows_mini_calculator_example_stdin_stdout_exact_bytes,test_x64_macos_windows_mini_calculator_recursive_descent_example_stdin_stdout_exact_bytes vlib/v2/gen/x64/x64_runtime_smoke_test.v |
| 73 | Assert-LastExit 'reduced V2 x64 runtime smoke tests' |
| 74 | Remove-Item Env:\VJOBS -ErrorAction SilentlyContinue |
| 75 | .\v.exe test vlib/v2/builder/native_test.v vlib/v2/builder/target_os_test.v |
| 76 | Assert-LastExit 'vlib/v2/builder/native_test.v vlib/v2/builder/target_os_test.v' |
| 77 | .\v.exe test vlib/v2/ssa/optimize |
| 78 | Assert-LastExit 'vlib/v2/ssa/optimize' |
| 79 | $env:V2_VERIFY_STRICT = '1' |
| 80 | .\v.exe test vlib/v2/ssa/optimize |
| 81 | Assert-LastExit 'strict vlib/v2/ssa/optimize' |
| 82 | Remove-Item Env:\V2_VERIFY_STRICT -ErrorAction SilentlyContinue |
| 83 | - name: backend x64 examples |
| 84 | run: | |
| 85 | function Assert-LastExit([string] $label) { |
| 86 | if ($LASTEXITCODE -ne 0) { |
| 87 | throw "$label failed with exit code $LASTEXITCODE" |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | function Assert-ByteArrayEqual([byte[]] $actual, [byte[]] $expected, [string] $label) { |
| 92 | if ($actual.Length -ne $expected.Length) { |
| 93 | $actual_text = [System.Text.Encoding]::UTF8.GetString($actual) |
| 94 | $expected_text = [System.Text.Encoding]::UTF8.GetString($expected) |
| 95 | throw "$label mismatch. Expected length $($expected.Length), got $($actual.Length).`nExpected:`n$expected_text`nActual:`n$actual_text" |
| 96 | } |
| 97 | for ($i = 0; $i -lt $actual.Length; $i++) { |
| 98 | if ($actual[$i] -ne $expected[$i]) { |
| 99 | $actual_text = [System.Text.Encoding]::UTF8.GetString($actual) |
| 100 | $expected_text = [System.Text.Encoding]::UTF8.GetString($expected) |
| 101 | throw "$label mismatch at byte $i.`nExpected:`n$expected_text`nActual:`n$actual_text" |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | function Get-PeImportedDlls([string] $exe) { |
| 107 | $output = & dumpbin.exe /imports $exe 2>&1 |
| 108 | if ($LASTEXITCODE -ne 0) { |
| 109 | $text = ($output | Out-String).Trim() |
| 110 | throw "dumpbin import inspection failed for $exe with exit code $LASTEXITCODE`n$text" |
| 111 | } |
| 112 | |
| 113 | $dlls = [System.Collections.Generic.List[string]]::new() |
| 114 | foreach ($lineObject in $output) { |
| 115 | $line = [string] $lineObject |
| 116 | if ($line -match '^\s*([A-Za-z0-9_.-]+\.dll)\s*$') { |
| 117 | $dlls.Add($matches[1].ToLowerInvariant()) |
| 118 | } |
| 119 | } |
| 120 | return $dlls | Sort-Object -Unique |
| 121 | } |
| 122 | |
| 123 | function Assert-TinyPeImports([string] $name, [string] $exe, [string] $pe_import_mode) { |
| 124 | if ($pe_import_mode -eq '') { |
| 125 | return |
| 126 | } |
| 127 | if ($pe_import_mode -eq 'tiny-used') { |
| 128 | [string[]] $allowed_dlls = @('kernel32.dll') |
| 129 | } elseif ($pe_import_mode -eq 'tiny-shell32-argv') { |
| 130 | [string[]] $allowed_dlls = @('kernel32.dll', 'shell32.dll') |
| 131 | } else { |
| 132 | throw "$name has unknown PE import validation mode: $pe_import_mode" |
| 133 | } |
| 134 | |
| 135 | [string[]] $dlls = @(Get-PeImportedDlls $exe) |
| 136 | if ($dlls.Count -eq 0) { |
| 137 | throw "$name PE import validation found no imported DLLs with dumpbin.exe" |
| 138 | } |
| 139 | |
| 140 | $crt_dlls = $dlls | Where-Object { |
| 141 | $_ -match '^(msvcrt|ucrtbase|vcruntime)[0-9]*\.dll$' -or $_ -match '^api-ms-win-crt-.*\.dll$' |
| 142 | } |
| 143 | if ($crt_dlls.Count -gt 0) { |
| 144 | throw "$name tiny PE unexpectedly imports CRT DLL(s): $($crt_dlls -join ', ')" |
| 145 | } |
| 146 | |
| 147 | $unexpected_dlls = $dlls | Where-Object { $_ -notin $allowed_dlls } |
| 148 | if ($unexpected_dlls.Count -gt 0) { |
| 149 | throw "$name PE imports unexpected DLL(s): $($unexpected_dlls -join ', ')" |
| 150 | } |
| 151 | |
| 152 | $missing_dlls = $allowed_dlls | Where-Object { $_ -notin $dlls } |
| 153 | if ($missing_dlls.Count -gt 0) { |
| 154 | throw "$name PE import validation missed expected DLL(s): $($missing_dlls -join ', ')" |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | function Invoke-NativeX64Example([string] $source, [string] $name, [byte[]] $expected_stdout, [string] $pe_import_mode = '', [string[]] $program_args = @()) { |
| 159 | $exe = Join-Path $tmp "$name.exe" |
| 160 | $stdout_path = Join-Path $tmp "$name.out" |
| 161 | $stderr_path = Join-Path $tmp "$name.err" |
| 162 | |
| 163 | $compile_output = & .\v.exe -v2 -no-parallel -b x64 $source -o $exe 2>&1 |
| 164 | $compile_exit = $LASTEXITCODE |
| 165 | foreach ($line in $compile_output) { |
| 166 | Write-Host $line |
| 167 | } |
| 168 | if ($compile_exit -ne 0) { |
| 169 | throw "compile $source failed with exit code $compile_exit" |
| 170 | } |
| 171 | Assert-TinyPeImports $name $exe $pe_import_mode |
| 172 | |
| 173 | Remove-Item -LiteralPath $stdout_path, $stderr_path -Force -ErrorAction SilentlyContinue |
| 174 | if ($program_args.Count -gt 0) { |
| 175 | $process = Start-Process -FilePath $exe -ArgumentList $program_args -NoNewWindow -Wait -PassThru -RedirectStandardOutput $stdout_path -RedirectStandardError $stderr_path |
| 176 | } else { |
| 177 | $process = Start-Process -FilePath $exe -NoNewWindow -Wait -PassThru -RedirectStandardOutput $stdout_path -RedirectStandardError $stderr_path |
| 178 | } |
| 179 | if ($process.ExitCode -ne 0) { |
| 180 | if (Test-Path -LiteralPath $stderr_path) { |
| 181 | Get-Content -LiteralPath $stderr_path |
| 182 | } |
| 183 | throw "$source exited with code $($process.ExitCode)" |
| 184 | } |
| 185 | |
| 186 | [byte[]] $stdout = if (Test-Path -LiteralPath $stdout_path) { [System.IO.File]::ReadAllBytes($stdout_path) } else { New-Object byte[] 0 } |
| 187 | [byte[]] $stderr = if (Test-Path -LiteralPath $stderr_path) { [System.IO.File]::ReadAllBytes($stderr_path) } else { New-Object byte[] 0 } |
| 188 | $empty = New-Object byte[] 0 |
| 189 | Assert-ByteArrayEqual $stdout $expected_stdout "$source stdout" |
| 190 | Assert-ByteArrayEqual $stderr $empty "$source stderr" |
| 191 | } |
| 192 | |
| 193 | function Invoke-NativeX64ExampleWithStdin([string] $source, [string] $name, [byte[]] $expected_stdout, [string] $stdin_path, [string] $pe_import_mode = '', [string[]] $program_args = @()) { |
| 194 | $exe = Join-Path $tmp "$name.exe" |
| 195 | $stdout_path = Join-Path $tmp "$name.out" |
| 196 | $stderr_path = Join-Path $tmp "$name.err" |
| 197 | |
| 198 | $compile_output = & .\v.exe -v2 -no-parallel -b x64 $source -o $exe 2>&1 |
| 199 | $compile_exit = $LASTEXITCODE |
| 200 | foreach ($line in $compile_output) { |
| 201 | Write-Host $line |
| 202 | } |
| 203 | if ($compile_exit -ne 0) { |
| 204 | throw "compile $source failed with exit code $compile_exit" |
| 205 | } |
| 206 | Assert-TinyPeImports $name $exe $pe_import_mode |
| 207 | |
| 208 | Remove-Item -LiteralPath $stdout_path, $stderr_path -Force -ErrorAction SilentlyContinue |
| 209 | if ($program_args.Count -gt 0) { |
| 210 | $process = Start-Process -FilePath $exe -ArgumentList $program_args -NoNewWindow -Wait -PassThru -RedirectStandardInput $stdin_path -RedirectStandardOutput $stdout_path -RedirectStandardError $stderr_path |
| 211 | } else { |
| 212 | $process = Start-Process -FilePath $exe -NoNewWindow -Wait -PassThru -RedirectStandardInput $stdin_path -RedirectStandardOutput $stdout_path -RedirectStandardError $stderr_path |
| 213 | } |
| 214 | if ($process.ExitCode -ne 0) { |
| 215 | if (Test-Path -LiteralPath $stderr_path) { |
| 216 | Get-Content -LiteralPath $stderr_path |
| 217 | } |
| 218 | throw "$source exited with code $($process.ExitCode)" |
| 219 | } |
| 220 | |
| 221 | [byte[]] $stdout = if (Test-Path -LiteralPath $stdout_path) { [System.IO.File]::ReadAllBytes($stdout_path) } else { New-Object byte[] 0 } |
| 222 | [byte[]] $stderr = if (Test-Path -LiteralPath $stderr_path) { [System.IO.File]::ReadAllBytes($stderr_path) } else { New-Object byte[] 0 } |
| 223 | $empty = New-Object byte[] 0 |
| 224 | Assert-ByteArrayEqual $stdout $expected_stdout "$source stdout" |
| 225 | Assert-ByteArrayEqual $stderr $empty "$source stderr" |
| 226 | } |
| 227 | |
| 228 | function Invoke-NativeX64ExampleCapture([string] $source, [string] $name, [string[]] $program_args = @()) { |
| 229 | $exe = Join-Path $tmp "$name.exe" |
| 230 | $stdout_path = Join-Path $tmp "$name.out" |
| 231 | $stderr_path = Join-Path $tmp "$name.err" |
| 232 | |
| 233 | $compile_output = & .\v.exe -v2 -no-parallel -b x64 $source -o $exe 2>&1 |
| 234 | $compile_exit = $LASTEXITCODE |
| 235 | foreach ($line in $compile_output) { |
| 236 | Write-Host $line |
| 237 | } |
| 238 | if ($compile_exit -ne 0) { |
| 239 | throw "compile $source failed with exit code $compile_exit" |
| 240 | } |
| 241 | |
| 242 | Remove-Item -LiteralPath $stdout_path, $stderr_path -Force -ErrorAction SilentlyContinue |
| 243 | if ($program_args.Count -gt 0) { |
| 244 | $process = Start-Process -FilePath $exe -ArgumentList $program_args -NoNewWindow -Wait -PassThru -RedirectStandardOutput $stdout_path -RedirectStandardError $stderr_path |
| 245 | } else { |
| 246 | $process = Start-Process -FilePath $exe -NoNewWindow -Wait -PassThru -RedirectStandardOutput $stdout_path -RedirectStandardError $stderr_path |
| 247 | } |
| 248 | if ($process.ExitCode -ne 0) { |
| 249 | if (Test-Path -LiteralPath $stderr_path) { |
| 250 | Get-Content -LiteralPath $stderr_path |
| 251 | } |
| 252 | throw "$source exited with code $($process.ExitCode)" |
| 253 | } |
| 254 | |
| 255 | [byte[]] $stderr = if (Test-Path -LiteralPath $stderr_path) { [System.IO.File]::ReadAllBytes($stderr_path) } else { New-Object byte[] 0 } |
| 256 | $empty = New-Object byte[] 0 |
| 257 | Assert-ByteArrayEqual $stderr $empty "$source stderr" |
| 258 | return $stdout_path |
| 259 | } |
| 260 | |
| 261 | function Assert-RandomIpsStdout([string] $path) { |
| 262 | [string[]] $lines = [System.IO.File]::ReadAllLines($path) |
| 263 | if ($lines.Count -ne 10) { |
| 264 | throw "random_ips expected 10 lines, got $($lines.Count)" |
| 265 | } |
| 266 | $rows = [System.Collections.Generic.List[string]]::new() |
| 267 | $all_second_zero = $true |
| 268 | $all_fourth_zero = $true |
| 269 | foreach ($line in $lines) { |
| 270 | [string[]] $parts = $line.Split('.') |
| 271 | if ($parts.Count -ne 4) { |
| 272 | throw "random_ips invalid IPv4 shape: $line" |
| 273 | } |
| 274 | [int[]] $octets = @() |
| 275 | foreach ($part in $parts) { |
| 276 | if ($part -notmatch '^[0-9]+$') { |
| 277 | throw "random_ips non-numeric octet: $line" |
| 278 | } |
| 279 | $value = [int] $part |
| 280 | if ($value -lt 0 -or $value -gt 254) { |
| 281 | throw "random_ips octet out of range: $line" |
| 282 | } |
| 283 | $octets += $value |
| 284 | } |
| 285 | if ($octets[1] -ne 0) { |
| 286 | $all_second_zero = $false |
| 287 | } |
| 288 | if ($octets[3] -ne 0) { |
| 289 | $all_fourth_zero = $false |
| 290 | } |
| 291 | $rows.Add(($octets -join '.')) |
| 292 | } |
| 293 | if (($rows | Sort-Object -Unique).Count -lt 3) { |
| 294 | throw 'random_ips output has too little row diversity' |
| 295 | } |
| 296 | if ($all_second_zero -and $all_fourth_zero) { |
| 297 | throw 'random_ips suspicious old x.0.x.0 pattern' |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | function Assert-Rule110Stdout([string] $path, [int] $width) { |
| 302 | [string[]] $lines = [System.IO.File]::ReadAllLines($path) |
| 303 | $title = 'Rule 110 V Implementation' |
| 304 | $idx = -1 |
| 305 | for ($i = 0; $i -lt $lines.Count; $i++) { |
| 306 | if ($lines[$i].Contains($title)) { |
| 307 | $idx = $i |
| 308 | break |
| 309 | } |
| 310 | } |
| 311 | if ($idx -lt 0) { |
| 312 | throw 'rule110 missing title' |
| 313 | } |
| 314 | [string[]] $generations = @() |
| 315 | if ($idx + 1 -lt $lines.Count) { |
| 316 | $generations = @($lines[($idx + 1)..($lines.Count - 1)]) |
| 317 | } |
| 318 | if ($generations.Count -gt 0 -and $generations[0] -eq '') { |
| 319 | if ($generations.Count -eq 1) { |
| 320 | $generations = @() |
| 321 | } else { |
| 322 | $generations = @($generations[1..($generations.Count - 1)]) |
| 323 | } |
| 324 | } |
| 325 | if ($generations.Count -ne $width) { |
| 326 | throw "rule110 expected $width generation lines, got $($generations.Count)" |
| 327 | } |
| 328 | $has_live_cell = $false |
| 329 | foreach ($line in $generations) { |
| 330 | if ($line.Length -ne $width) { |
| 331 | throw "rule110 generation line has width $($line.Length), expected $width`: '$line'" |
| 332 | } |
| 333 | if ($line -match '[^ *]') { |
| 334 | throw "rule110 invalid generation chars: '$line'" |
| 335 | } |
| 336 | if ($line.Contains('*')) { |
| 337 | $has_live_cell = $true |
| 338 | } |
| 339 | } |
| 340 | if (-not $has_live_cell) { |
| 341 | throw 'rule110 expected at least one live cell' |
| 342 | } |
| 343 | if (($generations | Sort-Object -Unique).Count -lt 3) { |
| 344 | throw 'rule110 expected at least 3 distinct generation lines' |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | $tmp = Join-Path $env:RUNNER_TEMP 'v2_x64_native' |
| 349 | New-Item -ItemType Directory -Force -Path $tmp | Out-Null |
| 350 | |
| 351 | function Get-VRunExpectedBytes([string] $source, [string] $name, [string[]] $program_args = @()) { |
| 352 | $stdout_path = Join-Path $tmp "$name.vrun.out" |
| 353 | $stderr_path = Join-Path $tmp "$name.vrun.err" |
| 354 | Remove-Item -LiteralPath $stdout_path, $stderr_path -Force -ErrorAction SilentlyContinue |
| 355 | |
| 356 | $v_args = @('run', $source) + $program_args |
| 357 | $process = Start-Process -FilePath '.\v.exe' -ArgumentList $v_args -NoNewWindow -Wait -PassThru -RedirectStandardOutput $stdout_path -RedirectStandardError $stderr_path |
| 358 | if ($process.ExitCode -ne 0) { |
| 359 | if (Test-Path -LiteralPath $stderr_path) { |
| 360 | Get-Content -LiteralPath $stderr_path |
| 361 | } |
| 362 | throw "v run $source $($program_args -join ' ') failed with code $($process.ExitCode)" |
| 363 | } |
| 364 | |
| 365 | [byte[]] $stderr = if (Test-Path -LiteralPath $stderr_path) { [System.IO.File]::ReadAllBytes($stderr_path) } else { New-Object byte[] 0 } |
| 366 | $empty = New-Object byte[] 0 |
| 367 | Assert-ByteArrayEqual $stderr $empty "v run $source stderr" |
| 368 | return [System.IO.File]::ReadAllBytes($stdout_path) |
| 369 | } |
| 370 | |
| 371 | function Get-VRunExpectedBytesWithStdin([string] $source, [string] $name, [string] $stdin_path, [string[]] $program_args = @()) { |
| 372 | $stdout_path = Join-Path $tmp "$name.vrun.out" |
| 373 | $stderr_path = Join-Path $tmp "$name.vrun.err" |
| 374 | Remove-Item -LiteralPath $stdout_path, $stderr_path -Force -ErrorAction SilentlyContinue |
| 375 | |
| 376 | $v_args = @('run', $source) + $program_args |
| 377 | $process = Start-Process -FilePath '.\v.exe' -ArgumentList $v_args -NoNewWindow -Wait -PassThru -RedirectStandardInput $stdin_path -RedirectStandardOutput $stdout_path -RedirectStandardError $stderr_path |
| 378 | if ($process.ExitCode -ne 0) { |
| 379 | if (Test-Path -LiteralPath $stderr_path) { |
| 380 | Get-Content -LiteralPath $stderr_path |
| 381 | } |
| 382 | throw "v run $source $($program_args -join ' ') failed with code $($process.ExitCode)" |
| 383 | } |
| 384 | |
| 385 | [byte[]] $stderr = if (Test-Path -LiteralPath $stderr_path) { [System.IO.File]::ReadAllBytes($stderr_path) } else { New-Object byte[] 0 } |
| 386 | $empty = New-Object byte[] 0 |
| 387 | Assert-ByteArrayEqual $stderr $empty "v run $source stderr" |
| 388 | return [System.IO.File]::ReadAllBytes($stdout_path) |
| 389 | } |
| 390 | |
| 391 | $hello_expected = [System.Text.Encoding]::UTF8.GetBytes("Hello, World!`n") |
| 392 | Invoke-NativeX64Example 'examples/hello_world.v' 'hello_world' $hello_expected 'tiny-used' |
| 393 | |
| 394 | $fizz_lines = 1..100 | ForEach-Object { |
| 395 | if ($_ % 15 -eq 0) { |
| 396 | 'FizzBuzz' |
| 397 | } elseif ($_ % 3 -eq 0) { |
| 398 | 'Fizz' |
| 399 | } elseif ($_ % 5 -eq 0) { |
| 400 | 'Buzz' |
| 401 | } else { |
| 402 | [string] $_ |
| 403 | } |
| 404 | } |
| 405 | $fizz_expected = [System.Text.Encoding]::UTF8.GetBytes(($fizz_lines -join "`n") + "`n") |
| 406 | Invoke-NativeX64Example 'examples/fizz_buzz.v' 'fizz_buzz' $fizz_expected 'tiny-used' |
| 407 | |
| 408 | $fibonacci_expected = [System.Text.Encoding]::UTF8.GetBytes("1`n1`n2`n3`n5`n8`n13`n21`n34`n55`n89`n") |
| 409 | Invoke-NativeX64Example 'examples/fibonacci.v' 'fibonacci' $fibonacci_expected 'tiny-shell32-argv' @('10') |
| 410 | |
| 411 | $dump_factorial_expected = [System.Text.Encoding]::UTF8.GetBytes("120`n") |
| 412 | Invoke-NativeX64Example 'examples/dump_factorial.v' 'dump_factorial' $dump_factorial_expected 'tiny-used' |
| 413 | |
| 414 | function Get-HanoiExpectedLines([int] $n, [string] $a, [string] $b, [string] $c) { |
| 415 | if ($n -eq 1) { |
| 416 | "Disc 1 from $a to $c..." |
| 417 | return |
| 418 | } |
| 419 | Get-HanoiExpectedLines ($n - 1) $a $c $b |
| 420 | "Disc $n from $a to $c..." |
| 421 | Get-HanoiExpectedLines ($n - 1) $b $a $c |
| 422 | } |
| 423 | |
| 424 | $hanoi_lines = @(Get-HanoiExpectedLines 7 'A' 'B' 'C') |
| 425 | $hanoi_expected = [System.Text.Encoding]::UTF8.GetBytes(($hanoi_lines -join "`n") + "`n") |
| 426 | Invoke-NativeX64Example 'examples/hanoi.v' 'hanoi' $hanoi_expected 'tiny-used' |
| 427 | |
| 428 | function Get-SudokuExpectedLines { |
| 429 | $puzzle = @( |
| 430 | ,@(0, 3, 0, 0, 7, 0, 0, 0, 0) |
| 431 | ,@(0, 0, 0, 1, 3, 5, 0, 0, 0) |
| 432 | ,@(0, 0, 1, 0, 0, 0, 0, 5, 0) |
| 433 | ,@(1, 0, 0, 0, 6, 0, 0, 0, 3) |
| 434 | ,@(4, 0, 0, 8, 0, 3, 0, 0, 1) |
| 435 | ,@(7, 0, 0, 0, 2, 0, 0, 0, 6) |
| 436 | ,@(0, 0, 0, 0, 0, 0, 2, 1, 0) |
| 437 | ,@(0, 0, 0, 4, 1, 2, 0, 0, 5) |
| 438 | ,@(0, 0, 0, 0, 0, 0, 0, 7, 4) |
| 439 | ) |
| 440 | $solution = @( |
| 441 | ,@(2, 3, 5, 6, 7, 8, 1, 4, 9) |
| 442 | ,@(9, 4, 7, 1, 3, 5, 8, 6, 2) |
| 443 | ,@(6, 8, 1, 2, 4, 9, 3, 5, 7) |
| 444 | ,@(1, 2, 8, 7, 6, 4, 5, 9, 3) |
| 445 | ,@(4, 5, 6, 8, 9, 3, 7, 2, 1) |
| 446 | ,@(7, 9, 3, 5, 2, 1, 4, 8, 6) |
| 447 | ,@(3, 6, 4, 9, 5, 7, 2, 1, 8) |
| 448 | ,@(8, 7, 9, 4, 1, 2, 6, 3, 5) |
| 449 | ,@(5, 1, 2, 3, 8, 6, 9, 7, 4) |
| 450 | ) |
| 451 | $lines = [System.Collections.Generic.List[string]]::new() |
| 452 | |
| 453 | function Add-SudokuGrid([string] $label, [object[]] $grid) { |
| 454 | $lines.Add($label) |
| 455 | for ($i = 0; $i -lt $grid.Count; $i++) { |
| 456 | if ($i % 3 -eq 0 -and $i -ne 0) { |
| 457 | $lines.Add('- - - - - - - - - - - -') |
| 458 | } |
| 459 | $line = '' |
| 460 | for ($j = 0; $j -lt $grid[$i].Count; $j++) { |
| 461 | if ($j % 3 -eq 0 -and $j -ne 0) { |
| 462 | $line += ' | ' |
| 463 | } |
| 464 | $line += "$($grid[$i][$j]) " |
| 465 | } |
| 466 | $lines.Add($line) |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | Add-SudokuGrid 'Sudoku Puzzle:' $puzzle |
| 471 | $lines.Add('Solving...') |
| 472 | Add-SudokuGrid 'Solution:' $solution |
| 473 | return $lines.ToArray() |
| 474 | } |
| 475 | |
| 476 | $sudoku_lines = @(Get-SudokuExpectedLines) |
| 477 | $sudoku_expected = [System.Text.Encoding]::UTF8.GetBytes(($sudoku_lines -join "`n") + "`n") |
| 478 | if ($sudoku_expected.Length -ne 582) { |
| 479 | throw "sudoku stdout oracle shape changed: $($sudoku_expected.Length) bytes" |
| 480 | } |
| 481 | Invoke-NativeX64Example 'examples/sudoku.v' 'sudoku' $sudoku_expected |
| 482 | |
| 483 | $function_types_expected = [System.Text.Encoding]::UTF8.GetBytes("HELLO WORLD`nHELLO WORLD`nHELLO WORLD`n") |
| 484 | Invoke-NativeX64Example 'examples/function_types.v' 'function_types' $function_types_expected |
| 485 | |
| 486 | $submodule_expected = [System.Text.Encoding]::UTF8.GetBytes("5`n3`n") |
| 487 | Invoke-NativeX64Example 'examples/submodule/main.v' 'submodule' $submodule_expected 'tiny-used' |
| 488 | |
| 489 | function Get-TreeOfNodesExpectedLines { |
| 490 | @( |
| 491 | 'tree structure:' |
| 492 | ' Node{' |
| 493 | ' value: 10' |
| 494 | ' left: Tree(Node{' |
| 495 | ' value: 30' |
| 496 | ' left: Tree(Empty{})' |
| 497 | ' right: Tree(Empty{})' |
| 498 | ' })' |
| 499 | ' right: Tree(Node{' |
| 500 | ' value: 20' |
| 501 | ' left: Tree(Empty{})' |
| 502 | ' right: Tree(Empty{})' |
| 503 | ' })' |
| 504 | '}' |
| 505 | 'tree size: 3' |
| 506 | ) |
| 507 | } |
| 508 | |
| 509 | $tree_of_nodes_lines = @(Get-TreeOfNodesExpectedLines) |
| 510 | $tree_of_nodes_expected = [System.Text.Encoding]::UTF8.GetBytes(($tree_of_nodes_lines -join "`n") + "`n") |
| 511 | if ($tree_of_nodes_expected.Length -ne 259) { |
| 512 | throw "tree_of_nodes stdout oracle shape changed: $($tree_of_nodes_expected.Length) bytes" |
| 513 | } |
| 514 | Invoke-NativeX64Example 'examples/tree_of_nodes.v' 'tree_of_nodes' $tree_of_nodes_expected |
| 515 | |
| 516 | $js_hello_world_expected = [System.Text.Encoding]::UTF8.GetBytes("Hello from V.js (0)`nHello from V.js (1)`nHello from V.js (2)`n") |
| 517 | Invoke-NativeX64Example 'examples/js_hello_world.v' 'js_hello_world' $js_hello_world_expected 'tiny-used' |
| 518 | |
| 519 | function Get-VSingleQuotedLiteralBytes([string] $literal) { |
| 520 | $bytes = [System.Collections.Generic.List[byte]]::new() |
| 521 | for ($i = 0; $i -lt $literal.Length; $i++) { |
| 522 | $ch = $literal[$i] |
| 523 | if ($ch -eq '\') { |
| 524 | $i++ |
| 525 | if ($i -ge $literal.Length) { |
| 526 | throw 'unterminated escape in V single-quoted literal' |
| 527 | } |
| 528 | $escaped = $literal[$i] |
| 529 | switch ([int][char] $escaped) { |
| 530 | 110 { [void] $bytes.Add(10) } |
| 531 | 116 { [void] $bytes.Add(9) } |
| 532 | 114 { [void] $bytes.Add(13) } |
| 533 | 92 { [void] $bytes.Add(92) } |
| 534 | 39 { [void] $bytes.Add(39) } |
| 535 | 34 { [void] $bytes.Add(34) } |
| 536 | default { |
| 537 | foreach ($byte in [System.Text.Encoding]::UTF8.GetBytes([string] $escaped)) { |
| 538 | [void] $bytes.Add($byte) |
| 539 | } |
| 540 | } |
| 541 | } |
| 542 | } else { |
| 543 | foreach ($byte in [System.Text.Encoding]::UTF8.GetBytes([string] $ch)) { |
| 544 | [void] $bytes.Add($byte) |
| 545 | } |
| 546 | } |
| 547 | } |
| 548 | return $bytes.ToArray() |
| 549 | } |
| 550 | |
| 551 | function Get-VasciiExpectedBytes() { |
| 552 | $source = [System.IO.File]::ReadAllText((Join-Path (Get-Location) 'examples/vascii.v'), [System.Text.Encoding]::UTF8) |
| 553 | $start_marker = "println('" |
| 554 | $end_marker = "')" |
| 555 | $first = $source.IndexOf($start_marker, [System.StringComparison]::Ordinal) |
| 556 | if ($first -lt 0) { |
| 557 | throw 'missing vascii println literal start' |
| 558 | } |
| 559 | $second = $source.IndexOf($start_marker, $first + $start_marker.Length, [System.StringComparison]::Ordinal) |
| 560 | if ($second -ne -1) { |
| 561 | throw 'vascii stdout oracle expects one single-quoted println literal' |
| 562 | } |
| 563 | $start = $first + $start_marker.Length |
| 564 | $end = $source.LastIndexOf($end_marker, [System.StringComparison]::Ordinal) |
| 565 | if ($end -lt $start) { |
| 566 | throw 'missing vascii println literal end' |
| 567 | } |
| 568 | if ($source.Substring($end + $end_marker.Length).Trim() -ne '}') { |
| 569 | throw 'vascii stdout oracle expects the println literal to be the final main statement' |
| 570 | } |
| 571 | [byte[]] $bytes = Get-VSingleQuotedLiteralBytes ($source.Substring($start, $end - $start)) |
| 572 | return $bytes + [byte] 10 |
| 573 | } |
| 574 | |
| 575 | [byte[]] $vascii_expected = Get-VasciiExpectedBytes |
| 576 | Invoke-NativeX64Example 'examples/vascii.v' 'vascii' $vascii_expected 'tiny-used' |
| 577 | |
| 578 | [byte[]] $rune_expected = 0xF0, 0x9F, 0x98, 0x80, 0x0A, 0x40, 0x0A |
| 579 | Invoke-NativeX64Example 'examples/rune.v' 'rune' $rune_expected 'tiny-used' |
| 580 | |
| 581 | [byte[]] $bfs_expected = Get-VRunExpectedBytes 'examples/graphs/bfs.v' 'bfs' |
| 582 | Invoke-NativeX64Example 'examples/graphs/bfs.v' 'bfs' $bfs_expected |
| 583 | |
| 584 | [byte[]] $bfs3_expected = Get-VRunExpectedBytes 'examples/graphs/bfs3.v' 'bfs3' |
| 585 | Invoke-NativeX64Example 'examples/graphs/bfs3.v' 'bfs3' $bfs3_expected |
| 586 | |
| 587 | [byte[]] $primes_expected = Get-VRunExpectedBytes 'examples/primes.v' 'primes' @('20') |
| 588 | Invoke-NativeX64Example 'examples/primes.v' 'primes' $primes_expected '' @('20') |
| 589 | |
| 590 | [byte[]] $dfs_expected = Get-VRunExpectedBytes 'examples/graphs/dfs.v' 'dfs' |
| 591 | Invoke-NativeX64Example 'examples/graphs/dfs.v' 'dfs' $dfs_expected |
| 592 | |
| 593 | [byte[]] $dijkstra_expected = Get-VRunExpectedBytes 'examples/graphs/dijkstra.v' 'dijkstra' |
| 594 | Invoke-NativeX64Example 'examples/graphs/dijkstra.v' 'dijkstra' $dijkstra_expected |
| 595 | |
| 596 | [byte[]] $topological_sorting_greedy_expected = Get-VRunExpectedBytes 'examples/graphs/topological_sorting_greedy.v' 'topological_sorting_greedy' |
| 597 | Invoke-NativeX64Example 'examples/graphs/topological_sorting_greedy.v' 'topological_sorting_greedy' $topological_sorting_greedy_expected |
| 598 | |
| 599 | [byte[]] $topological_sorting_dfs_expected = Get-VRunExpectedBytes 'examples/graphs/topological_sorting_dfs.v' 'topological_sorting_dfs' |
| 600 | Invoke-NativeX64Example 'examples/graphs/topological_sorting_dfs.v' 'topological_sorting_dfs' $topological_sorting_dfs_expected |
| 601 | |
| 602 | [byte[]] $dfs2_expected = Get-VRunExpectedBytes 'examples/graphs/dfs2.v' 'dfs2' |
| 603 | Invoke-NativeX64Example 'examples/graphs/dfs2.v' 'dfs2' $dfs2_expected |
| 604 | |
| 605 | [byte[]] $minimal_spann_tree_prim_expected = Get-VRunExpectedBytes 'examples/graphs/minimal_spann_tree_prim.v' 'minimal_spann_tree_prim' |
| 606 | Invoke-NativeX64Example 'examples/graphs/minimal_spann_tree_prim.v' 'minimal_spann_tree_prim' $minimal_spann_tree_prim_expected |
| 607 | |
| 608 | [byte[]] $bellman_ford_expected = Get-VRunExpectedBytes 'examples/graphs/bellman-ford.v' 'bellman_ford' |
| 609 | Invoke-NativeX64Example 'examples/graphs/bellman-ford.v' 'bellman_ford' $bellman_ford_expected |
| 610 | |
| 611 | [byte[]] $binary_search_tree_expected = Get-VRunExpectedBytes 'examples/binary_search_tree.v' 'binary_search_tree' |
| 612 | Invoke-NativeX64Example 'examples/binary_search_tree.v' 'binary_search_tree' $binary_search_tree_expected |
| 613 | |
| 614 | # normal-required: IError/custom errors |
| 615 | [byte[]] $errors_expected = [System.Text.Encoding]::UTF8.GetBytes("wrong format`nempty input`n") |
| 616 | Invoke-NativeX64Example 'examples/errors.v' 'errors' $errors_expected |
| 617 | |
| 618 | [byte[]] $custom_error_expected = Get-VRunExpectedBytes 'examples/custom_error.v' 'custom_error' |
| 619 | Invoke-NativeX64Example 'examples/custom_error.v' 'custom_error' $custom_error_expected |
| 620 | |
| 621 | # normal-required: spectral norm/math.sqrt/formatted f64 interpolation |
| 622 | [byte[]] $spectral_expected = Get-VRunExpectedBytes 'examples/spectral.v' 'spectral' @('10') |
| 623 | Invoke-NativeX64Example 'examples/spectral.v' 'spectral' $spectral_expected '' @('10') |
| 624 | |
| 625 | [byte[]] $quick_sort_expected = [System.Text.Encoding]::UTF8.GetBytes("length of random array is 1000`nbefore quick sort whether array is sorted: false`nafter quick sort whether array is sorted: true`n") |
| 626 | Invoke-NativeX64Example 'examples/quick_sort.v' 'quick_sort' $quick_sort_expected |
| 627 | |
| 628 | $get_raw_line_input = Join-Path $tmp 'get_raw_line.in' |
| 629 | [System.IO.File]::WriteAllText($get_raw_line_input, "hello`nworld`n", [System.Text.UTF8Encoding]::new($false)) |
| 630 | [byte[]] $get_raw_line_expected = Get-VRunExpectedBytesWithStdin 'examples/get_raw_line.v' 'get_raw_line' $get_raw_line_input |
| 631 | Invoke-NativeX64ExampleWithStdin 'examples/get_raw_line.v' 'get_raw_line' $get_raw_line_expected $get_raw_line_input |
| 632 | |
| 633 | $mini_calculator_input = Join-Path $tmp 'mini_calculator.in' |
| 634 | [System.IO.File]::WriteAllText($mini_calculator_input, "3+4*2`nexit`n", [System.Text.UTF8Encoding]::new($false)) |
| 635 | [byte[]] $mini_calculator_expected = Get-VRunExpectedBytesWithStdin 'examples/mini_calculator.v' 'mini_calculator' $mini_calculator_input |
| 636 | Invoke-NativeX64ExampleWithStdin 'examples/mini_calculator.v' 'mini_calculator' $mini_calculator_expected $mini_calculator_input |
| 637 | |
| 638 | $mini_calculator_recursive_descent_input = Join-Path $tmp 'mini_calculator_recursive_descent.in' |
| 639 | [System.IO.File]::WriteAllText($mini_calculator_recursive_descent_input, "3+4*2`nexit`n", [System.Text.UTF8Encoding]::new($false)) |
| 640 | [byte[]] $mini_calculator_recursive_descent_expected = Get-VRunExpectedBytesWithStdin 'examples/mini_calculator_recursive_descent.v' 'mini_calculator_recursive_descent' $mini_calculator_recursive_descent_input |
| 641 | Invoke-NativeX64ExampleWithStdin 'examples/mini_calculator_recursive_descent.v' 'mini_calculator_recursive_descent' $mini_calculator_recursive_descent_expected $mini_calculator_recursive_descent_input |
| 642 | |
| 643 | $random_ips_stdout = Invoke-NativeX64ExampleCapture 'examples/random_ips.v' 'random_ips' |
| 644 | Assert-RandomIpsStdout $random_ips_stdout |
| 645 | |
| 646 | $rule110_stdout = Invoke-NativeX64ExampleCapture 'examples/rule110.v' 'rule110' @('31') |
| 647 | Assert-Rule110Stdout $rule110_stdout 31 |
| 648 | - name: Build V with WX |
| 649 | run: v -cflags /WX self |
| 650 | - name: All code is formatted |
| 651 | run: v -silent test-cleancode |
| 652 | - name: Test -cc msvc works |
| 653 | run: v -no-retry-compilation run examples/hello_world.v |
| 654 | - name: Install dependencies |
| 655 | run: | |
| 656 | v retry -- v setup-freetype |
| 657 | .\.github\workflows\windows-install-sqlite.bat |
| 658 | - name: v doctor |
| 659 | run: | |
| 660 | v doctor |
| 661 | - name: Verify `v test` works |
| 662 | run: | |
| 663 | echo $VFLAGS |
| 664 | v cmd/tools/test_if_v_test_system_works.v |
| 665 | ./cmd/tools/test_if_v_test_system_works |
| 666 | - name: Test pure V math module |
| 667 | run: v -silent -exclude @vlib/math/*.c.v test vlib/math/ |
| 668 | - name: Self tests |
| 669 | run: v -silent test-self vlib |
| 670 | - name: Test v->js |
| 671 | run: v -o hi.js examples/js_hello_world.v && node hi.js |
| 672 | - name: Test v binaries |
| 673 | run: v build-vbinaries |
| 674 | - name: Build examples |
| 675 | run: v build-examples |
| 676 | - name: v2 self compilation |
| 677 | run: v -o v2.exe cmd/v && .\v2.exe -o v3.exe cmd/v |
| 678 | |