Skip to content

Commit

Permalink
files_equal_test: rewrite as genrule + sh_test (#2035)
Browse files Browse the repository at this point in the history
Rewrite the files_equal_test() rules as a macro.
The macro creates:
- a sh_test, the actual test rule
- a genrule that writes the source for the sh_test

The old implementation was a Starlark test rule
with a Bash script executable. Such a test cannot
run on Windows with Bazel's Windows-native test
wrapper, because the test wrapper cannot create a
subprocess for .sh file, only for native binaries
like an .exe file.

The new implementation wraps the .sh file in a
sh_test, which builds an .exe file on Windows.
Bazel and the new native test wrapper can now run
files_equal_test.

[1] see bazelbuild/bazel#5508
    and --incompatible_windows_native_test_wrapper

Fixes #2034
  • Loading branch information
laszlocsomor authored and jayconrod committed Apr 19, 2019
1 parent 2169c12 commit 52836fb
Showing 1 changed file with 102 additions and 57 deletions.
159 changes: 102 additions & 57 deletions go/private/tools/files_equal_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,62 +15,107 @@

"""Tests that two files contain the same data."""

def _impl(ctx):
if ctx.file.golden == ctx.file.actual:
fail("GOLDEN and ACTUAL should be different files")
ctx.actions.write(
output = ctx.outputs.executable,
content = "\n".join([
"#!/bin/bash",
"function checksum() {",
" if command -v openssl >/dev/null; then",
" openssl sha1 $1 | cut -f 2 -d ' '",
" elif command -v sha256sum >/dev/null; then",
" sha256sum $1 | cut -f 1 -d ' '",
" elif command -v shasum >/dev/null; then",
" cat $1 | shasum -a 256 | cut -f 1 -d ' '",
" else",
" echo please install openssl >&2",
" exit 1",
" fi",
"}",
"SUM1=$(checksum %s)" % _runpath(ctx.file.golden),
"SUM2=$(checksum %s)" % _runpath(ctx.file.actual),
"if [[ ${SUM1} != ${SUM2} ]]; then",
" echo ERROR: %s >&2" % ctx.attr.error_message,
" echo %s ${SUM1} >&2" % _runpath(ctx.file.golden),
" echo %s ${SUM2} >&2" % _runpath(ctx.file.actual),
" exit 1",
"fi",
]),
is_executable = True,
)
return [DefaultInfo(runfiles = ctx.runfiles([
ctx.file.golden,
ctx.file.actual,
]))]
def files_equal_test(name, golden, actual, error_message = None, **kwargs):
# This genrule creates a Bash script: the source of the actual test.
# The script:
# 1. Initializes the Bash runfiles library (see
# @bazel_tools//tools/bash/runfiles/runfiles.bash).
# 2. Stores command line arguments into variables.
# 3. Computes runfile paths for the GOLDEN and ACTUAL files.
# 4. Calls "rlocation" from runfiles.bash to locates the runfiles.
# 5. Computes and compares checksums.
native.genrule(
name = name + "_src",
outs = [name + "-src.sh"],
executable = True,
visibility = ["//visibility:private"],
cmd = r"""cat >$@ <<'eof'
#!/bin/bash
# sh_test() source, generated by @io_bazel_rules_go//go/private/tools/files_equal_test.bzl
### 1. initialize the Bash runfiles library
# --- begin runfiles.bash initialization ---
# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
set -euo pipefail
if [[ ! -d "$${RUNFILES_DIR:-/dev/null}" && ! -f "$${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
if [[ -f "$$0.runfiles_manifest" ]]; then
export RUNFILES_MANIFEST_FILE="$$0.runfiles_manifest"
elif [[ -f "$$0.runfiles/MANIFEST" ]]; then
export RUNFILES_MANIFEST_FILE="$$0.runfiles/MANIFEST"
elif [[ -f "$$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
export RUNFILES_DIR="$$0.runfiles"
fi
fi
if [[ -f "$${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
source "$${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
elif [[ -f "$${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
source "$$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
"$$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
else
echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
exit 1
fi
# --- end runfiles.bash initialization ---
### 2. Store command line arguments into variables.
declare -r GOLDEN="$${1}"
declare -r ACTUAL="$${2}"
declare -r ERROR_MSG="$${3:-FILES DO NOT HAVE EQUAL CONTENTS}"
### 3. Compute runfile paths.
# Strip "external/" prefix OR prepend workspace name and strip "./" prefix.
[[ "$$GOLDEN" =~ external/* ]] && F1="$${GOLDEN#external/}" || F1="$$TEST_WORKSPACE/$${GOLDEN#./}"
[[ "$$ACTUAL" =~ external/* ]] && F2="$${ACTUAL#external/}" || F2="$$TEST_WORKSPACE/$${ACTUAL#./}"
def _runpath(f):
"""Figures out the proper runfiles path for a file, using voodoo"""
if f.path.startswith("bazel-out/"):
return f.short_path
else:
return f.path
### 4. Locate the runfiles.
files_equal_test = rule(
attrs = {
"golden": attr.label(
mandatory = True,
allow_single_file = True,
),
"actual": attr.label(
mandatory = True,
allow_single_file = True,
),
"error_message": attr.string(
default = "FILES DO NOT HAVE EQUAL CONTENTS",
),
},
test = True,
implementation = _impl,
)
F1="$$(rlocation "$$F1")"
F2="$$(rlocation "$$F2")"
if [[ "$$F1" == "$$F2" ]]; then
echo >&2 "GOLDEN and ACTUAL should be different files"
exit 1
fi
### 5. Compute and compare checksums.
function checksum() {
if command -v openssl >/dev/null; then
openssl sha1 $$1 | cut -f 2 -d ' '
elif command -v sha256sum >/dev/null; then
sha256sum $$1 | cut -f 1 -d ' '
elif command -v shasum >/dev/null; then
cat $$1 | shasum -a 256 | cut -f 1 -d ' '
else
echo please install openssl >&2
exit 1
fi
}
SUM1=$$(checksum "$$F1")
SUM2=$$(checksum "$$F2")
if [[ $${SUM1} != $${SUM2} ]]; then
echo "ERROR: $$ERROR_MSG" >&2
echo "$$GOLDEN $${SUM1}" >&2
echo "$$ACTUAL $${SUM2}" >&2
exit 1
fi
eof""",
)

native.sh_test(
name = name,
srcs = [name + "-src.sh"],
data = [
"@bazel_tools//tools/bash/runfiles",
actual,
golden,
],
args = [
"$(location %s)" % golden,
"$(location %s)" % actual,
error_message,
],
)

0 comments on commit 52836fb

Please sign in to comment.