OmniMake Frequently Asked Questions
Last modified November 17th, 1997 - luke

Since OmniMake hasn't been publically released until now (November, 1997), this is more of a "Likely To Be Asked Questions" list, than a FAQ. But, naming it an LTBAQ would've been a little strange. Really though, we have had a few external groups using OmniMake for the past couple months, and most of these issues have come up at one point or another. As more developers start using the system, and have new questions, we'll try to keep the FAQ and other documentation updated.

1. Where are my executables going?
2. How do I export symbols from my frameworks?
3. What's this business about /Store and /Cache I keep seeing in the documentation?
4. How do I build projects so they load their frameworks from /LocalLibrary/Frameworks, etc., rather than /Cache/me/InstallRoot/Frameworks/...?

Where are my executables going?

OmniMake doesn't use your source directories as the default build output location. Instead, it provides two environment variables which specify where build output should go:

        OMNIMAKE_INSTALL_ROOT
        OMNIMAKE_BUILD_OUTPUT_ROOT

OMNIMAKE_BUILD_OUTPUT_ROOT specifies the root location of your build output tree, where build output consists of derived source, object files, product binaries, etc. The build output tree is organized so that each project type is in its own subdirectory, and each project exists within a unique subdirectory below that. For example, if your OMNIMAKE_BUILD_OUTPUT_ROOT was set to /Cache/zuul/BuildOutput, the build output tree could look something like this:

        /Cache/zuul/BuildOutput/
            Applications/
                OmniWeb/
                    OmniWeb.app/
                    ProjectHeaders/
                    derived_src/
                    obj-i386-opt/
            Frameworks/
                OmniBase/
                    OmniBase.framework/
                    ProjectHeaders/
                    derived_src/
                    obj-i386-opt/
            WebObjectsApplications/
            Tools/

The other build output related variable, OMNIMAKE_INSTALL_ROOT, specifies the root location of a similar, but more compact version of OMNIMAKE_BUILD_OUTPUT_ROOT. Assuming that your OMNIMAKE_INSTALL_ROOT was set to /Cache/zuul/InstallRoot, and you were working on the same projects as listed in the above example, your trimmed build output tree would look like this:

        /Cache/zuul/InstallRoot/
            Applications/
                OmniWeb.app/
            Frameworks/
                OmniBase.framework/
            WebObjectsApplications/
            Tools/

As you can see, the OMNIMAKE_INSTALL_ROOT tree doesn't include derived_src, ProjectHeaders, object files, and other resources which are unnecessary for the final product. Both OMNIMAKE_BUILD_OUTPUT_ROOT and OMNIMAKE_INSTALL_ROOT are maintained during normal builds. On Mach, OMNIMAKE_INSTALL_ROOT is built by using symbolic links into the associated OMNIMAKE_BUILD_OUTPUT_ROOT location. Since NT doesn't support symbolic links, copies are made on that platform.

How do I export symbols from my frameworks?

OmniMake provides an automatic mechanism for exporting framework symbols which currently works on all OPENSTEP and PDO-supported platforms.

When you build a framework, a file named FrameworkDefines.h is generated in your derived_src directory using the FrameworkDefinesTemplate located in $OMNIMAKE_ROOT/utilities. Once generated, this header provides a processor definition named: "YourFrameworkName_EXTERN". You can use this definition to export symbols in your framework.

For example, consider you have a framework called Wonky.framework which includes the files: Snurble.h and Snurble.m. If you wanted to export a string (to serve as an exception name) called "TooMuchWeirdnessException", you could do this:

    [Snurble.h]

    #import "FrameworkDefines.h"

    Wonky_EXTERN NSString *TooMuchWeirdnessException;

    [Snurble.m]

    #import "Snurble.h"

    NSString *TooMuchWeirdnessException = @"TooMuchWeirdnessException";

You can use the same method to export functions. Be sure to #import "FrameworkDefines.h", or the "YourFrameworkName_EXTERN" keyword will cause a compilation error.

What's this business about /Store and /Cache I keep seeing in the documentation?

A simple OmniGroup convention, really. /Store and /Cache are typically directories on the developer's local disk. /Store is intended for information, such as source, which is important and should be saved by nightly backup, etc. /Cache is intended for information which isn't particularly important and can be derived from information in /Store (such as build products).

Since OmniMake separates source and build output, it's easy to build with your source directory mounted over the network and still have the compiled products stored on your local disk (minimizing network traffic).

And, of course, since OmniMake is easily configurable you can specify your OMNIMAKE_INSTALL_ROOT, OMNIMAKE_BUILD_OUTPUT_ROOT, and OMNIMAKE_ROOT to be anything you like.

How do I build projects so they load their frameworks from /LocalLibrary/Frameworks, etc., rather than /Cache/me/InstallRoot/Frameworks/...?

This is really one of the sneakiest problems when building a product for release.

A quick piece of background information: In the mach-o file format, dynamic libraries are identified by their LC_ID_DYLIB and their version information. The LC_ID_DYLIB is a string which represents the path to the framework in the filesystem. This value is set by the linker argument: -install_name. When something links against a framework, it looks at the framework's LC_ID_DYLIB, and uses that value to set the name of an LC_LOAD_DYLIB load command. When the final executable runs, each load command is evaluated, and the dynamic linker (dyld) attempts to load each framework from the specified location.

So, getting frameworks to load properly for a final product is simply a matter of getting the correct argument passed to the linker.

To explicity define the framework path for a particular framework, you can set OMNIMAKE_FINAL_DESTINATION_DIR at build time, like so:

    omnimake OMNIMAKE_FINAL_DESTINATION_DIR=/LocalLibrary/Frameworks

For those of you who prefer to build using ProjectBuilder, you can use the final_install targets. From within ProjectBuilder, go to the Inspector, view Build Attributes, then view Build Targets. You probably won't have the final-style build targets added to your project, so add them manually by typing the target names in the text field and clicking, Add. The target names are as follows:

        final_install
        final_debug_install
        final_profile_install

Then, when you go to the Build window, you can click on the check button to view Build Options, and set your build target to the appropriate final_install target. When building for final_install, the default OMNIMAKE_FINAL_DESTINATION_DIR location is set to /LocalLibrary/Frameworks. You can set this on a per project basis by editing your Project's Makefile.preamble and defining OMNIMAKE_FINAL_DESTINATION_DIR_OVERRIDE to the appropriate framework installation path.

For a definitive explanation of what the final_install targets do, see common.make.postamble in OMNIMAKE_ROOT/Makefiles.