Arena Red » 29 May 2005 » CodeWarrior vs. Mac OS X 10.4 Tiger
« Boxster Suspension Upgrade | Mac OS X 10.4 Screen Rotation and Gather Windows »
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.

Top 10 of 256 Referrers:
[53] Google: "codewarrior mac"
[11] Google: "codewarrior mac"
[10] Google: "codewarrior mac"
[7] Google: "codewarrior tiger"
[6] Google: "Mac OS X 10.4 CodeWarrior "
[5] Google: "codewarrior mac os x"
[4] Google: "codewarrior mac os x"
[3] Google: "codewarrior mac"
[3] Google: "OSX 10.4 gcc"
[3] Google: "codewarrior 9 tiger"