MacOS X Dynamic Libraries

Dynamic libraries are difficult on any platform, MacOS X is no different. Basically, there are two kinds of dynamic libraries, those that a vendor installs, and those that you want to ship with an application.

If the dynamic libraries are installed by your OS vendor, no big deal. Those are the dynamic libraries you have available. If the dynamic libraries are installed by some other application, you may have a problem. If all you ever want to do is run programs, those libraries will be fine. If you want to develop your own programs, you will have problems. If the program you want to write depends on that vendor library, you are requiring that anyone who uses your program also has that library installed, preferably in that location. Even worse, you can’t reliably test your program because you now have a non-standard, system requirement that you can’t control. You could always use an installer to install your own system-level shared libraries. Users like that. I know I do. That is what is known as DLL-Hell.

For a developer, the way to solve this is by using the install_name_tool in MacOSX. This tool goes into an application and changes the path for a dynamic library. The idea is that, as one of your build scripts, you copy any required dynamic libraries into the application bundle of your program. Then, use install_name_tool to change the location of that library to @executable_path. Now, your application is linked against itself. It can be distributed to anyone.

Run “otool -L” against your executable to verify that any dynamic libraries are either system libraries or @executable_path libraries.

The downside of this is that a user could have 100 applications, each with the same dynamic library. Perhaps the OS is smart enough to only load one of them. I hope so. This is not quite DLL-Hell, more like DLL-Purgatory. There really is no better solution. You don’t want a user linking their own dynamic library with your app. You don’t want to link with a static library. That would make the application difficult to update and eliminate any possibility of OS efficiency.

Things today are designed to use dynamic libraries. No point in fighting it. Learn to live with it. Just don’t let that cause you to ship applications with odd dependencies or complicated installers. Bundle everything into a standalone app that is easily uninstalled.

Perhaps I should take my own advice and re-work Privoxy into an application bundle. That would be an excellent idea.

Unix executable inquiry tools

Once again today I found myself struggling to remember a certain obscure Unix command to inquire about what files were linked to an executable. That means itโ€™s time to post to Etreblog again. So, for the record:

Utility Description
file
Display handy information about a file or library. MacOS X just tells the architectures while Linux provides some additional info.
ar t
List source files inside a static library
ldd
Print shared library dependencies. Not on MacOS X.
otool
Like ldd for MacOS X, only much more powerful.

Darwin versions for open-source development

I’m trying to support Intel in 10.4 and PPC in 10.2. I found the following table from Wikipedia very helpful:

Date Darwin release Mac OS X release
March 16, 1999 Darwin 0.1 Mac OS X Server 1.0
April 5, 2000 Darwin 1.0
April 13, 2000 Darwin 1.0.2 Mac OS X DP4
Darwin 1.2.1 Mac OS X public beta
April 13, 2001 Darwin 1.3.1 Mac OS X 10.0 to 10.0.4
October 2, 2001 Darwin 1.4.1 Mac OS X 10.1
Darwin 5.1 Mac OS X 10.1.1
Darwin 5.2 Mac OS X 10.1.2
Darwin 5.3 Mac OS X 10.1.3
Darwin 5.4 Mac OS X 10.1.4
Darwin 5.5 Mac OS X 10.1.5
September 23, 2002 Darwin 6.0.1 Mac OS X 10.2
October 28, 2002 Darwin 6.0.2 Mac OS X 10.2
Darwin 6.1 Mac OS X 10.2.1
Darwin 6.2 Mac OS X 10.2.2
Darwin 6.3 Mac OS X 10.2.3
Darwin 6.4 Mac OS X 10.2.4
Darwin 6.5 Mac OS X 10.2.5
Darwin 6.6 Mac OS X 10.2.6
Darwin 6.7 Mac OS X 10.2.7
Darwin 6.8 Mac OS X 10.2.8
October 24, 2003 Darwin 7.0 Mac OS X 10.3
Darwin 7.1 Mac OS X 10.3.1
Darwin 7.2 Mac OS X 10.3.2
Darwin 7.3 Mac OS X 10.3.3
Darwin 7.4 Mac OS X 10.3.4
Darwin 7.5 Mac OS X 10.3.5
Darwin 7.6 Mac OS X 10.3.6
Darwin 7.7 Mac OS X 10.3.7
Darwin 7.8 Mac OS X 10.3.8
April 15, 2005 Darwin 7.9 Mac OS X 10.3.9
April 29, 2005 Darwin 8.0 Mac OS X 10.4
May 16, 2005 Darwin 8.1 Mac OS X 10.4.1
July 12, 2005 Darwin 8.2 Mac OS X 10.4.2
October 31, 2005 Darwin 8.3 Mac OS X 10.4.3
January 10, 2006 Darwin 8.4 Mac OS X 10.4.4
February 14, 2006 Darwin 8.5 Mac OS X 10.4.5
April 3, 2006 Darwin 8.6 Mac OS X 10.4.6
August 7, 2006 Darwin 8.7 Mac OS X 10.4.7
September 29, 2006 Darwin 8.8 Mac OS X 10.4.8

A short primer on Objective-C++ exception handling and command line building

I found the following Objective-C try, catch, finally example recently. It also included a short blurb about building from the command line. I updated it a bit and here is my version.

As a C++ programmer, I’m not familiar with the “finally” phase. I wanted to verify that it really got called. Here is the code:

#import 

int exceptionTest(int argc);

int main(int argc, char * argv[])
  {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  
  int result = exceptionTest(argc);
  
  NSLog(@"Result was %d", result);

  [pool release];

  return result;
  }

int exceptionTest(int argc)
  {
  @try 
    {
    if(argc > 1)
      @throw [NSException exceptionWithName: @"Throwing a test exception" 
        reason: @"Testing the @throw directive." userInfo: nil];

    NSLog(@"No exception this time");

    return 1;
    } 
  @catch (id theException) 
    {
    NSLog(@"%@", theException);

    return 2;
    } 
  @finally 
    {
    NSLog(@"This always happens.");
    }
  
  NSLog(@"Leaving function...");

  return 0;
  }

And here is another example that shows the finally clause really does get executed “out of bounds”:

#import 

#include 

int exceptionTest(int argc);

int main(int argc, char * argv[])
  {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  
  int result = exceptionTest(argc);
  
  NSLog(@"Result was %d", result);

  [pool release];

  return result;
  }

template 
class Returner
  {
  public:
   
    Returner(T value) :
      myValue(value)
      {
      std::cout << "Creating returner with value " << myValue << std::endl;
      } 

    T returnValue(void)
      {
      std::cout << "Returning with value " << myValue << std::endl;
      return myValue;
      }

  private:

    T myValue;
  };

template 
Returner createReturner(T value)
  {
  return Returner(value);
  }

int exceptionTest(int argc)
  {
  @try 
    {
    if(argc > 1)
      @throw [NSException exceptionWithName: @"Throwing a test exception" 
        reason: @"Testing the @throw directive." userInfo: nil];

    NSLog(@"No exception this time");

    return createReturner(1).returnValue();
    } 
  @catch (id theException) 
    {
    NSLog(@"%@", theException);

    return createReturner(2).returnValue();
    } 
  @finally 
    {
    NSLog(@"This always happens.");
    }
  
  NSLog(@"Leaving function...");

  return createReturner(0).returnValue();
  }

To build it, do the following:

g++ -framework Cocoa -fobjc-exceptions etest.mm

Hmm… The “-framework” option seems pretty easy. Why did Codewarrior have such trouble with it? The whole concept seems easier on the command line.

NSProgressIndicator inside an NSTableView

I actually found something useful in CocoaDev. Well, about as useful as CocoaDev gets. Of course it didn’t work, but I was able to hack it up and it is working nicely now.

So, without further ado, here is how to put an NSProgressIndicator inside an NSTableView.

First, start off with ProgressCell.h

#import 

// Some handy definitions.
#define kControl @"control"

// An NSCell that contains a progress indicator.
@interface ProgressCell : NSCell
  {
  }

// Initialize.
- (id) init;

// Draw the cell.
- (void) drawInteriorWithFrame : (NSRect) cellFrame 
  inView: (NSView *) controlView;

@end

Next, add ProgressCell.mm (yeah, that’s .mm – Objective-C++ baby!)

#import "ProgressCell.h"

@implementation ProgressCell

// Constructor.
- (id) init
  {
  self = [super initImageCell: nil];
  
  return self;
  }

// Draw the cell.
- (void) drawInteriorWithFrame : (NSRect) cellFrame 
  inView: (NSView *) controlView
  {
  NSProgressIndicator * indicator = 
    [[self objectValue] objectForKey: kControl];
  
  // Removing subviews is tricky, if the progress bar gets removed when it gets
  // 100%, it could get re-created on resize. This is perhaps kludgy and should
  // be fixed.
  if([indicator doubleValue] < 100)
    {
    if(![indicator superview])
      [controlView addSubview: indicator];
	
    // Make the indicator a bit smaller. The setControlSize method
    // doesn't work in this scenario.
    cellFrame.origin.y += cellFrame.size.height / 3;
    cellFrame.size.height /= 1.5;
    
    [indicator setFrame: cellFrame];
    }
  }
  
@end

Now to use it, create an NSTableView. Add a column for the progress bars. Drag an NSImageCell into that column to turn it into an image column. Don't forget to setup your data source and maybe delegate.

In your controller's awakeFromNib, do this:

[[myTableView tableColumnWithIdentifier: kProgress] 
  setDataCell: [[ProgressCell alloc] init]];

I've got some handy macros for my column identifiers. They are:

// Some handy NSDictionary keys.
#define kPath      @"path"
#define kName      @"name"
#define kIcon      @"icon"
#define kProgress  @"progress"

My data source methods use an NSMutableArray. Here is the code:

// Get the number of rows in the table.
- (int) numberOfRowsInTableView: (NSTableView *) aTableView
  {
  return [myData count];
  }

// Get a table item value.
- (id) tableView: (NSTableView *) aTableView
    objectValueForTableColumn: (NSTableColumn *) aTableColumn
    row: (int) rowIndex
  {
  // Use those macros above for column identifiers.
  return [[myData objectAtIndex: rowIndex] 
    objectForKey: [aTableColumn identifier]];
  }

Now, to put something in myData so it will get displayed in the table, do the following:

// This icon stuff is just for a file icon. It doesn't have anything
// to do with the progress indicator. No point in removing it.

// See if there is an icon for this file.
NSImage * icon = [[NSWorkspace sharedWorkspace] iconForFile: path];
    
if(!icon)
    
  // No icon for actual file, try the extension.
  icon = [[NSWorkspace sharedWorkspace] iconForFileType: 
    [path pathExtension]];
     
NSProgressIndicator * progress = 
  [[[NSProgressIndicator alloc] init] autorelease];

// Unless you want it indeterminate, of course.  
[progress setIndeterminate: NO];

// This is an important plot point. You can't just put the control inside a dictionary.
// It complains about copyWithZone or something. You have to put the control inside
// a dictionary.
NSDictionary * progressData =
  [NSDictionary dictionaryWithObjectsAndKeys:
    progress, kControl,
    0];

// This dictionary is optional. You could do this differently.      
[myData addObject:     
  [NSDictionary dictionaryWithObjectsAndKeys:
    path, kPath,
    [path lastPathComponent], kName, 
    icon, kIcon, 
    progressData, kProgress,
    0]];

// Don't forget!  
[myTableView reloadData];

To update the control, use this snippet:

// Get the control.
NSProgressIndicator * progress = 
  [[[myData objectAtIndex: i] objectForKey: kProgress] 
    objectForKey: kControl];

[progress setDoubleValue: newValue];

Finally, to remove a row, there is an extra step:

// Get the control.
NSProgressIndicator * progress = 
  [[[myData objectAtIndex: i] objectForKey: kProgress] 
    objectForKey: kControl];
  
[progress removeFromSuperview];
  
// Now remove the row.    
[myData removeObjectAtIndex: i];

The latest word on configure-based Univeral Binaries for MacOS X

After some e-mails on Apple’s Xcode mailing list, I have concluded that my previous advice about how to build configure-based Univeral Binaries for MacOS X was incorrect. All the ones I have build so far have worked fine. However, there is a possibility that some poorly designed autoconf project could set an endian macro based on the architecture passed to the configure script. Basically, while autoconf has the ability to do cross-compilation, almost no one uses that ability. It is completely unable to do Universal Binaries.

The only valid solution is something like the following:
rm -Rf /tmp/ppc
env CFLAGS=”-isysroot /Developer/SDKs/MacOSX10.2.8.sdk -arch ppc” ./configure –prefix=/tmp/ppc
make
make install
rm -Rf /tmp/i386
env CFLAGS=”-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386″ ./configure –prefix=/tmp/i386
make
make install
lipofiles /tmp/ppc /tmp/i386

I just remembered! You don’t want to do that either. Your binaries could very likely have hard-coded paths to /tmp/i386 or /tmp/ppc because the –prefix line says that is where they’ll be installed. What a mess!

Here is a another idea:
Avoid open-source code, if possible. Apple has given us quite a bit already. Learn to use it and write cross-platform wrappers if required.
If you must use an open-source library, configure it to a non-native platform. For example, if you have an Intel machine, do the following:
env CFLAGS=”-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc” ./configure
Then hack up the makefile and add “-arch i386” right after the “-arch ppc”.
Build and test hoping that, if it is going to beak, it will break on your primary test machine.

I apologize in advance if this idea is hopelessly dangerous and stupid. At this point, I’m just confused. I’ve already wasted too much time on it. Building the “official Apple way” has worked for me so far.

More MacOS X Installer fun

What is the difference between “AdminAuthorization”, “RootAuthorization”, and “NoAuthorization” in the Info.plist of a MacOS X PackageMaker file?

  • NoAuthorization
    Runs with the privileges of the user installing.
    $USER and $HOME are those of the user installing.
    Files inside the PAX take ownership and permissions of user installing.
  • AdminAuthorization and RootAuthorization
    No discernable difference between the two.
    User is asked for Administrator password.
    Runs with the privileges of root
    $USER and $HOME are those of the user installing.
    Files inside the PAX retain their original ownership and permissions.

Clearly, if I need to install into /Library and change permissions to root, I need either AdminAuthorization or RootAuthorization. But, seeing as how I can just copy files into /Applications, I shold be able to install applications using NoAuthorization, and, in fact, I can do just that.

What about ownership and permissions of the files in the PAX? Should I set them before I create the package? Or should I set them in the post* script? If I do nothing, they will all be “jdaniel” with uid 501. If a user with uid 502 is installing, that will cause problems. Should I make them root beforehand? Root is always going to be uid 0 (hopefully). But that isn’t quite like a drag-n-drop install. What if I need a special user? There is no guarantee a new user will have the same uid on all systems so I must use the post* script and a string identifier in that case.

I think I have convinced myself to always do a “chown -R $USER:$USER” in the noAuthorization case and a “chown -R root:wheel” in the AdminAuthorization case. Furthermore, I should always do that in the post* scripts. Changing them beforehand will only work in the root case and may fail in the NoAuthorization case. No, that’s all wrong!

User jblow (an admin user with uid 502, his computer having been setup by his hacker wife who has uid 501), installing Decoder, will get a bunch of files owned by jdaniel, uid 501. When his installer tried to chown those file to jblow, it will fail because he doesn’t own them to begin with, his wife does. Therefore, I must force jblow to authenticate as administrator and then he can correctly assign ownership and permissions to himself.

NoAuthorization should never, ever be used because, with a standard user, the install succeeds, but the receipt fails to be written. I guess I’ve got some more work to do…

MacOS X Installers

I’ve just spent the past two days fighting MacOS X installers. I saw that Privoxy still didn’t have a Universal Binary, so I set out to create one. The end result is available on on Etresoft.org. I used what I learned to finally put together a nice installer for , after only a couple of tries ๐Ÿ™‚

What follows here is a detailed description of how to put together a MacOS X installer package. The end result is available in the Privoxy source code, but this is the thinking behind it. You should be able to follow these steps to create a Universal Binary installer for just about any open-source project.

First, you’ll need a place to work. I suggest creating a directory for all your Mac installer stuff. Create a directory named something like “PackageMaker”.

Next, you want to know what the final install is going to look like. Run “configure” on your utilities and specify an unusual prefix – something like ‘./configure –prefix=PackageMaker/Root”. Do a make and install. That will give you a self-contained snapshot of what you want to install.

Also, if you want to build a Universal Binary, you will probably need a nasty configure call like “env CFLAGS=”-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc” ./configure –prefix=PackageMaker/Root –disable-dependency-tracking” and probably run make using “env CFLAGS=”-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc” make”. You may even need to specify LDFLAGS just like CFLAGS. YMMV.

Fire up /Developer/Tools/PackageMaker.

  1. Create a “Single Package Product”.
  2. In the “Installer Interface” tab, give your utility a nice title and description.
  3. If you have a nice background image you want to use for when the installer runs, add it and specify the position in the “Show Installer Interface Editor”. That is the only thing you want to use the “Installer Interface Editor” for. You’ll handle the License, Readme, and Welcome message elsewhere.
  4. In the “Contents” tab, specify the Root location. This is the prefix where you installed your utility – in this example, it is “PackageMaker/Root”. Make sure the “Discard Resource Forks” is checked. Otherwise, the user will have to authenticate as Administrator in order to install. Don’t go into “File Permissions” as it is very poorly implemented. Do that manually later. Set up the desired install location in “Locator Definitions” using “CheckPath”. I don’t know how well this particular feature works. In “File Filters” specify any files or directories you want to installer to ignore. CVS and svn stuff is ignored by default.
  5. In the “Configuration” tab, define the install location for your utility. Everything under “PackageMaker/Root” will be installed under the directory you specify here. The only other really important setting is “Authentication”. If the utility you are installing needs to be owned by root, use “Administrator”. Otherwise, use “None”. Turn on “Relocatable” if you want the user to be able to specify where to install. Don’t change anything else here.
  6. Don’t do anything in the “Scripts” tab.
  7. In the “Package Version” tab, set values as appropriate.
  8. Save and build a package

The PackageMaker GUI is pretty crippled and buggy, so you really don’t want to use it other that for that one time. What you need it to do for you is build the “Info.plist” and “Description.plist” files. Once you have those files, you can use the command line tool “/Developer/Tools/packagemaker” that works much better.

In order to use that packagemaker tool, you (may) need one additional thing. You probably want to display a license to make the user agree to. You might also want to display a read me and welcom message. You will also need this if you have specified a background image. What you want is an “Install_Resources” folder with the appropriate files. That folder shall be organized as follows:

Install_Resources/
Install_Resources/background.gif
Install_Resources/English.lproj
Install_Resources/English.lproj/License.html
Install_Resources/English.lproj/ReadMe.html
Install_Resources/English.lproj/Welcome.html

Everything in this folder is optional. You can add additional languages. You can move the background image (if you have one) into the language-specific folders if you want. You can use various standard file formats, but otherwise, case and spelling are significant.Tip:If you use HTML, you can also include separate style sheet files.

There are some scripts you can include in the Install_Resources folder that will be run by the installer automatically. The primary ones are “postinstall” and/or “postupgrade”. Since the installer is run by the (possibly Administrator-authenticated) user, you can do whatever you want in these scripts and they will run as the user. Typically, you will want to assign good permissions and ownership, backup old config files, etc.

Run the following command line:

/Developer/Tools/packagemaker -build -f "PackageMaker Files/Applications" -p "MyUtility.pkg" -ds -v -r "PackageMaker/Install_Resources" -i "PackageMaker/Info.plist" -d "PackageMaker/Description.plist"

and with any luck, it will work.

By this point, you probably have a script to do this stuff for you. Add the following to it:

echo Creating a disk image for the installer.
rm -Rf "MyUtility"
mkdir "MyUtility"
mv "MyUtility.pkg" "MyUtility"

hdiutil create -srcfolder "MyUtility" -anyowners -format UDZO "MyUtility.dmg"

rm -Rf "MyUiltity"

That will create a disk image with the installer package. If you have something like DropDMG or a similar utility, you can make your installer image all fancy-like.

If you know how to add all this stuff to a configure-generated makefile, I’ll love to see it!

And that’s all there is to it! What could be simpler?

I’m not the only one!

According to this post at the Apple Mailing Lists, one can extract the MIME type for a file using the following code:

#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>

int main (int argc, const char * argv[]) {
    CFStringRef extension = CFStringCreateWithCString (NULL, argv[1], kCFStringEncodingUTF8);
    CFStringRef uti = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension,  extension, NULL);

    CFStringRef mime = UTTypeCopyPreferredTagWithClass (uti, kUTTagClassMIMEType);

    CFShow (mime);

    CFRelease (extension);
    CFRelease (uti);
    CFRelease (mime);
    return 0;
}

Unfortunately, it doesn’t work at all – as this poor schmuck can attest. I’m not the only one. For this particular app I don’t need that much data validity. I’m not going to put Internet Config back again. I’ll just hack it for now and revisit it later in Leopard.