Tools • 42:10
Take an in-depth look at migrating your Cocoa or Carbon project from Metrowerks CodeWarrior and Apple's original Project Builder to the new version of Project Builder for Mac OS X. This includes advanced project editing, compiler transition issues, and how to handle special build needs for your project.
Speakers: John Graziano, Anders Bertelrud, Mike Ferris
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
So today I'm going to talk about basically some of the nuts and bolts features of Project Builder. And we're going to have a few of the engineers up here. What we're going to cover in detail is basically what should be your first day of using Project Builder. We want to talk you through getting the code that you have into the new Project Builder. I'll cover the first bullet up there, importing a project to Project Builder.
Then Anders Bertelrud is going to come up, one of the designers of the build system, and talk about how we customize the build process to support some other features that you may have in your project. And then finally, Mike Ferris, who is the lead designer of the CFBundle objects on Mac OS X, will talk about how you actually create those bundles for Mac OS X.
So just to recap what we covered in the last session, Project Builder sits at the middle of a lot of other tools. It's sort of the central clearinghouse for the developer experience. We talked to Interface Builder. We talked to the performance tools. We'll be integrating with all the WebObjects tools. And we invoke command line compiler and debuggers to do the real work of producing your project and debugging it.
So this is what Project Builder looks like today. In order to actually get your code into it, we've built in some support for three different streamline transitions, one taking a basic source code project. Taking a Project Builder Woe project, the old pb.project files, and also we've built in fairly recently support for importing CodeWarrior projects. So importing source directories. Importing source directories is fairly straightforward. This is a mainline feature of the application. So we allow you to do it not only when you're creating a new project, but whenever you want to add some more source code to your project.
Basically, in creating a new project, you can make the project either an empty project or any of the existing project templates that we support. Make sure you have some targets in there. It makes it a little more convenient when you actually import the source code. And then just grab some folders out of the finder, drop them into our group view of Project Builder, and you're good to go.
You'll get all the factory default build settings that you usually would have. So you may have to do some work there if you're importing a bare source code directory. For importing pb.project files, the old Project Builder or Project Builder Woe or whatever we decide to call it today, we have a very sophisticated automatic import feature.
This is a feature that we used ourselves to bring the PBX code base into our own IDE. So we initially started developing on the old Project Builder when we got enough stuff there. We did switch the entire code base. We switched the entire team over to developing on our own app. And we've had a lot of testing on this because there are a lot of people who are using the old Project Builder at Apple and need to get their code into the new system.
So we've had a lot of testing. We have a lot of features on this. So if you currently have a pb.project file, this should be a very simple process for you. The one caveat is that if you've written sophisticated makefile post-AML and pre-AML in old Project Builder users, we'll know what that is. The makefile pre-AML will have some settings of build variables.
You may have to go into the target editor and set up some of those settings yourself. And if you have sophisticated build actions that you've put into the post-AML, we do have support for that in the new environment, but our model is very different. So we aren't able to give you an automated transfer of those build actions. But we do give you a very straightforward way to implement them. And hopefully it shouldn't take much time, unless your project is extremely sophisticated.
We also support Code Warrior projects. The Code Warrior projects that we support are the XML Code Warrior projects. So you'll need to output an XML-based file from Code Warrior. And then again, just invoke our automatic import feature. We'll bring in all of your groups and files. We'll bring in the compatible targets as far as we can go. Some of the rad features and things are not, of course, in our project model, so they don't translate over.
And we'll translate your compiler settings, again, as best we can, given that we're running on a tool that's very different from what MetroWorks uses. And again, as you can imagine, once you import the Code Warrior project, you'll have to tune it a little bit. You'll have to tweak some of the settings to get some of the features that you're going to want. And as we see some of the tuning that needs to be done, again, this is a newer feature, we'll be adding more and more automation, and hopefully get better and better, and eventually on par with the current level of old Project Builder import that we have.
So now I'd like to bring up Anders Bertelrud. He's one of the designers of our build system. And he's also the guy that's responsible for that whizzy slide views that you see in the UI. So what we're going to do is just cover the three ways that I just talked about of getting your source code into Project Builder. The first way is going to be a simple import of a source directory. We'll use the simple text sources to show this.
So the first thing that Anders is going to do is bring up the new project. Because we want to start out with a Carbon application because we know that simple text is going to be eventually a Carbon application. So he'll select that and choose a spot for it on the file system.
and then we're ready to go. So what we get here is basically created out of our project template and it does give you a lot of nice factory defaults. It gives you default target. It gives you some standard groups. But since we're going to be importing these things, we really don't want the sources that we put in here or the resources. So I'll just select those and delete them from the project.
[Transcript missing]
So the other thing that I wanted to show is just the way that we have imported these files. If Anders wants to just bring up the inspector briefly.
If we raise the inspector, what we try to do is make all of these files as relative as possible. That allows you to the most flexibility in terms of structuring your project. So we'll bring everything in as a group relative reference if you've told us to recursively create those groups for you. And of course you're welcome to change that to whatever kind of reference you want. But most of these imports are going to import these relative to the path we've assigned to the particular group.
So why don't we close that project. And now what we're going to do is import the same project, but we've created an old pb.project file in here, and we're going to import it as a pb.project file. So it says pb.woldproject, and what Anders has done here is brought up our file importer, and you can select that off of the file menu. He's selecting the pb.project file.
And all you have to do once you've selected that file is just hit finish. And because we own the PB.Project model, we go through and take every setting that we know about and translate it over into the new project model. So again, we have the groups and files.
And what you'll see here, if you're an old Project Builder user, you'll be very used to the hierarchy that we've set up, very much like the old suitcases that were in PBWO. And of course, the difference is that now you can go through and move these files around however you want. It affects nothing in the build system. It affects nothing in the file system unless you tell it you want it to affect it.
And again, we'll have a very similar target, so Anders can show that target setting. Basically, this is going to look very similar with the added benefit that because we know this format, because we have a lot more information here, we're able to bring over a lot more of your compiler settings and build settings that you're used to having inside of the old Project Builder. So finally, we're going to show the newest feature, which is importing the CodeWarrioR project. And again, we've made simple text into a CodeWarrioR project. We've saved it out as XML. Anders will raise the CodeWarrioR importer at this point. Select the XML file and hit finish.
And what we have is, again, Project Builder project. We've got our sources. We've got all of our files that we expect. Again, this is going to be paralleling whatever structure you have in CodeWarrior. The other thing that we have is, again, a target. And since we actually have two targets inside of the CodeWarrior project, we brought both of them over and translated all the settings that we knew about. And again, this is going to look very similar to the Project Builder project that you've seen before.
So that's how you get your code into Project Builder. And now I'm going to go back to the slides and let Anders come out and talk about How you customize the build process once you've got everything in there. Thanks, John. So now we've seen a couple of different ways in which you can bring your source code into Project Builder. And now that we have a project, I'm going to talk a little bit about how you can customize it. So again, here's how Project Builder fits in. And we've seen the importing. And we're focusing on this part.
So there are a couple of ways of customizing the build. I'm going to talk about three of them in roughly the order of increasing flexibility. First one is just editing build settings, compiler flags, linker flags, preprocessor defines, that type of thing. If you need more flexibility, you can actually modify how a target is built and add your own build phases to, for example, copy files or run scripts at any point in the build. If you have a GNU based project, for example, with its own make file or highly customized build system, you can also, as Dave mentioned in the previous session, you can also invoke a custom build tool to build the project that way.
Editing the build settings. Again, a Project Builder target consists of one or more build settings that do things like compile your source files, incorporate the resources into the product, and that finally link your compiled sources against the frameworks and produce the output. And there are target settings that apply to one or more of these build phases. For example, compiler flags.
Do you want optimization? What kind of alignment do you want? Those sorts of things. We also have other target-based settings such as the install location. If you intend to install this framework somewhere on the file system, where do you intend it to go when you build for deployment? You can also add custom settings. In the first session, Dave showed the target settings panel. And we have a table view where right now it's the expert mode.
And so you can add really any build setting that you want. And those will be available in the build phases that I'm going to describe later. So this is the simplest way of editing your project settings. Just checking switches, entering values and tables. And for many of the projects, this is all you ever need to do.
If you need a little bit more flexibility, you can actually add a build phase to copy arbitrary sets of files or folders to arbitrary places in the file system. You can either copy it into special folders inside of the bundle that you're building. For example, if you're building an application, well, that's packaged as an application wrapper in Mac OS X, and it has various sub-locations. So, for example, if you're packaging frameworks together with your application, they would go into a frameworks subfolder. and you could actually use copy files build phase to accomplish that.
[Transcript missing]
The other thing you can use it for is a post-install action. Suppose you want to change some permissions of a particular file or suppose you want to invoke a tool like NewTar to just package up your project or whatever. You can do anything you like with that.
And finally, the feature that Dave mentioned that we've talked about before for a project with its own customized build system, such as, for example, a new project, a project that you got from somewhere and you want to commit sources, commit your changes back to that without changing the build system, you can use a legacy target.
And what you can do in this case is to take the project, bring it into Project Builder so you can use the editor, debugger, all those nice features, and for the build, you say, "Go use that tool over there." And that could be GNU Make, for example, or it could be any other type of a tool.
And in fact, you can invoke any command you want there, but typically it will be used for Makefiles. And so the one caveat with this is it's great. You get a lot of flexibility. You can override the build system completely, but of course that means that if you add project files, Project Builder's not going to know about that, so in that case you will still have to edit the Makefile. So we expect most people will want to bring their projects in through one of the importers and use it as a full Project Builder project, but we have this legacy target feature in case you're working with open software, things like that.
So I'm going to ask Mike Ferris to come up and help me do a demo of this. So, show off some of these features. First of all, let's go ahead and make the simple text "target" active. It's already active. And let's go ahead and add an icon in this case.
Sorry, I'm on the wrong script here. So we imported the SimpleText project, and if we go to the SimpleText.c file, I've taken a look at this project before, so I happen to know that it has a special preprocessor defined to use the new framework syntax for importing Carbon headers. That define was not set in the CodeWarrior XML project, so let's go ahead and add that now. So let's scroll down to the SimpleText.c there, and we'll just go ahead and copy the string so we can paste it into the build settings.
And if we switch over to the targets, And we go into the build settings. We see some build settings here, the general settings up at the top, the install location. We have some directed user interface at this point for the compiler settings. As Mike shows you, you can collapse those if you don't want to see all of it.
And let's go down to the build flags at the bottom there and add a -d and then paste that preprocessor macro into the other C flags. So we're planning on adding directed user interface for this as well, a nice little list of all the preprocessor macros you want to define, those kinds of things. In the meantime, the expert mode gives you the ability to add anything you want.
So that should be all we need. So let's go ahead and hit Build and watch Simple Text Build here. So as Dave mentioned in the first session, the top part of the pane there shows the warnings and errors that you have. The bottom part shows the expert settings.
That part shows the expert build log. That part can actually be collapsed if you want by just dragging the split view there. We have a couple of warnings that I think are harmless in this case, but we do have some link errors, and those look like the speech synthesis framework is missing. So Mike's going to go up to the project menu.
And we'll add the framework. It's a standard system framework. The open panel takes them to the standard system location for frameworks and just choose speech synthesis. And if we build again, so let's go ahead and add that to the simple text Mac OS target. And as you see, it appears down in the list of frameworks that this target links against. So if we hit build again, it should work fine this time. So it's linking to simple text. And build succeeded. So let's go ahead and run that.
And so we built Simple Text after importing it from the CodeWarrioR project that had been exported as an XML, and we're able to get it up and running and building here. in fairly short order. That's great. Let's go ahead and take a look at some of the other ways in which you can configure the build here.
Let's add a copy-files build phase. Suppose we have a release node file that we want to install into a particular file system location after we build for install. Mike's going to add a new copy-files build phase. As you can see in the pop-up there, we list a set of standard system locations. Currently it says resources, but you can pick anything there that's inside of the application wrapper. These are standard sub-locations. Or you can pick an absolute path, and that's what Mike's going to do in this case.
And just enter an appropriate path in the system for Apple-provided release nodes. You won't be installing here. And we're going to check the little box that says "copy only when installing."
[Transcript missing]
So what we need to do now is to add the actual release note file to the project. And so under the project menu we have add files. We're going to go ahead and add, just have a sample RTF file there. It could be anything. And we're going to add that. And we're not going to add it to a target in this case because we're going to drag it into our special build phase later on. So we just want to add it to the project for now.
and it's going to appear right there under speech synthesis and we can just drag and drop it into the files area of the target. We can drag as many files as we want, any folders as well, and they'll all get put into that location. We can also add other copy files, build faces if we had other locations.
Okay, so now I'm going to show how we could actually add custom build commands as part of the build. So Mike's going to add a shell script build phase and And by default, the shell there is BenSH, but it could be anything. You could, in fact, invoke Perl and invoke some Perl commands as part of your build.
In this case, this may be hard for those of you sitting in the back to read, but Mike's entering a little script that just echoes the one-line timestamp to a log file whenever we build the project. So it just emits a log whenever the project builds successfully. So that's all done. And we're not checking the run only when installing box because in this case we actually want it to execute every time we build the product.
So if we just hit build, we're going to see Then we saw some output in the build log up there, but if we open the build log file, we can actually see that a line was emitted to that file. So right there. So just to show it's not fake, we'll build again and then just revert to show you that it's executing all those commands and it's updating. So this is a powerful way in which you can add any kind of customization you want as part of a build. And it really is quite flexible.
And finally, let's go ahead and show a legacy target. And in this case, I'm actually not going to invoke new make to invoke make. I'm actually going to invoke some other tool, just an arbitrary command. So Mike is creating a new legacy make file target. Let's call it count lines. We're going to have a little command that counts all the lines of your source code.
And the default tool is bin make, and the default parameter is the action. So if you don't change anything, it's going to try to invoke make, which is going to look for make file. And this is probably the primary reason you would use a legacy target. We're just going to enter a fun little command here to just find your source files and pass them through a little tool that's going to count the lines. So just give us an idea of how flexible this can be.
Does that look correct? Looks right. So that's going to find our headers files, our C files, and the resource files. And so let's go ahead and make this target active. If you notice, Mike's checking the little check box there. And we'll just hit build to run this target. And in this case, All it does is just count the lines of source code.
So you can actually use this for a wide range of different uses if you want. And normally, you're not going to need to do this to build your product, but you may want to do this kind of thing to, for example, package up your sources or whatever. And by dependencies, we can also make the count lines be invoked any time the main simple text dot o is built. So just add a dependency, make simple text dot o be the default target, and we can build it. And the output will get emitted to the log, and then we'll proceed with the build.
So this summarizes some of the features of Project Builder. We're working on adding convenient UI so that you should never have to type any of these commands if you don't want to. But the power is there underneath, so you can do pretty much anything you'd like to. And at this point, I'm going to hand it over to Mike, and he's going to talk a little bit about the application features, application packaging features of Project Builder. Mike Ferriss: So I can't emphasize enough how important the application packaging and the bundling structure is to Mac OS X. It's pervasive.
Almost everything on the system is packaged as a bundle. And so it was very important for us to make sure that we had full support for building bundles in Project Builder. We've seen this slide a couple times before, and we're going to be talking about how Project Builder builds stuff.
So I want to just briefly talk a little bit, and then we'll get right on to the demo because that's the most exciting part. So we'll talk a little bit about building bundles. We'll talk a little bit about managing your Info.plist, which is one of the main pieces of your bundle. It provides all the metadata that's needed to tell other subsystems what your bundle does. We'll talk a little bit about resource handling. We'll talk about bundle resources, which are just files that go in your bundle. as well as resource-managed resources for Carbon Apps.
And specifically, I want to touch on localization features and how we support that in Project Builder. And then finally, we'll talk a little bit about how you get just random other stuff embedded in your bundle if you need it to be. So first of all, Project Builder has full support for building bundles. Applications, frameworks, and bundle targets all produce bundles in the end. Bundle targets is what you would use to build, say, a plug-in.
We're going to automatically construct the bundle as part of the build process. So you don't have to configure that at all. If you have an application framework or bundle target, we're going to produce the full bundle for you. And the build system knows where all the pieces go. And for any pieces that you have that are custom, you can tell us where to put them.
Now, the Info.plist is an XML file that each bundle has, which declares various things that are needed by different pieces of the system. So CFBundle, which is the core foundation facility for dealing with bundles, uses a lot of keys in the bundle. For instance, the executable name of the actual binary that is inside your bundle that is the app that's supposed to be run, that's declared in the Info.plist.
The finder also declares various things that it wants to find in your Info.plist to do things like associate documents with your application, to figure out what icon to show for your application, and so forth. And Project Builder allows you to manage your Info.plist completely. And we provide some custom UI for all the common stuff that you want to do. We'll show that a little bit. And we also provide an arbitrary plist editor if you have custom content that you need to put in your plist that you'll use if we've forgotten something. And in the custom UI, you can do it in the expert UI.
So, okay, resources. There's two kinds of resources. The term is a little bit overloaded. Bundle resources are just files that go inside your bundle, and resource manager resources are the resources that you typically find in the resource fork of a Mac OS application. Both of these types of resources in Mac OS X can be global or localizable. When they're localizable, you have multiple versions for different languages that you're going to support.
They can also be platform-specific. So if you need to have a slightly different version of a resource for your Mac OS 9 Carbon application and your Mac OS X Carbon application, you can still package those all in the same bundle. And the current version of the resource is going to be the same as the previous one. will be used depending on which platform the app is running on.
Now, Project Builder also helps you to make sure that the bundles that you're building satisfy the requirements. And the main requirements are that for a localizable resource, you must have a version for your development region. Your project is going to have a development region, so by default that's English, but if you choose to develop your primary interface in French or in Japanese or whatever, you can do that.
The bundle knows what its development region is. Project Builder knows what the development region is. And it's going to make sure that, at the very least, you have a version of each resource for the development region. And then you can have versions for all the other regions as well. And then finally, if you're going to have platform-specific resources, you also must have a platform-generic resource. And that's just in case you find yourself running on a platform that you did not anticipate, there'll be a version to use.
Okay, and then finally, we'll cover, like I said, other bundle content. You can use copy files build phases, as has been mentioned before, to copy stuff into your bundle. Tools can be put into the executables directory of your bundle if your app needs to make use of some command line tool in some cases. You can put frameworks inside the bundle. You can put other support files, whatever you want to put in there.
All right, now I'm going to invite Anders back up to do some of this demoing. So the first thing we're going to do, because of course, Aqua, you have to have an icon for Aqua. So let's add an icon to simple text. Now, the CodeWarrior project did not have the ICNS file as part of the project, so it didn't import when we imported that CodeWarrior project, but I happen to know that there is an ICNS file here. So Anders is going to actually go and add that ICNS file to the project. And we'll go ahead and let it get added to the simple text target.
And OK, you see it appear there. And if we actually go to the target editor for simple text, You'll see that it also appeared in the bundle resources phase. In Mac OS X, icons are just files that go inside the bundle. They're stored in the ICNS format, but they're just separate files that sit inside the resources directory of your bundle. So Project Builder knew to put that file into the bundle resources phase so that it'll be copied when you build.
Now the other thing, though, is that we have to tell the info.p list about our icons so that Finder will know that it's supposed to display it for us. Now we do that in the application settings tab of the target. And you can see that there's a bunch of text fields here. Anders will enter the icon name in the icon field.
And then while we're here, maybe we can set a few other things. We should probably have a creator code other than for question marks, so let's do that. And it's a good idea for bundles to have an identifier string as well. Bundle identifiers are useful for integrating with the CFPreferences facility. They're also useful for locating the core foundation bundle instance at runtime for your application or for your framework or for your loadable bundle. Okay, and by the way, we recommend Java package naming conventions for the identifiers just to ensure that uniqueness.
Now, we have settings for a bunch of stuff in the custom UI here, but there's more stuff, and we'll be adding more custom UI in the future, but we also have this expert mode. And this just shows you the raw property list. You can edit stuff there arbitrarily if you have custom content that you want to put in your plist, because maybe your program wants to access it at runtime, you can do that there. You'll notice that the things that we changed in the custom UI have already showed up here. Now, we won't bother with any other plist settings now, though.
So let's move on to resources and in particular localization. Now, Anders is going to go ahead and go find all the rest of the .R files, because actually in the CodeWarrio project, the only one that was part of the project is main.R, because it just imports all the other ones. But because we want to do some localization, we'll go ahead and add all the other .R files to the project, just so we have them to work with. So Anders is finding all those, and we'll add them.
Now we won't add them to any target because the main.R is already part of our resource manager build phase, and it just imports all the other ones. So we're not gonna bother with adding them to the target, but now they're part of our project. There's one in particular, localized.R, that happens to contain all of the strings that need to be localized in simple text interfaced. So that's the one that we wanna make a localized version of. And to do that, we'll use the project inspector.
And you can see there's a pulldown called localization and platform. And all we need to do is say, make this a localized resource. You'll notice that a disclosure triangle appeared next to it, and if we disclose that, you can see that there's basically a list of the various versions that we have of this resource. When we first make it localized, you're only going to have a version for your development region. But the whole point of this is we want to localize the thing, so let's make another variant for, say, Japanese.
Now notice that it added another line there. So we have English and Japanese now. The English version, or sorry, the Japanese version starts out as just a copy of the English version, but it's a separate copy. So now you can go in and edit the Japanese version to translate it into Japanese.
And the Project Builder build system takes care of all of this. So it knows where the global resources are supposed to go and the localized resources are supposed to go. It compiles them all separately. It puts them in the right places and everything will be automatic. So that's just a taste of the localization support.
And finally, let's just show one more thing, which is let's say that Simple Text had a command line tool that it wanted to run in some circumstances. Of course, it wouldn't show it to the user at all, but it would maybe use it in the background to do some stuff. And you would like to have that command line tool be part of Simple Text. So let's go ahead and create a new target. and it will be a tool target that we'll just call My Helper Tool.
So now we have this new target, and if you open it up, we can see that it doesn't actually have any files in it, and we're not actually going to build a tool now. But this would-- if we filled in some source files to be compiled, this would build us a command line tool.
Now, we want to set up a dependency because for us to build simple text and have it be complete, we also have to build this command line tool. So Anders will just set up the target dependency to make sure that the helper tool builds whenever we build simple text.
And if we switch back to the files tab, When we added the tool target in the products group, which is sort of an automatically managed group of the project, you'll see that a line was added for the helper tool. This line refers to the actual tool that will be produced. It shows up in red because we haven't built it yet, so it doesn't actually exist on the disk, but the reference is there and we can make use of it.
So we'll go back to the simple text target, And we're going to create another one of these copy files build phases. Now we can't use the one we did before because this one is going to copy stuff to a different place. So we'll just do a separate one. And we did an absolute path one before. This time what we want is for the tool to be installed into the executables area of the bundle.
So we just choose executables. If you wanted it to be nested underneath the executables somewhere, you could use that subpath field, but we don't want to do that. We also don't want to do the only one installing because this tool should be part of the thing whenever we build it.
And finally, we have to tell it what it's going to copy. So we just drag in the reference for the helper tool there. And now, whenever we build Simple Text, it'll first build that tool and then make sure that tool gets installed into the executables of the Simple Text app package so that Simple Text can find it at runtime.
Now, this is just sort of a taste of the degree to which we've tried to integrate the bundle packaging scheme into Project Builder's basic model. And we can--there'll be some more detail on some of this in the Mac OS X localization session, especially as it applies to localizing Carbon and Cocoa applications.
For now, I want to invite John back up here one last time. And he's going to sum some of this up for us. And then we'll do some Q&A. So what we talked about today is just the first step in using Project Builder. There's a lot of other stuff that we could have covered today. But we'll leave that for now for you to explore.
Again, you can take any one of these three variants of a project, pull it into Project Builder, use the app then to customize it however you want. And then finally, produce one of the Mac OS X bundles, an app, a framework, or a kernel-loadable module. So there's some support resources, public emailing list if you have some feedback for us. And the feedback alias is Mac OS Tools [email protected]. And we also have the fee-based support with DTS.