CodeWarrior vs. Mac OS X 10.4 Tiger
I use a variety of compilers and IDEs, in many cases in conjunction
with code that uses my Code Vault cross-platform C++ class library. One of
the trickier environments to set up is Metrowerks CodeWarrior on Mac OS X, because
when you access the BSD/POSIX subsystem, which I do in the Code Vault (for
POSIX threads and sockets, etc.), you immediately
run into conflicts between the MSL header files supplied by Metrowerks and the
BSD header files supplied by the OS. CodeWarrior 8.3, the version I use, does not live
too harmoniously with the BSD headers; CodeWarrior 9 is reported to fix some but
not all of those issues.
I knew I'd have to be careful when upgrading to Tiger, because any changes to
the BSD headers or the GCC 4.0 environment could easily break something. Below I
detail the things I ran into and how I resolved them in a backward-compatible way,
so that the Code Vault and my other code will work with CodeWarrior on Tiger,
with GCC 4.0 on Tiger, and on earlier systems and other platforms and IDEs, without
change.
There were five areas I had to address to make things work with Tiger:
- Object format changes in /usr/lib files.
- Compile error in CodeWarrior console source.
- CodeWarrior compile errors in BSD header files.
- Math header conflicts between BSD and MSL.
- New GCC compile errors.
Changes to /usr/lib Object Format
My typical CW projects include two libraries from /usr/lib:
- /usr/lib/crt1.o (the C runtime library)
- /usr/lib/libiodbc.a (the iODBC library)
These files' object format has changed in Tiger, and CodeWarrior 8.3 is unable
to read them now. This prevents the project from proceeding to the link phase.
Metrowerks supplies a version of crt1.o called mwcrt1.o. Replacing the use
of /usr/lib/crt1.o with {CodeWarrior}MacOS X Support/Libraries/Startup/mwcrt1.o
makes CodeWarrior happy. I actually chose to check a renamed copy of this file
("cw8_mwcrt1.o") into CVS so that projects can locate it more easily without
regard to installation paths and future version changes. This also more clearly
identifies the distinction in runtime library files used for CodeWarrior builds
and GCC builds.
Since libodbc.a is supplied by the system, my solution here was to go grab a copy
of the library from a Mac OS 10.3 (Panther) machine and try it. It worked. So I
took the same approach and checked a renamed copy of this file ("cw8_libiodbc.a")
into CVS.
In both of these cases, we are just changing the file that is used in the CodeWarrior
project; the libraries used for GCC on Mac OS X, and on other platforms and compilers,
does not change.
Compile Error in CodeWarrior Console Source
CodeWarrior projects typically include a file named console_OS_X.c, which performs
the magic necessary to do console i/o in the CodeWarrior environments (including the
special console window used by the source debugger). This file got a slew of compile
errors. Fortunately, when I tracked down the conflicts, they all stemmed from the
file's #include of <size_t.h>, and when I commented out this include statement, the
file compiled successfully; it's an extraneous include. Like the library files provided
by the OS, this file is
not under source control, in this case because it's part of the compiler's bundled support
files. Because we're editing the file, I decided it would be best to check a
renamed copy of this file ("cw8_console_OS_X.c") into CVS and use that version of
the file in the CW project. Otherwise, each machine updating to Tiger would have to
manually edit the local copy of the file under the CodeWarrior application directory.
BSD Header File Errors
Two of the BSD headers files have changed with Tiger in a way that breaks under
CodeWarrior. In both /usr/include/netinet/in.h and /usr/include/netinet6/in6.h,
there are places where a numeric token has been placed after an #endif directive.
For example:
#if 1 /*IPSEC*/
#define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */
#endif 1
The "1" after the "#endif" is the problem. It should be enclosed in a block comment.
I don't know if there is a uniform specification for whether the preprocessor should
allow extra non-commented commentary after an #endif. Apparently GCC accepts it, but
CodeWarrior does not.
I just fixed the local copies of in.h and in6.h to take care of this problem.
Math Header Conflicts
One of the main problems with using CodeWarrior under Mac OS X and accessing the BSD
headers is that there is a natural conflict between the BSD headers and MSL headers
supplied by CodeWarrior. You have to have the project's "Access Paths" (include
search paths) set up just right to avoid conflicts. After upgrading to Tiger, I ran
into a few more conflicts, and had to solve them by updating my platform-specific
header file. In the Code Vault, there is a separate include file, vtypes_platform.h,
that gets included first and contains all of the platform-specific quirks and
workarounds. So to solve this problem, only the Mac version of this file had to be
modified. The Unix and Windows versions are not affected.
Platform Differences: FP definitions
The header files included because of <math.h> had several symbol conflicts with MSL.
In fact, the conflicting symbols were identical, but in the BSD headers an enum is
used to define them, but in MSL a #define is used, so compiler complained that the
symbol was re-defined. The workaround is, when compiling with CodeWarrior and MSL, and
we're on Mac OS X 10.4 or later, to carefully define or undefine things to avoid the
conflicts. Here is the Mac-specific header code I added to the existing Mac-specific
vtypes_platform.h. (The symbol V_LIBRARY_METROWERKS is one I set earlier to indicate
that we're compiling
with the MSL, so this would be undefined when compiling with GCC.)
#include <AvailabilityMacros.h> /* so we can test MAC_OS_X_VERSION_xxxx values' presence */
#ifdef VLIBRARY_METROWERKS
#ifdef MAC_OS_X_VERSION_10_4
#define __FP__
#include <math.h>
#undef FP_NAN
#undef FP_INFINITE
#undef FP_ZERO
#undef FP_NORMAL
#undef FP_SUBNORMAL
#undef HUGE_VALF
#undef HUGE_VALL
#endif /* MAC_OS_X_VERSION_10_4 */
#endif /* VLIBRARY_METROWERKS */
Note that AvailabilityMacros.h contains OS version symbols that can be tested
for a minimum available development platform version. That is, if we are using
the Tiger development tools or something later, MAC_OS_X_VERSION_10_4 is defined.
If later version of the tools change the requirements here, we can look at the
later symbols.
Platform Differences: min / max / abs / fabs
The standard library math functions min(), max(), and abs() have been something that
I have always had to wrap with conditional macros in the Code Vault, because they
are so inconsistently defined across the variety of platforms and compilers. In some
environments a function may be in the std namespace; in others it's not. In some,
you need to call fabs() for floating-point operations; in others, not. And there are
even cases where there is no function defined, so a macro is needed to fully
define it. The whole thing is hit-and-miss and has to be tweaked to make the compiler happy.
When compiling on the Mac, it turns out that min() and max() are both defined and
available in the std namespace, whether compiling with GCC or CodeWarrior, whether
under Tiger or not. So my V_MIN and V_MAX macros just map to std::min() and std::max().
But abs() and fabs() are inconsistent, so their macros V_ABS and V_FABS have to
be tuned to whether we're using GCC or CW/MSL, and whether we're on Tiger or not.
GCC Return Value Errors
One thing that has changed in GCC with Tiger is that it is less tolerant of
code that returns from non-void functions solely from inside an else clause.
That is, if you have code like this:
int f()
{
if (conditionA)
{
// do some stuff ...
return 1;
}
else if (conditionB)
{
// do some stuff ...
return 2;
}
else
{
// do some stuff ...
return 3;
}
// Note that we cannot get here, and don't have a return statement.
}
GCC 4.0 may complain that you are reaching the end of the function without
returning a value. It seems to depend on how complex and long the individual
conditional blocks are. The example above will compile, but if the function is
hundreds of lines long, with several very long conditional blocks, GCC might
complain. I have not determined a minimal test case to trigger the compiler error.
Looking at the code that GCC complained about (which I didn't write :) ),
the length was a symptom of code that grew over time into something bad
and ugly and difficult to maintain. The quick solution is to add a final return
statement, and comment it as "never reached":
int f()
{
if (conditionA)
{
// do some stuff ...
return 1;
}
else if (conditionB)
{
// do some stuff ...
return 2;
}
else
{
// do some stuff ...
return 3;
}
return 0; // Never reached; satisfies GCC 4.0.
}
Given that the code I saw that triggered the error was ugly and monolithic,
the less quick solution would be to refactor the code into something more
manageable, and less sketchy to GCC's eye.