diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel
index 1b39c7b13..1399c8c44 100644
--- a/docs/BUILD.bazel
+++ b/docs/BUILD.bazel
@@ -32,6 +32,11 @@ stardoc_with_diff_test(
bzl_library_target = "@aspect_bazel_lib//lib:diff_test",
)
+stardoc_with_diff_test(
+ name = "executable_test",
+ bzl_library_target = "@aspect_bazel_lib//lib:executable_test",
+)
+
stardoc_with_diff_test(
name = "expand_make_vars",
bzl_library_target = "@aspect_bazel_lib//lib:expand_make_vars",
diff --git a/docs/executable_test.md b/docs/executable_test.md
new file mode 100644
index 000000000..7e535d8a2
--- /dev/null
+++ b/docs/executable_test.md
@@ -0,0 +1,33 @@
+
+
+A test rule that checks the executable permission on a file or directory.
+
+
+
+## executable_test
+
+
+load("@aspect_bazel_lib//lib:executable_test.bzl", "executable_test")
+
+executable_test(name, file, executable, size, **kwargs)
+
+
+A test that checks the executable permission on a file or directory.
+
+The test succeeds if the executable permission matches executable
.
+
+On Windows, the test always succeeds.
+
+
+**PARAMETERS**
+
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| name | The name of the test rule. | none |
+| file | Label of the file to check. | none |
+| executable | Boolean; whether the file should be executable. | none |
+| size | standard attribute for tests | `"small"` |
+| kwargs | The common attributes for tests. | none |
+
+
diff --git a/docs/write_source_files.md b/docs/write_source_files.md
index 8c8adaa59..f06ec2fd9 100644
--- a/docs/write_source_files.md
+++ b/docs/write_source_files.md
@@ -23,7 +23,7 @@ To update the source file, run:
bazel run //:write_foobar
```
-The generated `diff_test` will fail if the file is out of date and print out instructions on
+The generated tests will fail if the file is out of date and print out instructions on
how to update it.
If the file does not exist, Bazel will fail at analysis time and print out instructions on
@@ -132,7 +132,8 @@ write_source_file(name,
@@ -175,7 +176,7 @@ write_source_files(name, executable.
+
+ On Windows, the test always succeeds.
+
+ Args:
+ name: The name of the test rule.
+ file: Label of the file to check.
+ executable: Boolean; whether the file should be executable.
+ size: standard attribute for tests
+ **kwargs: The common attributes for tests.
+ """
+ _executable_test(
+ name = name,
+ file = file,
+ executable = executable,
+ size = size,
+ **kwargs
+ )
diff --git a/lib/private/executable_test_tmpl.bat b/lib/private/executable_test_tmpl.bat
new file mode 100644
index 000000000..b83578b4f
--- /dev/null
+++ b/lib/private/executable_test_tmpl.bat
@@ -0,0 +1,5 @@
+@rem @generated by @aspect_bazel_lib//lib/private:executable_test.bzl
+@echo off
+:: No way to check the executable permission on Windows, so the test always
+:: succeeds.
+exit /b 0
diff --git a/lib/private/executable_test_tmpl.sh b/lib/private/executable_test_tmpl.sh
new file mode 100644
index 000000000..6116cb0b2
--- /dev/null
+++ b/lib/private/executable_test_tmpl.sh
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+set -o errexit -o nounset -o pipefail
+escape() {
+ echo "$1" |
+ sed 's/&/\&/g; s/\</g; s/>/\>/g; s/"/\"/g; s/'"'"'/\'/g' |
+ awk 1 ORS='
' # preserve newlines
+}
+fail() {
+ cat <"${XML_OUTPUT_FILE:-/dev/null}"
+
+
+
+
+
+
+
+
+EOF
+ echo >&2 "FAIL: $1"
+ exit 1
+}
+resolve_exec_root() {
+ local RUNFILES_PARENT
+ RUNFILES_PARENT=$(dirname "$RUNFILES_DIR")
+ local BIN_DIR
+ BIN_DIR="${RUNFILES_PARENT%$BUILD_FILE_DIR}"
+ local EXEC_ROOT
+ EXEC_ROOT=$(dirname $(dirname $(dirname "${BIN_DIR}")))
+
+ echo -n "$EXEC_ROOT"
+}
+find_file() {
+ local F_RAW="$1"
+ local F="$2"
+ local RF=
+
+ if [[ -f "$TEST_SRCDIR/$F1" || -d "$TEST_SRCDIR/$F" ]]; then
+ RF="$TEST_SRCDIR/$F"
+ elif [[ -d "${RUNFILES_DIR:-/dev/null}" && "${RUNFILES_MANIFEST_ONLY:-}" != 1 ]]; then
+ EXEC_ROOT=$(resolve_exec_root)
+ if [[ -e "$EXEC_ROOT/$F_RAW" ]]; then
+ RF="$EXEC_ROOT/$F_RAW"
+ else
+ RF="$RUNFILES_DIR/$F1"
+ fi
+ elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ RF="$(grep -F -m1 "$F " "$RUNFILES_MANIFEST_FILE" | sed 's/^[^ ]* //')"
+ else
+ echo >&2 "ERROR: could not find \"${F_RAW}\""
+ exit 1
+ fi
+
+ echo -n "$RF"
+}
+BUILD_FILE_DIR="$(dirname "{build_file_path}")"
+F1="{file}"
+[[ "$F1" =~ ^external/ ]] && F1="${F1#external/}" || F1="$TEST_WORKSPACE/$F1"
+RF1="$(find_file {file} "$F1")"
+DF1=
+[[ ! -d "$RF1" ]] || DF1=1
+if [[ "$DF1" ]]; then
+ if [[ -n '{executable}' ]]; then
+ if [[ -n "$(find -L "$RF1" ! -executable ! -type d)" ]]; then
+ fail "some files in directory \"{file}\" are not executable, but should be. {fail_msg}"
+ fi
+ else
+ if [[ -n "$(find -L "$RF1" -executable ! -type d)" ]]; then
+ fail "some files in directory \"{file}\" are executable, but shouldn't be. {fail_msg}"
+ fi
+ fi
+else
+ if [[ -n '{executable}' ]]; then
+ if [[ ! -x "$RF1" ]]; then
+ fail "file \"{file}\" is not executable, but should be. {fail_msg}"
+ fi
+ else
+ if [[ -x "$RF1" ]]; then
+ fail "file \"{file}\" is executable, but shouldn't be. {fail_msg}"
+ fi
+ fi
+fi
diff --git a/lib/private/write_source_file.bzl b/lib/private/write_source_file.bzl
index 072cf6b60..6633ef23e 100644
--- a/lib/private/write_source_file.bzl
+++ b/lib/private/write_source_file.bzl
@@ -2,6 +2,7 @@
load(":diff_test.bzl", _diff_test = "diff_test")
load(":directory_path.bzl", "DirectoryPathInfo")
+load(":executable_test.bzl", _executable_test = "executable_test")
load(":fail_with_message_test.bzl", "fail_with_message_test")
load(":utils.bzl", "utils")
@@ -28,7 +29,8 @@ def write_source_file(
**kwargs):
"""Write a file or directory to the source tree.
- By default, a `diff_test` target ("{name}_test") is generated that ensure the source tree file or directory to be written to
+ By default, `diff_test` and `executable_test` targets ("{name}_diff_test" and "{name}_executable_test")
+ are generated that ensure the source tree file or directory to be written to
is up to date and the rule also checks that the source tree file or directory to be written to exists.
To disable the exists check and up-to-date test set `diff_test` to `False`.
@@ -80,7 +82,7 @@ def write_source_file(
**kwargs: Other common named parameters such as `tags` or `visibility`
Returns:
- Name of the generated test target if requested, otherwise None.
+ Names of the generated test targets if requested, otherwise empty list.
"""
if out_file:
if not in_file:
@@ -112,10 +114,11 @@ def write_source_file(
)
if not in_file or not out_file or not diff_test:
- return None
+ return []
out_file_missing = check_that_out_file_exists and _is_file_missing(out_file)
- test_target_name = "%s_test" % name
+ diff_test_target_name = "%s_diff_test" % name
+ executable_test_target_name = "%s_executable_test" % name
update_target_string = "//%s:%s" % (native.package_name(), name)
suggested_update_target_string = str(utils.to_label(suggested_update_target)) if suggested_update_target else None
@@ -148,23 +151,25 @@ To create an update *only* this file, run:
# Note that we cannot simply call fail() here since it will fail during the analysis
# phase and prevent the user from calling bazel run //update/the:file.
fail_with_message_test(
- name = test_target_name,
+ name = diff_test_target_name,
message = message,
visibility = kwargs.get("visibility"),
tags = kwargs.get("tags"),
size = "small",
)
- else:
- if suggested_update_target == None:
- default_message = """
+
+ return [diff_test_target_name]
+
+ if suggested_update_target == None:
+ default_message = """
%s is out of date. To update this file, run:
bazel run %s
""" % (out_file, update_target_string)
- else:
- default_message = """
+ else:
+ default_message = """
%s is out of date. To update this and other generated files, run:
@@ -176,19 +181,26 @@ To update *only* this file, run:
""" % (out_file, suggested_update_target_string, update_target_string)
- message = _do_diff_test_message_replacements(diff_test_failure_message, default_message, update_target_string, suggested_update_target_string)
+ message = _do_diff_test_message_replacements(diff_test_failure_message, default_message, update_target_string, suggested_update_target_string)
- # Stamp out a diff test the check that the source file is up to date
- _diff_test(
- name = test_target_name,
- file1 = in_file,
- file2 = out_file,
- failure_message = message,
- diff_args = diff_args,
- **kwargs
- )
+ # Stamp out tests to check that the source file is up to date
+ _diff_test(
+ name = diff_test_target_name,
+ file1 = in_file,
+ file2 = out_file,
+ failure_message = message,
+ diff_args = diff_args,
+ **kwargs
+ )
+ _executable_test(
+ name = executable_test_target_name,
+ file = out_file,
+ executable = executable,
+ failure_message = message,
+ **kwargs
+ )
- return test_target_name
+ return [diff_test_target_name, executable_test_target_name]
_write_source_file_attrs = {
"in_file": attr.label(allow_files = True, mandatory = False),
diff --git a/lib/tests/write_source_files/BUILD.bazel b/lib/tests/write_source_files/BUILD.bazel
index 949d34d48..b6644d978 100644
--- a/lib/tests/write_source_files/BUILD.bazel
+++ b/lib/tests/write_source_files/BUILD.bazel
@@ -140,6 +140,13 @@ write_source_file(
out_file = "es_dir",
)
+write_source_file(
+ name = "es_dir_executable_test",
+ executable = True,
+ in_file = ":es_dir-desired",
+ out_file = "es_dir_executable",
+)
+
write_source_file_test(
name = "f_test",
in_file = ":f-desired",
diff --git a/lib/tests/write_source_files/e2_dir/e-contained.js b/lib/tests/write_source_files/e2_dir/e-contained.js
old mode 100755
new mode 100644
diff --git a/lib/tests/write_source_files/es2_dir/subdir/e-contained.js b/lib/tests/write_source_files/es2_dir/subdir/e-contained.js
old mode 100755
new mode 100644
diff --git a/lib/tests/write_source_files/es_dir/subdir/e-contained.js b/lib/tests/write_source_files/es_dir/subdir/e-contained.js
old mode 100755
new mode 100644
diff --git a/lib/tests/write_source_files/es_dir_executable/subdir/e-contained.js b/lib/tests/write_source_files/es_dir_executable/subdir/e-contained.js
new file mode 100755
index 000000000..ede7a9241
--- /dev/null
+++ b/lib/tests/write_source_files/es_dir_executable/subdir/e-contained.js
@@ -0,0 +1 @@
+console.log("e*");
diff --git a/lib/write_source_files.bzl b/lib/write_source_files.bzl
index aa01c484c..1b783f06f 100644
--- a/lib/write_source_files.bzl
+++ b/lib/write_source_files.bzl
@@ -21,7 +21,7 @@ To update the source file, run:
bazel run //:write_foobar
```
-The generated `diff_test` will fail if the file is out of date and print out instructions on
+The generated tests will fail if the file is out of date and print out instructions on
how to update it.
If the file does not exist, Bazel will fail at analysis time and print out instructions on
@@ -120,7 +120,7 @@ def write_source_files(
**kwargs):
"""Write one or more files and/or directories to the source tree.
- By default, `diff_test` targets are generated that ensure the source tree files and/or directories to be written to
+ By default, `diff_test` and `executable_test` targets are generated that ensure the source tree files and/or directories to be written to
are up to date and the rule also checks that all source tree files and/or directories to be written to exist.
To disable the exists check and up-to-date tests set `diff_test` to `False`.
@@ -188,7 +188,7 @@ def write_source_files(
this_suggested_update_target = name
# Runnable target that writes to the out file to the source tree
- test_target = _write_source_file(
+ new_test_targets = _write_source_file(
name = update_target_name,
in_file = in_file,
out_file = out_file,
@@ -203,8 +203,8 @@ def write_source_files(
**kwargs
)
- if test_target:
- test_targets.append(test_target)
+ if new_test_targets:
+ test_targets.extend(new_test_targets)
if len(test_targets) > 0:
native.test_suite(