Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Contact ADC

Creating Cross-Platform Applications with Core Foundation and Open Source

One of the most powerful yet under-appreciated frameworks in Mac OS X is Core Foundation, also known as CF. CF provides the fundamental C data types (for example, String, Dictionary, Array, Data and Number) as well as the essential services (such as plug-ins, URL handling and networking) that underlie both Cocoa and Carbon. In particular, CF provides convenient facilities for importing and exporting these types as part of a rich, flexible data structure known as a property list, which is used extensively for system configuration, application parameters, and user defaults.

A very useful thing about CF is that most of it is available from Apple as an open source project known as CF-Lite, available on Apple's Open Source website. Because CF is open source, developers can compile and run it on Macintosh as well as other platforms—and this opens up a number of interesting possibilities. These cross-platform opportunities, and how to exploit them using open source, are the focus of this article.

As an example, consider a Cocoa application that provides an administrative front-end to an open source database, such as MySQL. Using Cocoa bindings, a rich graphical front-end can be created using virtually no code, which would save the user's choices as a property list, in either an XML or binary format. That file can be sent to the server using DAV or a shared filesystem, where a trusted server application would read it and perform the necessary administrative tasks.

If both client and server systems are running Mac OS X, you could write the entire application in Cocoa. But, what if your servers are running Linux? Do you have to give up and write the entire thing as a web application? Or, must you learn how to define and parse your own XML schema to share information between the client and server?

In a word: No. Instead, you can take advantage of the open source nature of CF-Lite to create a cross-platform tool that will read standard Mac OS X property lists virtually anywhere.

In this article, we start by describing the kind of environment you need to build CF-Lite on Mac OS X, Darwin, Linux and even Windows (via Cygwin). Next, we walk you through the basics of CF-Lite, including a simple application for reading and displaying property lists. After that, we'll delve into the detailed mechanics of actually building, installing, and testing CF-Lite on various platforms; and we even provide the project on the ADC Member Site (you can join for free). Finally, we'll suggest ideas for future exploration, including modifying CF-Lite or making use of other Darwin projects.

What You Need to Get Started

1. You need access to at least one of the following: Mac OS X, a Darwin volume, a Linux volume, or a Windows system with Cygwin installed (see the section below, "What is Cygwin?"). On Mac OS X, install the Xcode development tools from the installation CD.

2. You need to download the CoreFoundation project from Apple's Darwin Projects source site. Look for the project name that begins with "CF": this is the latest version of CF-Lite, and should be your primary reference point.

If you'd like to download the entire project at once, you can use rsync from the community-run OpenDarwin mirror:

$ rsync -vL "darwinsource.opendarwin.org::darwinsource/10.3.8/*.tar.gz" ./

Though not as definitive as the Apple site, OpenDarwin may be more convenient, and include changes from other members of the community; see For More Information below for details.

3. In addition, on platforms other than Mac OS X you need the following header files from /System/Library/Frameworks/CoreServices.framework. Without them, you will get compiler errors regarding Mac OS X version availability and undefined data types. Copy the files to /usr/include/, so they will be included in GCC's standard search path:

    AssertMacros.h
    AvailabilityMacros.h
    TargetConditionals.h

4. You can download and use this sample Xcode project, plistProject, from the ADC Member site. All of the functions discussed in this article are included in main.c. A sample schema.xml file is also included in the project folder.

What is Cygwin?

Cygwin is a collection of UNIX tools, coupled with a Linux emulation layer, that run in a shell environment on Windows 95 and later. The tools are ports of FSF (Free Software Foundation) programs and other sources. The environment permits the use of many applications and utilities that are standard in Linux distributions. Programs (should) behave within Cygwin the same way they would under a Linux shell.

Note that it is theoretically possible to build CF-Lite using native Windows services and tools; however, currently the Cygwin build is simpler and more robust, so we will use that for the purposes of this article.

In general, the same commands apply to Linux and Cygwin. If you have access to a Windows system, but not a full Linux installation, you can build and test CF-Lite using Cygwin. Check out the Cygwin website for more information. When installing Cygwin, you first download and run the setup program, then select the packages you want to install. For this article, install the developer packages for GCC, including the C compiler and the make utility. There are many additional packages available for you to experiment with.

CF-Lite Overview

CF-Lite, the Darwin version of Core Foundation, is a subset of CF that does not include some functionality available on Mac OS X.  However, it does contain the data structures used for managing common application objects—such as strings and numbers of various formats—which are of particular interest for cross-platform projects. 

Data types

Core Foundation's object model supports encapsulation and polymorphic functions, and is based on opaque types. The individual fields of an object based on an opaque type are hidden from clients, but the type's functions offer access to most values of these fields.

"Class" is not used to refer to opaque types because, despite the conceptual similarity of class and 
opaque type, many might find the term confusing. However, the Core Foundation documentation frequently 
refers to specific, data-bearing instances of these types as "objects".

Core Foundation has many opaque types, and the names of these types reflect their intended uses. For example, CFString is an opaque type that "represents" and operates on Unicode character arrays. ("CF" is, of course, a prefix for Core Foundation.) CFArray is an opaque type for indexed-based collection functionality. The functions, constants, and other secondary data types in support of an opaque type are generally defined in a header file having the name of the type; CFArray.h, for example, contains the symbol definitions for the CFArray type.

A link to Core Foundation documentation is provided at the end of this article. In particular, read the overview.

A Property List Example

Our primary example involves the use of Core Foundation property lists, or "plists," as a common data exchange mechanism between Mac OS X, Darwin, and Linux. All plists have an XML syntax, which imposes structure on the data stored in the plist. Also, plists use a key-value association, where you specify an identifier, or key, for the data element to retrieve, and the plist returns the associated value(s). Plists are more flexible than simple 1-to-1 mappings: A key may map to an array of values, or multiple data elements. Plist keys and values may be any of several Core Foundation data types, including String, Dictionary, Array, Data, and Number. The example includes each of these in the sample plist.

Core Foundation property lists are of type CFPropertyList, and are usable across many types of Xcode projects. For this example, we started from the CoreFoundation Tool template, as shown in Figure 1.

Xcode template dialog

Figure 1: Creating a CoreFoundation Tool project in Xcode

Listing 1 shows a slightly revised version of DictionaryExample.c. This file is available on Mac OS X as part of the Core Foundation example code, in the folder /Developer/Examples/CoreFoundation/Dictionary. This example is included as part of the Developer Tools install from the Mac OS X Developer Tools CD.

The #include statement at the top of Listing 1 adds to the GCC search path the header file CoreFoundation.h, which is part of the CoreFoundation framework. This particular header is an umbrella header, in that it then #includes other headers. Apple has modified GCC to accomodate frameworks in the search path. Later in the article, we will address this issue on Linux.

The function propertyListExample in Listing 1 creates and populates a dictionary (of data type CFMutableDictionaryRef). It then creates a CFDataRef object by calling CFPropertyListCreateXMLData. There are several calls to CFShow to print out messages or formatted objects to the console (see Figure 2).

Listing 1: Creating and saving a property list

#include <CoreFoundation/CoreFoundation.h>

void propertyListExample( void );
void writePropertyListToFile( CFDataRef data );

const char * kFilename = "/schema.xml";

int main( int argc, const char * argv[] ) {
    // Create and save the plist.
    propertyListExample();

    return 0;
}

void propertyListExample( void ) {
    CFMutableDictionaryRef dict;
    CFNumberRef num;
    CFArrayRef array;
    CFDataRef data;
    #define NumKids 2
    CFStringRef kidsNames[] = { CFSTR( "John" ), CFSTR( "Kyra" ) };
    #define NumPets 0
    int yearOfBirth = 1965;
    #define NumBytesInPic 10
    const unsigned char pic[ NumBytesInPic ] = 
        { 0x3c, 0x42, 0x81, 0xa5, 0x81, 0xa5, 0x99, 0x81, 0x42, 0x3c };
    CFDataRef xmlPropertyListData;
    CFStringRef xmlAsString;

    // Create and populate a pretty standard mutable dictionary: CFString keys, CF type values.
    // To be written out as a "propertyList", the tree of CF types can contain only:
    //   CFDictionary, CFArray, CFString, CFData, CFNumber, and CFDate.
    // In addition, the keys of the dictionaries should be CFStrings.

    dict = CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, 
        &kCFTypeDictionaryValueCallBacks );

    CFDictionarySetValue( dict, CFSTR( "Name" ), CFSTR( "John Doe" ) );

    CFDictionarySetValue( dict, CFSTR( "City of Birth" ), CFSTR( "Springfield" ) );

    num = CFNumberCreate( NULL, kCFNumberIntType, &yearOfBirth );
    CFDictionarySetValue( dict, CFSTR( "Year Of Birth" ), num );
    CFRelease( num );

    array = CFArrayCreate( NULL, ( const void ** )kidsNames, 2, &kCFTypeArrayCallBacks ); 
    CFDictionarySetValue( dict, CFSTR( "Kids Names" ), array );
    CFRelease( array );

    array = CFArrayCreate( NULL, NULL, 0, &kCFTypeArrayCallBacks );
    CFDictionarySetValue( dict, CFSTR( "Pets Names" ), array );
    CFRelease( array );

    data = CFDataCreate( NULL, pic, NumBytesInPic );
    CFDictionarySetValue( dict, CFSTR( "Picture" ), data );
    CFRelease( data );

    // We now have a dictionary which contains everything we want to know about
    // John Doe; let's show it first:

    CFShow( CFSTR( "John Doe info dictionary: " ) );
    CFShow( dict );

    // Now create a "property list", which is a flattened, XML version of the
    // dictionary:

    xmlPropertyListData = CFPropertyListCreateXMLData( NULL, dict );

   // The return value is a CFData containing the XML file; show the data

    CFShow( CFSTR( "Shown as XML property list (bytes): " ) );
    CFShow( xmlPropertyListData );

    // Given CFDatas are shown as ASCII versions of their hex contents, we can also
    // attempt to show the contents of the XML, assuming it was encoded in UTF8
    // (This is the case for XML property lists generated by CoreFoundation currently)

    xmlAsString = CFStringCreateFromExternalRepresentation( NULL, xmlPropertyListData, 
        kCFStringEncodingUTF8 );

    CFShow( CFSTR( "The XML property list contents: " ) );
    CFShow( xmlAsString );

    writePropertyListToFile( xmlPropertyListData );

    CFRelease( dict );
    CFRelease( xmlAsString );
    CFRelease( xmlPropertyListData );
}

void writePropertyListToFile( CFDataRef data ) {
    CFStringRef errorString;

    CFPropertyListRef propertyList = CFPropertyListCreateFromXMLData( NULL, data, 
        kCFPropertyListMutableContainersAndLeaves, &errorString );

    if ( errorString == NULL ) {
        CFStringRef urlString = CFStringCreateWithCString( NULL, kFilename, CFStringGetSystemEncoding() );

        CFURLRef fileURL = CFURLCreateWithFileSystemPath( NULL, urlString, kCFURLPOSIXPathStyle, FALSE );

        CFWriteStreamRef stream = CFWriteStreamCreateWithFile( NULL, fileURL );

        Boolean isOpen = CFWriteStreamOpen( stream );

        CFShow( CFSTR( "Property list (as written to file):" ) );
        CFShow( propertyList );
  
        CFIndex bytesWritten = CFPropertyListWriteToStream( propertyList, stream, 
            kCFPropertyListXMLFormat_v1_0, NULL );

        CFWriteStreamClose( stream );
    }
    else {
        CFShow( errorString );
        CFRelease( errorString );
    }

    CFRelease( propertyList );
}

The Xcode run log

Figure 2: The plist Displayed in the Xcode Run Log Window

Notice, in the functions in Listing 1, that most of the CF objects are allocated by other functions within the Core Foundation API. In these cases, the caller is responsible for releasing the objects when finished, using CFRelease. Forgetting to do this will result in memory leaks.

The function writePropertyListToFile in Listing 1 accepts the raw plist data, creates a CFPropertyList object, and writes that out to an XML file. The plist contents get written out in XML format regardless of how you name the file; we used the .xml extension here as a hint to text editors on how to display the contents. The output file contents are shown in Listing 2. The file can then be copied for use on other machines and/or platforms, which is the subject of the next section.

Listing 2: The Property List as an XML file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>City of Birth</key>
	<string>Springfield</string>
	<key>Kids Names</key>
	<array>
		<string>John</string>
		<string>Kyra</string>
	</array>
	<key>Name</key>
	<string>John Doe</string>
	<key>Pets Names</key>
	<array/>
	<key>Picture</key>
	<data>
	PEKBpYGlmYFCPA==
	</data>
	<key>Year Of Birth</key>
	<integer>1965</integer>
</dict>
</plist>

If you open the plist file using /Developer/Applications/Utilities/Property List Editor on Mac OS X, you will see the contents formatted slightly differently, as shown in Figure 3.

Property List Editor

Figure 3: The plist in the Property List Editor

Reading the Property List on Darwin

Because Darwin includes CF-Lite, all of the API functions used in these examples should just work. So, the next step is to copy the plist file to a Darwin system. The example application discussed in this section opens and reads the plist. Because Darwin shares the same version of GCC as Mac OS X, this plist reader is structured similarly to the plist writer discussed above. As shown in Listing 3, the reader includes similar declarations at the top, and the CF calls work the same way. Note that the plist file is assumed to be in the root directory.

Listing 3: Reading the property list from the XML file

#include <CoreFoundation/CoreFoundation.h>

void readPropertyListFromFile( void );

const char * kFilename = "/schema.xml";

int main ( int argc, const char * argv[] ) {
    // Read the plist.
    readPropertyListFromFile();	

    return 0;
}

void readPropertyListFromFile( void ) {
    CFDataRef data = NULL;
		
    FILE *file = fopen( kFilename, "r" );

    if ( file != NULL ) {
        int result = fseek( file, 0, SEEK_END );
        result = ftell( file );
        rewind( file );

        char * buffer = ( char * )calloc( 1, result );

        if ( buffer != NULL ) {
            if ( fread( buffer, result, 1, file ) > 0 ) {			
                data = CFDataCreate( NULL, buffer, result );
            }

            free( buffer );
        } 

        fclose( file );
    }

    if ( data != NULL ) {
        CFPropertyListRef propertyList = CFPropertyListCreateFromXMLData( NULL, data,
            kCFPropertyListImmutable, NULL );

        CFShow( CFSTR( "Property list (as read from file):" ) );
        CFShow( propertyList );
    }

    CFRelease( data );
}

On Darwin, you can compile the above source code using GCC, like this:

gcc -framework CoreFoundation -o reader main.c

The -o flag precedes the name of the object file that GCC should create, which we can then execute:

$ ./reader
Property list (as read from file):
<CFDictionary 0x5009a0 [0xa01900e0]>{type = mutable, count = 6, capacity = 17, pairs = (
        1 : <CFString 0x500ad0 [0xa01900e0]>{contents = "Name"} = 
            <CFString 0x500ae0 [0xa01900e0]>{contents = "John Doe"}
        4 : <CFString 0x500b00 [0xa01900e0]>{contents = "Pets Names"} = 
            <CFArray 0x500b20 [0xa01900e0]>{type = immutable, count = 0, values = ()}
        5 : <CFString 0x500960 [0xa01900e0]>{contents = "City of Birth"} = 
            <CFString 0x500980 [0xa01900e0]>{contents = "Springfield"}
        13 : <CFString 0x500bc0 [0xa01900e0]>{contents = "Year Of Birth"} = 
             <CFNumber 0x500be0 [0xa01900e0]>{value = +1965, type = kCFNumberSInt64Type}
        16 : <CFString 0x500a90 [0xa01900e0]>{contents = "Kids Names"} = 
             <CFArray 0x500680 [0xa01900e0]>{type = mutable-small, count = 2, values = (
        0 : <CFString 0x500770 [0xa01900e0]>{contents = "John"}
        1 : <CFString 0x500640 [0xa01900e0]>{contents = "Kyra"} )}
        19 : <CFString 0x500b30 [0xa01900e0]>{contents = "Picture"} = 
             <CFData 0x500b90 [0xa01900e0]>{length = 10, capacity = 10, bytes = 0x3c4281a581a59981423c}
    )}
$ 

CFShow writes to the console, similarly to printf in the C standard library. This makes it a portable, though limited, debugging tool.

Reading the Property List on non-Darwin systems

Linux and Cygwin do not include CF-Lite, so before we can use the plist in an application, we first need to create and deploy the CoreFoundation library. To do this, you need to download the CF-299 project, as discussed above in the section "What you need to get started".

A Patch for Linux

If you are developing on Linux, Cygwin, or another non-Darwin platform, download and install the
CF-299.linux.diff patch onto CF-299. Several files in CF-299 require updating or they won't compile. Patching is the easiest way to bring them up-to-date.

To install the patch, rename or copy the directory CF-299 to CF-299.orig, and then execute the patch command, like this:

$ cp -R CF-299 CF-299.orig
$ patch -p0 < CF-299.linux.diff
patching file CF-299.orig/AppServices.subproj/CFUserNotification.c
Hunk #1 succeeded at 80 (offset 2 lines).
...
$

CF-299.orig now contains the updated files, and should build on Linux and Cygwin.

Building and Deploying CF-Lite

This section discusses the Darwin and Linux platforms in detail. Although Darwin already includes GCC and other tools, the build process can be modified by passing parameters to make. This section provides instructions on how to do that. Plus, you can use these instructions if you later decide to modify CF-Lite, and need to rebuild and redeploy it on Darwin (or Linux).

Note that you can build CF-Lite on Mac OS X, and copy the library to a Darwin volume for use. But do not copy a CF-Lite version of the library named CoreFoundation into /System/Library/Frameworks/CoreFoundation.framework on a Mac OS X volume. The reason is that the Darwin version (CF-Lite) omits important functionality available in the Mac OS X version. So, you cannot substitute a CF-Lite build for the Core Foundation library on a volume that boots Mac OS X. The operating system will crash on restart, or even hang after you have copied the updated file into the framework directory. In the latter case you will need to reboot from an installer CD or select a different startup volume (hold the Option key down while booting). Again, you can build CF-Lite on Mac OS X for use on Darwin, but do not replace the Mac OS X CoreFoundation libraries (this includes CoreFoundation, CoreFoundation_debug, and CoreFoundation_profile) with CF-Lite versions.

Building and Deploying on Darwin

On Darwin, most packages should build without modification, as long as you retrieve the package version corresponding to your kernel version. Core Foundation has not changed much since the release of Mac OS X 10.3 Panther. This article uses CF-299 (Core Foundation build 299) and Darwin 7.2.1, although later versions of either may work fine together. However, introducing other packages may add dependencies, so you should always first try to download the package versions appropriate for your kernel version. On the website, package versions are grouped by kernel version. If you want to build Darwin 7.2.1 packages, you will find the appropriate package archives linked off the Darwin 7.2.1 page.

The Makefile removes most of the drudgery from the build process. It determines the operating system for which the package is being built, sets paths for generated or copied files, invokes the compiler, and so on. The most comprehensive target in the CF-Lite Makefile is named 'install'. You invoke the install target like this:

$ make install

If you are unfamiliar with Makefiles and/or the GNU build tools, you should first read the ADC article The GNU Compiler Collection on Mac OS X. That article lists several references that provide even more detail about the GCC tools.

All Darwin projects use the following environment variables while building, with the corresponding default values:

Variable Name Description Default Value
SRCROOT Path to the sources being built. . (current directory)
OBJROOT Path where intermediate build objects (.o's) should be stored. /tmp/CoreFoundation.obj
SYMROOT Path where build results with debugging symbols are stored. /tmp/CoreFoundation.sym
DSTROOT Path where final build projects are stored. /tmp/CoreFoundation.dst

You can override any of the environment variables on the command-line. For example, to change the destination of the final product(s), you can specify a new value for DSTROOT like this:

$ make DSTROOT=/usr/lib install
...
Done!
$

The Darwin Build Scripts are a collection of tools that assist compilation of the many projects contained in Darwin. Here are some of the highlights:

  • The included Makefile will compile the tools and install them.
  • The darwinxref tool is used to retrieve the list of projects, and versions, that compose a Darwin release. Additionally, darwinxref can store and retrieve information about which tools, libraries, and headers are necessary for building specific Darwin projects.
  • The darwinbuild script can be used to build Darwin projects.

If you are cross-compiling—building on one platform for deployment on another (for example, Darwin on Linux)—you will need to explicitly specify the target platform when invoking make. However, a problem with cross-compiling CF-Lite is the difference in include paths and filenames. If you want to build for Linux, you should perform the build on a Linux system, or a volume that has booted Linux.

If you modify any header files, you should precede any 'make build' or 'make install' with a 'make clean.' This deletes the existing build products, and indirectly forces a recompile of the .c files.

$ make clean
Deleting build products...
$

Building and Deploying on Linux

CF-Lite contains #ifdefs in each source and header file, and also in the Makefile, that distinguish between code blocks unique to Darwin (usually denoted by the symbol __MACH__) versus other operating systems. Linux #ifdefs are included in places, although you may find that various files do not have their Linux code up-to-date. This condition will manifest itself as compiler warnings and errors. You are certainly welcome to attempt to fix the problems: after all, that is the essence of the open source philosophy. You may submit your changes back to the OpenDarwin community for inclusion in later releases. If you get stuck, consider asking for help on the darwin-development mailing lists. Another developer may have already fixed the same problem, and/or the update is not yet public. Or you may have found a previously unnoticed problem that someone with a different skill set will be more adept at fixing.

If you look through the CF-Lite .h and .c files, you will notice that most of them include headers using one of two paths:

#include "CFInternal.h"
#include <CoreFoundation/CFBase.h>

The first searches the current directory for the file, the second looks in one or more known locations. This includes /usr/include, and directories included on the gcc command line. On the second line, because the reference is to a file located in a folder named CoreFoundation, the compiler will complain unless we provide such a folder. You could copy every header to this CoreFoundation subdirectory, but that gets tedious and error-prone, as any changes to one set of headers need to be reflected in the other set.

A better approach is to create a directory of symbolic links back to the headers in each subproject:

$ mkdir CoreFoundation
$ cd CoreFoundation
$ find ../*.subproj -name \*.h -exec ln -s {} ';'

This works on Linux and Cygwin. It is not necessary on Darwin because GCC has been modified to accept the parameter -framework, and will search the framework directories for headers.

You then build as described previously in the Darwin section. In the following example, the completed library is placed at /tmp/CoreFoundation.dst/usr/local/lib/libCoreFoundation.a (the default value for DSTROOT).

$ make clean
Deleting build products...
$ make install
...
Done!
$

When working from a shell, you may lose text as it scrolls past, if you cannot set the window buffer size large enough. For a project the size of CF-Lite, the number of error or warning messages may be substantial. Since those print to stderr, you should redirect stderr to a file. Here is how you can redirect both stdout and stderr to the same log file:

$ make > my.log  2>&1

Building and deploying on Cygwin

You should first create the directory of symbolic links as described above. Once that is done, use the sequence of build commands shown in Figure 4.

Building the library in Cygwin

Figure 4: Building the library in Cygwin

Note the output from the debug target. This is an addition to the Makefile, and is included here only for illustration (it has no impact on the actual build). Running make and invoking the debug target shows that the platform name is "CYGWIN_NT-5.1". This value is read from the Cygwin environment. (On a true Linux system the debug target prints "Linux".) However, you should force make install to use a PLATFORM value of "Linux", since there is no "CYGWIN" platform defined in the Makefile.

The code for the debug target is this:

debug:
	$(SILENT) $(ECHO) $(PLATFORM)

This prints the value of the PLATFORM variable, which is defined in the Makefile but assigned a value at runtime. You can add statements to display additional variable values. The debug target is not included in the original CF-299 source, but you can insert the above code into the Makefile target section; the end of the file is a good location.

Testing CF-Lite

With the library in place, we can write a simple test application that loads the plist from the XML file and prints the result to the console. Let's address this question for Darwin and Linux.

Testing on Linux

The plist reader application works the same under Linux as under Darwin (see the section titled "Reading the property list on Darwin"), but we first need to build it. Compiling main.c requires a different command-line than on Darwin:

gcc -o reader -I/tmp/CoreFoundation.dst/usr/local/include/CoreFoundation main.c
 -L/tmp/CoreFoundation.dst/usr/local/include/CoreFoundation
 -lCoreFoundation -lpthread -lm

Both the compiler and linker need to know of any additional directories to search: the compiler for included header files, the linker for libraries. The flags are different (-I for cc, -L for ld), but the paths both reflect the distribution directory from the CF-Lite build product.

We also need to specify the libraries to link against. The -l flags indicate the library names. On the gcc command-line, order matters, so CoreFoundation (the physical file can be named either libCoreFoundation.a or libCoreFoundation.so) must be listed first, because it depends on the pthreads library (/lib/libpthread.so) and the math library (/lib/libm.so).

Once built, the application runs the same as on Darwin:

$ ./reader
Property list (as read from file):
<cfdictionary 0xa050958 [0x459160]>{type = mutable, count = 6, capacity = 17, pairs = (
    1 : <cfstring 0xa051330 [0x459160]>{contents = "Name"} = 
        <cfstring 0xa051348 [0x459160]>{contents = "John Doe"}
    4 : <cfstring 0xa051360 [0x459160]>{contents = "Pets Names"} = 
        <cfarray 0xa051378 [0x459160]>{type = immutable, count = 0, values = ()}
    5 : <cfstring 0xa050918 [0x459160]>{contents = "City of Birth"} = 
        <cfstring 0xa050938 [0x459160]>{contents = "Springfield"}
    13 : <cfstring 0xa051418 [0x459160]>{contents = "Year Of Birth"} = 
        <cfnumber 0xa051438 [0x459160]>{value = +1965, type = kCFNumberSInt64Type}
    16 : <cfstring 0xa050988 [0x459160]>{contents = "Kids Names"} = 
        <cfarray 0xa0509a0 [0x459160]>{type = mutable-small, count = 2, values = (
        0 : <cfstring 0xa0509b8 [0x459160]>{contents = "John"}
        1 : <cfstring 0xa051318 [0x459160]>{contents = "Kyra"}
)}
    19 : <cfstring 0xa051388 [0x459160]>{contents = "Picture"} = 
        <cfdata 0xa0513e8 [0x459160]>{length = 10, capacity = 10, bytes = 0x3c4281a581a59981423c}
)}
$

Testing on Cygwin

Cygwin uses a similar sequence as Linux. In Figure 5, we create the symbolic links to the CoreFoundation headers under /CoreFoundation, since that is on the include search path for gcc. Here, we nest a directory of links within another. This is because of the relative paths in the #include statements within the header files, which the header files use to include each other. We use relative directory locations in this example, although you can also use absolute values that start with the Cygwin root (/).

Symbolic links in Cygwin

Figure 5: Setting up the header file symbolic links in Cygwin

Figure 6 shows the GCC invocation, and the execution of the reader application. Note here that the -I/ flag instructs the compiler to search under the root directory for the include files, or symbolic links to the header files. So, instead of looking in /tmp/CoreFoundation.dst/usr/local/include/CoreFoundation (as in the Linux example above), here we instruct GCC to look in /CoreFoundation, which is the directory we populated in Figure 5. Where to put the header files or symbolic links is an arbitrary decision. Just remember to include that directory in the search path when invoking GCC.

Building the Test Application in Cygwin

Figure 6: Building and running the test application in Cygwin

Ideas for Future Exploration

CF-Lite can always benefit from developer input. Since the Darwin implementation receives the most attention, you might consider working on the Linux, Cygwin, FreeBSD, or Win32 ports. Browse the source code or use the compiler warnings to help you determine what needs to be implemented or improved. For example, when compiling CF-Lite on Linux you will receive numerous warnings stating: CF spin locks not defined for this platform -- CF is not thread-safe. In CFInternal.h, spin locks are undefined for every non-Mach platform. CFRunLoop support also needs attention on non-Mach platforms. If you have the time and skills, consider working on these or other areas.

Providing Feedback to Apple

Bugs relating to CoreFoundation functionality on Mac OS X should be reported through Apple Bug Reporter.

Platform specific bugs (for platforms other than Mac OS X) should be reported to OpenDarwin Bugzilla. Specify the CoreFoundation component of the OpenDarwin product.

Changes can be submitted to OpenDarwin using bugzilla, and to Apple using the Modification Notice form. As always bugs, changes, and ideas for enhancements can be discussed on the darwin-developers mailing list.

For More Information

Using the techniques discussed in this article, you can modify, build, and deploy your own versions of Darwin projects. You may also submit modifications for inclusion in future releases. The best starting point is Apple Developer Connection's Darwin project page, which contains links to source code and documents, including FAQs. The links below will help you find more specific information.

Source Code

  • Apple's Darwin source site should serve as your primary reference for Darwin projects. Look for the project named "CF-nnn"; this is the latest version of CF-Lite.
  • OpenDarwin CVS is the place to obtain a work in progress version of the same projects. Here you can download the Darwin 7.0/Mac OS X 10.3 version of the CF-299 project, which served as the example for this article.

Tools

Documentation

Posted: 2005-4-11