diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fa5e2e5..2fdd92e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -77,6 +77,20 @@ jobs: ! bazelisk test //:failing_test ! bazelisk test //:broken_test ! bazelisk test //:empty_test + - name: WARNINGS AS ERRORS FAIL + working-directory: test + run: | + # "! cmd" does not stop a "bash -e" step, so test the status + # explicitly. + for app in unused undef io_warn; do + if bazelisk build //warnings:$app 2> warnings_$app.log; then + echo "//warnings:$app built despite --warnings-as-errors" + exit 1 + fi + cat warnings_$app.log + grep -F "Compilation failed due to warnings while using the --warnings-as-errors option" \ + warnings_$app.log + done - name: UPLOAD TEST LOGS if: always() uses: actions/upload-artifact@v4 diff --git a/private/elixir_bytecode.bzl b/private/elixir_bytecode.bzl index 2359411..3e7acc6 100644 --- a/private/elixir_bytecode.bzl +++ b/private/elixir_bytecode.bzl @@ -58,11 +58,26 @@ fi {env} {setup} -set -x +diagnostics=$(mktemp) +status=0 ${{ABS_ELIXIR_HOME}}/bin/elixirc \\ -o {out_dir} \\ {elixirc_opts} \\ - {srcs} + {srcs} 2>"$diagnostics" || status=$? +cat "$diagnostics" >&2 +# Released Elixir ignores --warnings-as-errors in elixirc and exits 0 on +# warnings. Fail here instead. Once elixirc fails by itself, status is +# not 0 and this check does nothing. The launcher's warning about a +# latin1 file name encoding is not a compiler warning, so it is skipped. +if [ "$status" = 0 ] && [ -n "{warnings_as_errors}" ] && awk ' + /^[[:space:]]*warning:/ && !/native name encoding of latin1/ {{ found = 1 }} + END {{ exit !found }} +' "$diagnostics"; then + echo "Compilation failed due to warnings while using the --warnings-as-errors option" >&2 + status=1 +fi +rm -f "$diagnostics" +exit "$status" """.format( maybe_install_erlang = maybe_install_erlang(ctx), erlang_home = erlang_home, @@ -73,6 +88,7 @@ ${{ABS_ELIXIR_HOME}}/bin/elixirc \\ out_dir = ebin.path, elixirc_opts = " ".join([shell.quote(opt) for opt in ctx.attr.elixirc_opts]), srcs = " ".join([f.path for f in ctx.files.srcs]), + warnings_as_errors = "1" if "--warnings-as-errors" in ctx.attr.elixirc_opts else "", ) inputs = depset( diff --git a/test/warnings/BUILD.bazel b/test/warnings/BUILD.bazel new file mode 100644 index 0000000..ab9779a --- /dev/null +++ b/test/warnings/BUILD.bazel @@ -0,0 +1,29 @@ +load("@rules_elixir//private:elixir_bytecode.bzl", "elixir_bytecode") + +# Each source has one warning. With --warnings-as-errors the build must +# fail; .github/workflows/test.yaml checks that, so these are manual. +# unused: an unused variable, a compile-time warning +# undef: a call to an undefined remote function, a warning the +# compiler emits after the module is compiled +# io_warn: IO.warn at compile time, printed without indentation +[ + elixir_bytecode( + name = name, + srcs = [name + ".ex"], + dest = name, + elixirc_opts = ["--warnings-as-errors"], + tags = ["manual"], + ) + for name in [ + "unused", + "undef", + "io_warn", + ] +] + +# The same warning as :unused, without --warnings-as-errors. It builds. +elixir_bytecode( + name = "allowed", + srcs = ["unused.ex"], + dest = "allowed", +) diff --git a/test/warnings/io_warn.ex b/test/warnings/io_warn.ex new file mode 100644 index 0000000..57e4b19 --- /dev/null +++ b/test/warnings/io_warn.ex @@ -0,0 +1,7 @@ +defmodule Warnings.IoWarn do + # A warning printed by code that runs at compile time. Unlike the + # compiler's own warnings, it is not indented. + IO.warn("a warning from IO.warn at compile time") + + def f, do: :ok +end diff --git a/test/warnings/undef.ex b/test/warnings/undef.ex new file mode 100644 index 0000000..e1d16f6 --- /dev/null +++ b/test/warnings/undef.ex @@ -0,0 +1,3 @@ +defmodule Warnings.Undef do + def f, do: NoSuchModule.nope() +end diff --git a/test/warnings/unused.ex b/test/warnings/unused.ex new file mode 100644 index 0000000..0d4454d --- /dev/null +++ b/test/warnings/unused.ex @@ -0,0 +1,6 @@ +defmodule Warnings.Unused do + def f(x) do + y = 1 + x + end +end