GRASS Mac OS X Build
(and other Mac notes)

2007-12-1

Contents

Quickstart
About
Building GRASS
	Application Build
	Universal Build*
	Configure Example*
	Leopard Notes*
	Building an Installer Package
	Bundling Libraries and Programs
Tcl/Tk
Developer Notes
Help Files*
Addon Modules
Dependency Build Notes
	Universal X11 Tcl/Tk*
	Universal GPSBabel*
	Universal NetPBM*
	Universal FFMPEG*

(* apply to any OSX build, not just an application package build)

Quickstart

To build a Mac OS X application, add this to your configure options:

--prefix=/Applications --enable-macosx-app

About

This builds GRASS as a Mac OS X application, more or less.

The startup wrapper is a simple AppleScript that fires up a custom grass.sh (instead of the standard grass6x) and quits, leaving grass.sh running in a new Terminal window.  The custom grass.sh takes care of some default and required environment settings (so the user doesn't have to fiddle with .bash_profile), makes sure X11 in running, then starts init.sh.

Building GRASS

Building GRASS as a Mac OS X application is pretty much the same as for a unix build.  For an application, just add this option to configure:

--enable-macosx-app

With this option, the prefix specified is the path where GRASS.app will be installed.

The default GRASS prefix is /usr/local, but for a Mac OS X application it should be /Applications.  It is not possible to have alternative default prefixes in configure, so you must set --prefix=/Applications yourself for configure.

The makefile tries to figure out the Tcl/Tk prefix from the configuration.  This may not work in some cases.  All that is configured are the includes and libraries locations, but the bundling step needs to know the prefix.  This will only work if they both have the same prefix.  If it has problems figuring out the Tcl/Tk prefix, set TCLTKPREFIX in the Terminal before running make:

export TCLTKPREFIX=[/path/to/tcltk/prefix]

It also tries to figure out the location of the GDAL programs, from the configured GDAL libs.  This is set in grass.sh, and if it doesn't correctly figure it out (some GRASS commands fail to find needed GDAL commands), set GDAL_BIN before running make:

export GDAL_BIN=[/path/to/gdal/bin]

Universal Build

The GRASS build system is very friendly to a Universal build (unlike libtool-based builds).  This applies to a standard unix build as well as the app build.  First, a couple things to check:

• The Universal SDK.  This is only needed when building on OSX 10.4 PPC - the Intel system is all Universal, and so is 10.5 for both architectures.  Using the SDK forces GCC to use an alternate root for everything, so if a dependency is not in the SDK (/usr/local is NOT in the SDK), you will get configure, compile and link errors about missing stuff.  To fix this, create symlinks in the SDK to where you have your extra dependencies.  The SDKs in Leopard do not have this problem, no need to fix.

For example, /usr/local:

sudo ln -s /usr/local /Developer/SDKs/MacOSX10.4u.sdk/usr/local

If there are subfolders involved that are not already in the SDK, add them first.  Say you have some libraries in /Users/Shared/unix (I put my static libraries there):

sudo mkdir -p /Developer/SDKs/MacOSX10.4u.sdk/Users/Shared
ln -s /Users/Shared/unix /Developer/SDKs/MacOSX10.4u.sdk/Users/Shared/unix

For /Library/Frameworks:

sudo mkdir -p /Developer/SDKs/MacOSX10.4u.sdk/Library
sudo ln -s /Library/Frameworks /Developer/SDKs/MacOSX10.4u.sdk/Library/Frameworks

• Universal Dependencies.  Make sure all your extra dependencies are Universal.

•  Tiger+.  This will NOT work on anything less than OSX 10.4.

If those check OK, here's what to do.  Simply add the universal flags to all compile and link commands.  This is done by setting CFLAGS, CXXFLAGS and LDFLAGS before configuring the build.  The basic flags are:

-arch ppc -arch i386

If you are building on OSX 10.4 PPC only, also add this:

-isysroot /Developer/SDKs/MacOSX10.4u.sdk

Put it all together, for an Intel Mac or any Mac on 10.5+:

export CFLAGS="-arch ppc -arch i386"
export CXXFLAGS="-arch ppc -arch i386"
export LDFLAGS="-arch ppc -arch i386"

Or for OSX 10.4 PPC:

export SDKROOT=/Developer/SDKs/MacOSX10.4u.sdk
export CFLAGS="-arch ppc -arch i386 -isysroot $SDKROOT"
export CXXFLAGS="-arch ppc -arch i386 -isysroot $SDKROOT"
export LDFLAGS="-arch ppc -arch i386 -isysroot $SDKROOT"

Configure Example

As an example, to build GRASS using my frameworks and Tcl/Tk as built in the build notes below, this should work (for a unix build, just remove the --prefix and --enable-macosx-app flags):

./configure --with-freetype --with-freetype-includes="/Library/Frameworks/FreeType.framework/unix/include/freetype2 /Library/Frameworks/FreeType.framework/unix/include" --with-freetype-libs=/Library/Frameworks/FreeType.framework/unix/lib --with-gdal=/Library/Frameworks/GDAL.framework/Programs/gdal-config --with-proj --with-proj-includes=/Library/Frameworks/PROJ.framework/unix/include --with-proj-libs=/Library/Frameworks/PROJ.framework/unix/lib --with-proj-share=/Library/Frameworks/PROJ.framework/Resources/proj --with-jpeg-includes=/Library/Frameworks/UnixImageIO.framework/unix/include --with-jpeg-libs=/Library/Frameworks/UnixImageIO.framework/unix/lib --with-png-includes=/Library/Frameworks/UnixImageIO.framework/unix/include --with-png-libs=/Library/Frameworks/UnixImageIO.framework/unix/lib --with-tiff-includes=/Library/Frameworks/UnixImageIO.framework/unix/include --with-tiff-libs=/Library/Frameworks/UnixImageIO.framework/unix/lib --without-postgres --without-mysql --with-odbc --with-sqlite --with-sqlite-libs=/Library/Frameworks/SQLite3.framework/unix/lib --with-sqlite-includes=/Library/Frameworks/SQLite3.framework/unix/include --with-fftw-includes=/Library/Frameworks/FFTW3.framework/unix/include --with-fftw-libs=/Library/Frameworks/FFTW3.framework/unix/lib --with-cxx --with-tcltk-includes=/usr/local/tcltk/include --with-tcltk-libs=/usr/local/tcltk/lib --with-x --without-motif --without-glw --with-opengl=x11 --with-opengl-libs=/usr/X11R6/lib --without-readline --prefix=/Applications --enable-macosx-app

That's a long line, but you have to be very explicit in the GRASS configure (and yes, the UnixImageIO framework is used multiple times).

If you don't have the PROJ framework programs folder in your shell PATH, it will fail to find nad2bin.  To correct this set NAD2BIN before running configure:

export NAD2BIN=/Library/Frameworks/PROJ.framework/Programs/nad2bin

Sometimes, for mysterious reasons so far, the automatic check for X11 fails.  Depending on various conditions, you may immediately notice this because configure stops.  Other symptoms include failing to build NVIZ.  If X11 isn't found automatically, add this to the configure line (this applies to both the unix build and app build):

--x-includes=/usr/X11R6/include --x-libraries=/usr/X11R6/lib

If there are still problems building NVIZ, add this to configure:

--with-opengl-includes=/usr/X11R6/include

If you want ffmpeg support (see build instructions below), add this:

--with-ffmpeg --with-ffmpeg-includes=/usr/local/include/ffmpeg --with-ffmpeg-libs=/usr/local/lib

Leopard Notes

• Linker Problem

The linker in Leopard's GCC has a bit of a problem with some libraries, notably X11's libGL.  When trying to link libGL (for example), it spits back an error:

ld: cycle in dylib re-exports with /usr/X11/lib/libGL.dylib for architecture i386

The standard workaround for this is to link using the -dylib_file flag, which tells the linker where the library really is:

-dylib_file /usr/X11/lib/libGL.dylib:/usr/X11/lib/libGL.dylib

Notice that both paths are the same, this is just how the workaround operates.  This causes some undefined symbol errors, so -undefined dynamic_lookup is also needed.

To get libGL to work in GRASS, before configuring, edit include/Make/Platform.make.in and change the OPENGLLIB= line to:

OPENGLLIB = -dylib_file /usr/X11/lib/libGL.dylib:/usr/X11/lib/libGL.dylib -undefined dynamic_lookup

• 64bits

Almost all of GRASS can be built 64bits. Use the above universal flags section and set the arch's to "ppc64" for PPC processors (I believe all are inherently 64bits), and "x86_64" for Intel processors (ie Core 2 Duo, but not Core Duo).  You don't need to build universal, but can have only one arch flag if desired. The SDK option is not needed.

BUT, Tcl/Tk 8.4 cannot be built 64bits on OSX (8.5 can, but there are other problems).  So, NVIZ cannot be built 64bits.  You should specify --without-tcltk when building 64bits.

What if you want 64bit GRASS + 32bit NVIZ?  Then you have to build GRASS, or at least the libraries, as 32+64bits, and it gets a bit messy.

Building an Installer Package

After running make, make bindist will assemble an installer package.  This will have the name "GRASS-[version].pkg" and will be found in the macosx folder in the GRASS source.  There will be some non-fatal errors - this is normal and the installer package is still generated correctly.

The installer takes care of binary installation, and creating the /Library/GRASS support folder and Help Viewer link.

This installer currently does NOT do any verification of installed dependencies.

Bundling Libraries and Programs

With the installer package build, additional dependent libraries and programs can be bundled with the application package to make a more portable and easily installed GRASS application.  See macosx/pkg/bundle.make for details.

GRASS uses DYLD_LIBRARY_PATH, which is set to the application's lib folder, so it will find any libraries that are there that are not found in their expected locations.  It also adds the application bin folder to the PATH, so helper programs installed there will be found.

The Tcl/Tk bundling below is an example of bundling both libraries and programs.  This is hardwired into the install make target.

There is NO need to bundle everything.  Bundling readily available libraries and programs installed in standard locations is not necessary.  Some things you might want to bundle include:

- GPSBabel (CLI)
- NetPBM programs (just what is used in GRASS)
- ffmpeg libraries
- Postgres library (libpq)

Things to leave out:

- applications that duplicate the above items (ie GPSBabel.app)
- /Library/Frameworks stuff

Tcl/Tk

By default, the application is built expecting a local copy of Tcl/Tk inside the application package (to simplify installation).  This will override any GRASS_WISH and GRASS_TCLSH environment setting you may have in your shell startup (ie .bash_profile).  This is currently hardwired to Tcl/Tk 8.4.

If you want to try building GRASS using Tcl/Tk Aqua (not recommended), set TCLTK_INTERNAL to nothing before compiling:

export TCLTK_INTERNAL=

Tcl/Tk Aqua has some cosmetic issues in general - widgets displayed from OSX are often too big and mess up layout, some widgets don't even use OSX equivalents (sliders are an example).  There are also problems with NVIZ that come and go.

If you want to experiment with Tcl/Tk 8.5 (ie to try a 64bit NVIZ build), configure with the tcltk option pointing to TclTk 8.5 binaries, then set TCLTKVER before compiling to 8.5:

export TCLTKVER="8.5"

Developer Notes

The applescript startup and grass.sh can be customized if desired, ie for a more complex startup that might give the user a dialog for startup options.  They just provide the basic core to build upon.

Help Files

Help files are linked into the OSX help system.  Independent of what browser you choose to open help files (GRASS_HTML_BROWSER), you can open Help Viewer from almost any application's Help menu (it's best to use Mac Help from the Finder Help menu), and GRASS Help will be available in the Library menu.

Since they are linked to the help system, if GRASS.app is moved or renamed, the link will be broken.  This may change in the future.

Addon Modules

Do not use GEM (at this time) for building and installing addon modules.  It installs in the app dir, which is not the right thing to do in OSX.  It is still installed - the gem executable is in Contents/MacOS - and may work, but it's untested.

GRASS.app is setup to find addon modules in /Library/GRASS/$VERSION/Modules and ~/Library/GRASS/$VERSION/Modules.  The version is the Major.minor version of GRASS.  /Library modules are globally available to all users, but require admin privileges to install.  Home/Library modules are only available to the user that installed them.

When GRASS is started, grass.sh adds these to the GRASS_ADDON_PATH, GRASS_ADDON_ETC and DYLD_LIBRARY_PATH.  It also rebuilds the html help index, and GUI menu file.

GRASS_ADDON_ETC is fairly new, and many addon modules may not make use of it yet.  Instead of accessing it's etc/ support files from $GISBASE/etc, the function G_find_etc("file") should be used to locate the file wherever it may be in any of the folders listed in GRASS_ADDON_ETC.  $GISBASE/etc is searched last and does not need to be included in GRASS_ADDON_ETC.

Help indexes and GUI menus for both global and user modules are maintained in the user's Library/GRASS folder, so the user doesn't need admin privs to write to the global folder.

The modbuild target creates a GEM-like build skeleton that can be used to build modules from the Terminal.  It does not require any special setup of the module source.  It does not install, or rather I haven't tested that, but it uses standard GRASS make files so it should be able to.

Universal X11 Tcl/Tk

Building a universal X11 Tcl/Tk is easy, and not worth the time to make an Xcode sub-project for.  First, you need the latest 8.4.x source for both Tcl and Tk (they are separate packages).

Unzip them, and in a Terminal (fill in [bracketed] items with appropriate values, drag-n-drop folders from the Finder for easy path entry), each prompt is a complete line, no returns except at the ends:

$ cd [/path/to/Tcl/source]/unix
$ CFLAGS="-arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3" ./configure --prefix=/usr/local/tcltk --x-includes=/usr/X11R6/include --x-libraries=/usr/X11R6/lib --enable-threads
$ make
$ sudo make install
$ sudo ln -sf libtcl8.4.dylib /usr/local/tcltk/lib/libtcl.dylib

$ cd [/path/to/Tk/source]/unix
$ CFLAGS="-arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3" ./configure --prefix=/usr/local/tcltk --enable-threads --with-x --with-tcl=/usr/local/tcltk/lib
$ make
$ sudo make install
$ sudo ln -sf libtk8.4.dylib /usr/local/tcltk/lib/libtk.dylib

This will install Tcl and Tk in the same folder, /usr/local/tcltk, and isolates it from other libraries.

With this method, if built on Tiger, the Tcl library will only work on Tiger.  To make it Panther-compatible while building on Tiger, after configuring Tcl, but before make, edit both the Makefile and tclConfig.sh and remove all occurrences of "-DHAVE_OSSPINLOCKLOCK=1".

Universal GPSBabel

A universal GPSBabel is also possible to build without too much fuss.  First, you need the GPSBabel source from www.gpsbabel.org.  If you need support for USB GPS units, get the libusb source from libusb.sourceforge.net.

For libusb, unzip the source and cd to the source folder in a Terminal.  Then:

$ ./configure --disable-shared --disable-dependency-tracking CXXFLAGS="-Os -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk" CFLAGS="-Os -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk" LDFLAGS="-arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk"
$ make
$ sudo make install

Libusb will be installed in /usr/local.  GPSBabel uses the static library, so the shared libusb is not needed, and you might have problems building libusb as shared.

For GPSBabel, unzip the source and cd to the source folder in a Terminal.  Then:

$ ./configure --disable-dependency-tracking --with-zlib=system --with-expathdr=/usr/X11R6/include --with-libexpat=/usr/X11R6/lib CFLAGS="-Os -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk" LDFLAGS="-arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk"
$ make
$ sudo make install

If you installed libusb, it will be found and built into gpsbabel.  The gpsbabel program will be installed in /usr/local/bin.

Universal NetPBM

NetPBM is not too hard to build, it's just weird.  These instructions use my UnixImageIO framework for the tiff, jpeg, png and jasper libraries.

First, grab the NetPBM source and unzip it.  Now it gets strange - it's an interactive configuration, and the various stages don't communicate with each other.  Cd to the source folder in a Terminal and:

$ ./configure

<enter> to begin

Platform [darwin] ==> <enter>

Netpbm shared library directory [default] ==> /usr/local

choose where you will install it
default is NOT /usr/local or any location at all, so you MUST set this

regular or merge [regular] ==> <enter>

static or shared [shared] ==> <enter>

could use static, since GRASS only needs a few of the progs

'#include' argument or NONE [<inttypes.h>] ==> <enter>

What is your JPEG (graphics format) library?
library filename or 'none' [libjpeg.so] ==> /Library/Frameworks/UnixImageIO.framework/unix/lib/libjpeg.dylib

JPEG header directory [default] ==> /Library/Frameworks/UnixImageIO.framework/Headers

What is your TIFF (graphics format) library?
library filename or 'none' [libtiff.so] ==> /Library/Frameworks/UnixImageIO.framework/unix/lib/libtiff.dylib

TIFF header directory [default] ==> /Library/Frameworks/UnixImageIO.framework/Headers

What is your PNG (graphics format) library?
library filename or 'none' [libpng.so] ==> /Library/Frameworks/UnixImageIO.framework/unix/lib/libpng.dylib

PNG header directory [default] ==> /Library/Frameworks/UnixImageIO.framework/Headers

What is your Z (compression) library?
library filename or 'none' [libz.so] ==> -lz

Z header directory [default] ==> <enter>

Documentation URL [http://netpbm.sourceforge.net/doc/] ==> <enter>

ignore warning about libz

$ echo "JASPERLIB = /Library/Frameworks/UnixImageIO.framework/unix/lib/libjasper.dylib" >> Makefile.config
$ echo "JASPERHDR_DIR = /Library/Frameworks/UnixImageIO.framework/Headers" >> Makefile.config
$ echo "CC = /usr/bin/gcc -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk" >> Makefile.config

$ make

$ make package pkgdir=/path/to/some/temp/dir

it will create the folder and copy all binaries there

$ sudo ./installnetpbm

then answer some more questions:

Where is the install package you created with 'make package'?
package directory (/tmp/netpbm) ==> /path/to/some/temp/dir

same location as specified in the make package step (lack of inter-communication!)

install prefix (/usr/local/netpbm) ==> /usr/local

same location used for the Netpbm shared library directory (again, lack of inter-communication!)
(it may ask you if you want to create the dir)

It'll ask for subfolders for bin, lib, data, headers, man

For data, type: /usr/local/share/netpbm

For man, type: /usr/local/share/man

For the rest, use the defaults

don't create the manweb.conf file

And that's it.

Universal FFMPEG

revision: 10837

FFMPEG is constantly changing, there is no released version, it's all in SVN.  These instructions are work on the SVN revision listed above, so if a current SVN version doesn't work, try checking out that revision.  You need SVN installed to download the FFMPEG source.

This will build a basic FFMPEG as static libraries for use with GRASS.  No extra formats are included, such as mp3lame and xvid.  Note that by default debug symbols are included, so you will get a large binary out of this.  Stripping the libraries doesn't seem to work.

First, create a folder for your ffmpeg source and cd into it.  Now:

svn checkout svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg
cd ffmpeg
mkdir build-ppc
mkdir build-i386

For a specific revision, use this instead for the checkout command:

svn checkout -r revision# svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg

It's a two-step build, 1 for each architecture, and I'm being explicit for each arch.  The -fno-common extra cflag is needed or there will be link errors in GRASS, and OSX Intel has MMX issues, so it is disabled.  For the Intel build:

cd build-i386
../configure --disable-ffserver --enable-gpl --enable-pthreads --disable-ffplay --disable-mmx --disable-vhook --arch=i386 --extra-cflags="-fno-common -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk" --extra-ldflags="-arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk"

If you want to remove the debug symbols, edit config.mak at this point and remove "-g" from OPTFLAGS.

make
sudo make install

Install at this point installs all the headers.  We don't care about the installed libraries at this point.

Now, the PPC build:

cd ../build-ppc
../configure --disable-ffserver --enable-gpl --enable-pthreads --disable-ffplay --enable-altivec --disable-vhook --arch=ppc --extra-cflags="-fno-common -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk" --extra-ldflags="-arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk"

Again, if you want to remove the debug symbols, edit config.mak at this point and remove "-g" from OPTFLAGS.

make

Don't install this one, there is no need.  Now use lipo to merge them together:

cd ..
sudo lipo -create build-ppc/libavcodec/libavcodec.a build-i386/libavcodec/libavcodec.a -output /usr/local/lib/libavcodec.a
sudo lipo -create build-ppc/libavformat/libavformat.a build-i386/libavformat/libavformat.a -output /usr/local/lib/libavformat.a
sudo lipo -create build-ppc/libavutil/libavutil.a build-i386/libavutil/libavutil.a -output /usr/local/lib/libavutil.a
sudo ranlib /usr/local/lib/libavcodec.a
sudo ranlib /usr/local/lib/libavformat.a
sudo ranlib /usr/local/lib/libavutil.a
sudo lipo -create build-ppc/ffmpeg build-i386/ffmpeg -output /usr/local/bin/ffmpeg

Done.

- William Kyngesburye
kyngchaos@kyngchaos.com
http://www.kyngchaos.com/