Chapter 16. Targets

WHAT'S IN THIS CHAPTER?

  • Creating targets

  • Adding source items to targets

  • Defining target dependencies

  • Customizing targets

Targets are the engines of Xcode. They define the steps that will transform your source code and resource files into a finished product. Targets can be very complex or ridiculously simple. Xcode comes with targets that automatically take care of the myriad details needed to produce standard products such as application bundles, or you can take complete control and choose a target that abdicates all of the responsibility for building a product to you.

The purpose of a target is to produce something. Most targets produce a product. There are different types of targets, depending on what kind of product is being produced. Each target refers to the source files and resources required to produce its product, along with the instructions on how those source files get transformed, how the product is assembled, and the order in which all of those things happen.

Targets appear in the Targets smart group of the project window, as shown in Figure 16-1. Most project templates come with a target already defined, appropriately configured for that project type. If you stick to developing simple applications and tools you may never need to add a target to an existing project. Nevertheless, understanding what a target is and does is a prerequisite to customizing one and is fundamental to understanding the build process as a whole.

FIGURE 16-1

Figure 16-1. FIGURE 16-1

TARGETS VS. SUB-PROJECTS

Anytime you have a project that makes two or more products, you have a very important decision to make: you can add a new target to your existing project or you can create a new project. Which you choose will have far reaching consequences on your project's structure and maintenance. My advice is always: add a new target to your existing project unless there is a compelling need to confine the product in its own project.

In other words, if you can't think of a good reason to make it a separate project, make it a target. Managing, editing, building, and testing a project with multiple targets is generally easier and more efficient than separating the parts into individual sub-projects. The following table lists some of the pros and cons of adding a target to a project versus creating a sub-project:

CONCERN

TARGET

SUB-PROJECT

Project Folders

Targets share the same project and build folders.

Projects generally have independent project folders, but you can place multiple project documents in the same project folder. If separate, source references between them will have to extend outside the project folder. If shared, they must be configured to either peacefully share build folders or use independent build folders.

Source Items

Targets can refer to or share any of the project's source items.

Projects cannot share source items. Any common items will have to be defined twice, and maintained in parallel.

Source Files

The source files referred by source items are inherently shared by all targets.

Source files can be shared through duplicate source items, but Xcode may not recognize them as being the same file.

Build Settings

Each target has its own build settings, but shares the common build settings for the project.

Projects have independent build settings. Build settings common to multiple projects must be maintained in parallel.

Build Configurations

All targets share the same matrix of build configurations.

Build configurations in sub-projects must be coordinated so that they match, or appropriate default configurations defined.

Source Control

All source items in a project share the same source control management repository.

Source control for separate projects must be configured independently, but could (potentially) share the same SCM repository, or a branch therein.

Xcode Management

All targets and source items belong to the open project.

Common items shared by two open projects belong to one or the other, but not both. This can sometimes lead to some confusion about which project is active.

The Ideal Single-Project Project

A single-project project is one where everything produced by the project is contained in a single project folder and described in a single project document. A separate target produces each product of the project. Target dependencies determine the build order. They all share the same source control management, object browser, project search window, build configurations, and smart groups. Examples of this kind of project are:

  • A Cocoa application project that contains an embedded Spotlight indexing plug-in

  • A project that produces a pair of applications, like a client and server

  • A project that builds a suite of POSIX command-line tools

In each of these projects, the benefits of keeping everything in a single project are clear:

  • Targets easily share common source files

  • Centralized build settings and configurations

  • Unified source control management and snapshots

  • Multi-file searches across all targets

  • A single Code Sense index

It is likely that these kinds of projects will need to share common source files, header files, and other resources. They are also likely to all use the same, or a similar, set of build settings. Changing a source file or build setting will correctly rebuild all dependent targets.

The Ideal Multi-Project Project

The kind of project that should be subdivided into multiple projects are those where you want to explicitly isolate one from the other. Idea examples would be:

  • A Cocoa application that uses an open-source processing engine

  • A suite of applications where each component is being developed by a separate team of engineers

  • A framework used by other applications

In the first situation, you have your Cocoa application and another large collection of source files that belong to an open source project. The open source project is probably under its own source control management system, it might require special compiler settings to build, and probably includes a large number of internal functions that your application shouldn't use directly.

Frameworks are particularly well-suited to being sequestered into separate projects because frameworks are specifically designed to be development resources. A project using the framework has access to its API (in the form of header files), resource files, and even documentation without the need to explicitly include those files in the project.

In these situations, it's advantageous to isolate the open source code, individual applications, or frameworks into their own projects, which you can then make your project targets dependent on.

The Project in the Middle

Often, your decision won't be so cut and dried. A good example would be a project that produced a service, like a streaming media server, and a desktop application to control and configure that server.

There may not be that much code, if any, shared by the two products. It isn't clear that they need to be built using a common set of build settings, or whether having separate build settings might not be an advantage. Is each version of the desktop application intimately tied to specific versions of the server, or are the two loosely interoperable? Does one team of programmers work on the server and another on the desktop application?

These are the questions you need to ask when deciding on whether to start a second project or not. If you can't decide, start with a single project and multiple targets. It's much easier to split a project into two than trying to combine two projects into one. To split a project, start by making a copy of the entire project folder. Rename the project document, and then delete all "B" assets from project A and all "A" assets from project B.

THE ANATOMY OF A TARGET

Targets consist of several interrelated parts: dependencies, build phases, build settings, build rules, and a product, as described in the following table. Different target types may include all, some, or only one of these parts. All of the components are described in detail later in this chapter.

COMPONENT

DESCRIPTION

Dependencies

The other targets on which this target depends.

Build phases

Defines the steps required to build a target. Each phase defines a procedure and the source files involved.

Build settings

Variables available to the build phases that can be used to customize the build.

Build rules

Rules that define how files of a particular type are compiled or transformed.

Product

The end result of the target.

You edit most of these parts using various tabs in the target's Info window or via the Inspector palette. To open a target's Info window, select the target and choose Get Info from the File menu or the Right/Control-click contextual menu. For native targets (which are explained later), you can just double-click the target or select it and press the Return key. If the target you want to edit is the active target, use the Project

THE ANATOMY OF A TARGET

Every target has a name. You can change the name of a target by selecting the target group in the project window and choosing Rename from the Right/Control-click contextual pop-up menu. You can also edit it in the General tab of the target's Info window.

Target Dependencies

A target may depend on other targets. A target dependency is a target, either in this project or an external project, which must be built before this target can be built. Target dependencies appear in the target group before the target's build phases — an allusion to the order of build events. They are also listed in the General tab of the target's Info window.

A target build phase may also maintain its own set of internal dependencies. These are the implicit dependencies between intermediate files and the source files used to produce them. These are calculated by the target and are not under your control. Whenever possible, a target only recompiles files affected by changes made since the last build. You can see the internal dependencies at work in the details pane of the project window. The small hammer column displays a check mark for files that need to be rebuilt.

Target Build Phases

A target can have one or more build phases. You can see a target's build phases by expanding the target's group in the project window, as shown in Figure 16-2. Build phases define the broad order in which steps will occur during a build. For example, all of your C source files must be compiled into object files before those object files can be linked together. All of the object files must be linked together before the final executable can be copied into the application's bundle, and so on.

FIGURE 16-2

Figure 16-2. FIGURE 16-2

The order of steps within a build phase is entirely up to that phase. The order within some phases is fixed, whereas others are very dynamic. A build phase that compiles a gaggle of source files may compile them in alphabetical order or in the order in which they were most recently modified. It may compile them one at a time or compile twenty at a time using a network of distributed build servers. Intelligent build phases try to optimize the order whenever possible. Your only guarantee is that all of the work performed in a phase will be completed before the next phase begins.

Target Membership

Here's an important concept about targets and target membership: a source item is a member of a target if, and only if, it is included in one or more build phases of that target.

You see the target membership of items in your project — usually as a check box — in many places: its Info window, in the details pane, in the source group, when you add an item to your project, and elsewhere. It might seem like target membership is a Boolean property of the individual source items, but it isn't. The targets that refer to an item imply its target membership.

Changing an item's target membership has the following effects:

  • Adding an item to a target adds that item to the (hopefully) appropriate build phase of the target.

  • Removing an item from a target simply deletes all references to that item from all build phases of the target.

  • If a target is disabled for an item, it means that no build phases in that target are compatible with that item's type.

For most targets, adding and removing an item from a target is a simple and predictable operation. In the case of a target that contains two or more build phases compatible with an item, you might need to fix the target membership in the target — a simple add might not insert the item into the correct phase, or phases. This is described in detail in the "Files in a Build Phase" section, later in this chapter.

Target Build Settings

If a target includes build settings, you can edit those settings in the Build tab of the Info window or Inspector palette for that target, as shown in Figure 16-3.

FIGURE 16-3

Figure 16-3. FIGURE 16-3

Targets can also have multiple sets of settings called build configurations. The project itself has multiple sets of build settings. Because build settings and build configurations are not unique to targets, the editing of build settings, the management of build configurations, and how they all interact with each other are explained in Chapter 17.

Target Build Rules

Targets that compile files have a set of build rules. Build rules tell a target what compiler or translator to use when building each source file. The build rules can be modified in the Rules tab of the target's Info window, shown in Figure 16-4. Each rule is a pairing of a file type, which you select using the Process menu, and a compiler, which you select using the Using menu. If necessary, you can define your own rules or redefine standards.

FIGURE 16-4

Figure 16-4. FIGURE 16-4

Target Product

Most targets produce a product. The kind of product produced is determined by the type of the target. Application targets produce applications, library targets produce libraries, command-line targets produce UNIX executables, and so on. A special type of target, called an aggregate target, doesn't produce anything itself. It exists solely to group other targets so they can be treated as a single target. In other words, it's a target that "produces" other targets.

TARGET TYPES

It's difficult to classify target types in Xcode neatly, because the types form a kind of spectrum. However, the spectrum of target types can be roughly divided between the native and non-native targets. Native targets are at one extreme of the spectrum. They are sophisticated, are tightly integrated into Xcode, are highly configurable, have a flexible number of build phases, and produce complex products (like frameworks). At the other extreme is the non-native external target. An external target simply launches some external process with the understanding that said process will do whatever is necessary to build the target. Xcode doesn't know what an external process will do, what files it depends on, or even if it produces anything. An external target has no build phases and is not configurable (from within Xcode). In between these two extremes are target types such as aggregate targets and the legacy Jam-based targets. The sophistication of these target types varies. They may have some of the same parts as native targets but are usually simpler and are not as tightly integrated into Xcode.

The type of a target is displayed in the General tab of the target's Info window. You cannot change the type of a target. If you find you are using the wrong target type, you must delete the target and create a new target of the correct type.

Native Targets

The native target types in Xcode are Application, Command-Line Tool, Dynamic Library, Static Library, Framework, Bundle, Kernel Extension, and IOKit Kernel Extension. Native targets are easily identified by their colorful and emotive target icons. Application targets have a small application icon, Command-Line Tool targets are represented by a little terminal screen, Framework targets have a toolbox icon, and the remaining targets appear as plug-ins. Native targets usually have a full complement of parts (build phases, settings, rules, and dependencies). Native targets that include an Info.plist file in their product also include a set of properties. See the "Properties" section, later in this chapter, for more details.

External Targets

An external target defines a build target that is produced by some external process. This was designed to permit Xcode to integrate with existing workflows based on build tools like Make or Ant. The target is little more than a placeholder. It specifies the tool that will perform the build and the arguments that are passed to it, as shown in Figure 16-5. Xcode has no knowledge of what files the process requires, what it does, or what it will produce. You cannot add project files or build phases to an external target. The section "Java and Other Jam-Based Targets" has more information.

FIGURE 16-5

Figure 16-5. FIGURE 16-5

Aggregate Targets

Aggregate targets, as the name implies, group several targets together using dependencies. An aggregate target that depends on several different targets can be used to build all of those targets as a unit. Suppose you have developed a suite of BSD command-line tools, each produced by a separate target. To build all of these tools at once, you would create an aggregate target, say it's named "Tools," that depends on all of the Command-Line Tool targets. Now whenever you want to build all of the tools, you simply build the one Tools target. Likewise, you might have applications or other projects that depend on having all of those tools built. Making those targets dependent on the single Tools target is much easier to maintain than adding each tool target to every target that depends on them.

Aggregate targets can also be used for utilitarian purposes. Aggregate targets don't produce a product, but they can still be made to do useful work by adding Copy Files or Shell Script build phases. These build phases are executed whenever the target is built, regardless of whether it has any dependencies. See the section "Build Phases" for more details about adding build phases to a target.

Java and Other Jam-Based Targets

Xcode still includes support for the legacy Jam build system, which it inherits from its predecessor, Project Builder. A Jam-based target is a non-native target; the target's build logic is external to Xcode, and the target's configuration isn't integrated with the rest of Xcode. Jam-based targets are used to implement the external and aggregate target types, and to support legacy targets that were created in Project Builder or obsolete versions of Xcode.

Using Jam-Based Targets

All Jam-based, non-native targets (as well as the aggregate targets) appear as a red bull's-eye in the Targets smart group. Editing the details of some Jam-based targets is different from editing native targets (you configure native targets using the different panes in the target's Info window). All Jam-based targets are configured using a target editing window, like the one shown in Figure 16-5. See the "Jam-Based Target Editor" section later in this chapter for the details.

Upgrading Jam-Based Targets

Except for the external and aggregate targets, you'll probably want to upgrade any other Jam-based targets to native targets whenever possible.

Convert your Jam-based targets into a native target using either the Project

Upgrading Jam-Based Targets

The conversion also results in a report, an example of which is shown in Figure 16-6. In addition to making a new target, the conversion process may duplicate project files. The example in Figure 16-6 shows that a duplicate of the Info-StockMarketTicker.plist file was made and was named Info-StockMarketTicker__Upgraded_.plist. Delete the original .plist file when you delete the original target. You may also want to rename the new .plist file, which requires editing the new target's settings to match.

FIGURE 16-6

Figure 16-6. FIGURE 16-6

CREATING A TARGET

Creating a target is much like adding a new file to your project. Choose the Project

CREATING A TARGET
FIGURE 16-7

Figure 16-7. FIGURE 16-7

Choose a target template from the list. You'll immediately notice that there are far more templates than target types. Many of the templates produce targets of the same type, but are preconfigured for different purposes. For example, the Aggregate, Copy Files Target, and Shell Script Target templates all create an aggregate target with no build phases, one Copy Files build phase, or one Run Script build phase, respectively. Because build phases can be easily added or removed, the differences among these templates are trivial. Choose a target template that is as close as possible to the type of product you want to produce.

What is of utmost importance is to create a target of the correct type, because you cannot change the type of the target later. If you end up with a target of the wrong type, your only option is to delete the target and start over. The rest is just details.

After you've selected the target template, click the Next button. The assistant presents you with a dialog box to enter a name for the target and select the project you want it added to, as shown in Figure 16-8. The product produced by the target initially has the same name as the target. You can alter both the target name and product name later, but to avoid confusion, I suggest keeping them the same whenever practical. If you have more than one project open, select the project that will receive the new target from the Add to Project menu. Click the Finish button.

FIGURE 16-8

Figure 16-8. FIGURE 16-8

Some templates will also add one or more support files to your project. For example, almost any target the produces a bundle (application, framework, plug-in, unit test, and so on) will also need a Info.plist file to describe its contents. Adding a new bundle target automatically adds a new Info.plist file to your project, typically with a filename that mimics the name of the target.

Target Template Groups

Target templates are organized into groups to make them easier to locate. Start by choosing the group that most closely describes the type of development you are doing (Cocoa, Cocoa Touch, BSD), and then choose your template. Be careful of similarly named templates. The Application template in the Cocoa group produces a significantly different product than the Application template in the Cocoa Touch group.

Cocoa Touch Templates

Templates in the Cocoa Touch group create targets suitable for producing iPhone applications, static libraries, and unit tests. These are similar to the targets in the Cocoa group, but have different compiler settings and link to different frameworks.

TEMPLATE

DESCRIPTION

Application

A native iPhone or iPod Touch application target. The template adds an Info.plist file to your project.

Static Library

A native Static Library target that produces a static library file.

Unit Test Bundle

A unit test bundle, linked to the Foundation framework. Also adds an Info.plist file to your project. See Chapter 20 for more information about unit testing.

Cocoa Templates

Use these targets, listed in the following table, to produce Mac OS X applications, libraries, and shell tools.

TEMPLATE

DESCRIPTION

Application

A native Application target that produces an application bundle linked to the Cocoa framework. This template adds an Info .plist file to your project.

Dynamic Library

A native Dynamic Library target that produces a .dylib library file, itself linked to the Cocoa framework.

Framework

A native Framework bundle target. This template adds an Info .plist file to your project.

Loadable Bundle

A native Bundle target that produces a generic bundle linked to the Cocoa framework. This template adds an Info.plist file to your project.

Shell Tool

A native Command-Line target that produces a BSD executable binary.

Static Library

A native Static Library target that produces a static library file, itself linked to the Cocoa framework.

Unit Test Bundle

A native Bundle target configured to produce a unit test written using the Cocoa framework. See Chapter 20 for more information about unit testing. This template adds an Info.plist file to your project.

The Loadable Bundle is the catch-all template for creating virtually any kind of bundle. If you can't find a template for the particular type of bundle (application, plug-in, and so on) that you're trying to create, start with the Loadable Bundle template.

Application Plug-In Templates

The only template currently in this group is the Automator Action template:

TEMPLATE

DESCRIPTION

Automator Action

A native Bundle target that includes a Compile AppleScript Files phase, suitable for producing Automator Action bundles. This template adds an Info.plist file to your project.

BSD Templates

The BSD templates, listed in the following table, create targets that produce flat BSD executable or library files.

TEMPLATE

DESCRIPTION

Dynamic Library

A native Dynamic Library target that produces a .dylib library file.

Object File

A native Object File target that compiles a single module using the BSD APIs.

Shell Tool

A native Command-Line target that produces a BSD executable binary.

Static Library

A native Static Library target that produces a static library file.

System Plug-Ins Templates

The two templates, listed in the following table, create the specialized targets used to produce kernel extension bundles.

TEMPLATE

DESCRIPTION

Generic Kernel Extension

A Kernel Extension target that produces a kernel extension bundle.

IOKit Driver

An IOKit Kernel Extension target that produces a kernel extension bundle.

Other Templates

The targets listed in the following table create empty or placeholder targets. None of these targets produces a product, although the process launched by an external target is expected to build something.

TARGET

DESCRIPTION

Aggregate

An empty Aggregate target.

Copy Files

An Aggregate target with a single Copy Files build phase.

External

A Jam-based External target. External targets run some external process (like make) to build the target. They cannot have build phases.

Shell Script

An Aggregate target with a single Run Script build phase.

Legacy

If you upgraded an earlier version of Xcode, your installation may contain additional target templates that have since been removed from the Xcode Developer Tools. These might include Java Applet, Carbon, and other deprecated targets. For the most part, Xcode still supports these legacy target templates, but may not in the future.

Duplicating Targets

You can also create a new target by duplicating an existing one. This is especially useful if you need a new target that is very similar to one that already exists. Select the target in the Targets smart group. In the Right/Control+click contextual menu choose the Duplicate command. A duplicate of the target is created with the suffix "copy" appended to the target's name. Note that the new target is an exact duplicate except, of course, for its name. If the original target produced a product, the new target will produce the same product. Assuming you want the targets to build separate products, remember to edit the settings of the target so the two products don't overwrite one another.

Deleting Targets

To delete a target, select the target in the Targets smart group. Press the Delete key or choose Delete from the Right/Control+click contextual menu. Xcode presents a warning that removing a target will also remove the reference to it in any targets that depend on it. Click the Delete button to acknowledge the warning and delete the target.

BUILD PHASES

Build phases define the steps that must occur in order to build a target. Build phases tend to paint with a broad brush. Targets typically have one Compile Sources build phase that compiles all of the source files for that target, even if that includes source files from many different languages. Consequently, targets rarely have more than three or four build phases.

Use the disclosure triangle next to the target's name in the Targets smart group to reveal the build phases for the target, as shown in Figure 16-9. The list of files on which a build phase will operate, or depends, is referred to as the phase's input files. Selecting a build phase lists the input files in the details pane of the project window. Alternatively, you can use the disclosure triangle on a build phase to list the input files in the Groups & Files list. The number of input files is listed in parentheses after the name of the build phase. Note that this applies only to build phases that process files using Xcode's internal build system. The input files listed for a build phase include only those source files that you have explicitly added to the build phase. Intermediate files produced by another build phase are not listed or counted.

FIGURE 16-9

Figure 16-9. FIGURE 16-9

Build phases occur in the order in which they appear in the target. Build phases can be reordered by dragging a build phase to a new position in the target. Be very careful when doing this. The reason build phases exist is because build steps have to occur in a specific order for the build to be successful — you can't link code before it's compiled. About the only time you'll find yourself reordering build phases is if you add your own build phase and need that phase to occur before or after another phase. You can also reorder the files that a build phase includes. For some build phases, this may be a significant change. For others, the order of files is superfluous. You cannot drag build phases into other targets, nor can you copy them via the clipboard.

New build phases are simple to add. Select a target and choose a new build phase from the Project

FIGURE 16-9

To remove a build phase, select it and press the Delete key or choose Delete from the Right/Control+click contextual menu.

Files in a Build Phase

Every build phase has a set of preferred source file types. A Compile Sources phase prefers the source file types it has build rules for (.c, .cpp, .m, .l, and so on) — build rules are described later in the "Build Rules" section. The Copy Bundle Resources phase prefers the resource files one typically copies into an application's bundle (.tiff, .icn, .png, and so on). When you add a source file to a target, the file becomes an input file for the first phase that prefers that file type the most. If a target contains both a Copy Headers phase and a Copy Files phase, adding an .h file to that target adds the file to the Copy Headers phase. The Copy Headers phase is more specific than the Copy Files phase, and prefers .h files more than the Copy Files phase does. If a target contains only two Copy Headers phases, adding an .h file adds it to the first Copy Headers phase in the target, because both phases prefer an .h file equally.

Adding Items to Targets

You typically add files to targets via the targets list that appears when the file is created, added to the project, in the Info window for the file, or using the target check box column in the project window. This is usually foolproof and effective, allowing Xcode to choose automatically the appropriate phase to add the file to. If a target has no phases that prefer a source file's type, that file cannot be added to that target using any of the aforementioned dialogs.

Precisely Controlling Target Phase Membership

Using the target group, you have more precise control over what input files are included in what phases, and you can occasionally break the rules. You can add a source file to a build phase by dragging the file into that phase and dropping it. Figure 16-10 shows the Image1.png file being added to the Copy Bundle Resources phase.

FIGURE 16-10

Figure 16-10. FIGURE 16-10

You can move or copy a file from one phase to another. In the previous example, where there was both a Copy Files and a Copy Headers phase, you may really have wanted a specific header file to be in the Copy Files phase, not the Copy Headers phase. Just adding the file to the target puts it in the Copy Headers phase. Grab the file in the Copy Headers phase and move it into the Copy Files phase. Hold down the Option key before dropping it, and Xcode duplicates the reference, adding the file to both phases.

Using drag and drop, you can include a file in more than one phase and you can include the file in phases that don't prefer it. Let's say you have developed a Java application that dynamically produces executable Java code by manipulating a source file template and then compiling it. You want the Java source template file, GenericTransformTemplate.java, to be included in the application's bundle as a resource file. To accomplish this, drag the GenericTransformTemplate .java file into the Copy Bundle Resources phase. When the application is built, this Java source file is copied into the resource bundle of the finished application, something that would normally never happen.

Removing an Item from a Target

You can remove a source file from a target by selecting it and pressing the Delete key or choosing Delete from the Right/Control+click contextual menu. You will receive a warning from Xcode asking if you want to delete the item. Don't panic. You are only deleting the phase's reference to the source file, not the source file itself.

If you have added a source file to any phase of a target, Xcode indicates that it is a member of that target. Removing (unchecking) a target in a target list removes that file from every phase it is included in.

Note

It's possible to create a paradox if you have manually forced a source file to be included in a target that has no phases that prefer that file type; the target for that source file will indicate that it is a member, but the check box control for the target will be disabled (because there are no phases that prefer that type). To remove a file from a target under these circumstances, you must delete the file directly from the build phase or phases.

Build Phase Types

There are nine build phase types: Compile Sources, Compile AppleScripts, Link Binary With Libraries, Copy Headers, Copy Bundle Resources, Copy Files, Build Java Resources, Build ResourceManager Resources, and Run Script.

Compiler Phases

The Compile Sources phase is the most sophisticated of the build phases. It is responsible for compiling any kind of source file. The kinds of sources files it will compile and what compiler it will use for each are determined by the build rules for the target, described later in the "Build Rules" section. The Compile Source phase understands a vast array of build settings that apply to the compilers. It parses all of these and makes the appropriate adjustments to the parameters passed to each compiler.

The Compile AppleScripts phase is similar to Compile Sources, but it only compiles AppleScript source files. The Build ResourceManager Resources phase is another specialized compiler phase just for resource definitions files. It compiles and merges legacy .r and .rsrc files to produce a resource file in the product's bundle, or inserts them into the resource fork of the product file. You're not likely to run across any ResourceManager Resource phases unless you're working on older Carbon projects.

The Link Binary With Libraries phase is the companion to the Compile Sources phase. It takes whatever intermediate files were produced by the Compile Sources phase and links them together, along with whatever libraries and frameworks they need. The input files to the link phase are the external libraries and frameworks that the object files (from the previous phase) need to be linked to.

Copy Phases

The Copy Headers, Copy Bundle Resources, and (legacy) Build Java Resources phases are specialized versions of the Copy Files phase. Their sole purpose is to copy files into the product's bundle.

Copy Headers is designed for use with framework and library targets. It uses the "role" of its input files, be they public or private, and copies them to the appropriate location in the output project. The role of a header is set using the role column of the details pane with that file or phase selected.

Copy Bundle Resources copies any input file into the Resources folder of the product's bundle. This is the phase to use if you want a literal copy of a file stored in your bundle's resource folder for access at run time. This phase is also responsible for constructing and copying the Info.plist file into the product's bundle. (See the "Properties" section later in this chapter for more about how to configure the Info.plist file.)

The Copy Files phase is a utilitarian phase that copies all of the phase's input files or folders to the destination of your choice. That destination is set in the Info window of the phase, as shown in Figure 16-11. The destination location can be either an absolute path or a path relative to the product's location. For targets that produce a bundle, predefined locations are provided that will target many of the standard locations in the product's bundle. An absolute path is just that. Use this to copy files to a particular installation or test location.

FIGURE 16-11

Figure 16-11. FIGURE 16-11

Relative paths require a bit of an explanation. The build location — the location to which the product of a target will be written — is unique for each build configuration. If you have two build configurations, Debug and Release, every target will produce two different products: a Debug product and a Release product. The remaining paths in the destination menu are all relative to the product's output directory. The choices are the output directory itself or one of the standard folders inside a bundle, assuming the target produces a bundle. (See Chapter 17 for a complete explanation of build locations and build configurations.) The path field can be left blank for any choice other than Absolute. Any folders that do not exist when the phase executes are automatically created.

The path can also contain build variables using the form $(BUILD_VAR). The macro will be substituted for its actual value when the build phase runs. By using build variables in the path, you can customize the normal destination location calculated by Xcode.

Note

Speaking of build locations, remember when Chapter 5 mentioned that -product-relative references are based on the active build configuration and target? This fact is most applicable to product file references used as input files to a Copy Files phase. Product references also change based on the active build -configuration, making both the source and destination locations of the phase variable. An example would be an application that includes a BSD executable in its bundle. A Copy Files phase could be used to copy the project-relative product produced by the BSD target into the resource bundle being produced by the application target. Both the source (product reference) and the destination (application bundle) are variable. When built using the Debug build configuration, the Debug version of the BSD product is copied into the Debug version of the application bundle. When the Release configuration is active, the Release version of the BSD product is copied into the Release version of the application bundle.

If the Copy Only When Installing option is set, the copy phase is only executed when the build is being performed with the install option set. You can do this from within Xcode by turning on the DEPLOYMENT_LOCATION build setting or passing the install option to the xcodebuild tool. Both of these are described in Chapter 17.

Script Phase

The Run Script phase is the "backdoor" by which you can interject virtually any custom build procedure. You configure a Run Script phase using the Info window of the phase, as shown in Figure 16-12. The Shell field determines the shell, or interpreter, that will be used to execute the script. Below that is the shell script that will be executed.

FIGURE 16-12

Figure 16-12. FIGURE 16-12

When the phase is built, the shell executable is put on a so-called "she-bang" line (#!/path/to/shell) and the remaining script is appended to it. The whole thing is written into an executable script file and run using the sh tool. One side effect is that the zeroth argument to the script will be the path to the temporary script itself, which makes it relatively uninformative.

The Shell field can be any valid interpreter. It could be perl, ruby, awk, or php. It doesn't matter, as long as the given interpreter can execute the script file. Most of the build settings are transferred into environment variables before the script is executed, so your script has unfettered access to your build settings.

If you're interested, or are having trouble with the build settings passed to your script, check the Show Environment Variables In Build Log option. This option dumps the entire shell environment prior to execution into the build log for later analysis.

Checking the Run Script Only When Installing option prohibits the script from running unless the DEPLOYMENT_LOCATION build setting is turned on or the install command is passed to the xcodebuild tool.

Xcode, of course, has no idea what your script will do, what files it needs, or what it produces. Assuming that the script produces some output files from some input files, Xcode would like to optimize the build by skipping this build phase if the modification dates of the output files are newer than the input files. You can satisfy Xcode's curiosity by manually setting the Input Files and Output Files for the phase. Click the + button to add an input or output file to the list. When the new entry appears, enter the file's path. Sadly, you can't drag a source file from the project window into the list. However, once an entry is created you can drag a source file into the field and then edit the path. It can be an absolute path or a path relative to the project directory. The script is run only if the modification date of one or more of the input files is more recent than the oldest output file. If either list is empty, the script runs every time.

Warning

The files in the Input Files list are not passed to the script as parameters, nor are they piped to it via stdin. They are used by Xcode solely to determine if the script should be run. It is up to the shell script to read the listed Input Files and produce all of the files promised in the Output Files. If you want to reuse a script in multiple targets and need a variable list of input or output files, define them in a custom build setting.

BUILD RULES

Build rules define the transformation of source files into their compiled results. The compiler phase uses the target's build rules to determine which compiler should be used to compile each source file. For example, a build rule might specify that all C source files (C, C++, and Objective-C) are compiled using the gcc compiler.

A build rule applies to all the files of a specific type. The type of a file is determined by its extension or the file type assigned to that source file. A file's type is typically in agreement with the filename extensions, but it doesn't have to be. You can change the file type of a .java source file to sourcecode.cpp. The Compile Sources phase will then apply the sourcecode.cpp rule to that file as if it were a C++ source file — probably without much success.

You'll probably never need to alter the build rules in Xcode. However, there are several reasons why you might want to:

  • Force Xcode to use an older, newer, or custom compiler.

  • Add a rule to compile a source type that is not normally compiled by Xcode.

  • Add a pre- or post-processing script to every compilation.

  • Define your own transformation.

You can examine and alter the build rules, previously shown in Figure 16-4, for a target in the target's Info window. When it comes time to compile an input file, the file is tested against each rule starting from the top of the list. The first rule that matches a particular input file is the rule used to compile that file.

Note

Each target has one set of build rules, shared by all of the build phases in that target. You cannot create different rules for different phases of the same target.

Every set of build rules includes a set of system rules that cannot be edited. The system rules are always at the bottom of the list. You can add your own custom rules to a target. These custom rules can only be added to the top of the list and are always evaluated before any of the system rules. If you define a rule that matches the same type of file as a system rule, your custom rule is used instead. Click and drag the title of a custom rule to reorder it in the list.

Creating Custom Build Rules

To add a new rule, click the + button at the bottom of the screen. A new, undefined rule is created. Another way to create a new rule is to modify one of the system rules; Xcode balks and warns you that you cannot edit system rules, as shown in Figure 16-13. Click the Make a Copy button and Xcode duplicates the system as a new custom rule, which you are free to alter.

FIGURE 16-13

Figure 16-13. FIGURE 16-13

Choose the type of file that the rule will process from the Process menu. This can be one of the Xcode file types, as set in the source file's properties, or it can be a filename pattern. To match a filename pattern, choose the Source Files With Names Matching option at the bottom of the menu and a filename pattern field appears, as shown in Figure 16-14. Enter the filename pattern, such as *.xml. This field is a globbing pattern like you would use in the shell; it is not a regular expression. The pattern is case-sensitive. By using filename patterns, it is possible to partially override system rules. For example, the "C source files" type encompasses C (.c), C++ (.cpp), and Objective-C (.m) files. By creating a custom rule that matches only *.c files, you can redirect plain C files to an alternate compiler while allowing C++ and Objective-C files to "fall through" and match the default system rule for compiling any kind of C file.

FIGURE 16-14

Figure 16-14. FIGURE 16-14

Note

In Xcode 3.0 and 3.1, if you want to use a particular version of the compiler, say gcc 3.3 instead of gcc 4.0, you should create a custom build rule that overrides the system build rule for C files. In other versions of Xcode the GCC_VERSION build setting selects the desired compiler. That setting was deprecated in Xcode 3.0, but is supported again in Xcode 3.2 (with many new choices).

If you choose to redefine the C compiler by creating a custom build rule, you must duplicate the rule in every target. Currently, there is no means by which you can edit the build rules for the entire project; you must create a new build rule in every target that compiles C source files. Modify the System C rule to quickly create a custom C rule, and then select the desired compiler.

A less refined approach is to use the gcc_select tool to change the default gcc compiler for your entire system. This will change the default compiler for every project you build on your system. See man gcc_select for details.

Customizing the Build Rule Compiler

With the file type selected, choose the compiler that will process each file of this type with the Using menu. This can be one of the standard Xcode compilers or you can direct compilations to your own script or tool by choosing the Custom Script item. When you choose a custom script as the compiler additional fields appear, as previously shown in Figure 16-14. The first field is the path and filename of the script to execute. The path can be an absolute path or a path relative to the project folder. The With Output Files control lets you specify what files are produced when your compiler is executed. The naming of output files is dynamic and requires build rule variables, described in the next section.

Build Rule Variables

When your custom script or tool is executed, the following occurs:

  • The current directory is set to the project's folder.

  • Environment variables are created that describe the file that Xcode wants processed and where Xcode expects the output file, or files, to be written.

  • The rule's script or tool is executed.

Note that no parameters are passed to the script. The script must determine what to process by examining its environment variables. If you're using an existing compiler or other tool that expects the input file to be in an argument, you will need to write a "wrapper" script that extracts the environment values and passes them to your compiler. The following table lists the key environment variables passed to a custom build script when it is executed:

ENVIRONMENT VARIABLE

DESCRIPTION

INPUT_FILE_PATH

The full path, including the filename, to the source file being processed.

INPUT_FILE_DIR

Just the directory portion of INPUT_FILE_PATH, without the filename.

INPUT_FILE_NAME

Just the filename portion of INPUT_FILE_PATH, without the directory.

INPUT_FILE_BASE

The base name of the file; in other words, the value of INPUT_FILE_NAME without any filename extension.

INPUT_FILE_SUFFIX

Just the filename extension of INPUT_FILE_NAME.

DERIVED_FILES_DIR

The complete path to the directory where Xcode expects the intermediate files to be written. Intermediate files are kept between builds and used to determine if the source file needs to be compiled again by comparing the modification dates of the two.

TARGET_BUILD_DIR

The complete path to the target's product directory; in other words, where the final product of this target is being constructed.

The following is a simple shell script that demonstrates the use of a custom build rule script. In this example, the project includes XML files that define patterns of text. The project contains an SXL transform file that converts an XML file into a LEX file describing the pattern. The details are unimportant. The key ingredients are that there is some process that converts the input file into one or more output files and the shell's environment variables tell the script where the source and destinations are.

#!/bin/bash

# Xcode compile script
# Run the input file through the java XSLT transformer
# The KeyPatternToLEX.sxlt file contains a transform that
# will convert the pattern record into LEX syntax.

XSLT = "Source/Transforms/KeyPatternToLEX.sxlt"
IN = "$INPUT_FILE_PATH"
OUT = "${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}.l"

java org.mycompany.patterns.Transformer "$XSLT" "$IN" > "$OUT"

Writing the Build Rule's Output Files

If the files produced by the script go into the target's product, they should be written to the appropriate location in TARGET_BUILD_DIR.

If the script produces intermediate output files, it should write those to the DERIVED_FILES_DIR directory. Intermediate output files are files that will be consumed later in this phase. If a compiler produces one or more intermediate output files, Xcode takes those files and runs them back through the build rules. It continues this process until no rule matches the files. This example defined a rule that takes an XML file and produces a LEX source file. When built, Xcode will run the XML file though the custom build script, producing a LEX source file. That LEX source file will be run through the rules again. This time, it will match the LEX rule that will compile the LEX file into a C source file. That C source file is again run through the build rules, this time matching the System C rule, and ultimately producing an object file.

This brings up an interesting conundrum. Xcode has no idea what a custom build script produces or where. You have to communicate that to the build rule by listing the files that the script will produce. Under the With Output Files section in the build rule, enter the filename that will be produced by the script. You can use any of the environment variables listed in the previous table. For this example, the output file is $(DERIVED_FILES_DIR)/$(INPUT_FILE_BASE).l, which agrees with the output filename in the script. The syntax for using environment variables in the output files list is $(VAR_NAME), which may be different than the syntax required by your script's interpreter. If the script produces more than one file, say a matched pair of .h and .c files, add more files by clicking the + button immediately below the list.

A Build Rule Example

Assume a hypothetical application, called PrimeRules, that needs a precompiled table of prime numbers. You've already written a shell script that generates the C source code for a table of prime numbers between 2 and some arbitrary limit. Now you could run that script manually, capture the output, save it as a file, and add that to the project. If the maximum number ever changed, you'd have to repeat the process. The solution is awkward, and the generated C table could (potentially) be huge.

What you'd like is simply to set the maximum prime number somewhere in the project and have the build automatically generate the table of primes, compile it, and link the results into your application. One solution is to use a custom build rule:

  1. Start with a Command Line project that produces a BSD executable.

  2. Add the compilePrimeTables.sh shell script to your project. (Not strictly necessary, but you'd want to check it into source control and make it obvious that it's part of the project.) The script should not be included in any targets.

  3. Add a new build rule to the PrimeRules target, as shown in Figure 16-15. The rule takes *.primetable files and uses the compilePrimeTables.sh script to transform them into C source files.

  4. Create a new knownprimes.primetable file and add it to the project. Edit the file; it should contain nothing except a single number.

  5. Add the knownprimes.primetable file to the PrimeRules target by dragging it into the target's Compile Sources build phase.

FIGURE 16-15

Figure 16-15. FIGURE 16-15

The project is now finished, and should look something like the one in Figure 16-16.

FIGURE 16-16

Figure 16-16. FIGURE 16-16

When the PrimeRules target is built, the Compile Sources phase runs each of its source items through the build rules for the target. The main.c file gets compiled as you would expect. Because of your custom build rule, the knownprimes.primetable file runs the compilePrimeTables.sh script, which produces an intermediate .c file. The intermediate file is run back through the rules again, and is eventually compiled by the C compiler.

The two final object files, main and knownprimes, are linked together to form the finished executable.

DEPENDENCIES

A target dependency ensures that the targets required by the current target are built first. Basic targets produce a product from one, or possibly thousands, of source files. These are the atoms from which you assemble your project's finished product. When the product of one target is a source file in another target, it creates a dependency; you communicate that relationship to Xcode using target dependencies.

You must define your target dependencies; Xcode can't do it automatically. You create dependencies to guarantee that source items produced by other targets are up-to-date before proceeding, as well as to order and group targets. A target can depend on another target in the same project or in a different project.

Adding Target Dependencies

To add a dependency to a target, open the target's Info window. The General tab contains the Direct Dependencies list. Drag a target, or targets, from the project's Groups & Files list into the list. Or, you can click the + button just below the list and Xcode presents a list of targets from which to choose, as shown in Figure 16-17. Select one or more targets from the list and click the Add Target button. To remove a target, select it in the list and click the - button.

FIGURE 16-17

Figure 16-17. FIGURE 16-17

Xcode (cleverly) prevents you from creating circular dependencies. Targets in the list that contain a dependency to the target you are editing, either directly or indirectly through other targets, are disabled and cannot be added.

After you've added target dependencies, they also appear along with the other build phases of the target. This brings up the second way of creating target dependencies; you can add dependencies by dragging one target directly into another target's group.

Logically, all dependent targets are built before any of the target's build phases begin, and the order in the target group reflects this. Just as you can reorder build phases, you can reorder dependent targets to control the order in which they are built. However, you cannot move a dependent target after a build phase.

Adding Project Dependencies

You can also make a target dependent on a target in a different project. This is called a cross-project dependency. Before you can create a cross-project dependency, you must first add the sub-project to your project using any of the techniques described in Chapter 5. The easiest methods are to use the Project

Adding Project Dependencies
FIGURE 16-18

Figure 16-18. FIGURE 16-18

After you've added the sub-project, it appears in the list of potential target dependencies as a group of targets, as shown in Figure 16-19. Select and add any of the cross-project targets. When a cross-project target is listed in the target's dependencies, Xcode lists the target name and the project that contains it.

FIGURE 16-19

Figure 16-19. FIGURE 16-19

Some potentially important differences exist in how a project target and a cross-project target are built. The primary consideration is the build settings that will be used. Every target in the same project shares the same set of build configurations, so the Deployment version of one target always depends on the Deployment version of its dependent target. This is not necessarily true of cross-project targets, which might have a completely different set of build configurations. See Chapter 17 for a discussion on how Xcode handles build settings in other projects as well as shared build locations.

Strategies for Target Dependencies

Target dependencies serve a number of purposes. They:

  • Construct products

  • Control build order

  • Aggregate targets

The most common reason for a project to have multiple targets is because it produces multiple products. These are often intermediate products that are later combined into a final product by another target. For example, an application that includes two executable BSD command-line programs in its resource bundle requires three targets: an application target and two command-line targets. To produce the bundle, the application target depends on the other two command-line targets. The products of the two command-line targets are added to the application's Copy Bundle Resources phase. Whenever the application target is built, Xcode ensures that the two command-line tools are built first; then during the Copy Bundle Resources phase, it copies the two tools into the final product while constructing the application's bundle.

Large projects can be made more manageable by isolating common code into libraries or separate projects, which can be built using a single target. All of the other products that rely on that common code will have a dependency on that single target. Whenever any of those products are built, Xcode first makes sure that the common library target is built first.

Target dependencies also impose order on the build process. All dependent targets must be completely and successfully built before a target begins to build. In the "Build Rules" section, you added rules to turn XML files into object files. The assumption was that the individual source files and their outputs are all independent of each other. It didn't matter which source files, or in what order, Xcode decided to build these files. Often, as in the case of C source files, this doesn't matter, but what if it did? In the Copy Bundle Resources phase example, used earlier, the phase clearly expects all of the source items to be built already. You solve this by moving the compilation of the prerequisite files into another target, and then create a target dependency to ensure that these are all built beforehand.

Aggregate targets depend on other targets, but don't build anything themselves. An aggregate target effectively groups other targets so that you, or another target, can build multiple targets by referring to a single target. When you build in Xcode, you always build the single active target. If your project produces several products, which aren't dependent on one another, you can create an aggregate target to build them all at once. Let's say you have a project that produces two applications, a client and a server. To build both the client and server at once, create an aggregate target that depends on both. Setting the active target to the aggregate targets causes both applications to be built using a single build command.

By combining target dependencies, you can create large trees of targets. It would not be uncommon to have an aggregate target that builds five applications, each of which depends on a single common framework, which in turn depends on building several libraries. The Xcode build system is intelligent and eliminates all excess building of targets. Building that single top-level aggregate target will only cause the common framework target to be built once. As Xcode progresses through the build, it knows what targets have already been built. Before the first application is built, Xcode first builds the framework target, which in turn builds its dependent targets. However, the other four applications can then be built without rebuilding the framework target.

BUILD SETTINGS

Most targets have their own set of build settings, accessible in the Build tab of the target's Info window. Build settings are a collection of named values (key/value pairs) used to customize a target's behavior. You can edit any of the predefined build settings. You can also define your own variable names and assign them any value you choose. All build settings are accessible by the target and all of its build phases.

Xcode defines a large number of predefined build settings, and the Xcode-supplied build phases expect specific variables with strictly defined values. As an example, the INFOPLIST_FILE setting tells the Copy Bundle Resources phase where to get the Info.plist file for the bundle. The GCC_UNROLL_LOOPS setting tells the Compile Sources phase to pass the -funroll-loops flag to the gcc compiler. There are hundreds of predefined settings. This chapter explores some of them, some are covered in the "Building Projects" chapter, and still others in the "Debugging" chapter. Many are specific to the compiler or linker. For more details, consult the build settings help or your compiler's documentation.

A target can have more than one complete set of build settings. Each set is called a build configuration. By using build configurations, you can use a single target to produce variations of the same product. In addition to targets, both the project and individual files define their own build settings; the build settings for a target form one layer in the build settings hierarchy. Target build settings are part of a larger system of build variables, inheritance, and build configurations, discussed in Chapter 17.

JAM-BASED TARGET EDITOR

As I mentioned earlier, the external target type, as well as some legacy targets, use the Jam build system. If you need to configure an external target, or have inherited an old project that you can't easily upgrade, you'll need to read this section.

In the Jam build system, a target editor window performs the same functions as that of the Build Settings tab, the properties editor (see the next section), and the build phase Info windows found in Xcode. Double-click a Jam-based target or select it and press Return to open its target editor window, as shown in Figure 16-20. Jam-based targets still have an Info window, which you can open using the Get Info command, but it only permits editing of the target's name, dependencies, and comments.

FIGURE 16-20

Figure 16-20. FIGURE 16-20

If you have used Project Builder in the distant past, this interface will look very familiar to you. In fact, it is the interface used in earlier versions of Xcode. The Jam-based settings window isn't quite as flexible, intelligent, or well integrated into Xcode as its native cousin. Nevertheless, most of the settings that you would find in the Info window of a native target are here, just in a different organization.

The settings that are available depend on the type of target. Generally, every target has a Settings group that controls the build settings for that target. Similar to the panes in the Info window, the settings are subdivided into groups for easier browsing. Selecting an individual setting or a group of settings reveals them in the right pane. Edit any of the settings as needed. At the bottom of each major section of settings is an Expert View group. This displays all of the setting in their raw form. You can also edit any of the settings here, or add your own build variables. The editing of settings in expert view is not intelligent, so be careful that the values you enter here are valid. A field that expects to be either YES or NO may not be happy if you set it to 1. Also, custom variables only appear in the expert view.

A build configuration menu has been added to the target editor to make the Jam-based settings compatible with the newer Xcode system of build configurations. Select the build configuration you want to work with from the Settings Show Configuration menu. All changes will be applied to that configuration. However, the target editor interface is not really build-configuration savvy. Unlike the settings editor for native targets, it does not highlight settings that differ from the project build settings. You can't view a combined display of all of the build settings, nor can you set a build setting for all configurations at once. If you need to change a build setting in a Jam-based target for all build configurations, you must enter it separately for each configuration.

Also in this window are the target's build phases. You can manipulate the build phases of a Jam-based target in its settings window or from the project window, as described earlier in the chapter for native targets.

Finally, if the target produces a bundle the Jam-based target editor also defines all of the Info.plist settings for the product. You can define simple property values in the expert view, which become tagged values in the product's Info.plist file.

PROPERTIES

Native targets that produce an Info.plist file as part of their product have a Properties tab in their Info window, as shown in Figure 16-21.

FIGURE 16-21

Figure 16-21. FIGURE 16-21

Each field in the Properties tab becomes one of the standard properties in the Info.plist file. For example, the Executable field becomes the CFBundleExecutable property in the Info.plist file. The Identifier field becomes the CFBundleIdentifier property, and so on. Make sure these values are correct, because Xcode does not check their validity. Depending on the type of product, any or all of these values may be superfluous. The Runtime Configuration Guidelines at http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPRuntimeConfig/ has more details.

At the bottom of the pane are the document types that your application creates, owns, understands, or works with. This information is used by Launch Services to relate document files with applications. Each line defines a single document type. You can add or delete document types using the + and - buttons at the bottom of the pane. Double-click any field in the list to edit it.

The Version field contains the version of your application, bundle, or framework. This is not, however, the version number that will appear to the user in the Finder's Get Info window. To set that, you need to define the CFBundleShortVersionString property.

Now you're probably wondering where to set the CFBundleShortVersionString property. Actually, scores of other properties have meaning in an Info.plist file that do not have a convenient user interface in the Properties tab. To set any of the less common properties, click the Open Info.plist as File button at the bottom of the pane. This button opens the Info.plist file in an editor window — this is the same as opening the Info.plist file from the project source group — as shown in Figure 16-22. Here you can edit the property values in the document, much like you do the build settings; the only significant difference is that the property lists are hierarchical. The values that you set here will become the Info.plist file when the bundle is assembled. You'll notice that all of the values you set in the Properties tab also appear in the Info.plist file editor. Each field in the Properties tab corresponds to a specific Info.plist key. You can change the values in either place.

FIGURE 16-22

Figure 16-22. FIGURE 16-22

You'll also notice that the Info.plist file includes variable values, like ${PRODUCT_NAME}. As you will see in the next chapter, the Info.plist file can be additionally processed by the gcc compiler. Together, these allow you to use build setting variable names, conditional statements, and preprocessor macros in your Info.plist file — making it more of a template than a literal data file.

If you need to "freely edit" the contents of a Info.plist file, Right/Control-click the source item in the project and choose Open As

FIGURE 16-22

PRODUCTS

Most targets produce a product. Targets that produce a product create a product source item in the Products group, as shown in Figure 16-23. In this sense, the Products group is more like a smart group than a source group.

FIGURE 16-23

Figure 16-23. FIGURE 16-23

You use a product source reference just like you would any other source file item. For example, to include a command-line tool produced by a Shell Tool target in the bundle of a Cocoa application, you would do the following:

  1. Make the Cocoa application target dependent on the Shell Tool target.

  2. Add a Copy Files build phase to the Cocoa application target.

  3. Configure the build phase to copy its source file(s) into the Resources section of the application bundle.

  4. Drag the shelltool product from the Products group and drop it into the Copy Files build phase of the Cocoa application target.

When the application target is built, the shell tool target will be built first. The product of that build will then be copied into the final application bundle.

Like any source reference, the product reference turns red if the product that it refers to doesn't exist. Unlike source references, this isn't necessarily a bad thing. It just means that the product hasn't been built yet.

As explained in Chapter 5, the path type of a product is relative to the build product. Each build configuration has a different product location, so the product references change whenever the active build configuration changes. This means that if you add a product reference to to a target, the file the reference points to will change whenever you change the active build configuration. This is normally exactly what you want. When your build configuration is set to Debug, all product references refer to the Debug version of those products.

You cannot change the reference type of a product reference, nor can you rename or delete a product reference. To change a product you must edit the target that produces it. To delete a product you must delete the target that produces it.

All native targets that produce a product have a Product Name build setting. This setting defines the name of the product the target will produce. Changing this build setting changes the name of the product, renames the product reference in the source group, and updates any other targets that refer to it.

EXECUTABLES

Targets that produce executable programs also create an executable. Executables appear in the Executables smart group, which was also shown in Figure 16-23. Opening the Info window for an executable allows you to define the run time environment used when the executable is launched from within Xcode. Executables, and custom executables, are explained in Chapter 18.

SUMMARY

Up to this chapter, most of the organization of your project has been for convenience and clarity. Reorganizing your files and source groups has little impact on what your project builds, but the organization of targets literally shapes the end result of your project. Targets define what your project builds, what sources are used, how those sources are compiled, and in what order.

Ironically, Xcode provides so many templates with preconfigured targets that you may never need to deal with targets much beyond tweaking a few build settings, but a keen understanding of targets, their component parts, and how they can be interlinked is critical to constructing and maintaining complex projects.

If targets are the engine of your project, then the build commands are the fire that drives them. The next chapter looks at firing up those targets, controlling when and how they are built.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset