--errors-as-warnings for mix deps.compile ? (a missing function in a dependency caused a runtime error)

When running mix deps.compile we see this warning:

warning: :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
  lib/ex_aws/auth/utils.ex:13: ExAws.Auth.Utils.hmac_sha256/2

Generated ex_aws app

This code path was not covered by tests, and so when we deployed it, that warning became a runtime error:

[error] GenServer #PID<0.1944.0> terminating
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private

I am wondering if there’s a way to be more strict with warnings in dependencies? I can see this being a PITA if the warnings are things like deprecations. But this warning is one that will definitely become a runtime error.

So what I really want to know is: given that Elixir recognized there was an undefined function at compile time, is there an automated way to fail the compilation? --warnings-as-errors doesn’t seem to apply to dependencies.

warning-as-errors was disabled by design for dependencies. Force disable warnings_as_errors for dependencies · Issue #4913 · elixir-lang/elixir · GitHub

The question is how to determine what is crucial and what is not. Maybe pipe it into a shell script that will grep for “undefined or private” and other critical terms.

1 Like

Thanks, but I am not trying to solve that specific error. My point is that Elixir detected a compilation error in a dependency, but it didn’t stop it from compiling successfully.

If any dependency has a similar issue, then it’s possible to inadvertently get runtime errors. Ideally we would have tests that exercise every path and so that doesn’t happen, but it’s not always the case that we have that, and certainly not for libraries that call external services.

Sorry, I realized right after I hit REPLY and there is a bug in the forum that does not allow to remove posts (really annoying. sorry about that)

One challenge is that due to the BEAM’s ability to load code at any point, just that it couldn’t find the function at compile time does not mean that the function won’t exist at runtime. In this specific case it seems fairly obvious that :crypto isn’t going to gain new functions at runtime, I definitely understand why it feels like it should have been caught. I’m just noting that it isn’t a trivial problem to determine in the general case.

1 Like

YES, it can be done!

This script will run the command, and as soon as specific string is caught (in this case “undefined or private”), it kills the execution.

#!/bin/bash

exit_on_undefined() {
  sed '/undefined or private/{q 2}' || kill "$BASHPID" 
}

run() {
  mix deps.compile --force
  mix compile --force --warnings-as-errors
  echo "Succesfully compiled!"
}

run 2>&1 | exit_on_undefined | cat || cat

echo "Exit status: ${PIPESTATUS[0]}"

The output: (In this example. WarnMe is the dependency)

$ ./compile.sh 
==> warn_me
Compiling 1 file (.ex)
warning: variable "x" is unused (if the variable is not meant to be used, prefix it with an underscore)
  lib/warn_me.ex:15: WarnMe.hello/1

warning: :crypto.hmac/3 defined in application :crypto is used by the current application but the current application does not depend on :crypto. To fix this, you must do one of:

  1. If :crypto is part of Erlang/Elixir, you must include it under :extra_applications inside "def application" in your mix.exs

  2. If :crypto is a dependency, make sure it is listed under "def deps" in your mix.exs

  3. In case you don't want to add a requirement to :crypto, you may optionally skip this warning by adding [xref: [exclude: [:crypto]]] to your "def project" in mix.exs

  lib/warn_me.ex:16: WarnMe.hello/1

warning: :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
Exit status: 141

And when the dependency has non-crucial warnings:

$ ./compile.sh 
==> warn_me
Compiling 1 file (.ex)
warning: variable "x" is unused (if the variable is not meant to be used, prefix it with an underscore)
  lib/warn_me.ex:15: WarnMe.hello/1

Generated warn_me app
Compiling 1 file (.ex)
Generated compile_warnings app
Succesfully compiled!
Exit status: 0

Source: bash - How can a command within a pipeline abort the pipeline? - Unix & Linux Stack Exchange

1 Like

Just came here to mention how useful this script is.

There was a warning that was happening when generating docs in Elixir core, that I couldn’t figure out what was causing it.

I ran a git bisect with an adapted script

and voila.