diff --git a/e2e/MODULE.bazel.lock b/e2e/MODULE.bazel.lock index 009e98d352a03c837db833110aa1af9592d11a90..a3b486a5c8c9223336c46f66d830161f26ef5ffb 100644 --- a/e2e/MODULE.bazel.lock +++ b/e2e/MODULE.bazel.lock @@ -835,7 +835,7 @@ "moduleExtensions": { "//:MODULE.bazel%_repo_rules": { "general": { - "bzlTransitiveDigest": "9JxXrmfNb78Xi25kLIofG+wYfaaT/4WtRWFscwj7N9s=", + "bzlTransitiveDigest": "D+LVccvjhC6Fe6jgT0CX0WPaNhyl1RcBJygqKAExA8o=", "accumulatedFileDigests": {}, "envVariables": {}, "generatedRepoSpecs": { diff --git a/e2e/toolchain/echo/echo.bat b/e2e/toolchain/echo/echo.bat index 187afc3acee3409e80be87828307d2de6b6815be..2da4198f837fd2fcaf31a9b218dee32feac47f41 100644 --- a/e2e/toolchain/echo/echo.bat +++ b/e2e/toolchain/echo/echo.bat @@ -1,18 +1 @@ -:: Enable Batch extensions -@verify other 2>nul -@setlocal EnableExtensions -@if errorlevel 1 ( - echo "Failed to enable extensions" - exit /b 120 -) - -:: Enable delayed expansion of variables with `!VAR!` -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - echo "Failed to enable extensions" - exit /b 120 -) - -:: Execute! @echo.%* \ No newline at end of file diff --git a/toolchain/test/BUILD.bazel b/toolchain/test/BUILD.bazel index 5a9399aa9cd8ffe10458fb17c091156c837e5f64..19450e25d6da91ed24d4f129b5283700d082707f 100644 --- a/toolchain/test/BUILD.bazel +++ b/toolchain/test/BUILD.bazel @@ -1,4 +1,7 @@ -exports_files(["posix.tmpl.sh"]) +exports_files([ + "posix.tmpl.sh", + "nt.tmpl.bat", +]) exports_files( [ @@ -8,3 +11,12 @@ exports_files( ], visibility = ["//visibility:public"], ) + +alias( + name = "template", + actual = select({ + "//toolchain/constraint/os:windows": ":nt.tmpl.bat", + "//conditions:default": ":posix.tmpl.sh", + }), + visibility = ["//visibility:public"], +) diff --git a/toolchain/test/nt.tmpl.bat b/toolchain/test/nt.tmpl.bat new file mode 100644 index 0000000000000000000000000000000000000000..fca3626ef231cac15c2f42e7cdb1dace9ac90531 --- /dev/null +++ b/toolchain/test/nt.tmpl.bat @@ -0,0 +1,215 @@ +@echo off + +:: Enable Batch extensions +verify other 2>nul +setlocal EnableExtensions +if errorlevel 1 ( + echo "Failed to enable extensions" + exit /b 120 +) + +:: Check for delayed expansion of variables with `!VAR!` +verify other 2>nul +setlocal EnableDelayedExpansion +if errorlevel 1 ( + echo "Failed to enable extensions" + exit /b 120 +) +setlocal DisableDelayedExpansion + +:: Bazel substitutions +set "EXECUTABLE={{executable}}" +set "STDOUT={{stdout}}" +set "STDERR={{stderr}}" + +:: Runfiles +if [%RUNFILES_MANIFEST_ONLY%] neq [1] ( + echo>&2.Only runfile manifests are supported + exit /b 2 +) +setlocal EnableDelayedExpansion +for %%v in (EXECUTABLE,STDOUT,STDERR) do ( + for /f "tokens=1,2* usebackq" %%a in ("%RUNFILES_MANIFEST_FILE%") do ( + if "_main/!%%v!" == "%%a" ( + set "%%v=%%~fb" + ) + if "!%%v!" == "../%%a" ( + set "%%v=%%~fb" + ) + ) +) +setlocal DisableDelayedExpansion + +:: Execute! +for /f %%a in ("%EXECUTABLE%") do set EXTENSION=%%~xa +if "%EXTENSION%" == ".bat" set LAUNCHER=call +%LAUNCHER% "%EXECUTABLE%" %* >stdout.txt 2>stderr.txt +set "CODE=%ERRORLEVEL%" +if %CODE% neq 0 ( + >&2 echo.Failed to run: %EXECUTABLE% %* + >&2 echo.stdout: + >&2 type stdout.txt + >&2 echo.stderr: + >&2 type stderr.txt + exit /b %CODE% +) + +:: Compare +set "JUNIT=junit.xml" +if not [%XML_OUTPUT_FILE%] == [] set "JUNIT=%XML_OUTPUT_FILE%" +call :junit CODE stdout.txt "%STDOUT%" stderr.txt "%STDERR%" >"%JUNIT%" +exit /b %CODE% + +:junit - creates JUnit XML output from comparing files +:: %1 - return code variable +:: %* - pairs of files to compare +setlocal + +:: Output TAP/JUnit headers +set COUNT=0 +for %%a in (%*) do set /a "COUNT+=1" +set /a "TESTS=COUNT/2" +echo.^ +>&2 echo.1..%TESTS% +set SUM=0 + +:: Loop through each file pairing +set INDEX=0 +:loop +set /a "INDEX+=1" +call :compare CODE %INDEX% "%~2" "%~3" +set /a "SUM+=CODE" +shift /2 +shift /2 +if exist "%~3" goto :loop + +:: Output the JUnit footer +echo.^ + +endlocal & set "%~1=%SUM%" +goto :eof + +:compare - compare two files +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +if "%EXPECTED:~-19%" == "\toolchain\test\any" ( + call :any CODE %INDEX% "%FILEPATH%" "%EXPECTED%" +) else if "%EXPECTED:~-21%" == "\toolchain\test\empty" ( + call :empty CODE %INDEX% "%FILEPATH%" "%EXPECTED%" +) else if "%EXPECTED:~-25%" == "\toolchain\test\non-empty" ( + call :non-empty CODE %INDEX% "%FILEPATH%" "%EXPECTED%" +) else ( + call :diff CODE %INDEX% "%FILEPATH%" "%EXPECTED%" +) +endlocal & set %~1=%CODE% +goto :eof + +:any - a file can have any content +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +>&2 echo.ok %INDEX% - %FILEPATH% contained any content +echo.^ +endlocal & set %~1=0 +goto :eof + +:empty - a file must have zero content +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +for /f %%a in ("%FILEPATH%") do set "SIZE=%%~Za" +if %SIZE% equ 0 ( + >&2 echo.ok %INDEX% - %FILEPATH% was an empty file + echo.^ + set "CODE=0" +) else ( + >&2 echo.not ok %INDEX% - %FILEPATH% contained content when an empty file was expected + echo. ^ + echo. ^%FILEPATH% contained unexpected content: + type "%FILEPATH%" + echo. ^ + echo. ^ + set "CODE=1" +) +endlocal & set %~1=%CODE% +goto :eof + +:non-empty - a file must have some content +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +for /f %%a in ("%FILEPATH%") do set "SIZE=%%~Za" +if %SIZE% neq 0 ( + >&2 echo.ok %INDEX% - %FILEPATH% was a non-empty file + echo. ^ + set "CODE=0" +) else ( + >&2 echo.not ok %INDEX% - %FILEPATH% was an empty file when content was expected + echo. ^ + echo. ^%FILEPATH% was an empty file when content was expected^ + echo. ^ + set "CODE=1" +) +endlocal & set %~1=%CODE% +goto :eof + +:diff - compare the two passed files for content equivalence +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +set "DIFF=%SYSTEMROOT%\\system32\\fc.exe" +if not exist "%DIFF%" ( + >&2 echo.not ok %INDEX% - missing file compare executable: %DIFF% + echo. ^ + echo. ^Missing file compare executable: %DIFF%^ + echo. ^ + set CODE=1 + goto :fail +) +set "CODE=%ERRORLEVEL%" +if %CODE% equ 0 ( + >&2 echo.ok %INDEX% - %FILEPATH% was identical + echo.^ +) else if %CODE% equ 1 ( + >&2 echo.not ok %INDEX% - %FILEPATH% had different content to %EXPECTED% + echo. ^ + echo. ^%FILEPATH% contained different content: + "%DIFF%" "%FILEPATH%" "%EXPECTED%" + echo. ^ + echo. ^ +) else ( + >&2 echo.not ok %INDEX% - unknown exit code from %DIFF%: %CODE% + echo. ^ + echo. ^Unknown exit code from %DIFF%: %CODE%^ + echo. ^ + set CODE=1 +) +:fail +endlocal & set %~1=%CODE% +goto :eof diff --git a/toolchain/test/posix.tmpl.sh b/toolchain/test/posix.tmpl.sh index d17d4c49a0c6fc380adf22d292fb19fd8ffccdd9..66da711b1c82fdb0160838436950a1f97da7f091 100644 --- a/toolchain/test/posix.tmpl.sh +++ b/toolchain/test/posix.tmpl.sh @@ -152,7 +152,7 @@ junit() ( TESTS=$((COUNT / 2)) readonly COUNT TESTS printf '\n' "${TESTS}" - printf >&2 '1..%i\n' $((TESTS)) + printf >&2 '1..%i\n' "${TESTS}" INDEX=1 while ! test -z ${2+x}; do FILEPATH="${1}" diff --git a/toolchain/test/rule.bzl b/toolchain/test/rule.bzl index 3c33f6da3838ec72c85251da05d71e275ffbf1f0..45ea29f38285657f04c9b20c380ad805c2885183 100644 --- a/toolchain/test/rule.bzl +++ b/toolchain/test/rule.bzl @@ -32,7 +32,7 @@ Can be overridden to a custom script that receives the following replacements: - `{{stdout}}`: the expected standard output - `{{stderr}}`: the expected standard error """, - default = ":posix.tmpl.sh", + default = ":template", allow_single_file = True, ), } @@ -42,15 +42,17 @@ def implementation(ctx): fail("Only one toolchain can be provided") toolchain = ctx.attr.toolchains[0][platform_common.ToolchainInfo] - executable = ctx.actions.declare_file("{}.executable".format(ctx.label.name)) + executable = ctx.actions.declare_file("{}.{}".format(ctx.label.name, ctx.file.template.extension)) + + substitutions = ctx.actions.template_dict() + substitutions.add("{{executable}}", str(toolchain.executable.short_path)) + substitutions.add("{{stdout}}", str(ctx.file.stdout.short_path)) + substitutions.add("{{stderr}}", str(ctx.file.stderr.short_path)) + ctx.actions.expand_template( template = ctx.file.template, output = executable, - substitutions = { - "{{executable}}": str(toolchain.executable.short_path), - "{{stdout}}": str(ctx.file.stdout.short_path), - "{{stderr}}": str(ctx.file.stderr.short_path), - }, + computed_substitutions = substitutions, is_executable = True, )