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: wwdc2007-311
$eventId
ID of event: wwdc2007
$eventContentId
ID of session without event part: 311
$eventShortId
Shortened ID of event: wwdc07
$year
Year of session: 2007
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC07 • Session 311

Unleashing the Power of the Xcode Build System

Developer Tools • 1:09:12

Building Xcode projects just got faster with Xcode 3.0. Whether working on a laptop or taking advantage of a server farm, you can get significantly better build times by fine-tuning your Xcode projects. Learn the most efficient way to create 64-bit applications, how to set up targets to build in parallel on multi-core Macs, and how to use composite SDKs for Xcode libraries and frameworks.

Speakers: Eric Espinosa, Rick Ballard

Unlisted on Apple Developer site

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good afternoon everybody. I'm Chris Espinosa of the Xcode team and I want to welcome you to Unleashing the Power of the Xcode Build System. We have a good session for you this afternoon. We're going to take you on a tour of the dark under belly of the Xcode Build System. You'll learn a couple of important things. One is the new features in the Xcode 3.0 Build System that will allow you to build faster and add more customization to your projects. And two is we're going to show you how to tune customized projects to build faster.

I'm going to give you tips on building for multiple architectures, how and when to use the new feature called Parallel Target Builds. How to tune your builds by managing your precompiled headers so you build fewer and better precompiled headers. How to use some of the new build settings in order to embed frameworks in your applications or to use an embedded framework for multiple applications. How to create and use your own SDKs and integrate them with Apple's SDKs so you can build against multiple SDKs at the same time.

How to build the same project in an Xcode 2.5 and 3.0 even on the same machine. And also how to use Tiger's gnu tools when you're running on a Leopard system. It's a lot to cover. It's going to be very detailed And so I hope you take a lot of notes.

But before I can explain any of that, I have to get on some common ground with some terminology and give you an overview of the main areas of the build system we're going to be talking about today. An Xcode project is a set of targets. And a target builds a build product. And when I'm talking about the build system, I'm talking about taking a target and building its, taking a project and building its targets in sequence to create build prod, build products.

Now each target is a clump. And it's a clump of inputs. Source files, nib files, build settings, all sorts of things. In order for Xcode to have a methodical way to build a target into a build product, it has to organize this clump of inputs and it organizes them into build phases. I'm going to be talking really about only one build phase today, the most important one which is compile source.

There are a lot of other build phases that copy your resources or that run shell scripts or things like that. But the main one is compiling and linking the source files into executables that go into your build product. The most important thing about build, build phases is that they are the things that build settings effect. When you change build settings in your project or your targets, the build phases are the ones that take those settings and put them to work.

The build settings inherit, they inherit from the project to the target. So you can set build settings at your project level that will affect all of the targets in your project. And so those settings that you set at your project level, you'll really only want to set the ones that are generally applicable to everything you're building such as maybe the major version number or, or some setting that you want to use globally such as optimization.

What optimization you want to use or the architectures you want to build for. Things that are specific to a target like what kind of target this is, what's its name, where does it go, what specific build flags do I want to build it with. You should set those at the target level.

Build settings then are used by the target and applied to each build phase to build, to produce an output from that build phase. Okay? Now the build settings, you'll see them in the build setting inspector. Usually you'll just see a column of names and values. If you command click on the build settings inspector, you'll, you'll be able to control click on it, you'll be able to see two other things. You'll be able to see the build settings definition which is the way it comes up with the value.

And you'll be able to see the build settings identifier that looks kind of like an environment variable, okay? So we have kind of two different ways of viewing the build setting. You can view is as an English name and a string constant or you could view it as something that looks like an environment variable with what looks like a calculation behind it kind of like an Excel spreadsheet.

Build settings have definitions that evaluate to values. They can be strings, they can be lists of identifiers, they can be values, they can be Booleans. Okay? And there are three basic groups of build settings. The first kind are the predefined build settings. These are the ones that we define in the Xcode build setting for you to use generally.

We take the UNIX shell's environment variables and propagate them through so you can use them like user and home. We take things that the build systems invoked with like action, just like in a Makefile. But we also set constants in the, in the build settings. So for example, if you were building the, the debug configuration, of your product or tar, your project or target, the configuration build setting will be set to the value debug, okay? The second kind of build settings are the definable build settings.

These are build settings that you can change in the build settings inspector to make your project or target build the way you want it to. Sometimes these have default values. Sometimes these values are set by the template you created the project or target from, sometimes they're just blank. These are things like the product name, things like the header search paths. Things like the optimization level or things like the architectures you are choosing to build for. This is going to come up most importantly in a couple of slides.

And then finally, you can create your own build settings that look and work exactly like Xcode's predefined build settings or definable build settings. And you can use these to pass values to your compiled code or you can use them to change the way other build settings are defined. You can define a build setting in terms of another build setting.

You can even link build settings together by saying you know this Boolean, the value of this Boolean build setting is equal to the value of that Boolean build setting. So when you set one, the other is automatically changed. So when you're building for 64-bit, the build setting that's most important is the architectures build setting. Now you've heard us talk a lot the last couple of days about building 64-bit code and building 64-bit applicatdions.

When you build 64-bit, it's thde same process, but a different set of steps than building for a single processor architecture. Back in the day when we only had PowerPC, when you built your Xcode project or your project builder project back then, only one thing happened. The, you took your set of source files, you ran them through the PowerPC compiler, they created a PowerPC binary and it was copied into your application bundle or your executable. That's because the value of the architecture's build settings arch was a single value ppc.

Well, nowadays, same sources, same kind of project, but you take the source files and you build them four times. Take every source file and you build it for PowerPC, every source file, build it for Intel, every source file, build it for PowerPC 64, every source file, build it for X86_64.

Why does it do that? Because you've set the value of architecture's build setting to a list of ppc, ppc64, i386, X86_64, and the compile sources build phase simple iterates over that list, compiles every source file for every architecture, links together for each architecture and does this thing called lipo, I didn't come up with the name. To put all of the executables together in one binary.

Now the story is simple. How do you build for 64-bit? Well, Bertrand Serlet or Steve Jobs will tell you just check this checkbox that says 64-bit right? And then you build and then you get errors and warnings and you fix those and you repeat until the errors and warnings are gone and then you debug it until it works. So that's the theory.

In practice, you want to do something so that building for 64-bit doesn't have a severe effect on your workflow. The severe effect on the workflow is that if you compile every file four times, it's going to take substantially longer than compiling every file once. And three of those compilations are going to have no relevance to what you're trying to do because they won't run on your machine.

So when you're building for 64-bit, it's important to set the value for the architecture's build setting differently on the desktop when you're debugging than for building and deploying to people testing or using your product. And we built some easy ways to do that and to manage the side effects of that in to Xcode 3.0.

Here's what's behind the checkboxes. If the value of the architectures build settings starts out as a list of architecture identifiers, like ppc or x86_64, the checkboxes have a certain set of meanings. Notice that we don't have individual checkboxes for individual architectures. We are strongly implying, in case you don't get this message, that you should not build for a single chip set.

You should build universal, universal 32-bit, universal 64-bit, universal 4-way, but still there are very, very few times when you should build only Intel or only PowerPC. You can of course just manually edit the architecture's build setting as a text string and do whatever you want, but when you're using the checkbox interface, it really implies, you better be universal. One way or the other. So the checkbox in 32-bit means ppc i386.

The checkbox in 64-bit means ppc64 x86_64 and both checkboxes mean all four architectures. Very simple. Now if you've manually edited the field and you have a mixed set, that is you are building it then, then you'll get a dash. So when you see a dash in that architectures box, it means that you're building thin not fat for that bit width, 32-bit, 64-bit.

So this is really good if you're building the release configuration or the deployment configuration or something that's going to go outside your group. Because you want to build as many architectures as possible, as many architectures as your code is written for, to give to other people so your application can run everywhere.

But you don't want to do that on your local machine. You don't necessarily want to build all four architectures every time you make a simple bug fix. You just want to build for the architecture you're using now. We've provided some pre-defined build settings that will really help you do that. Now one that has existed all along is NATIVE_ARCH.

NATIVE_ARCH is the native chip set of the machine and typically it's been just ppc or i386. Even when we started introducing 64-bit machines, since we didn't have a full 64-bit model, d it stayed the 32-bit equivalen2t of that 64-bit model. You really shouldn't use NATIVE_ARCH, it could be confusing.

We've introduced a new pre-defined build setting called NATIVE_ARCH_32_BIT, which has exactly the same value and means exactly the same thing, but is a little clearer. NATIVE_ARCH_32_BIT is a pre-defined build setting that evaluates to ppc on a G3 or G4 or G5 and i386 on of course a lower core duo. Very easy.

NATIVE_ARCH_64_bit is the 64-bit equivalent of that. So regardless of what machine you're on, NATIVE_ ARCH_64_BIT will be ppc64 or x86_64. It'll be the 64-bit version of that. So if you want to build something that's going to build fat, but just for the chip set you're on, you set your architectures to NATIVE_ARCH_32_BIT, NATIVE_ARCH_64_BIT and on PodwerPC machine, that'll evaluate to ppc i386, ppc ppc64 and on an Intel machine they'll get i386 x86_64. So you'll be able to single processor, multiple-bit widths.

But if you really want to have a project that's going to build with the best bit width on the machine I'm running on and only build once, the build set you want is called NATIVE_ARCH_ACTUAL. NATIVE_ARCH_ACTUAL evaluates the actual chip set of the actual machine of the actual process of the actual bit width you're running on. ppc or 64 or i386 or x86_64.

Now when you set the architecture's build setting to one of these pre-defined values or a list of these pre-defined values, you still get check boxes, but the behavior of the checkboxes are different. When you check and uncheck the boxes, it will change them from set of pre-defined values to another set of pre-defined values, it won't take you back to the set of constants. And they mean different things.

And when you have the checkboxes set with the architecture's values with pre-defined values, you will always build one kind of thin. It's really optimized for debugging. So you really only want to do this in your debug configuration. 32-bit check, when 32-bit is checked, that means the value is native arch or NATIVE_ARCH_32_BIT. When 64-bit is checked, it means that it's NATIVE_ARCH_64_BIT and when both are checked, that means its NATIVE_ARCH_ACTUAL. So on your debug configuration you check both checkboxes and you should build for the best architecture thin on any machine you're building on.

This is great if you're sharing the project among multiple people with multiple different machines. This way you don't have to go in and change the architectures every time you open up the project, in order to build for you machine, if the guy on the next desk has a G5 and the woman in the next cubicle is working on a core solo, all three of you can open the same project, you'll build best for your machine, no changes to your project file, no contention or conflict in your SEM system when you check it back in.

Now when you have these build settings set, we give you some other aids in the user interface in order to use the different architectures that you're building for if you happen to be building a release configuration. Say you want to see both the PowerPC and the Intel assembly code generated by a certain source file side by side. Well you couldn't do that before, but in Xcode 3.0, we have something on the next slide to do that. On this slide, we have pre-architecture build settings.

When you have a configuration that has multiple architectures, you can also change any compilation related build setting to be different for each architecture and you do it with the little gear menu at the bottom of the build setting page. You select a build setting, you pull down the menu and you can add a per-architecture setting for any architecture to customize any build setting to have different values for each architecture. You can do this for SDKs.

For example, you may want to use the 10.5 SDK if you're building for a 64-bit, but still use the 10.4 SDK if you're building for 32-bit just because you want to restrict yourselves so those to those APIs so you can run back on Tiger. You may want to set the deployment target, you may want to set optimization flags, you may want to set anything compilation related you can customize per-architecture in the user interface.

And then as I said, if you have a configuration that has multiple architectures configured and you want to see the effects of one of those architectures, you can use the Set Active Architecture menu item in the project menu and there's also an optional tool bar item that you can just put in your tool bar.

To pick a single architecture for the operations of compile, pre-process, show assembly code and fix and continue. So if you happen to be, if you happen to have a source file and you want to see how it looks both in Intel assembly code and in PowerPC assembly code, you can Set Active Architecture and show assembly code and you'll see one and then Set Active Architecture to another architecture, show assembly code again and you can look at the assembly codes side by side without having to go and edit your build settings and that's going to be really handy.

At the command line, you can do a similar thing. If you're building using Xcode build command line and you want to force a particular architecture, you just put ARCHS equals and then the architecture list after that and build settings set at the command line, trump all build settings in the project and the target and force the Xcode build system to use the value you set there so if you just want to build one thin architecture or if you want to build an alternate architecture to what's already configured, you can do that at command line just by forcing the build setting.

When you're using per-architecture build settings as I showed a couple slides ago with the user interface, if you happen to be using xcconfig files that allow you to specify build settings in a text file in your project, you can use this special bracket and arch equals notation to specify per-architecture build settings in your xcconfig file.

So if you're sharing a configuration file among multiple targets which we highly recommend, it's a great way to have to, to be able to make global changes without having to go through and open a lot of inspectors, you can use the bracket arch equals value close bracket notation to set per-architecture build settings in your xcconfig files.

And then if you're using custom build settings, if you have something that's being passed through to say a shell script build phase or you're expanding it into a preprocessor macro, you can do that on a per-architecture basis simply by using the calculated value, which is you know, your flag underscore and then CURRENT_ARCH.

Each time Xcode goes to that compilation group, it'll set current arch to a different value and then it'll evaluate the value of debug ppc, debug i386, separately and then it'll take that value and pass it through to your build, to your shell script build phase or in your preprocessor macro.

When you build with multiple architectures, one important thing will happen, is that your builds will take longer. You experienced this going universal, you'll experience it again going 64-bit. It's more and more important to improve your build speed when you're building for full 4-way PowerPC X86 32-bit, 64-bit. I'm going to cover four important ways to improve your build speed here today. One is using all available processors. This basically comes, well I'm not going to say free. This basically comes for the small cost of buying an 8-way Macintosh.

( laughter )

Comes for free in the software.

The second, which is most important, is to make the best use of your precompiled headers. The more you build the more precompiled headers you're generating and the faster and fewer you generate precompiled headers, the faster your build will go. Third is using the new multiple targets feature, parallel targets feature in order to build multiple targets in parallel and fourth is to use our distributed build options So as I said, building for multiple processors.

A lot of this was covered in Bertrand Serlet's State of the Union for Mac OS X. In Tiger and Leopard, we try whenever possible to dispatch jobs to multiple processors and in Xcode we do this primarily with the compile sources build phase which is the one where you're doing the most work. It's the one where you're doing multiple architectures.

When you're compiling your sources, we will use all processors available in order to do the compilation jobs. If you are on a four processor or more machine, we'll reserve one processor to handle the UI tasks and everything else going on in your system, so that Xcode remains responsive. You can get your mail, you can do Spotlight searches, we'll save you one processor for that. But if you've got an 8-way machine, seven GCC jobs are going to be going in parallel and that will really help speed up your build.

Now in the Compile Sources phase is parallel, but the other steps are choke points. All the prerequisites to compilation are going to be choke points. They have to be done serially before we can fan out and do the compile job and one of those choke points is pre-compiling the headers because the precompiled header is a pre-requisite to doing any compilation whatsoever. And so it follows that if you have to precompile a header, you're slowing down the whole machine. So try not to.

Now precompiled headers are interesting because they are very sensitive to configuration. You're going to need a separate precompiled header for each language dialect that you're using, C, C++, Objective-C, because the compiler interprets the headers differently for each language. You're going to need one for each architecture you build. If you build four architectures, you're going to need four precompiled headers.

You may need one for each target because for each prefix file by name, we generate a different precompiled header and you're going to need one for each variant you use. If you do different compile flags that affect the way that the headers are interpreted and preprocessed, they'll generate a separate precompiled header in those cases. It's easy to see how one little target could generate 8, 12, 16, precompiled headers, each of which are 32 megabytes in size.

That's a lot of data moving back and forth. That can make your build sub-optimal. The most important things you can do are reduce the number of precompiled headers. First use the same prefix file over and over again if you can. Instead of having a different named prefix file that just pound includes Cocoa, Cocoa.h or Carbon, Carbon.h, use the same prefix file. If you use the same name, you will cut the number of precompiled headers in half if you follow all the other advice.

But the second is to reduce the number of different variants you have to produce because of compiler flags you use and it's really hard to tell how to, why you're having on precompiled header on your system and then you go and build and another precompiled header is generated.. What's that all about? Well the secret is in, the secret is in this large string.

That's a MD5 hash of the settings we used to generate the precompiled header and so you can see that if there's a unique hash, a unique header was generated. Now all the question is, why was it generated and the why is actually contained in this secret filed called pch.gch.hash-criteria.

By looking inside that file, you'll be able to see what caused Xcode to generate a different precompiled header and to show you some useful techniques in finding that out and fixing the problems, here's Rick Ballard of the Xcode team with the demo.

- Thanks Chris.

( applause )

So I've got a very simple project here with two targets and this tool target builds the framework target when you build it, they're related and I want to see what I can do about improving my build time.

So I'm going to start out and see what the current situation is. Let's open up the build results window and I'm going to make sure if you're not familiar with the build results window, in the lower left or in the middle if you've got an embedded editor showing there are these little buttons. I'm going to make sure that this left most button is on.

That will show me all the build steps even after they've occurred and I'm going to make sure that the third to left button is on, which shows the build transcript. That'll show me the underlying commands that Xcode is using. So let's go ahead and build this project and you can see, it build a precompiled header here from my framework target.

And then when it went to build the tool target it built another precompiled header here. So we're building that twice and this is a small project so it built very quickly, but if this was a larger project or it had more targets, that precompiled header generation could be a substantial portion of the time of my build. It's definitely a place to get some savings.

So let's look at how we can make these two targets build and share one precompiled header instead of building it twice. This is a neat little trick. I'm going to first start out by selecting the precompiled header, build step in the build log and since I've turned on my complete build transcript, you can see it's highlighted in the build transcript, the actually command Xcode used to precompile that prefix header.

The last thing on that line, you don't need to read it here, but the last thing in the line is going to be the actual precompiled header generated. What I'm going to do here is I'm going to go launch file merge, which is an application we ship in the develop folder. If you go to developer, applications, utilities, file merge, this will help me compare the two files I want to look at in a nice way. I'm going to go ahead go back to my project, select the precompiled header directory it created. Not the actual precompiled header file.

You can see there I've selected everything up to the actual precompiled header I'm just going to drag that, oops, just going to drag this line into file merge, you could copy, paste it as well. So that tells the file merge that I'm going to want to compare that directory with well let's select the other precompiled header step and again, the last line is the precompiled header it generated, I'm going to select the directory, drag it right into file merge. When I hit compare here, file merge is going to tell me what's different about these two precompiled header directories. Let's go ahead and do that.

And there are two things that are different here. One is the actual precompiled header that's generated, but the other says hash-criteria file that Chris told you about, which tells us what the criteria was that made Xcode decide it had to compile two of these. So if I go ahead and double click on this hash-criteria file, there's one difference and that's this -DHWDEBUG flag. Well that's just a preprocessor define, a C preprocessor define.

I'm defining the HWDEBUG define and that's because this project is using that as a debug flag to do some extra logging when I'm in my debug configuration. Well I'm only defining that for my framework target, because my tool target either doesn't use that same flag or doesn't want debug logging on.

I could add it to both to make them use the same criteria or I can go back, I'm going to close file merge. Close my build results and I'm going to look at my build settings and my framework target and look for the preprocessor to find I actually set. So I could scroll through this list, but instead I'm going to filter on preprocessor and you can see, I'm defining a preprocessor macro here but we provide a second setting which is named preprocessor macros not defined in the precompiled headers.

In this case, I don't need my personal debugging macro defined when I'm pre-compiling the header so I can go ahead and delete this preprocessor macro setting and I can edit the preprocessor macros not used in precompiled headers, add it to that setting. Now when I build I'll still get this preprocessor macro defined, but it won't effect my precompiled header generation. Let's see what effect that has. I'm going to go back and clean out my build, build again.

You can see from my framework, it's building a precompiled header but when it built the target for the tool, it didn't rebuild a precompiled header. That tool target is now sharing the same precompiled header I built for my framework. Can't tell on such a fast machine with such a small project, but I probably just cut my build time in half. This is a technique you can use to make substantial savings in your own build times. Back to you Chris.

Thanks Rick.

( applause )

So because building a precompiled header is a choke point in building your target, it's really important to eliminate that in order to get as much speed as possible in also in reusing precompiled headers, shares space on your disk, there's less swapping, there's less disk access. It's all around better to try to use, share as many precompiled headers as possible. I mean think of a situation where you have a project that's building 40 targets that are all plug-ins and they're all identical.

If they all have their own prefix file and each builds a precompiled header, that may be two or three seconds per target to build that precompiled header. If you can eliminate that two or three seconds per target that's several minutes shaved off your build. Now even with that, there are other choke points.

Such as copying resources and linking and things like that and when you build a project that has an aggregate target, that is a target that simply says build this target and then that target and then that target and then that target, Xcode serializes that operation. First it looks at the dependencies among them then builds the prerequisites first and then builds everything in order after that, one by one.

Now this is problematic because you have, you most of the time, you're not using all of the extra processors in your machine. Because they only build phases that can really use those processors are the Compile Sources build, the Compile Sources build phases. All the other build phases are just going one by one by one.

In Xcode 3.0, we allow you to do something very interesting. You can check a checkbox in the project inspector and say, I want to build my targets in parallel whenever possible. And what that means is that after Xcode determines the dependencies between targets and builds the early ones first, it will build all of the build phases of each target in parallel.

So that they can overlap and use as much processor is as available at that time and that means if one target is doing a copy files phase which is only using one processor, because it's primarily disk bound and not really processor bound, another target which is heavily processor bound, building a lot of source code, can be using the other six or seven processors on the machine in order to do that.

You can see how in that project that built 40 targets for example, 40 plug-ins, building those 40 plug-ins ser@ially, where each one has a single source file, may take a long time. But building those targets in groups of eight, when you can compile all eight source files for eight targets in parallel and then go on to the next one, will be much, much faster.

In order to make this work, you have to ensure that your intertarget dependencies are rock solid. Because this will expose hidden assumptions in your project. If you happen to have a linear list of targets that are always built in this order, when you check that checkbox, sometimes this target might be built first, just because we could.

If that target happens to depend on something that needed to built earlier, that build is going to fail and what's worse it's going to fail intermittently. Because introducing parallelism introduces non-deterministic behavior. So you have to have your intertarget dependencies rock solid before you turn on parallel targets or you're going to get behavior that is non-deterministic and sometimes erroneous.

On case we found is when one target used Yak to build an intermediate C file out of a Yak file and that C file was actually compiled by another target, that didn't have a dependency back on the first and so if that target happened to fire first, it looked for the C file, the other target hadn't even run yet, it failed. That's a place where the linear structure of the project that used to impose order didn't work when we went in parallel. But once again, I think this is better demoed than talked about and here's Rick again.

So to show you Parallel Target Builds, I'm going to demo this with a neat little application called Colloquy, it's a free internet chat client that makes their source available and one of the things about Colloquy is that it has a plug-in API with a lot of different plug-ins and plug-ins, these plug-ins don't have any dependencies on each other and they don't compile a whole lot of files. They're doing more of the sort of steps that aren't parallelized already.

So they're a really great case to take advantage of Parallel Target Builds. So I've got a simple project here which just builds a few of their plug-ins and I'm going to go ahead and open up the build results and show you how this works right now. If I click build, you can see it's building the first plug-in here which is a support plug-in. Now it's building the next and the next, then the next and when this one's checking it's dependencies, the next one hasn't started yet, they're all building in serial right now.

What happens if I clean this out, go back and if I go to my project menu and edit project settings, this will open up our project info window, there's a little checkbox here, shows, let me zoom in on, called build independent targets in parallel. All I have to do is click this one checkbox, close the project settings, open my build results back up and let's see what happens when I build this again. See all that going on in parallel? Each target is building while the other targets are building. I'm not waiting for one to finish. That went by really quickly. Let me show you that again.

Boom. Five of them at once, six of them at once, they're all going and we're done. Parallel Target Builds. If you can straighten out your dependencies or have targets where you don't have dependencies between them that aren't expressed, turn it on and you can see a big improvement in your build times.

( applause )

Thanks Rick. Have I mentioned that your dependencies have to be rock solid in order to really use this feature? We're expecting a whole lot of support calls on you know, I turned on parallel targets and my project failed to build. You really have to make sure your dependencies are rock solid before you turn on parallel targets.

Also, it won't necessarily do much if you have a typical application project that is very heavy on the source code files. It's not, it's only going to parallelize the steps that are currently choke points. If you have something that's very heavy on source code compilation, you're probably using all the processor you've got anyway.

Can I have the slides back please? Now parallel targets is going to be used best with our distributed build features. Distributed build features let you go beyond the four or eight processors that are in one machine and use as many processors as you can find in the local area or on a dedicated build farm.

It's always a trade off because you have to spend the time preparing the files and shipping them over to the other machines and so on certain kinds of files you know, short C files, you're not going to gain as much by letting another machine do the work for you as you will in preparing, sending and receiving the results. But if you're compiling a lot of C++ files that are computationally intense, then using other machines to do your builds for you, can really improve your build speeds.

We have two basic ways to do distributed builds on Xcode. One is called shared workgroup builds and it's for casual ad hawk, just using what other resources are around. A couple of machines in your office or that guy who's gone to lunch. Dedicated network builds is more for setting up a build farm of machines that your whole workgroup is going to use that everybody can share.

The way shared workgroup builds works is it uses a common UNIX tool called distcc, to distribute jobs. Because distcc works best when you're just sending a single file at at time, what we do is we take your source code file and we pre-process it so we get one .i file.

We send the one .i file using UNIX pipes over the network, to the volunteer machine, it builds it and then sends back the .o files. It's just a one time eyes closed transaction. There's no persistent state. One file goes across, one file comes back. That's why it's really good for casual set up, just whatever machine happens to be available.

If machines come and go, we don't mind, we just pick another machine. We actually use Bonjour to browse local resources to set it up At some point though you spend too much time on your host machine preparing, preprocessing and shipping out these files and taking them back, than you actually get benefit of the other machines compiling them.

That's why shared workgroup builds is really good for you know, under five other machines, it can speed up certain builds but if you you know, you try to connect to six or more machines, it's just not going to improve your build times that much. To do that you need dedicated network builds and dedicated network builds uses a different architecture.

First you pre-configure the hosts you're going to use by host name or IP address and really those tend to be dedicated machines. They can be Xserves, they can be Mac minis sitting on a rack in a room and they're configured to be optimal compilation systems and they're not doing anything else.

Second is we map in a file system, a shared file system so that the volunteer machines can get at your prefix file and your precompiled header file and any other resources you're using so all we have to ship across is the source code file and they can come back to the shared file system and get any other resources they need.

If all of the volunteer systems are going to one shared system to pick up or map in the same precompiled header file, then it's a lot faster than to send the preprocessed files over every time. So it scales much, much better and it's much better if you have a rack and centralized machine. Now a caveat right now in both Xcode 2.5 Leopard Developer Preview and Xcode 3.0 Leopard Developer Preview, neither shared workgroup builds nor dedicated network builds is actually working.

It's working in the shipping Xcode 2.4.1 on Tiger and we will have both of these shipping and working really well. We're working with a couple of our key partners to make this sing for the Leopard final release. But due to a couple of other things, they're both not working right now. You can look at the user interfaces and see what we're going to ship in the Leopard final, but they're not working right, right yet.

Okay, we've covered a lot of project configuration, we've covered building for different architectures, we've covered optimizing for speed, now you want to build your project bigger. You want to build more complex projects, you want to build more intricate projects and you need to customize them. Customization is the most interesting thing you can do with the Xcode build system, because that's where the power of all these build settings comes in. And I'm not going to go through the hundreds of build settings we have that allow you to customize your project, but I'm going to focus on a couple of the new ones that make things a lot easier on Xcode 3.0.

We're going to talk about some new build settings to build embedded frameworks, we're going to talk about the SDK root build setting that allows you to build against software development kits and we're going to talk about building the same project against multiple versions of Xcode as well as building gnu-based projects using the core gnu tools, especially now that the developer tools folder is self contained.

So let's start with applications with embedded frameworks. This is the Mac OS X way of packaging software. We talk about this in the very basic introductory materials. We talk about it in how you structure an application. Don't install shared libraries on the machine, don't give users a package of stuff. Embed your frameworks, embed your subsidiary code as frameworks inside your application.

Yet it's surprisingly difficult to set that up in Xcode. We were talking to our technical writer about it when we were explaining this feature and he said, you just cut a page and a half out of the manual. Because it's a lot of work right now. You have to run an auxiliary tool to set up the installation path of the dynamic library, the framework, so that the application can find it.

Well we've added a new build setting into the Xcode build settings called Dynamic Library Install Name and you set this on any framework that you build, that's going to be embedded in an application and you set it's value to this special path @executable_path means what was launched, .. means go up past the Mac OS folder, frameworks means oh the frameworks directory of that executable and then EXECUTABLE_PATH is how you get to me from there.

And that means every, when your application links against your framework and your framework's loaded, the framework will know hey, this is my load location and I can be invoked from the application. On the application target, all you have to do is take the built framework build product from the other target or project and copy it into your applications frameworks directory in the path that's indicated there and that's it. You don't have to invoke lib tool, you don't have to run a separate install name tool, it's all set up there in one build setting. It's going to be much more convenient.

( applause )

Now there are some cases where you want to build a framework that is used by multiple applications or maybe put in multiple locations. You don't want to hard coat it to being up and over from the application executable because it may be used in different places.

Well the Linux community has long had this thing called rpath and we have it in Mac OS X now in dyld and rpath is a Runpath Search Paths facility that allows a dynamic library to say, you can find me relative to any of your known search paths. So using Runpath Search Paths when you build an embedded framework, you just say my dynamic library install name is @rpath/my path. So it's whoever's using me, whoever linked to me, can find me via their rpaths according to my name.

And then in each client that you build against that framework, you simply say the Run Set Path Search Paths are well here's my executable and I'm going to put in the frameworks folder or if another application wants to put it in a shared location like we do this with Xcode, we put all of our frameworks in a library folder relative to the Xcode executable.

And so we find all our frameworks that way, via that Runpath Search Paths. So this is a very powerful facility to ship a suite of applications or tools that all share a set of common libraries and can all find them very easily. And to demo that I'm going to turn that over to Rick again.

So I'm going to start out by showing you what Chris first showed you, simple use of the new dynamic library install name build setting. I should mention this is only available in Xcode 3.0 so you need to wait until you can move entirely to Xcode 3.0 before you can adopt it.

But here I've got a framework and a self contained application that wants to link against this framework and embed it in it's application bundle. So that it's a completely removable, relocatable application. So I've got a cross-project dependency on my framework project. My target for my application depends on my framework target. I link against it, it's red because I haven't built it yet and I actually copy it into my applications frameworks directory using a copy files build phase.

Well how did I set up this framework target? If I go and look at it's build settings, filtered for the new dynamic library install name, you can see here that the dynamic library install name for my framework is @executable_path/../ Frameworks/the EXECUTABLE_PATH build setting which resolves to the name of frameworks dyLib binary inside it's wrapper.

So when I go ahead and build my self contained application here, it's building right now, if I go and take a look at it, here's my built application, I'm going to look at it in the finder because when you run within Xcode, Xcode helps your applications find things in your own build products directory with a little dyld magic. So we're going to run them from the finder so that we can be absolutely sure that we have our linking set up right and that we're not relying on the crutches that Xcode gives us.

Here I've got my application I just built, I can take a look inside with the show package contents contextual menu item and you can see that there's a frameworks directory in this application and it's got my framework embedded in it. When I run the application it links or runs rather and if I drag it somewhere else, since it's got the framework it needs embedded inside it, it still runs.

The disadvantage of this current approach here is that if I want to use that framework somewhere else, some client which doesn't embed it at the main executable/ ../frameworks relative to itself, I'd have to add some run script phase, run some extra tool, do some other magic or build a different version of that framework. I shouldn't have to build that framework twice.

See what happens with that fixed framework app, which it instead of embedding the framework, for whatever purpose, wants to put it in a /library in it's own location and link against it there. Well if I build this application and I go take a look at it in the finder, if I launch it it crashes immediately with some obscure dyld error about not being able to load the framework. Well the easiest way to fix this is to adopt Runpath search paths and I'm going to show you how to do that.

First I'm going to go to my framework target, inspect it's build settings and I'm just going to search for Runpath and see there's a new Runpath Search Paths build setting and it can have multiple values, in this case I'm sorry, I won't actually set the dynamic library install name on this target, so right now it's set to the @executable_path based value. I'm just going to get rid of all this that tells whoever links against it where to find it and say find me wherever you want to with your Runpath Search Paths. So now the dynamic library install name of my framework is just @rpath/the rest of my path.

Go ahead and commit that. Now I'm going to go to my application here and this is where in it's build settings, oops going to double click on the target, going to filter on Runpath Search Paths, here I can give it a value of well in this case I want that rpath to be still @executable_path/../Frameworks, so I can just type that in oops, little Xcode 3.0 bug, build my self contained application and see I can still move it, it still launches just fine, even thought it's framework didn't specify the full dynamic library install name anymore, it used the Runpath Search Paths in the self contained application.

Let's go over and look at my fixed framework app. Well it also need to specify where to find that framework. It's going to find it in a different location. So I'm just going to go to my copy files phase here, which is where I specified where to put the framework, I'm going to copy that and put that in as a Runpath Search Path for this application.

Let's go ahead and build this, take a look at it here and now it launches because it's using it's Runpath Search Paths to find the framework where it copied it. There's a lot more you can do with rpaths, it's a very powerful and useful tool. At the very least it can help you organize how you link to things even if you don't need to use it for it's more complicated affordances.

I highly encourage you to check it out. One thing to keep in mind again, this is a new Leopard feature, so you need to wait until you can adopt, you can use Leopard as your minimum platform before you can use rpaths. Back to you Chris.

Thank you Rick.

( applause )

So using embedded frameworks is one of the things that we exhort you to do which we've made it more difficult to use in Xcode than it ought to be and we're making easier in Xcode 3.0. Another one of those is using SDKs. We told you from the time of the universal transition that you should always target SDKs rather than building against the content of your native system.

It's going to make it a lot easier for you to be portable from one system to another, to build from one platform on another platform. It's actually a very crucial part of our strategy of having multiple copies of the development environment on one machine. But there's a little problem.

Is that we support only one SDK and a lot of people have said well that's not SDKs, the whole purpose of SDKs is to blend them together and so one of the very common things that happens is well you have an application, you want to build it against the 10.4u SDK because that's the way to build universal software for Tiger, but you want to use the Omni frameworks and the Omni frameworks come in their own SDK. They have their own path from library frameworks and all their frameworks in them. Well with Xcode you have to choose one or another.

You have to install the Omni frameworks in your system and then build against the root, the current system or you can choose to build against the SDK and lose out on the Omni frameworks or you can do something really skanky like trying to install the Omni frameworks into your 10.4u SDK and create your own custom franken SDK and people don't do that and I'm glad.

But in Xcode 3.0, we fixed that. The SDK root build setting, the system SDK build setting, now accepts a list. A list of paths and one of those paths can be the Apple SDK, the system SDK and another of those path can be any other SDK that happens to have libraries, frameworks and headers that you build against and then when you go to build, Xcode blends those together into a composite SDK and compiles and links your application against that and it'll do that for every target that uses that combination of SDKs. So it only happens once. This makes it very easy for you to use third party SDKs as well as an Apple SDK like the 10.4u universal SDK or the Leopard 10.5 SDK.

You can even create your own SDK, it's very easily to be used this way. All you have to do is take your software the builds into what's called the dest root, the destination root and that will look like slash with library or applications or wherever you want to install your things and you call that root you know my.sdk. You take the executables in it and you strip them with strip -c which takes away all of the executable code but leaves all the symbols for linking behind and then you take away everything else that's not your header files and the strip executables.

You put in one file at the top level called SDKSetting.plist and all it has to have is one key value pair of the name of your SDK so that can show in the pop up list and then any build settings that you choose to have in effect when your SDK is in use.

If you have a build settings dictionary in your SDKSettings.plist, those build settings will be preset automatically when somebody choose your SDK even if it's blended with an Apple SDK. So we hope that those of you who vend frameworks for other developers to use will take advantage of this and those of you who want to use those frameworks can now very easily use third-party SDKs along with the Apple SDKs when building your software.

One last thing. We talked this morning and we talked on Monday about the self contained developer folder. The self contained developer folder for Xcode 3.0 and the self contained developer folder for Xcode 2.5 and how Xcode uses everything in that self contained folder to make it portable. Well one important thing about that is that includes all of the standard gnu development tool chain that usually is installed in the system root. You can see the theme here. We're trying to get you independent from installing things in slash so that you can have your whole development environment in a separate location and be very portable.

One of the things we do is we take, we put in the developer folder our own usr directory slash user and it has /usr/include and usr/bin and those have all of the header files and all of the tools and all of the link libraries that are normally only installed by a developer tool installation. They're all there in the self contained developer folder.

The good news is when you launch Xcode, we map everything so that we use the compiler and the linker and the debugger and the standard C library and the link libraries and the system headers and everything, from the developer tools folders copy as slash user rather than from root and when you're building with Xcode 2.5, even on the Leopard system, we do the same thing.

So when you launch Xcode 2.5 on a Leopard system, you're not touching Leopard's slash user directory at all. You're using everything from Xcode 2.5's usr/bin, user included directory. That gives you some integrity. That keeps you from mixing and matching Tiger and Leopard components. That's really important but, that's done automatically by Xcode.

If you go beyond the bounds of that, if you're doing gnu style development, if you're doing Makefile development, if you're invoking things from the command line, if you have shell scripts that invoke Makefiles and refer to things in usr/bin, usr/lib, usr/include directly, you need to adapt to that.

Because if somebody picks up and moves your developer folder or if somebody doesn't install the tools in slash, then those are going to fail. So you need to know how these are laid out and what special things to do with them. Here are a couple of important pointers.

The first thing is, how many of you use gcc_select? You may stop now.

( laughter )

For a couple of reasons. One is when you have multiple developer folders and multiple sets of tools, gcc_select is ambiguous. What am I selecting and for whom. The best way we figured to eliminate that ambiguity was to eliminate gcc_select and just say, if you're using Xcode, you specify the compiler you want to use by default in Xcode's compiler rules pane and if you're using Makefile or gnu development tools, you have to specify which compiler you're using, by compiler name and path rather than just having some mystical system setting that sets up simlinks and assuming you'll get the right answer.

We don't think it's going to be that much trouble to migrate to it, especially since the second thing is that there's only one compiler to select among which is GCC 4.0.1. There is one compiler installed by Xcode 3.0. There is one compiler installed by Xcode 2.5. That's GCC 4.0.1.

If you want to develop for 10.3.9, you have to install an optional install package and that includes the 10.3.9 SDK and the GCC 3.3 compiler. So really there's nothing to select among unless you're specifically doing pre-Tiger development. Then you get the 10.3.9 SDK and you get GCC 3.3 and then you have to use GCC 3.3 either with the rules pane or by specifying it explicitly in your Makefile's compilerpath, your CC setting in your Makefile.

Now there's one special thing. We try very hard to keep from mixing Tiger and Leopard content on a Leopard system. Leopard content will never be installed on a Tiger system, it wouldn't work. It would blow up. Tiger content is only installed on the Leopard system in the 2.5 folder with one exceptions and that's GCC 3.3.

There is no Leopard GCC 3.3, there's only a Tiger GCC 3.3 and so if you install GCC 3.3 on a Tiger of a Leopard system, it always goes into /usr/bin. It doesn't go into the /usr/bin in the developer folder. So that's one thing you don't have to worry about. There's only one GCC 3.3 and it's only available in the 10.3.9 development support package. Now I said that there's one GCC 4.0, GCC 4.0.1, well there's a twist.

There are different GCC 4.0.1s for Tiger and Leopard. They have different build numbers and different features, different compatibility, different performance, different errors and warnings, different bug fixes. They are different compilers. But they both report themselves as GCC 4.0.1. So just a little warning. If you want to report a bug against GCC, please specify the build number from GCC -v, don't just say GCC 4.0.1, because well we've got a couple of them. Okay? If you want to have as I said, you own make files or your own shell script build phases, have access to specific tools even though you have a portable Xcode DEVELOPER_DIRectory, we've provided a few build settings to make that easy and Rick's going to demo them.

We've got a really simple Makefile base, really, really simple Makefile-based project here which compiles one source file and it uses a C compiler that it specifies with an absolute path. In the past, on OS X, you've been able to assume that the compiler is at an absolute path in /usr/bin, but the linker isn't in absolute path in /usr/bin and that all the tools are in absolute path.

Say goodbye to absolute paths if you want to opt in to using the developer tools out of you're currently running Xcode version's developer folder. It's entirely optional, the command line support is on by default in the installer, so your run script build phases and Makefiles can continue to invoke tools at a /usr/bin if they want.

But if you want to opt in to using the Xcode 2.5 tools when your project is built with 2.5, even on Leopard or opt into letting people turn off the command line support package so they have no compiler tools in /usr/bin and still be able to build your project, then there a few simple modifications that you should make to your Makefiles, and run script build phases and so forth, to invoke things out of the developer /user/dir. So first of all we're going to get rid of this absolute path.

What are we going to put in it's place? Well when Xcode invokes your Makefiles and run script phases, it prepends the path environment variable with the path to the DEVELOPER_BIN_DIR. So I could do that, I could specify no path at all, just the name of the tool and since I'm being built inside Xcode, it'll find the version in my developer /usr/bin directory first.

Or I could use one of the new build settings we provided which is called DEVELOPER_BIN_DIR. So this is passed in the environment and if I make my tool invocation relative to it, I'll invoke GCC 4.0 out of my DEVELOPER_BIN_DIR. So let's open up the build results here and see what happens when I build this.

Oops, this machine doesn't have zooming on. Well you may not be able to read this at the moment, but it's invoking GCC 4.0 out of /developer/usr/bin GCC 4.0, instead of out of /usr/bin GCC 4.0. What that means is that if I move this project over to 2.5, it should invoke 2.5's version of GCC 4.0.

That's pretty easy to test because I've installed Xcode 2.5 on this machine, it's available as a download from the developer.apple.com website. It's a pre-release. I can just go ahead on Leopard, launch Xcode 2.5, here it is running side by side with Xcode 3.0 and I can open my project in Xcode 2.5.

Now before I build this, there's something I've forgotten which is my Makefile is building against the system headers. But as soon as I move over to building with Tiger's tools, I don't have Tiger system headers on my Leopard system. There are all the Tiger developer tools headers and libraries inside the Xcode 2.5 folder, but what I really need to do is build against an SDK to get the rest of the system headers for my Tiger system. So when I go ahead and build this project, you're going to see oops, should clean first, you're going to see that I get an obscure error from the linker.

So what I'm going to do is when you're compiling from the command line with a Makefile, use the -isysroot flag, which is documented in the GCC man page to specify the SDK you want to specify. In the past I might have said isysroot /Developer oops, /Developer/SDKs/MacOSX 10.4u.sdk, but that will build against the SDK out of /Developer.

I might not have an SDK in /Developer, I might have chosen to install my tools elsewhere and besides, /Developer is my 3.0 developer tools folder. Instead, I'm going to use another one of the new environment variables we passed down, the DEVELOPER_SDK_DIR. So I'm passing -isysroot ${DEVELOPER_SDK_DIR}/MacOSX10.4u.sdk. This is going to make me build against the SDK out of my Xcode 2.5 folder, wherever that may be. On different people's systems if they install it in different places, it'll find GCC and my SDK in the right place.

Let's go ahead and build and you can see that my build is succeeded using 2.5 on Leopard. So the important things to remember always build against SDKs, whether you're using a Makefile based project or a native project, also requires that you build against an SDK if you build with 2.5 on Leopard or you choose not to install the command line support, in which case you won't have system headers. Build against an SDK and don't use absolute paths. Use DEVELOPER_BIN_DIR and there's some others which are in the documentation like DEVELOPER_DIR and /Developer/usr/dir to find the tools that you want to invoke, to invoke them out of the self contained developer directory, wherever that may be.

Back to you Chris.

Thanks Rick. ( applause ) Well it's been a longish tour through a lot of intricate details of the build system, but let's summarize. We've got new build system support for building 64-bit. There's a lot of power behind the checkboxes that just turn on the architecture's build setting.

We've got new predefined build settings to help you build fat for your release builds, but build as fast as possible thin for your debug builds. We've got several useful techniques we've shown you for managing precompiled headers, for using parallels and write and for using distributed builds in order to speed up your builds.

We've shown you some useful new build settings for building embedded framework so that you can actually use Xcode to build software the way we tell you to and some techniques for building using multiple SDKs and multiple versions of the tools. We really hope that you'll be able to take these, apply them using Xcode 3.0 to build better software faster.

For more information, Matt Formica has been around all week as your Developer Tools Evangelist, there's plenty of documentation in both the Xcode 2.5 and the 3.0 drops. I strongly recommend that you open up the new documentation viewer in Xcode 3.0, browse around inside the developer tool documentation. There's a gold mine of information there.