Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2006-412
$eventId
ID of event: wwdc2006
$eventContentId
ID of session without event part: 412
$eventShortId
Shortened ID of event: wwdc06
$year
Year of session: 2006
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC06 • Session 412

Developing and Porting UNIX Applications on Mac OS X

OS Foundations • 46:04

Mac OS X supports a rich and evolving set of POSIX-based APIs that make it easy to write, port, and run UNIX software on the Macintosh. Discover Mac idioms and delve into best practices and APIs that will be of great value to anyone who wants to build on the UNIX-based foundations of Mac OS X.

Speaker: Kevin Van Vechten

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good afternoon, everyone, and welcome to the talk titled Developing and Porting UNIX Applications for Mac OS X. My name is Kevin Van Vechten, and I'm the BSD team manager here at Apple. And today we're going to talk a little bit about what's new in Leopard at the UNIX layer. And if you've heard in either the State of Mac OS X speech or some of the kernel speeches, we've been making a lot of changes about supporting UNIX 03, and we'll go into quite some detail about some of those changes and what you can expect when you're compiling your applications for Leopard.

So, like I just said, this is about developing on Mac OS X's UNIX layer, and if you're either starting new applications from scratch or porting existing applications from other UNIX systems, I'll talk about some of the things that are different and unique about Mac OS X that you should try to take advantage of on our platform.

We'll also cover the new features in Leopard. And to start that off, open source. So virtually all of the UNIX layer of Mac OS X is open source. It's available at the URL up here, developer.apple.com/opensource, and then that links to a bunch of different pages that categorize it by each release. And the majority of Apple's original sources are published under the Apple Public Source License 2.0, which is both an open source initiative-approved license as well as a free software foundation-approved license.

And there's lots of open source for Mac OS X. Since you are probably interested in running common UNIX applications, I just wanted to mention Fink and DarwinPorts, which are two projects which have thousands of open source packages available. And actually, DarwinPorts recently changed their name to MacPorts. So this URL is a little bit out of date, but it'll still direct you to the right place.

So on Mac OS X, there are many different layers to the system. At the bottom of this graph here is this big red area called Kernel. Well, we're not going to talk about the Kernel much today, but we will talk about the UNIX layer that's immediately above it. And then the things higher up on the stack, Core Foundation, Carbon, and Cocoa, again, are higher level than we are going to talk about today. But all of these things are available for your applications to use.

So, what's new in the UNIX layer on Mac OS X? Well, we have native DLopen and DLSim. Historically, Mac OS X didn't have these APIs. They were added in, and now in Leopard, that's actually the official API you should be using when you want to dynamically load a bundle or a library.

We also have native pull in the kernel. It's no longer emulated in terms of select. We have KQs, K events. Starting in Tiger, we had get and set extended attributes on files. Now we have a lot more 64-bit API. In Tiger, it was just lib system that was 64-bit, and Leopard, a lot of our open source libraries are fully 64-bit.

There's also more integration with LaunchD, which was introduced in Tiger. To the extent now in Leopard, there's not even a net CRC script, so there's a lot of things that are changing in startup. And like I said earlier, there's a lot of changes for UNIX03 behavior, both at the command line and at the API layer in lib system. There's also a lot new at the UNIX layer that isn't going to be covered today, but there have been other sessions and maybe still will be about things like toll-free bridging between scripting languages in Cocoa, Ruby on Rails, and we updated to Apache 2.0.

So to get started with Mac OS X, well, startup's quite a bit different than other UNIX systems. There are no System 5 run levels. There's not even an ETSI RC script. We do still have ETSI RC.local for some site-specific actions you can run at boot time. But we're really trying to move away from the scripted idea of startup and move to a much more dynamic on-demand way to start things, and there'll actually be a session in this room, the next session, on LaunchD, which would be very interesting if you're concerned with the boot up of the system or just managing your UNIX processes in general.

On Mac OS X, HFS, our file system, is case-insensitive but case-preserving. This is a big difference from most other UNIX systems. However, there is a case-sensitive variation called HFSX, which you can use to format a separate partition on your drive. Or you can even use it as your boot partition. In the disk utility in the installer, it's possible to select a case-sensitive file system.

We have multiple forks on HFS. So there's a resource fork, there's extended attributes. When you're copying files around, you need to take care to preserve those in your applications. And there's an API that will help you do that, which I'll talk about a little bit later. HFS, unfortunately, doesn't have any sparse file support, which is something that's commonly used on other UNIX systems. And also, starting in Leopard, the old UNIX file system has been deprecated. So if you need the case sensitivity, you really need to use HFSX now. It's not possible to use UFS anymore.

So on Mac OS X we organize things into the file system hierarchy that you've probably seen on other UNIX systems. We have /bin/dev/user, but only visible in the terminal application on the command line or when you SSH into the machine. These directories are not visible by default when you use the finder. So this is really kind of the UNIX support layer of the system, but we don't try to expose this to the users.

Kevin Van Vechten Another thing to point out is Apple doesn't ship anything in the user local directory. So if you're an administrator, you can use that directory for all the software you install in your site that's custom to your machines. And of course this is all documented in the hierarchy man page.

Mac OS X has a new concept, though, that's different from other UNIX systems, and that's a file system domains. And file system domains really represent a search path for resources on the system. There's the user domain, the local domain, the network domain, and the system domain. And there's an API for iterating over these domains in the NSSystemDirectories.h header file. And really, this is the order of preference where when the system's looking for a resource, it'll try each of these in order and see if it can find what the application's looking for.

So the system domain is really for Mac OS X system software. And effectively, you should treat it as being a read-only directory. Your application shouldn't be installing things into /system. That should be the domain of the OS installs from either a CD or a software update. But the notable exception to this, of course, is that kernel extensions, since they are extending the underlying system itself, do need to go into the /system domain.

The network domain is where all of the NFS or other network shares are presented, and it's a place where you can put site-wide configurations and applications so that they're accessible to all the machines at a given site. The local domain is a bunch of resources that are stored on a single computer that are visible to every user who's logged into that computer. This is where a lot of the power management settings for the machine go. And then finally there's the user domain, which is the user's home directory. And the home directory can contain all of the per user settings. And they are only available to the current user.

[Transcript missing]

So what is the structure of these individual resources that we do store in each of the domains? Well, we call them bundles. Bundles in the Finder appear as a single icon. They're really file system directories, though, that can contain many files. And they usually have an extension to indicate what type of resource the bundle is. A very common one is .app, which your applications will often have. .bundle is kind of a generic bundle. .plugin would be a code plugin. Dot framework is kind of a shared library, dynamic library.

The APIs for accessing bundles at the UNIX layer, well, there's really nothing that is specific toward bundles. You either need to use the POSIX open or read or write, close, standard file I/O. And there's also the File Traversal API, FTS, which you can use to enumerate what's inside of a bundle.

But up at a higher layer, Core Foundation and Cocoa, there are actually APIs for manipulating bundles in terms of URLs. And they can also help you find resources inside of a bundle. The generic structure of a bundle is that there's a contents directory, and then under that, there's a resources directory. And that's where things may be like graphics or sounds or movies or other resources that you want to aggregate with your application or with your library.

So frameworks are really dynamic libraries that are packaged up as a bundle. And this is a concept that's pretty unique to Mac OS X. A framework will have the dynamic library. It'll have header files that are associated with the library. So instead of looking in user include for header files, it'll actually be inside the framework. And there's some GCC extensions that accommodate this.

Inside your source code, you can do pound include and then the name of a framework slash the name of a header inside the framework. And actually, GCC knows where to find the frameworks on the system. And it'll pick that particular header out of the framework. So even though you won't find this path anywhere in user include or user local include, it'll find it in the framework path.

There's a couple flags you can use on GCC to accomplish this, the first of which is dash capital F, which is similar to the dash capital L or dash capital I flags in GCC on other systems. And those, of course, change the default search path for libraries and header files on the system.

Well, dash capital F really changes both of those concurrently so that the linker will find the dynamic library portion of the framework and the headers of the framework at compile and link time. And then to include a specific framework in your application, dash framework is the option you should pass. And this is really analogous to dash L on other systems for linking a dynamic library into your application.

So there are some notable frameworks on Mac OS X that provide a lot of the functionality at the basic layers of the system. Some of these are directory service, disk arbitration, I/O Kit, security, and system configuration. And I'll go into a little bit more detail on what each of these do and why you might want to use them in your applications.

Another big difference about Mac OS X is that we use the Maco file format. This is our executable file format. It's roughly analogous to ELF. I mean, they're semantically pretty similar. Or, sorry, syntactically pretty similar, but there are a few semantic differences that you should be aware of. One is Maco supports universal binaries, and universal binaries are one file with many architectures. So it would be possible to have a file that, say, has a PowerPC, PowerPC 64 architectures inside of the single file. Really, up to all four of these architectures could be in a single file.

And Mac OS also has the concept of bundles, which are distinct from libraries. And the idea behind a bundle is that a bundle is intended to be loaded by a particular application. It's really more of a plug-in than just a library that could be linked by anything. And what this allows is inside your bundle, the linker can resolve up calls into the application, since you know those symbols will be provided at runtime.

And so if you compile using GCC and specify -bundle and -bundle-loader in the path to the application that's responsible for loading the bundle, the linker can resolve these symbols at link time and enforce that, yes, these will be present at runtime. Whereas a library, since you don't know specifically who's going to be using your library, you aren't generally allowed to make up calls.

Mako also supports two-level namespaces. So in a lot of commercial applications, plugins are written by a variety of third parties. And you don't necessarily control the namespace of what symbols are used in each of the plugins. And you wouldn't want them to conflict with one another. Well, the two-level namespace means that each bundle gets its own sub-namespace and won't conflict with each other. So your third-party developers can have a lot more flexibility in implementing plugins for your application.

So going back to universal binaries for a moment, again, the idea behind universal binaries is you have the same set of sources that produce the same binary, but it can run on many different architectures. And this is accomplished by passing the -arch flag to GCC. So in this example, we're compiling something with four different architectures-- PowerPC, Intel, and 64-bit for each of those.

Now, sometimes your code does need to behave a little bit differently based on which architecture you're on. And the right way to deal with that is to look for the preprocessor macros. Usually, the difference is only going to be whether you're expecting memory to be laid out in a big-endian or little-endian format, so defining your C structures and whatnot. So you can test for the big-endian or little-endian preprocessor macros, and that will help you lay out your data.

Also, if you need to know the size of pointers, you can test for the LP64 preprocessor macro. And of course, R64. But the big-endian is 64-bit, which means that all of the types have the same size as they historically did, except longs and pointers are now 64 bits long.

So an int is still 32 bits, but a long is 64 bits. And then if you're doing anything with actual assembly code and you really do need to know specifically which architecture you're targeting, not just the general features of the architecture, you can test for one of these individual macros, whether it's PPC or PPC64, R386 or X8664.

So another important hint is to always use GCC as your linker driver. When you want to create a library or an executable, really you should let GCC go ahead and create that final result and not rely on invoking LD directly. For one reason, there's two different linkers. There's LD and LD64, and you don't want to have to manage which one's the right one to use and which circumstances. And so basically, it can lead to incorrect results if you try to do the linking manually for universal binaries. And we really recommend using the GCC linker driver.

So the dynamic linker on Mac OS X is known as DYLD. And as I mentioned before, DLopen is the native API to use for dynamically loading extra bundles into your application. DLopen compatibility was introduced in Panther, which became native in Tiger, and now in Leopard really is the preferred solution. So the other API for loading in Maco bundles is deprecated, and you should be using DLopen.

It's also possible to get DLopen functionality on 10.2 using the DLopen, I'm sorry, the DLcompat library. So if your application needs to work all the way back on older systems, you're going to have to investigate a way to use this compatibility library. And there's some details about that in technical Q&A article QA 1180. So I'm going to dive down into the Mac OS X API now and talk a little bit about those frameworks that I highlighted earlier and why you might want to use them in your applications.

So there's the main library on the system, userlib/libsystem. And that contains basically all of the UNIX and POSIX APIs that you'd find on other systems. It's a single library. We have symlinks and userlib so that you don't have to change all of your make files in most cases. So if you're used to link against libm or libdl or libpthread, well, those symlinks will all resolve into libsystem. But really, on Mac OS X, all of those libraries are concatenated into one big library.

Directory service is a framework. It's unique to Mac OS X. And it also works in combination with some of our info APIs, getAddrInfo, getPwInt, getGrInt. So when you're looking up user group or host information on Mac OS X, it actually communicates behind the scenes with the Directory Service Framework to get the information from a variety of sources, whether that's locally on the machine, from LDAP, from Active Directory, or from Bonjour, which means that while on other systems, a lot of times it's necessary to maybe establish a specific connection to LDAP to get information out of the LDAP server. On Mac OS X, that's all handled transparently. If you do a standard group lookup or user lookup, it'll grab the information from LDAP if the machine's configured to use LDAP.

Another big change in Leopard is that Directory Service subsumed LookupD. So in past releases of Mac OS X, LookupD was a daemon that handled caching all of this information. But now that's been consolidated into Directory Service, and that one daemon handles all of the user group and domain lookups on the system.

The transition away from NetInfo will actually be complete in Leopard. We've been moving that direction over the past several releases, but the big push is in Leopard, and we do not expect to have NetInfo shipping when Leopard's final. This means that NetInfoD, NICL, and iUtil, the two utilities you use for manipulating NetInfo, won't be found on Leopard.

And so you need to migrate all of the scripts and application usage of these commands over to use a new command called DSCL. Now, DSCL has been present on the system, I believe, ever since 10.3. So you do have a backward compatibility. You know, you can switch over to this and your application will still run on older systems. But again, NICL and iUtil won't be around when Leopard ships.

There's also some alternative graphical methods for changing the user and group information on the system. You need to enable the root user, if that's something you want to do, in the directory utility application, which is found in the utilities folder. Or, now you can control-click the list of users in the accounts preference panel. And by control-clicking a user, it'll bring up an advanced panel where you can set different attributes like the UID of the user or the default group of the user. So the NetInfo Manager, as you may have noticed, is no longer present in the Leopard Seed.

Disk arbitration is a framework which manages the appearance and disappearance of disks on the system, as well as the mounting and unmounting of the logical volumes on those disks. This is important, of course, if you're going to be dealing with external media. And also, disk arbitration can notify your application via callbacks when these events occur. And we really do encourage a callback model, not a polling model, for applications. So you should be using these frameworks to register for the events, and then you'll be notified when something of interest happens to your application.

I/O Kit is our framework for dealing with low-level device drivers. It's really a more complete view into the hardware of the system than you can get from SlashDev. We do have a SlashDev, which has a lot of the common devices you would see on other UNIX systems, but it's a subset of the hardware that's available, and I don't believe there's any way to get to some hardware through DashDev alone. You really need to use I/O Kit to get a complete view. And once again, I/O Kit does offer callbacks, so you can register for notification when devices appear and disappear on the system and deal with that interactively.

The Security Framework is API for managing authorization credentials, whether that's passwords to your mail server, to a website, or anything else you might need to store a password or a private key for. The Security Framework does encrypt this information in the key chain, which means it's a safe place to store your sensitive information.

And it also has another half to it, which is the Authorization API, which helps you decide which users are authorized to perform which tasks on the system. And it's a rights-based system. It's something that will prompt the user for a password if the user isn't currently authenticated with the system.

And it also allows for administrator control. It's possible to override the default settings as to which users are allowed to perform which rights. So instead of making your application, say, a set UID executable or kind of hard-coding privilege based on the UID or the GID of the application, we'd really encourage you to use the security API. Specifically, the authorization API they're in. And go for a rights-based system that will allow finer-grained control of what users are allowed to perform which activities on the system.

The System Configuration Framework is an API that lets you read and set different machine-specific configuration settings, whether that's power management settings for the machine, or also we have network settings that are affected by this. One of the big APIs we have is the Reachability API, and this is a high-level mechanism for you to determine whether a certain network address is routable or not.

It doesn't actually go out and attempt to connect to the address, but it makes a best guess as to whether it's even worth trying to connect to that machine. So it can see, for example, that, oh, there aren't any Ethernet interfaces currently configured, or there's no wireless available.

But when one of these comes online, you can register for a callback again and get notifications of network state changes to decide whether you should re-evaluate your network connections, see if there's any connections that no longer make sense to maintain, or whether you should perhaps, you know, try to re-initiate a connection you previously had because the network's come back online.

And this is a key part of mobility-aware applications. As I'm sure you know, a very large percentage of the hardware that Apple sells is portable, and portables tend to change their network environment very frequently. And so it's really great when applications can take advantage of this to be more adaptive of changes in the network.

Another API we offer on Mac OS X is the Notify API. And this is a simple, lightweight notification mechanism that's really analogous to UNIX signals. It has multiple delivery mechanisms in your code. You can either select on a file descriptor, and when that file descriptor becomes readable, that means you have a notification pending. You can also receive notifications via Mach ports, so it can tie into your run loop. UNIX signals can be used to deliver notifications, as well as you can just do synchronous polling, though of course we discourage you to do that.

But the notification mechanism, basically it's a named string. That's the notification. And if multiple notifications are received, they all get consolidated into one event that's raised, the same way that a UNIX signal would behave. But there's a much larger namespace with notifications than there are with UNIX signals. So this is why it's our preferred mechanism for sending information between processes. So instead of sending a SIGTERM or a SIG-- well, I guess a SIGHUP is what I meant to say.

Instead of sending a SIGHUP to an application to let it know to reload its configuration files, we'd really rather send a specific notification that meant reload your configuration files, instead of overloading the terminology of what a SIGHUP means. So you can see information about Notify in the notify.h header file in user include.

Another new API which we've made public for Leopard is Copyfile. And Copyfile is an API that lets you deal with extended attributes. It will automatically create Apple double files. Apple double files, I'm sure you've seen, are the dot underbar files that contain resource fork and extended attribute information on file systems which do not support extended attributes natively.

So, for example, if you have a sample JPEG that you've written an EA to and copy it over to, say, an NFS volume that's backed by something that doesn't support extended attributes, you'll see a dot underbar sample dot JPEG. Well, Copyfile takes care of all the details of splitting the single file into two different files.

And it'll also take care of all the details of taking those two files and reconsolidating them into a single file when you copy back to a file system like HFS. And this is what we use by CP, MV, and HFS. And you should look in user include copyfile.h for details on using this API.

Another thing I wanted to mention is for a while now we've supported X11 on Mac OS X. Probably the most important thing to keep in mind is that this is an optional install. Not all Mac OS X installations will have X11, so it's something you really should be testing for in your application so you can gracefully handle whether or not X11 is present.

Now I'm going to talk a little bit about Xcode, which is our developer tools. Xcode is also an optional install. So not every Mac OS X system out there has a C compiler, which is quite a bit different than most other UNIXes. Usually you can assume that there will be a C compiler on the system, but on Mac OS X this isn't necessarily the case. Xcode is based largely on the GNU tool chain. There's a lot of common utilities that you'll see elsewhere-- GCC, GDB, Make, GLIB Tool, AutoConf, Automake. And there's a very large list, and it's fairly standard stuff.

But there are some differences about how we develop our software and how we'd like you to develop software on Mac OS X. The main difference is SDKs. SDKs are a collection of headers and libraries for a particular release, and they're all bundled up in an SDK directory. And you can have several SDKs installed on the system.

And by selecting a particular SDK to compile against, you're indicating which features of the operating system the software should be using. So for example, you may be running on a Leopard system, but you want your software to work on Tiger. Well, you'd need to use the Mac OS 10.4 SDK to do that.

SDKs are also necessary for universal binaries. I'm sure you've seen a lot of literature on universal binaries. Well, most PowerPC systems out there, they don't have any Intel architectures in the dynamic libraries, so linking will fail unless you're actually using the SDK to compile your application. SDKs are accomplished by some GCC and Linker extensions.

The first extension is --i sysroot, which sets the SDK root, and basically whenever a header file is looked up or a dynamic library is looked for, this path is prepended to the paths that are opened, and so you're really limiting your search to the Mac OS SDK that was chosen. And there's also a similar Linker flag, the --syslibroot flag, which again tells the Linker that it should only be looking for libraries inside of this SDK.

Something very closely related to SDKs is deployment targets. And deployment targets let you declare what your target environment is. So for example, you can declare that the minimum version of Mac OS X that you would like this application to run on is, say, Mac OS 10.4. And this will provide some temporal scoping of the symbols in the header files. So functions which have not yet been declared in 10.4-- maybe they're new in 10.5-- they won't appear in the header files when you specify this flag.

On the other hand, if something that was used in 10.2 and was used in 10.3 and has now been deprecated in 10.4, since you're setting your minimum version to a system that has the deprecated symbols, it'll actually give you a warning and say, you really shouldn't be using these symbols anymore. You should use this new API that we have. Those are really legacy interfaces.

And this used to be done by setting the Mac OS X deployment target environment variable, but now you should be using this -m macosx-version-min GCC argument. But they both really have the same net result. And you can look in the availability macros header file to see what all the different preprocessor macros are for warning you about deprecated interfaces or scoping interfaces that haven't been defined yet in that particular version of the system.

So GCC has also changed in Tiger and in Leopard now. GCC 4 is used more predominantly. And GCC 4 has support for 128-bit long doubles. In earlier versions of GCC, long doubles were only 64-bit. And since a lot of standard I/O and other lib system interfaces used long doubles, we had to pull some tricks to maintain compatibility with older applications, which were compiled with 64-bit long double support and new applications compiled with GCC 4 that have 128-bit long double support.

And the system and compiler decides which of these to choose based on that Mac OS X deployment target that I was talking about a moment ago. So if your deployment target is greater than 10.4 and you're using GCC4, then functions with long double arguments will get suffixed with $LDBL128 behind the scenes.

You don't need to do it in your source code, but it happens. And if you were to do a dump of all the symbols in your executable, you'd actually see this. Now what this means is applications compiled in this way won't work on earlier versions of Mac OS X. Like, you wouldn't really be able to run this, say, on Jaguar.

However, when the deployment target is set to below 10.4 on applications compiled with GCC4, then it's set to $LONG-DOUBLE-STUB, and that causes some magic to get invoked, and this actually gives you compatibility back toward Mac OS 10.3.9, but I don't think it'll work on any earlier systems. So really, if you're compiling using GCC4, you should be targeting 10.3.9 or later.

And also, in this case, the GCC linker driver will implicitly link in the system stub's static archive, which has the magic for that LONG-DOUBLE-STUB, which is another reason why you should be using the GCC linker drivers, because a lot of times the GCC linker driver has some smarts like this in it that wouldn't be immediately obvious if you were to try to link object files together yourself. So, I'd like to take the rest of the time of the talk to talk about what we've changed to support UNIX 03 in the UNIX layer of Mac OS X.

We are more standards compliant than we had been in previous releases, which hopefully will make it easier for you to port UNIX software to Mac OS X. However, this did have some impact on source and binary compatibility, and I'll talk about how we addressed this impact and what you need to look out for in your applications. First, a brief discussion of the commands. Some of the command line tools have changed their arguments and how they behave based on what's required by the UNIX03 specification.

UNIX03 behavior is the default in Leopard, but the prior behavior was kept when it didn't conflict. So a great example of this is the PS command. The UNIX03 standard only dictates what happens when PS is invoked with a dash before the rest of its arguments. So in that case, if you do, say, -u, that has the UNIX03 behavior, whereas if you were relying on the old BSD behavior before, if you omit the dash, it continues to do the BSD behavior if you just were to do, say, PSu.

You can also disable the UNIX03 behavior by setting a command mode environment variable. The command mode is unset, but the default is equivalent to being set to UNIX 2003. However, if you were to set the command mode to legacy, then it will hopefully disable all of the new changes in the command line tools, and the command line tools will continue to parse their arguments like they did on Tiger and on earlier versions of the system.

For source compatibility, there's a global flag, this Darwin UNIX03 preprocessor macro. And this flag is on by default for Leopard. So if you're compiling your software for Leopard, you will get the interfaces that are UNIX03 compliant. I should point out that the 64-bit libraries on Leopard have this flag on implicitly by default.

There is no legacy non-UNIX03

[Transcript missing]

However, in order to maintain compatibility for your applications that you might be building on Leopard but want to deploy on Tiger or Panther, we will have this off by default if you're either using, say, the 10.4 SDK or the 10.3 SDK.

Or if you've set the Mac OS X deployment target to one of those earlier versions, it will disable the Darwin UNIX 03 macro, and you'll get the legacy behavior. And if you're looking for what this macro actually does and where it's defined and tested for, that's in the sys.cdefs.h header file.

So it's also important to notice that we're doing the same type of trick. We're doing the $UNIX2003 at the end of a symbol, which is put there by a preprocessor macro in the header files. What this means is implicit declarations of functions do not get this suffix. And if you are relying on an implicit declaration of a function, you might end up using the legacy version of the function unintentionally and mix and match legacy and non-legacy functions in your code. So it's very important that you do include all the header files that are needed to declare the various APIs that you use. And the best way to accomplish this is to warn on missing prototypes.

So there's some GCC commands that I've listed, "-w all "-w missing prototypes". And if you use those when compiling this example, it will warn you that there is an implicit declaration of some commands. And say, of some functions, putenv and getenv. And the right thing to do is include the header file which these are found in. I believe in this case, that would be the unistd.h header file. And then you will get the right suffixing through the preprocessor logic.

So it's also possible to opt out of Darwin UNIX 03. When you're compiling on Leopard, so you have a 10.5 deployment target, and you also define this non-standard source macro, it'll actually opt you out of the UNIX 03 behavior. However, non-standard source is not available on 64-bit, because like I said, 64-bit has been UNIX 03 since the beginning, so there's no way to disable that behavior. That's the only behavior that's available.

As far as binary compatibility is concerned, all applications compiled before Leopard should remain unaffected because they don't have any of these new symbols that are being used by them. All the functions that had changed in behavior were suffixed with $UNIX2003 internally. And what this means is that an application compiled on Leopard that's run on Tiger might be using some of these Darwin UNIX2003 symbols, which aren't defined on earlier versions of Mac OS X. So it's not likely to run.

In this example here, I'm trying to run an application that was compiled on Leopard back on a Panther machine, and you'll see there's a reference to an undefined symbol. In this case, it couldn't find the UNIX2003 version of the set env variable. So this really makes it important to be accurate in which Mac OS X deployment target and which SDKs you use. If you want your software to run on earlier versions of Mac OS X, you really need to be explicit about declaring that during compile time.

So what are these changes that we've made? Why did we have to suffix the functions? Well, one of the main changes is that the error nodes change. Things that historically succeeded might now start returning errors, because the UNIX03 standard is a little bit more strict about what arguments different functions can accept. And you'll see some of the stricter argument validation in functions like mmap and select, sigpause, setRlimit, and unsetenv.

It's also important to note that the eop_not_sup and e_not_sup erinos are now distinct values. So eop_not_sup really pertains to socket operations, whereas e_not_sup is the more generic, not supported erino. In the past, we had those defined to the same numeric value. Now they're two distinct values. And so most often, you will need to change any occurrences of eop_not_sup to e_not_sup in order to test for that error condition.

Some of the other changes include that fputs now returns the number of characters successfully written on success. In the past, it had only returned zero. Now it returns what characters were actually written, which means sometimes code that was testing for zero will think that the call failed when, in fact, it really did succeed. However, in both the legacy and the new behavior, they'll return negative one on error. So really, the correct thing to do is to test for negative one as the result.

Another big change is that open will now change the controlling terminal of a process if you open a device which is a terminal device. And you can disable this by passing the additional O underbar no CTTY flag to open to say no, do not change the controlling terminal when opening a terminal device.

Another change we noticed that affected a lot of programs was that putenv no longer copies its input buffer. This means that when you pass a pointer to putenv, it just puts that pointer directly in the environment. And if you ever subsequently free or change that string, it's going to change it in the environment, or it'll make the pointer invalid if you free it. This is actually consistent with the behavior that's found on Linux. So if you're porting Linux applications to Mac OS X, you shouldn't run into any problems here, because that's how that's behaved.

But BSD systems historically made a copy of the input buffer. So you're going to need to either use setenv if that's the behavior you want, or make sure to manage your buffers differently. unsetenv changed as well. It no longer parses the input string looking for an embedded equals sign. So if you have an equals sign in your string now, it actually interprets that as literally part of the environment variable name, and will look for that environment variable.

The M-map changed. The flags parameter now must specify either map private or map shared. Previously, we allowed those to be omitted, but you really must decide between one or the other and make sure that flag is specified. The size of the region you're M-mapping must not be 0, and the offset needs to be a multiple of the page size of the system. And luckily, there's an easy call you can make, the sysconf with sc page size as a constant will return the page size. You need to make sure your offsets are a multiple of that.

SigPause changed. SigPause now takes a signal value, not a signal mask. And this is a little bit of a tricky one, because sometimes you won't notice a problem until runtime, and sometimes not even then, depending on what values you're passing in. But if you want to work with masks, you really should be using the SigSuspend API, not SigPause. Select now limits the number of file descriptors that you can be waiting for. They must not be greater than the FD set size.

So you either need to use a smaller number of file descriptors when passing the, you know, sorry. You need to use a smaller number of file descriptors on the first argument of SELECT, or you can define the Darwin Unlimited SELECT preprocessor macro, which will allow the historic SELECT behavior.

Set RLimit no longer accepts some of the values that it historically did. For example, the number of files can no longer be set to RLim infinity. You need to now set it to a specific value. We'd recommend the minimum value of either the OpenMax constant or the RLimMax value that you currently have.

Set P group no longer takes any arguments, so you should use set PG ID instead. And this is, again, a thing that we would catch very easily if you are including the proper header file. You'll see the prototypes mismatch. But if you're relying on an implicit declaration, the compiler probably won't warn you about this change. And it would probably go unnoticed because there would be some extra arguments for the function, but it wouldn't pay attention to them and then, of course, wouldn't behave as you would expect it to.

There are some structure changes to data structures. Most notably, the ucontext structure is now opaque. So you should use the ucontext_t structure, which is defined in sys/ucontext.h. And the IPC perm structure has some members which were renamed. So if you see compile time errors about fields not existing in a structure, it's likely related to one of these two changes. There are also some namespace conflicts. We've changed things like port T and port NULL to be prefixed with Mach. So if you're working with Mach ports, you're going to want to call it Mach port T or Mach port NULL as your constant.

Now we've documented these changes and we're continuing to enhance our documentation, so I encourage you if you've run into any sort of compile time errors or runtime errors that you weren't seeing before to check out the man page for that function. And in all cases we want to have both the prototype of the new UNIX 03 conforming function and the legacy function that you would find on historic versions of Mac OS X in the man page. So you can compare, see what's changed, see what you need to do in your program to adapt to that.

Again, if you're getting error no's that you didn't use to get, please check the man page, see if there's an additional restriction on some of the arguments that you're passing into the function. And when the changes were relatively significant, we're going to add in legacy sections to the man page to discuss in depth what the changes were.

So for more information, you can always contact Ernie, our product marketing contact. And we also have a lot of notes that are available linked to this session on the ADC website, so you can download some information there that details some of the changes that I've described in this session. And we'll be keeping these notes up to date as we run across more issues in the development of Leopard.