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

WWDC05 • Session 408

Project Management Mastery with Xcode

Development Tools • 1:03:20

Your industrial-strength project needs the industrial-strength features of Xcode. Build multiple versions of products, create project trees, automatically set version numbers, and synchronize project changes among multiple engineers. We'll cover the advanced use of the SCM build system, and scripting features of the Xcode IDE.

Speaker: Anders Bertelrud

Unlisted on Apple Developer site

Transcript

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

Hi. Welcome to session 408, Project Management Mastery with Xcode. So it's Friday afternoon. We've seen a week full of great sessions about all kinds of aspects of Xcode. And in this session, I wanted to dive down into some of the little bit deeper and more interesting project management facilities Xcode has. So, I should introduce myself. I'm Anders Bertelrud. I'm the Xcode architect for the IDE part of Xcode.

as opposed to the compilers, linker, etc. So the motivation for this session is that Xcode does have a lot of facilities to make simple things simple, right? So there's a lot of project templates. You can set up a -- even a fairly complicated thing, you know, Coke application with core data support and building a spotlight importer. And, you know, Actually, I just realized that I'm mic'd here, so why don't I leave that there.

So, there are also though a lot of facilities in Xcode that you may not know about for setting up complicated projects, for managing that complexity, and for making it as easy as possible and fine-tuning your projects. And that's what we're going to talk about today. So what do I mean by a complex project? Well, simple one, you know, simple text, kind of application, basic thing. Complicated one typically has many targets of different kinds.

You might have a couple of bundles, a framework, application, some kernel extension. You might also have cross-project references, and it's good if you do, if you have a need for them, because that's one of the great ways in which you can manage the complexity in Xcode. I'll talk more about that in this session. You might have embedded frameworks and plug-ins. Maybe you have 20 bundles that you want to put inside of your app wrapper, or you have a couple of frameworks you depend on that you want to ship with the app.

Another thing you might do is to create custom build configurations. You probably by now have heard a lot about build configurations, which is a new feature in Xcode 2.1. And by default, you get debug and release configurations. And they're set up to be fairly convenient for default use. But you may want to create your own, and you may have a lot of them.

Another characteristic of what I would call a complex project that's covered in this session is shell script build phases. Xcode provides a lot of flexibility here. You can invoke anything you can invoke in terminal. You can invoke as part of a build in Xcode. And you can also invoke external build systems, such as Make or Ant.

And there are other special build requirements. I don't actually list Universal as a characteristic of a complex project and why you might want to be in the session, because that's fairly simple in Xcode for the most part. Or at any rate, the complexity lies in making your code Indian compatible with both.

So what we're going to talk about specifically is, first of all, some project model concepts. Many of you understand Xcode's project model pretty well, but there may be some things that you're not quite clear on, or I may be able to provide a different viewpoint on some of it.

Then we're going to talk specifically about targeting multiple OS versions with Mac OS X. A lot of you have Panther shipping products on Panther, or at least you want to be able to have your customers use Panther and run your app. And at the same time, you want to take advantage of Tiger features. There's a lot of API that's been talked about in this week that's Tiger-specific, and you certainly want to use that conditionally.

We're going to talk a little bit about creating universal binaries. Not a whole lot to talk about here in terms of Xcode, but there are some nuances you may not know, such as per architecture settings. We're going to talk a little bit about keeping track of version numbers.

That's something that almost any product, whether it's shareware or commercial, would want to use to have some facility for identifying which version of the software that you're dealing with. We're going to talk a little bit about fine-tuning your targets, and we'll look in more detail at what that is. But that's some things you may not know that you can set up to take better advantage of Xcode's build system.

What we won't cover, we're not going to talk about file editing or file navigation here. Also not about object modeling. There's been a lot of good sessions on that, and I'm sure there'll be some content provided after the conference that you can refer back to. We're not going to talk about project navigation either, or class browsing, or debugging, just to set the expectations. This is really project model and project management.

Back to what we will cover. Let's start on the first one, project model concepts. What's in a project? Pretty simple. Almost any project has groups, as we know, file references. The only nuance there is these files are not actually a part of your project. They're referenced by the project file. Most of you know that already, so that's not news. A project contains targets, and you can have as many targets as you like. In fact, you could have no targets if you like.

If all you wanted to do was organize them, you could have as many targets as you like. You could organize some files and be able to search on them. But most interesting projects have targets. Executables, that's the Xcode term for a launch context, such as starting working directory and executable to launch, command line arguments. This is of interest when you go to debug your application or even your plug-in.

Bookmarks. We provide a way to organize store name selections of contents of files. And smart groups. And this graphic list just nibbed files, but of course you can put anything in your smart group. And a complex project also has a couple of extra things. I've sort of souped up the text editor example, or the, sorry, the sketch example a little bit here. I don't know if you can see this way in the back. This room is deeper than I expected.

But we have externally built targets there as an example. That's again where you can invoke MakerAnt or any other build system you can run from the command line. There's unit test targets. That's new in Xcode 2.0, and you can create them right from the project menu, saying new target.

Custom executables. You might not be aware of this. When you create an application or a tool, Xcode automatically creates an execution context for you to run that, and you can configure it by adding things like command line flags and those sorts of things. If you are developing, let's say, a bundle or a framework, you may want to specify an external application that can run your bundle. So you can still debug and run from within Xcode.

Xcode would then launch some external tool, either in debug mode or just in normal run mode, which would then load your plugin. And you can specify command line arguments and all the other facilities there. That's pretty straightforward. We're not going to go into a huge amount of detail about that.

Another thing you may find is custom language source files. Xcode 2.0 and 2.1 let you set up custom build rules to invoke your own tools or your own scripts on any of your source files. And you can produce source code files. You can produce source code files, or you can produce resources.

And if you produce source code files, they get fed back in the dependency analysis. So that's a pretty powerful utility that not a lot of people use. And not everybody needs to use. But for those who do, it's a great facility. New in Xcode 2.1 is that you can preprocess your Info.plist file. I'll go into more detail about that. We might find that also in a complex project.

And references to other projects. I touched on this before, and we're going to talk a little bit more in detail about that. In Xcode, you can add, just like you can add a reference to a file, you can add a reference to another project, and there are some advantages to this. It lets you divide, obviously, large projects into manageable pieces. Many of you do this anyway by having separate targets, and maybe you have separate projects that aren't related.

The advantage here is, of course, you can localize your project configuration. If you have some settings that apply only to one target, you just set them on that target in the project. If you have a couple of related targets that are in the same project, you can set settings on the project, and those targets will share it. But you don't have to expose all the other targets to those settings. And, of course, targets can depend on other projects, targets in other projects. So you can have a single click of the hammer to build everything.

And one sort of subtle effect of this is to actually reduce SCM merge conflicts. As you may know, Xcode 2.1 has an improved file format that is both easier to write and read, and also to resolve conflicts in. But if you edit the same thing, if you're a large team, conflicts do happen when you check things in and out of source code management systems. By splitting your projects up, you can actually avoid some of that just through your workflow.

Let's talk a little bit more about targets. Again, they're the main unit of organization in the build system, and this means, what I mean by that is that each target determines which build system to use. You can have an AMP target living next to a make target living next to one of Xcode's native targets, and that all works together. We'll see that later in a demo. Each target defines a single logical product, and typically you wouldn't have more than one target producing the same product. You would use build configurations for that. And a target is really like a recipe, right? There's ingredients and then there's instructions.

And of course, targets can depend on other targets. So the ingredients of a target are really the dependencies on the other targets, the references to the source files that go in there. They could be products of other targets in turn. And the references to frameworks and libraries that you want to link against, and they too can be products. As far as the instructions, we have the build phases.

The build phases are available when you twist open the Xcode icon for a target. And here you can really look at the steps that Xcode is going to run when you go to build your target. And you can customize that. You can add your own shell scripts, etc. There are build rules. And the graphic here shows opening the inspector window on the target. And as you see, we have a list, an ordered list of build rules.

And they get evaluated in order. And the first one whose condition matches is going to run. We also have build configurations, which are new for Xcode 2.1. And this is how you can differentiate. Although you have one target producing one logical product, you can make it build a debug, release, test, or other kind of variant of your target.

I want to talk a little bit about header files in particular, because there's often a question I get, well, should I put my header file in the target? And I'm highlighting this just because there's been confusion on this. So the question is, well, should you? And the answer is really only add the target, only add the headers that are going to be part of your product.

So if you're going to put headers into a framework, or if you're going to copy headers to some place in the file system because you're building a static library, then go ahead and put them in there. But there's no need to add the headers if all you're doing is referring to them from source files, because Xcode sets up automatically an access path for that.

So all headers in the project are accessible from the source files. And Xcode 2.0 and, of course, 2.1 supports recursive search paths. And there's a checkbox now you'll see in the edit sheet for the search paths, whether they're header search paths or library search paths. They all have this checkbox, so you can make them recursive. immersive.

So that's what's in a target, and then the build system, of course, operates on the target. Most of you know this. Xcode has what's called a native build system. You can also integrate with external ones. Because Xcode's native build system is internal in the IDE, that means that any changes you make get reflected automatically.

New in Xcode 2.1 is that there is a column with a checkmark that indicates whether the source file needs to be built or not. That was actually present in previous versions as well, but what's new in 2.1 is you can actually click on that checkmark, and you can touch or untouch a file automatically.

So one other thing to note, with the internal build system, it starts compiling. Sometimes, even while you're still typing, it starts what we call predictively compiling. This means that when you go to build after editing a single source file, you might not actually see any compile line, or it might happen really quickly. Xcode didn't skip your file. It actually had just compiled it in the background and was just able to copy the result over. So just a small note.

So, build phases I mentioned before. These are the steps that the target goes through when it builds your product. And they're the standard ones. You can copy the headers into the framework and build the source files. The custom types are worth calling out. If you need to copy files from your project to an arbitrary location in the file system as part of a build, you can do that with a copy files phase. You can configure it to copy anywhere. You would use one copy files phase for each destination that you want to copy to. You can also run an arbitrary script, which could in turn do anything you'd like it to.

The build rules, going through the things that are part of a target here, again, they specify how source files are processed, and the important thing is every rule has a condition and an action. And if the condition matches, the action runs, and then the evaluation of the rule stops. So the first rule that matches is used, and this lets you shadow other rules that occur downstream of it.

You can use this to, for example, select GCC 3.3 versus 4.0, because you would change the action from 3.3 to 4.0. And as I mentioned, you can actually make the action be any custom script. So the condition and action might be all nib files, all C++ source files. We have many built-in actions. Or you could specify any file name pattern you want, and that uses the shell syntax. So star.dat, for example.

And then the action, of course, could be either one of the built-in compilers or file translator tools, or it could be any command you want. In this case, we're invoking a command-line tool called PLUtil, which works on property lists, and it would convert to XML whatever the source files that get passed through the rule.

So, just an overview here again. Here's the condition. In this case, set to just match via a specific shell pattern. The rule is a custom script. The script itself could be anything you can type in terminal. So, you can use Perl, you can use Ruby. You would typically specify that with a standard shell notation with the first line specifying the translator.

And then you can define the output files that will get produced by this rule. And this is what Xcode's dependency graph uses in order to determine whether your rule needs to fire. If any of the inputs is newer than any of the outputs, then the rule gets run.

So some useful settings. I mentioned that it gets invoked once for every source file, so obviously we need to know what the input file path is. The input file path in this case is an absolute path. And There are several variants of that because you may want to split that out into the base, into the suffix, and various other parts. So this is what you would use in your rule and specify in your output files so that you can tell Xcode what your script works on, what it's going to produce, and where that's going to go. And these are all documented very well in the Xcode documentation.

There are two special settings of interest that you may see over and over again being used. They are Target Builder and Build Productster. Can those of you in the back read this? Is this big enough? Okay, great. And the target builder, it's... That refers to the directory into which the target being built is going to put its output. So this rule is being invoked as part of a target, and this is the path where the outputs will go. The build productster is related.

It is a directory that contains references, some links, to all of the products of the other targets. So this is what you would use to refer to the outputs of any target. The two may be different if you're doing an install build. Again, there's a lot of good information on this in the documentation. I don't want to necessarily teach all the nuances here, but mainly highlight it and make sure that you're aware of these two. If you're doing your own custom shell script rules, you'll probably be looking at using either one or both of these.

I've talked about build settings a couple of times during the talk so far, and most of you know what those are. They're simply the knobs and switches on a build. Most of the time, you just want to define a certain value. My optimization level is 3. I want zero link on or off.

But you can also refer to other settings. So this is an example where the product name actually contains the version number, which is, in this case, another build setting. I mentioned the shell script build phases before and the shell script rules. We actually pass the build settings as environment variables to those rules. So whether you're using Perl or Ruby or any of these scripts or the shell, you have access to them.

They can be set at either the project or the target level. This is new in Xcode 2.1. In previous versions of Xcode, the only really useful place to set the build settings was in the target or in the build styles, which have now become configurations. In Xcode 2.1, you can actually set it at the project level for any or all configurations.

And this is a pretty powerful facility because very often, many of your targets in the same project, if not all of them, are going to want to share the same setting. So the project level is a good place to set them. This implies that there's a precedence hierarchy among the build settings. That's true.

And let's take a look at that. So at the bottom, we have the environment. And this is the typical environment variables like home and group and user and those things. You would never set those. You would just refer to them. But they can be a very useful thing to refer to. There's the built-in build settings, such as the wrapper suffix for an application is .app.

You would very rarely change that. In fact, for .app, it's not recommended at all. The built-ins don't get changed, but you can override them. The application settings come next. They really only get changed. They only get set through the preference panel of Xcode. When you specify your build output directory, that's changing the application settings. When you set a source tree, which is a facility we have in the prefs panel, then you're also changing the application settings. These apply to all projects that you open from your account in Xcode.

The project settings, as I mentioned before, they're new in Xcode 2.1. Here you would set things that are common to all targets in your project. And for many of you, you'll find that that's actually probably the majority of the settings. Things like the project version, you may have a string that contains your company name, you may have header search paths that are actually shared or common to all targets in the project. This is a great way to make sure that all your targets have the same settings, is to set it at the project level and let all the targets inherit.

The target settings are above that, meaning that they have higher precedence. Here you would set things specific to the target, such as the product name and any special flags that you may have for that target. But again, I think you'll find that with the addition of the project settings, most settings that you'll want to set are actually applied to the project level, and that's the most appropriate place for them.

Above that, at the very highest level, we have the command line. And what are those settings? Well, Xcode has a command line build tool called Xcode Build. And just like with Make or with any other command line build tool, you can pass arguments on the command line to that tool. You can override any build setting at the command line level.

You may want to do this if you're doing a command line build, and you say, you know what, just for this one build, I just want to put the output somewhere else, or I want the app to be called something different. You can do that by setting settings at the command line level. This is always empty when you're running the IDE itself, the application.

And again, the precedence hierarchy, as we saw there, is... Let me go through that. Clicked once, too many. What I wanted to show and emphasize is that the highest precedence is at the top, and the lowest precedence is at the bottom. So, any setting defined in a higher tier can override, or will override, settings at the lower level.

And we'll talk a little bit more about the macro expansion. Again, references to other settings. And the macro expansion, you can refer to anything that's in a higher or lower tier. It's going to get evaluated just the same. So you can have a setting at the project level, and then it can refer to another setting that's defined at the target level, and that each target can then customize what the project setting refers to.

It's also important to note that, just like in Make, the value of the expansion can be different at different times. The best example of this is a command line rule, sorry, a build rule, where you invoke your own command line tool. And let's say you have 20 source files with a custom suffix. The rule gets invoked 20 times. The build setting input file path refers to something different each time.

There's a special case when you override settings. You can actually refer to the sort of magic setting $_VALUE, and what this is going to do is it's going to substitute the overridden value. So in this way you can use inheritance of the build settings to set up a fairly sophisticated macro expansion.

And it's important to point out, references to undefined macros are elided completely, so they just become empty. You can use this to your advantage in order to include, for example, conditional flags. So if you want the flag, you'll define the setting, and if the setting is undefined, then the flag just doesn't appear on the command line.

Build settings are arranged into build configurations. As you probably heard earlier in the week, each configuration has a name, and there's no restriction except that it has to be unique within the project. And there are an arbitrary number of build settings that you can define in the configuration.

And I should point out, actually, you can define, you can override Xcode's built-in build settings, or you can make up your entirely own names that you use from your own shell scripts. New projects, by default, have a debug and release configuration, and upgraded projects from Xcode 2.0 and earlier also have a default configuration. And that corresponds to the target's own settings without any of the build styles, because by definition, an upgraded project comes from an earlier version of Xcode that had build styles.

You can, of course, have as many configurations as you want. You can name them anything you want. You can't delete the last configuration, but because you probably want some kind of settings for your targets, that's probably not a big restriction. You can really add as many as you like.

One way to think about how build configurations get used is to think of the name of the configuration as a method interface or a protocol, and to think of the actual settings in a build configuration as the implementation. So this is really polymorphism for your project file, right? It's kind of like the OOP where you can send a display or draw yourself message to any object, and it does something appropriate for its kind, for what that object is. It's the same thing here.

When you build with the debug configuration, you're not actually telling each target necessarily what flags to use. You're saying to each target to use its debug configuration. And it's up to the target to determine what that is, or what you've configured in the target. So you could have a framework target next to a bundle target and an application target.

Each of them has a unique notion of what debug means, but they all can build themselves for debugging. And this graphic just illustrates the example of a debug release configuration in a project, and the targets in the project have their own configurations that inherit from the project configurations. So settings at the project level.

Here, the second setting from the top, if you can read that, is the C language dialect. And in this example, we can have two targets that are both in the project. They are sort of above the project level, so they inherit from the project. What I'm going to do is, in the background there, change. It's hard to see, but the circled setting was changed to be the GNU 99 standard from being the compiler default. And we only changed it in the project both of the targets inherited.

Another facility in Xcode 2.1 is that you can actually create a style sheet or an XC config file. It's just a text file that has settings in it. And this lets you share your settings even beyond the borders of the project. And you can have two dozen projects that all refer to the same standardized configuration file.

You use a .XC config suffix. There is a file template for it. So you can just say new file and create a config file. It's at the very bottom of the sheet that comes up. You include that in your project, and you can set any kind of a value you want in there, any of the settings that you can define in the inspector. You can actually prototype, as you may have seen in an earlier demo, by just selecting a bunch of the settings and dragging them into the project file.

And multiple targets can share the same configuration file, and then of course you can check them into a source code management system or anything like that, reuse them across the projects. So, to revisit this diagram, this is where we left off with our inheritance hierarchy, but that's actually not the whole truth, right? If we just focus on the targets and settings, I just talked about XC config files.

Each of these two levels can also optionally depend on an XC config file. This means that in addition to what you define at the project settings level, right underneath that would be what you define in the configuration file. So you can still define anything you want in the project settings level, override what was in that configuration file, but you could consider these two parts of the stack to be double layered. And the bottom layer for each one is the chosen configuration file. If any, and the top layer is what you actually set right there in the inspector.

So, in many cases, you'll be able to set settings common to all projects in your config file. In other cases, you'll be able to set settings at the project level. And what I want to do, though, you may have a question, well, what if I select all my targets and just set settings on those? You can certainly do that. If you end up setting the same setting on all targets, you probably want to consider whether you shouldn't do that in the project level instead, which might be better, or in an XC config file.

Anders Bertelrud I wanted to make sure, though, that the relationship between the different configurations and the different targets is clear. So, I want to illustrate that. I'm just going to use the GLUT example, which is the GL Utilities Toolkit from Developer Examples folder. And this has a bunch of interesting targets. I've renamed the development deployment configurations to be debug release, and I've added a test configuration.

Anders Bertelrud So, this is an example with about 10 targets, I think, and three configurations. What happens if we select just the one target, Atlantis target, and then the debug configuration? Then we're operating on this box. Each of the boxes is a configuration. Each target, in this case, is three configurations.

They all have the same name in each target, but each target has a unique definition of what it means to be a debug configuration. Anders Bertelrud So, I'm going to change now so that I select the release configuration in the pop-up, but I still have just the one target selected. Now I'm editing this box.

And there's a special menu item called All Configurations, and that edits all configurations for that target. Pretty simple. So now, if I select a bunch of targets, I can do multiple selection, and I'm still editing all configurations, so the shaded boxes are now the ones that get edited. If I set a setting, let's say turn off zero link, then that's going to affect all of these configurations and all the selected targets.

Anders Bertelrud And again, if I switch back to a single configuration, then only that column, only that configuration in each target is affected. Now, of course, I can extend this to select all targets. I still have just the release configuration selected. And I can select all targets, all configurations, and if I set any setting now, it would get set for all of these configurations. Of course, in this case, you really should consider whether it might not be better to set it at the project level.

Okay, so that was a fairly brief recap of some of the project model concepts that are important to keep in mind as you're configuring your projects. These concepts permeate all of the configuration of projects you'll do. We're going to shift gears a little bit and talk about some specific tasks, some of which are particularly interesting in light of some of the announcements made now on Monday. First, we're going to talk about targeting multiple versions of the OS.

We do this with SDKs. SDKs are software development kits. Most of you are familiar with that. They provide build-time access to the API for a particular release of Mac OS X. And so, again, you don't need to ship an SDK to run your app. You would build against an SDK that matches your target OS. So this lets you build against, for example, Mac OS 10.4, use 10.4 API, and then you could still run your app all the way back to 10.2 if you're careful to check conditionally for any API differences between 10.2 and 10.4.

I wanted to point out that you actually already have made this choice, or are about to make this choice. Your use of API actually implicitly determines which OS you're going to be able to run on and not. If you unconditionally use 10.4 API, then you're only going to be able to run on 10.4.

The advantage of an SDK is it makes that choice explicit, so that you really think about and sort of declare up front to Xcode that, yes, I'm going to use 10.4 API, but I want to make sure that I only rely on 10.2 API. You do this by, you specify, so there's two bounds, right? The lower bound, the upper bound. The SDK specifies the upper bound. Mac OS X deployment target is the minimum required OS, that's the lower bound.

Um... you have some rationale why by default you build against whatever's installed in your machine. This is inherently kind of unstable. If you're running 10.3 and then your colleague is running 10.4, if you give him your project, he's now building against the 10.4 headers, there may be different results. Going backwards is even worse, because now it won't build at all, most likely if you're using 10.4 APIs.

So this provides some insurance that's gonna be stable over time, because we don't change the SDKs. The 10.2.8 SDK is the same now as it was when it came out, because that's a constant in time. So this also lets Xcode warn about accidentally using API that's too new, and also there's information there about deprecated API. So you can at least be aware that you're using deprecated API if some of the ones you're using are deprecated and you can switch over.

What's in an SDK? Mostly you don't need to know this, but it might be useful just to understand how they fit in. There's a sparse directory of the developer tools, so they contain the headers, and they're the same as the headers for the developer tools for the OS you're targeting. So the Mac OS X.8 headers are as they were with those developer tools.

There's dynamic library stubs. There's not actually the runtime code from 10.2.8 in this example, but there's just the glue that lets you bind to it. The static libraries are there with their code, so that if you link against the static library for the 10.2.8 SDK, you get the 10.2.8 code into your library. The idea is that it would be as if you had taken your code back to a 10.2.8 machine and compiled it there.

There are no compilers or other command line tools in the SDKs, so you'll need to be sure to configure to use the right compiler version for the SDK that you're targeting. So 10.4, for example, does not target Panther. An example of using SDKs, let's say you wanted to build against 10.4 API, take advantage of that, but then deploy on 10.2, only require 10.2. In this case, you specify 10.4 as the SDK, and set a setting in Inspector called Mac OS X Deployment Targeted 10.2.

So if we look at our timeline of OS releases here, we had 10.0 way back when, seems like forever ago, and then all the way up to 10.4, and we're probably going to be releasing some other version of Mac OS X at some point in the future, so I just put a question mark there. Not pre-announcing anything.

What you would do is to, in fact, I think that may have been announced in the keynote, actually. So, you specify 10.4 as the SDK. You do this in the project inspector. You specify the Mac OS X deployment as the minimum. This lets you unconditionally use any API up to 10.2.

And of course, be careful if it says it's deprecated, you'll want to look at that. You can unconditionally use API up to 10.2. You can conditionally use API up to 10.4. This means that you'll have to check if the function pointers are nil. And do not use future APIs in that. This is kind of uninteresting right now because 10.4 is new, but assuming you say my SDK is 10.3, that really prevents you from using 10.4 API, if that's the choice you made. And Xcode will help by enforcing that.

A couple of notes on debugging with SDKs. Most of the issues you'll find in practice are actually problems that occur both on, let's say, 10.4 and 10.3. If you do have specific issues that only occur on one OS, you can use Xcode's remote debugging facilities. You would have another machine that has the developer tools for the older release, and you would set up your Xcode 2.1 machine to connect to that. You do that in the executable inspector, which defines the launch context for your app. And there's a checkbox there you can specify a remote machine when you debug.

So let's switch gears a little bit, talk about the universal binaries. Related to SDKs, of course, Xcode fully supports building for Intel. As you know, you can create universal binaries, and they contain both PowerPC and Intel code. And for many projects, it really is as simple as clicking this checkbox, as many of you have found in the labs.

You may find that you'll want to set architecture-specific settings. Xcode currently has some of the build settings have architecture-specific forms, and the way you set these are to add them to the inspector using the notation I'll describe here. Keep in mind, again, if you have a setting, even if it's architecture-specific, if it's Intel-only setting, if it applies to all targets, you'll want to set it at the project level.

The general form of these settings is the same basic setting name without the architecture, and then an underscore, and then the architecture name. Now, for historical reasons, the Intel architecture is called i386. As you see, this doesn't mean we actually support a 386 chip. This means this is just the name of the architecture, and the chip model under that is separate. So in this case, if you have other C flags, you can have other C flags under bar i386. Incidentally, the same works for PowerPC 64. That's considered a third architecture from the build system's perspective.

You can also specify different SDK for different architectures. If you want to target Mac OS 10.3 and only use X.3 API, that's great for PowerPC, but for your Intel binaries, you're going to need to build against the X.4 SDK. So what you would do here is to specify your primary SDK, the X.3, it would be in the General tab of the Project Inspector as usual. And then, in the target, you would define an SDK with the architecture name in it, and that would override the one inherited from the project. So this shows an example of using an I3D6-specific SDK 10f4u that overrides the 10.3 one.

And some of the settings, as I mentioned, are configurable per architecture. Some of the more important ones are SDK root. I use the notation $ to indicate the architecture that's being compiled for at that time. When you build for Universal, Xcode takes each architecture and iterates through the target for that architecture.

So that the lowercase a-r-c-h variable, or the build setting, will actually be set to the architecture that it's compiling for in that iteration. And of course, if you only choose one of the checkboxes in the sheet for architectures, it won't iterate. It will just go once through it.

It's important to note, as I mentioned, that the architecture-specific version of the SDK overrides the non-architecture version. It really doesn't make sense to have both. But settings, the flags, are additive. So first the common settings would come, and then the architecture-specific settings when we're constructing the command line for the compiler. So this is very useful to know.

I mentioned that in more specific detail on this slide. If you have generic flags to set, let's say this warning sequence point, I managed to find one warning that you could set in GC that you couldn't set from the GUI, so here's one of them. And then you have additional flags for I3-D6.

The command line that you get out of that would have the generic ones first that apply to all architectures, and then the I3-D6 specific ones after that. That's useful because very often you can negate or invert the selection made in a previous setting by using a later setting, so you can actually override on the GCC command line.

Let's talk a little bit about version numbers. So Xcode supports project versioning. There is a pop-up that lets you choose Apple generic versioning. Most software products that you build, whether they're shareware or anything at all, they have at least a build number. Some of them also have a marketing version number. And there's a command line tool you may not know about called AGV tool, stands for Apple generic versioning tool, that helps with that.

And it supports project versioning. It supports both build and marketing versions. So the way you would configure this is you would configure the project to use Apple generic versioning. The version number can be anything you define. And in this case, I'm setting it at the project level, as you notice, because the version number is typically inherited by all targets, or you typically want it to be the same for all targets.

And you could have it be the build number or marketing number. Then from the command line, you can use AGV tool for example, as part of a nightly build to increment the build number or some other script. And it operates on the project, and it can set the marketing or the build number. You can configure it to automatically check in changes into CVS. And as you know, Xcode supports subversion and Perforce as well. So we'll probably end up updating the script for that as well.

It's best to quit the project before running Xcode, because right now the tool actually modifies the project file. Sorry, I think I said before running Xcode, I meant before running AGV tool, and then have AGV tool operate on it. So it's really mostly meant from a batch processing perspective.

You can, of course, use AppleScript also to set the build setting that encodes the version number. So that's a way to do it if you want to do it from within the IDE. And there's more info in the documentation, but I wanted to raise that as something to be aware of. You might get good usage out of that, good mileage. Some of the options, it supports a fair amount of options, and I encourage you to look at the docs for more information about that.

A couple of things you can do to your targets. One of them is to set up precompiled headers. This is a really important thing for your build time, and I'll talk a little bit about that. Also, Xcode 2.1 supports processing info plists. So, precompiled headers, usually most of the code seen by the compiler, if you're not using precompiled headers, is very redundant. It usually comes from headers.

It comes from headers that are typically the system headers. The Cocoa headers are very popular. The compiler sees those over and over and over and over again, and they never change, at least until you install the next version of Mac OS X. And if you use an SDK, they never actually change, because even when you upgrade your Mac OS X, the SDK remains stable, which is one of the advantages.

So just a small example, a pretty extreme one, 54 lines of sample code, and I think about 900,000 lines of total code seen by the compiler. So that's a pretty low percentage, right? 0.06% of interesting content during that compilation. So the solution is to create a precompiled header. So what you want to do is you want to create a prefix header that gets implicitly included at the beginning of each compilation unit, and then you set that up to be precompiled.

And in the prefix header, you put the common headers that don't change a whole lot. Xcode will precompile it on demand, so it doesn't actually compile it unless you use it. And if it precompiles it, it only does so once, if it's shareable between -- Even the projects, not only the targets, but even the projects.

So if you have the same prefix header containing the same include files, and if you have the same set of flags, then Xcode will actually share the precompiled header and only build it once, even beyond however many targets -- however many projects you have and share between them. Do keep in mind that if anything at all that goes into the precompiled header changes, we have to rebuild the precompiled header. That's pretty obvious. So Xcode will do that for you automatically, no problem. Everything will work.

But you don't want to be putting a lot of volatile headers into the precompiled header because you'll have to rebuild it over and over again, and that defeats the purpose. So put infrequently used -- infrequently changed headers into the precompiled header, and everything should be great. Another thing to be aware of is that the prefix header gets prepended to every source file, so it has to be aware of C versus C++. You can easily do this with #ifdef conditionals.

The InfoP-List processing, there's actually one kind of InfoP-List processing that's been around for a long time in Xcode. And that is that, well, I should point out, I jumped ahead here, the InfoP-List, if you don't know, it's defined by the Mac OS X system manual. It is a property list. It's a text-based format that contains information about your product, such as your application or bundle or kernel extension.

So, you can see here, the version numbers, the name of the executable, these are all things that are in there, document types. Some entities in there are localized, and so there is another file that contains just human-readable strings. And a lot of the information in the InfoP-List, such as the version number, is information that exists elsewhere.

We talked about Xcode's versioning support in the previous set of slides. So, why not just take the version from that and expand that in the InfoP-List? Turns out you can do exactly that. There's two ways to do it. You can refer to build settings from within the InfoP-List, or you can run the InfoP-List through the preprocessor.

So, to expand build settings, it happens automatically. All you have to do in this case, for example, is to just refer to the executable name, which is the sort of machine name for that little text field there that says what the product name is. And then there's the current project version, which is the internal name for the inspector entry that sets what the version number is. You saw that in the previous screenshot here. And when Xcode copies the info pillars to your product, it will just expand these as it goes.

And the other kind is to actually run the InfoP-List through the preprocessor. So this is enabled using the project or the target inspector. And there you can also define macros and other, for example, header search paths that are unique for your InfoP-List. In other words, in addition to what gets used for your source files. And the graphic here shows where to set that up.

So pretty straightforward, just checking a checkbox, and we'll run it through the preprocessor each time. And so what can you do with that? Well, because it's run through the preprocessor, it expands macro directives and macro references and any directives you have. In this case, the sample case, you can actually have a header file that includes some definitions, and then you can refer to those from within your InfoP-List.

You can also have conditional sections of your InfoP-List that simply disappear or appear. depending on your preprocessor macros. So, what I want to do is to do a little demo of some of these project configuration options. So, let's go ahead and switch to demo one, please. Ah, we're already there.

So in an earlier session, Bertrand encouraged the use of open source, so I figured I would do a little demo involving that, and also show cross-project references. So I'm going to go to the developer folder here. I'm just going to find one of the examples, and I'm going to go into OpenGL. This is what you all have on all of your machines. I'm just going to copy this GLUT toolkit example up here, and then we'll find the Xcode project and launch that.

So Xcode 2.1 has a new and improved project file format, so the first question is, do I want to upgrade my project? And I want to do that, so I'm just going to go ahead and save it using the recommended name. And this comes up, and can everybody read that in the back? And so, let's, most of you have probably seen this before. Let's just go ahead and do build and go here, and we'll take a look at what this, this is a little nifty little thing here. I guess it's pretty small. I can make it bigger. It moves around, but it doesn't really do much.

So, what if we add some physics to this? So, let's go ahead and quit this, and I'm going to go ahead and close here. Turns out that there is a really nice open source package called ODE, which is Open Dynamics Engine, I think. So, I just have an URL to that here, and we'll see if the web page is up. Otherwise, I have a local copy of it. Just wanted to show you that I haven't fiddled with the source code here. Looks like the network has gone down between the time I tested it and now. So, I'm going to take my own copy, which is identical.

We'll see if that finishes. Okay, there we go. So what this does is it's just a piece of open source, and it's a good demo because it actually compiles nicely for Intel as well. Okay, so there we go. So that was the downloaded version. So I'm just going to drag the other one to the trash and quit this. So here I have my little open source, piece of open source here, and a whole bunch of files in there. So what I'm going to do is I'm going to create another project because I wanted to show the cross-project references here.

And I'm just going to go ahead and create an empty project. And what I want to do is I'm just going to call this ODE 0.5. They've come up to version 0.5, which is surprisingly stable, given the name of it. And I'm going to choose to just put that on my desktop.

So what I want to do now is create just an empty project that has no targets of any kind. And the reason for that is I'm going to create an external target to build ODE. As you can tell, there are no file references, no groups, or anything at all like that. What I want to do, though, is I'm going to open up the ODE folder.

I'm just going to drag in the source code that I expanded. This is the folder containing the source code from the net. And then what I want to do is just drag this into my project. So this is a very convenient way, if you're working with open source, you can create kind of a wrapper project in Xcode.

You can say to not copy the items into the project. And instead of recursively creating groups which hard codes the file references in the project, you can create a single file reference to just the top level folder. This has some advantages and disadvantages. One advantage is that it's dynamic. As you change the folder structure on disk, it's going to change in Xcode. The disadvantage is you can't edit it and set as many reference settings as you could if the references were actually part of Xcode.

I'm going to want to configure this real quickly here. So it turns out that there are some user settings I can set. So this is a fairly nice one here. It's already configured for OS X, and it actually uses a whole bunch of X11. But what I'm going to do is I want to build the library only. So I'm just going to create a new little make target.

Again, this is not part of Xcode, but what I'm demoing is showing open source that has its own build system and how to call that through Xcode. So I'm going to go ahead and create a new target. I think that's the right one. And let me see what that target was called there. I did the research earlier. Yeah, odelib. So I'm going to save that file.

And what I'm going to do is to create a new target now. So I go into targets, and I'm just going to use the context menu here. You can do this through the menu bar too. Create an external target. I'm just going to call it ODE. And I'm going to create a new target.

And I can now edit the target here. Turns out that this project has a nice little setting in its make file for where to put the output. So I'm just going to take and use the setting that we talked about earlier. I'm going to see if I can enable zooming here just to show you what I typed there.

So that's what I was typing. What I'm doing here is I'm defining a variable for the Make project, and I'm doing that on the command line invocation of Make. So this is how I can use Xcode to control what the Make file in the external target does. So that's the point of doing that.

And then what I want to do is I want to have the make tool here start out in the directory that contains the ODE sources. This started out as being an absolute path, and so what I'm going to do here is, because I want to make my project a little bit portable, I'm just going to make that be a relative path.

So let's go back out. Okay, so that's great. So I have that. What I can do now is to build this, and I'm just going to click the hammer, and we're going to see some build results. Now, this particular project emits some errors at the beginning, but there's actually a nice little line in there that says...

[Transcript missing]

it tried to output to a directory that didn't exist. So one of the things that I want to do, I built-- I wanted to build everything into the same directory, debug versus release.

The example from GL, by default, had configurations that were named development deployment. So I'm just going to call that debug. I'm going to call that release. I'm actually going to take out the defaults, because I don't need that. I'm going to go ahead and build that again, just to get it into the shared build location. When I build now, the directory will exist, and it's going to create the static library. So now we have two separate projects. One wraps the open source project. The other one is just the standard GL examples.

So what I'll do now is I create a cross-project reference by just dragging the project. I say I don't want to copy it in. I just want to refer to it. I don't want to copy the actual project. And I'm not actually going to add it to any targets, because it's just a reference to a project. So I hit OK. What I'd like to do now is to go to my target, which I wanted to improve here a little bit. I'm going to inspect the target.

And zoom in a little bit so you can see. And then I'm going to add The remote target. I'm going to click Add. So now you can see that I've created a reference, a dependency from my target to a target in another project. And then because the remote project uses a make-based build system, it doesn't communicate to Xcode where the products live. So what I'm going to do is I'm going to go ahead and edit the header search paths.

I'm going to take the include directory from ODE, and as you see, Xcode automatically made that relative to my project. I'm going to hit OK. And then from looking at the open source project, I These were in there by default. I noticed that I have to link in the static library. And ODE also uses C++.

The OpenGL demos do not. So I'm going to go ahead and also link against the C++ runtime library. That's just depending on which open source project you use. And now I'm going to go ahead and build this and build succeeded. Now, I haven't actually made any changes yet to the source code.

So, let me go ahead and do that real quick. I'm not going to type a whole lot of stuff. I actually have something already written. So, wow, this is really big. So, I'm just going to take actually the contents of the source file here and just replace with what I had

[Transcript missing]

Now I'm going to go back to the GLUT examples.

I'm going to make this project depend on

[Transcript missing]

And now, if I build my example, it should go into-- here we see it going into the remote project, building the open source target. We again get these harmless errors, which it emits as errors, but they don't actually stop the build. Then we build and we link the underwater target.

If I open this up here, we can see that it compiled each file twice, and linked each file twice, and that was for the Intel and the PowerPC side. And then, of course, I can go ahead and run, and I see the physics animation up here. So, small example of cross-project references, one of them wrapping an open-source project, and using universal binaries, and even using the SDK from within the open-source project. So, with that, could I have the slides back? Thanks.

[Transcript missing]

This is Friday afternoon. There are a few related sessions remaining. One of them is the DevTools Feedback Forum. So that's at 5:00 PM. And I encourage you all to go there and let us know what you think and what you would like to see us do for next year. And with that, I'd like to ask Matt Formica to come up and handle the Q&A here. First of all, Matt Formica, the DevTools evangelist, and we also have the Xcode user discussion list.