tags:

views:

821

answers:

5

I want to create a bundle from an arbitrary bundle identifier
e.g. com.apple.iokit.IOStorageFamily

It's not an unreasonable thing to do as bundle IDs are supposed
to be unique, however the obvious code does not work:

NSString* bID = @"com.apple.iokit.IOStorageFamily";
NSBundle* bundle = [NSBundle bundleWithIdentifier:bID];

This code only works for bundles you've already loaded
(hello, chicken and egg problem), and in fact, you have
to know a little more than you'd like about the the identifier
before you can do anything. For the above style of ID
I grep out the final component and tranform it into
/System/Library/Extensions/IOStorageFamily.kext
which I then load by path.

Is this the state of the art or is there a more general way?

+1  A: 

Use this

NSString *path = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:@"com.apple.TextEdit"];
Dave Verwer
Not quite! That's only for apps, my example was a kext.
Rhythmic Fistman
A: 

If what you're looking for is definitely a kext, then you could look at the info dictionary for each bundle in the /S/L/Es/ folder until you find yours. There's no search for bundle by identifier apart from for Applications (where LaunchServices will do it), and loaded bundles as you've already found.

Graham Lee
So far they've all been kexts, but that could be pure chance. Let's say they are though, is there anything awfully wrong with hacking the path from the bundleID or should I really iterate the info dictionaries? Ta.
Rhythmic Fistman
There's always the "what if" a KEXT is installed that doesn't match that scheme. Of course, you don't really need to iterate _every_ time, you could build up a lookup scheme and use that (keeping it up to date…).
Graham Lee
+2  A: 

I don't think Mac OS X keeps a global database of all bundle IDs everywhere.

As noted, you can find an application in a pretty straightforward way with NSWorkspace.

Also, since you used a kext for your example, on Leopard (10.5) there's a tool called "kextfind" that you can run to search for kexts in the system Exensions folder (kexts in other places won't be found unless you point the tool at those other places). kextfind has lots of options--see the man page for details--but to find a kext by bundle ID you can do this:

kextfind -bundle-id com.apple.iokit.IOStorageFamily

We don't currently have a C-level API for looking up kexts by bundle ID.

As for hacking the path from the last component of the bundle ID: don't do that. There's nothing requiring the wrapper name to match the last component of the bundle ID, and I have seen kexts (to say nothing of other bundles), where the two do not match.

Nik Gervae
A: 

In order to answer this question, I think one really needs to know "Why are you looking bundle identifiers in this way?" If there are always kexts you can search in some fairly reasonable places, if they are apps you can use LS, I don't see a case in which you would want to do both, so I don't see a need to have a common way to do it.

It should be noted that you can have multiple instances of identical bundle identifiers on a Volume.

Louis Gerbarg
+2  A: 

Just recently Andrew Myrick answered a similar question on the darwin-dev mailing list:

KextManagerCreateURLForBundleIdentifier() in <IOKit/kext/KextManager.h> may be of use, though I believe it only works for kexts that are either 1) loaded, or 2) in /S/L/E/. Here is the Snow Leopard headerdoc:

/*!
 * @function KextManagerCreateURLForBundleIdentifier
 * @abstract Create a URL locating a kext with a given bundle identifier.
 *
 * @param    allocator
 *           The allocator to use to allocate memory for the new object.
 *           Pass <code>NULL</code> or <code>kCFAllocatorDefault</code>
 *           to use the current default allocator.
 * @param    kextIdentifier
 *           The bundle identifier to look up.
 *
 * @result
 * A CFURLRef locating a kext with the requested bundle identifier.
 * Returns <code>NULL</code> if the kext cannot be found, or on error.
 *
 * @discussion
 * Kexts are looked up first by whether they are loaded, second by version.
 * Specifically, if <code>kextIdentifier</code> identifies a kext
 * that is currently loaded,
 * the returned URL will locate that kext if it's still present on disk.
 * If the requested kext is not loaded,
 * or if its bundle is not at the location it was originally loaded from,
 * the returned URL will locate the latest version of the desired kext,
 * if one can be found within the system extensions folder.
 * If no version of the kext can be found, <code>NULL</code> is returned.
 */
CFURLRef KextManagerCreateURLForBundleIdentifier(
    CFAllocatorRef allocator,
    CFStringRef    kextIdentifier);

Note that prior to Snow Leopard, it may only work for kexts in /S/L/E; the API existed, but there was no headerdoc describing its behavior.

For me this worked really well on Mac OS X 10.5.

Georg