-Werror is Not Your Friend

I have never quite understood the obsession with the -Werror compiler flag. I regularly come across projects with the flag enabled, and it's not uncommon for me to fend off rabid developers who want the flag enabled in projects I work on.

In case you have been living under a rock, -Werror is a compiler flag that causes all warnings to be treated as build errors. On the surface, the stated motivation behind enabling -Werror are benevolent. Developers who enable -Werror are making a statement: we care about our code base, and we won't accept warnings here. I also maintain a 0-warning policy for my projects, and I hate when developers ignore warnings. I understand the motivation for enabling the -Werror flag.

However, from the project maintenance perspective, -Werror is not your friend. I am always frustrated when I find a project with -Werror, because inevitably my first clean build of the project fails due to a spectacular mess of warnings. If I made no changes to the source code, why the hell is not not compiling?

-Werror creates a project dependency on a specific compiler version. Even worse, this toolchain dependency is often not recognized by the development team and is therefore not noted anywhere. I need to scour the web to find the secret dependency link, or I need to start hacking up the project to get the build to finish. Is that really the experience you want your consumers to have when they use your project?

-Werror lays the groundwork for maintenance headaches. When a new compiler version is released, new warnings are added or other risk areas are discovered. These new warnings will now cause your previously working build to fail, often for no good reason. Since many developers have the "never update" mindset, these new warnings go unnoticed until someone on the team eventually updates. These failures are often localized rather than systematic, so the team as a whole tends to overlook the effect of -Werror:

  • Your build server doesn't work since the server software was updated, causing your build guru to spend time investigating and rolling back software
  • A single developer updated and now must assume the burden of fixing new warnings before resuming the actual work
  • Your new hire can't get your software compiling, and time is wasted finding out that it's the toolchain version that matters

Furthermore, there are lots of warnings that don't need to cause build failures, such as -Wunknown-pragmas. I am in the habit of using #pragma mark in my projects to provide nicer editor interactions. If I use an older GNU toolchain then #pragma mark is unrecognized and generates a warning - but it doesn't affect my final binary at all!

To get around issues like that, now you need to start disabling individual errors that you don't want: -Wno-error=unknown-pragmas. You have to maintain these settings for all new benign warnings that get added.

I don't say all of this to support tolerating warnings in your project. In my projects, I fix all warnings and continually drive the teams I work with to get to 0 warnings. My Jenkins builds all have a warning graph so I can see the warning trend over time, when they are introduced, and who regularly introduces them.

Rather than having all warnings turned into errors, I think that warnings that lead to major problems or are often ignored should be selectively promoted into errors. You can do this by specifying Werror=warning-name, which will cause that specific warning name (e.g. unknown-pragmas) to generate an error if it is encountered.

For example, a warning that I promote to an error is -Wreturn-type. This warning seems innocuous on the surface, but you can get into a dangerous situatione easily:

Missing return statement in function with return expected
aws.c:158:1: warning: control reaches end of non-void function [-Wreturn-type]

If your function should return a value but does not, your compiler is going to start picking up random garbage as the return value than the value you intended, leading to weird behavior and tricky bugs. Definitely worth being an error!

If you're still convined that you need to use -Werror, I suggest that you wire up a way to turn this behavior off, such as a make variable. Then, to disable it, developers can simply run:

$ make all WARNINGS_AS_ERRORS=n

This allows you to keep -Werror enabled by default but also enables developers from having to hack up your project if they are using a newer/older toolchain version with different warnings.

Before you enable -Werror on your projects, make sure that you really want to sign up for the maintenace headaches that come with it. You can utilize better strategies instead:

  • Promote specific warnings to errors
  • Track and drive down warning count using build metrics and developer feedback
  • Locally/globally disable benign warnings that you don't need to worry about in your project (e.g. -Wunknown-pragmas)

If you must enable -Werror, at least provide an easy method to disable the -Werror behavior.