Best practices for CFLAGS handling in configure scripts?
I'm not a fan of autoconf, but in the interest of principle of least surprise, I'm trying to make my (non-autoconf) configure scripts behave as closely as possible to what users expect from an autoconf-based build system. The GNU coding standards are actually rather reasonable on this topic, and explicitly mention the possibility of not using autoconf/automake but providing a compatible interface in another way:
One issue I cannot find any good description of, however, is how to best handle CFLAGS. It's clear to me that any essential flags (like -I$(srcdir)/inc) that are none of the user's business don't belong in CFLAGS in either the configure script or the makefile, as overriding them would completely break the build. So these I've either hard-coded in the makefile, or (if they require some detection) put the detection logic in configure, but passed them through a separate make variable instead of CFLAGS so they won't get overridden.
What I'm still unclear on how to handle best, though, is optional things like optimization levels/options, warning flags, debugging, etc. Should flags enabled by things like --enable-debug or --enable-warnings get added to CFLAGS or passed in some other make variable? What if they conflict with flags already in user-provided CFLAGS?
I can see where for a lot of projects the answer might should just be "don't mess with that at all and let the user choose their CFLAGS", but some of my usage cases are projects where the user base is generally interested in having out-of-the-box optimized and debugging configurations.
Edit: Another issue I forgot: when it comes to detecting and automatically using certain CFLAGS that aren't essential but preferred, should this be done only if the user left CFLAGS blank in the environment, or always?
If you want to handle argument like --enable-warnings, you have a lot of work to do. If you want to keep it simple, you can have that argument add something like
CFLAGS="$CFLAGS -Wall -Wextra"; CPPFLAGS="$CPPFLAGS -DWARNINGS"
in your configure script, but this actually opens a big can of worms. Are those options recognized by all compilers, or are you assuming that your user is using a compiler which recognizes those options and for which they do what you want? Probably you need to wrap that assignment in another check for a set of compilers you know about. Or you need to put in a check that the compiler being used by the user at least doesn't error out when passed those flags (perhaps the user has already set -Wsome in CFLAGS, and the compiler will error out with the conflicting argument -Wall.) The same problems apply to --enable-debug. It seems perfectly reasonable to respond to --enable-debug by appending -DEBUG to CPPFLAGS, so the user does not need to examine your source to determine if debugging is enabled via -DEBUG rather than -DDEBUG, but you cannot predict all situations and it is generally best to err on the side of caution and not do such things.
If you want your users to have "out-of-the-box optimized and debugging configurations", the configure script for the project is the wrong place to do it. Keep the configury simple, and have you users use a package management system like pkgsrc for such things. If you want your distribution tarball to have some of that functionality, provide a set of scripts (perhaps invoked automagically by the configure script after detecting a platform, but provide the ability to override that feature) for common platforms that make assignments for the user and thus provide the desired functionality. But keep the configure script itself bare-bones.
It would be sane enough to have the user expect to have to wrestle with things when providing custom flags. CFLAGS can be appended, CPPFLAGS can be prepended (header search paths options look at the first path, while compiler optimizations and directives look at the last options (or rather, they override prior options))
--enable-debug and perhaps other command-line directives provided to the configure script do not only have to change compiler options, but have the possibility of changing things within the source as well (maybe, for example, redefining inline macros to be actual functions), so their uses are somewhat different.
In conclusion, have user-specified CPPFLAGS prepended, user specified CFLAGS appended; any configure script option may be either appended or prepended depending on context.