views:

183

answers:

4

In xcode, while compiling apps with gcc, I want to throw compilation time errors if things like NSZombieEnabled is on for a distribution release, thus ensuring that compilation will fail and I won't accidentally do something stupid.

I did some googling, but could not figure out how to cause the compiler to bail if a certain condition is met. Surely it must be easy, am I just not finding it?

+7  A: 

Use the #error directive:

#if SHOULD_FAIL
#error "bad compiler!"
#endif

int main()
{
    return 0;
}
$ gcc a.c -DSHOULD_FAIL=0 # passes fine
$ gcc a.c -DSHOULD_FAIL=1
a.c:2:2: error: #error "bad compiler!"

Since NSZombieEnabled is an environment variable, you'll need to do something clever in your build script to define your macro as zero or one.

Strictly speaking, the #error directive occurs in the C Preprocessor, not gcc. But that shouldn't matter in the case you've described.

Mark Rushakoff
This seems tough… can't seem to find a way
coneybeare
@coneybeare: NSZombieEnabled is an environment variable that you set at **run time**. You don't need to worry about it at compile time. This technique is, however, useful if you have compile time debug macros.
JeremyP
+1 note that you can also do `#warning This is a warning` to issue a warning instead of an error.
Dave DeLong
+1  A: 

NSZombieEnabled is an environment flag and, to my knowledge, should not affect the resulting binary (though it may affect the speed of the compilation.)

fbrereto
A: 

Another interesting thing you can do is manipulate the output of your build script. If you're using a custom build script as part of your build process, you can do something like:

echo "error: this build step failed!"

Or:

echo "warning: this build step could be potentially faulty"

Those will produce errors or warnings (respectively) that will show up in the build results window. I've used this a couple times, and it's really useful.

Dave DeLong
+1  A: 

A complile-time assert()--a.k.a. static assert--may be helpful. Here is mine, derived mostly from http://www.pixelbeat.org/programming/gcc/static_assert.html:

/*-----------------------------------------------------------------------------
 *  Compile-time ASSERT(). Similar to the BOOST_STATIC_ASSERT(). And the C++0x
 *  static_assert(), which also has a parameter for a useless error message
 *  (see correction!). Our ASSERT() can be placed anywhere in the code, except:
 *
 *  o In a twice-included header file, without a #ifndef...#endif wrapper.
 *  o In the middle of a structure definition (or enum definition).
 *  o In C89 or C90, after a statement. But you can wrap it in braces!
 *
 *  If you want stick something in the middle of a structure definition
 *  you'll need to use the ugly, three-line construct #if...#error...#endif.
 *  And if you do do this, the pre-processor has a much more limited idea of
 *  what a "constant expression" is.
 *
 *  This is a refinement of ideas from the web (www.pixebeat.org is good). It
 *  is shorter than BOOST. And, I believe, is better than Linus Torvald's
 *  suggestion for an improved BUILD_BUG_ON(). And the do{...}while(0) wrapper
 *  you commonly see is totally inapplicable here: it limits permissible
 *  locations.
 *
 *  The web has many suggestions using arrays with a negative index. But with
 *  GCC, most of these do not detect a NON-CONSTANT arg (which is easy enough
 *  to do in error), except for the attractive 'extern int foo[expression]',
 *  which also gives an 'unused variable' warning (which might be fixable via
 *  (void)foo). GCC 4.3 apparently has a built-in static_assert(). Update:
 *  typedef int array[expression] seems also to be good.
 */
#define CONCAT_TOKENS(a, b) a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e) enum {EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}

But I corrected my opinion about the 'useless' message in C++0x:

/*-----------------------------------------------------------------------------
 *  Correction!: The message in static_assert() isn't quite useless, and we've
 *  added it to ASSERTM(). This is needed for the case where two different
 *  header files happen by chance to have two ASSERT()'s on the same line, or
 *  likewise for a source file and a header file.
 *
 *  We could also handle this via __COUNTER__, but this isn't supported by
 *  the SGI compiler (and is uglier). And we can't use __FILE__, because it
 *  doesn't usually expand to a valid C token (e.g. it has a dot c or dot h).
 */
#define ASSERTM(e,m) enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}

Some examples:

/*-----------------------------------------------------------------------------
 *  Example:
 */
ASSERTM (sizeof (int16) == 2, my_global_header_h);
ASSERTM (sizeof (ord32) == 4, my_global_header_h);
ASSERTM (sizeof (int64) == 8, my_global_header_h);

/*-----------------------------------------------------------------------------
 *  Equally good, I believe, is the following variant, but it is slightly
 *  longer (and not used by us at the present time):
 */
#define ASSERTt(e) typedef int EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__)[1-2*!(e)]
Joseph Quinsey