The Power Of Plist

August 3rd, 2011

Most Mac and iOS developers know that when you build an application, you advertise a number of details about the application in the “Info.plist” file, located inside the application bundle. You can examine any application on your Mac and see what kind of information the developer has conveyed about it:

  1. Navigate to a .app file in the Finder, e.g. /Applications/Mail.app
  2. Control-click the app icon and select “Show Package Contents”
  3. Navigate to Contents/Info.plist
  4. Open with a plist editor or your favorite text editor.

These files advertise a number of interesting attributes including the application’s official version and copyright strings. They also include important clues to the system about what kinds of files the application knows how to read and write, and what its minimum system requirements are.

Minimum System Requirements

Declaring the right Info.plist values not only allows a developer to control which operating systems a product should run on, but which of the application’s architectures should be considered for a particular operating system. For example, Black Ink uses a number of keyed values describing its requirements:

	<key>LSArchitecturePriority</key>
	<array>
		<string>x86_64</string>
		<string>i386</string>
		<string>ppc</string>
	</array>
	<key>LSMinimumSystemVersion</key>
	<string>10.4.0</string>
	<key>LSMinimumSystemVersionByArchitecture</key>
	<dict>
		<key>i386</key>
		<string>10.4.0</string>
		<key>x86_64</key>
		<string>10.6.0</string>
		<key>ppc</key>
		<string>10.4.0</string>
	</dict>

Notice the fidelity of some of these options. Black Ink is an application that is compiled with binary code for three separate architectures embedded into it: 64-bit Intel, 32-bit Intel, and 32-bit PowerPC. In order to give the user the best possible experience after they double-click the application icon, the system consults the Info.plist values to determine which binary is most appropriate to load on the current system.

The options for Black Ink start by indicating that all things being equal, it prefers to run as 64-bit Intel, but will settle for 32-bit Intel or PowerPC in a pinch. Next, it conveys that it should not be run on any operating system earlier than Mac OS X 10.4. Finally, it adds an additional restriction by architecture, stating that in the case of 64-bit Intel, the system should consider Mac OS X 10.6 the minimum required OS.

Why so picky? For example, Mac OS X 10.5 will run on computers that are new enough to support 64-bit Intel code, but the 10.5 operating environment itself doesn’t contain enough compatible libraries to make it very useful to a full-featured Cocoa application. So we need to convince the 10.5 system to ignore our 64-bit Intel code. And this is how it’s done.

Embedding Info.plist

This is all well and good for applications, but what about other code that may be run on a variety of hardware and operating systems? In particular, command-line tools do not come in the same bundled-folder format that applications do. Fortunately, Apple thought of this when they implemented support in their developer tools for embedding the contents of an Info.plist directly into the binary itself. You just have to pass the Info.plist path to Apple’s linker tool when it’s constructing your binary file. (Thanks to Paul Kim for reminding me of this functionality). What this means in practice is adding arguments like this to your Xcode target’s “Other Linker Flags” build setting:

-sectcreate __TEXT __info_plist $(INFOPLIST_FILE)

This instructs the linker to crete a new text segment named “__info_plist” in the resulting binary, and to fill it with the contents of the referenced Info.plist file. To make sure the INFOPLIST_FILE expands correctly, just make sure you set it in the separate build setting for identifying the Info.plist (the one you’d normally use in an app, and that is just ignored by default for a command-line tool target).

I brushed up on all this information because I wanted to help ensure that some recent work I did to create a standalone MultiMarkdown tool would be backward compatible with older systems. This required adding an embedded plist much as I’m describing here.

(Note: I have gotten some feedback to the effect that the __info_plist segment’s minimum system version requirements may not actually be consulted properly on Mac OS X 10.5. This would be a bummer, and in fact sort of reject the whole point of embedding the Info.plist in this case. I’m going to see if I can confirm whether it doesn’t in fact work.)

You could technically embed all kinds of stuff in the binary, with different section names. But the contract in this case is that when Mac OS X is determining information about an executable, it will consult the bundle’s Info.plist file, if it happens to be an executable in a bundle, or the binary’s __info_plist segment, if it happens to have one.

Another common reason for needing to embed Info.plist information into a standalone binary is if you are using Apple’s code signing mechanism to sign an individual binary tool. Code signing uses information from the Info.plist of a product, and in the case of standalone binaries, the only product-cohesive place to put this is in the binary itself.

Examining Binary Info.plists

After you’ve performed the magic above, you’ll probably be keen to inspect your command-line tool’s Info.plist to see if it worked. To do this, you can use the otool command from the Finder. If you run otool with the “-l” option to show all load commands, it will include the load command that identifies the __info_plist segment. Use grep to narrow it down to the part you’re interested in:

otool -l ./yourTool | grep info_plist -B1 -A10

Any results at will confirm that there is Info.plist information in the binary, but how does it look? The “-s” option to otool lets you examine the contents of a specific segment in a binary, but it dumps it out as hex code. Fortunately, another system-bundled tool called “xxd” is capable of easily converting between hex-dump and text formats. As it turns out, some of Apple’s bundled developer tools (at least on my Mac) contain these embedded __info_plist sections, so if you don’t have a command line tool of your own to try this on, you can peek at what the “atos” tool has to say for itself:

otool -X -s __TEXT __info_plist /usr/bin/atos | xxd -r

In Summary

Info.plist values are important for conveying information about your product, from the crudest, most obvious stuff like version number, to the most nuanced details such as which architectures are supported. Knowing how to specify an application or tool’s requirements as precisely as possible will ensure that the product performs optimally on whatever supported system your user happens to be using. I’ve barely scratched the surface here with a few common deployment keys that control execution behavior on Mac OS X. Be sure to skim Apple’s Information Property List Key Reference to see if there are other useful bits of information you can share for your application or for your command-line tool.

2 Responses to “The Power Of Plist”

  1. Cédric Luthi Says:

    I have written an Xcode plugin to easily deal with embedding Info.plist files in a command-line tool target: CLITool+InfoPlist

    You just have to set the INFOPLIST_FILE setting as you would do for a regular app target. The plugin also takes care of related Info.plist settings such as Expand Build Settings in Info.plist File, Info.plist Output Encoding, etc.

  2. Bavarious Says:

    I’ve released BVPlistExtractor, which contains a function that extracts a plist embedded in the __TEXT __info_plist section of a binary file. It can be used when the plist needs to be retrieved during runtime and it doesn’t resort to otool(1), which is only available in systems where Xcode has been installed.

    https://github.com/bavarious/BVPlistExtractor

Comments are closed.

Follow the Conversation

Stay up-to-date by subscribing to the Comments RSS Feed for this entry.