Embedding a command line tool within a Cocoa application

Back in 2006 I posted a forum question seeking help with embedding a command line tool inside a Cocoa application. After a few days I had muddled out a way to both embed the command line tool and set up Xcode to automatically build the command line tool when the Cocoa wrapper is rebuilt. I posted my solution answering my own question.

Recently I received a message from someone who stumbled on my solution and found it useful. I’m reposting the solution here so that this information does not get lost.

My original question:

I’m hacking together a small Cocoa application in Xcode. I have all the GUI and events coded up and now I need to integrate a command line tool that I coded up in C a while back.

Poking around in the package contents for a few applications I’ve noticed that it is not uncommon for developers to package up small command line tools within the main Cocoa application. Presumably these command line tools are executed as a new process from the Cocoa application.

I’m developing in Xcode and have manged to import the source code for the command line tool as say command_line.c. What do I need to do to have this file built as a command line tool that is embedded in the main Cocoa application?

My solution:
(Warning: Long and technical post.)

I wanted to end up with the application structure of:

MyCocoaApp.app/
    Info.plist
    Contents/
        MacOS/
            MyCocoaApp
            some_command_line_tool
        PkgInfo/
        ...

Where MyCocoaApp is the main Cocoa executable for this application and some_command_line_tool is a standard BSD C application coded to know nothing about Objective-C or Cocoa.

After creating a Cocoa application project for MyCococaApp.app in Xcode the steps below will add the command line tool into the project.

First we need to add the source files for some_command_line_tool into the Xcode project. In this example the command line tool is implemented in one file named some_command_line_tool.c.

  1. Click to highlight the project group for MyCocoaApp in Xcode. This is the first item in the Groups & Files tree view.
  2. Right-click (control-click) on the project group and select the Add->New File… option from the context menu.
  3. Select BSD C File in the File Assistant and click Next.
  4. Specify a name for the file, in this example the name is some_command_line_tool.c.
  5. Accept the default location and select the MyCocoaApp from the Add to Project drop-down.
  6. Do not select any Targets.
  7. Click Finish.

Now we need to tell Xcode about this new command line by setting up a new target for it. This target will compile the some_command_line_tool.c file to produce the some_command_line_tool executable.

  1. Double-Click to expand the Targets group in the Groups & Files tree view.
  2. Right-Click (control-click) on the Targets group and select the Add->New Target.. option from the context menu.
  3. Select BSD->Shell Tool in the Target Assistant and click Next
  4. Specify a name for the target, in this example the name is some_command_line_tool.
  5. Select the MyCocoaApp project from the Add to Project drop-down.
  6. Click Finish.
  7. Close the Target Info window that opens.

You can now select the some_command_line_tool as the active target from the project menu and open some_command_line_tool.c to hack away at implementing the command line tool and testing it. Once you have it implemented, set the active target for the project back to the Cocoa target MyCocoaApp and continue with these instructions.

Since our Cocoa application will invoke the command line tool we want to set up a dependency between the Cocoa application target and the command line tool target. This dependency will ensure that whenever the Cocoa application target is built then the command line tool will also be built.

  1. Right-Click (control-click) on the MyCocoaApp target and select the Get Info option.
  2. In the Target Info window that pops up select the General tab.
  3. Click on the + button at the bottom of the window and add the command line tool target from the pop-up list.
  4. Close the Target Info window.

Now, if you select the Project group and select Build->Clean All Targets from the menubar followed by a build on the MyCocoaApp target you should notice that the some_command_line_tool is also built.

The remaining step is to automate copying the some_command_line_tool executable file into the correct place in the MyCococaApp.app bundle. This is done by adding a new build phase to the MyCococaApp target.

  1. Right-Click (control-click) on the MyCocoaApp target and select the Add->New Build Phase->New Copy Files Build Phase option.
  2. You want to copy an executable file so set the Destination to be Executable in the pop-up window. Do not specify a Path. The Destination setting means that the files copied in this phase will be copied to Contents/MacOS in the Cocoa application bundle. The Path setting is to specify a sub-path below the MacOS folder.
  3. Close the Copy Files Phase Info window.
  4. Double-click to expand the MyCocoaApp target and notice the Copy Files target at the bottom of the build phases.
  5. Click to highlight the project group for MyCocoaApp.
  6. Find your some_command_line_tool executable file in the right side of the window and drag-and-drop the file onto the new Copy Files phase in the MyCocoaApp target.
  7. Clean and build the Cocoa application target.

If you now use the Finder or Terminal to navigate to the build folder in your Xcode project folder you will find the Cocoa application. Navigating into the package contents for the application should show both MyCocoaApp and some_command_line_tool executables in the MacOS folder.

This entry was posted in apple, cocoa, development, xcode. Bookmark the permalink.

3 Responses to Embedding a command line tool within a Cocoa application

  1. Scott S says:

    Just happened across this by way of Google. This answers an amazing amount of questions I’ve been trying like heck to find the answers to. A million thanks to you! Simply awesome.

  2. You’re welcome. Before I posted to the forums I’d spent a few days searching on Google to work out how to do this. Another couple of hours poking at Xcode resulted in the answer I posted here.

    There may be other (or better) approaches, but this one seemed to work at the time of writing (2006). I’m not sure how much of the Xcode interface has changed since then but the report from Scott S suggests that the interface is similar enough that the instructions still have use. 🙂

  3. Pingback: Stack Overflow

Leave a Reply

Your email address will not be published. Required fields are marked *