Building GTK-OSX

Building the core libraries and preconfigured applications in GTK-OSX is pretty simple: Download the installation script, run a few commands in Terminal.app (or your favorite substitute), and watch.

Building other libraries or applications can be only a little harder. See Building Other Programs below.

Quick Start

Open Terminal.app or your favorite terminal app. Download gtk-osx-setup.sh to your home directory.

./gtk-osx-setup.sh
jhbuild bootstrap-gtk-osx
jhbuild build meta-gtk-osx-bootstrap meta-gtk-osx-gtk3

Substitute meta-gtk-osx-core for meta-gtk-osx-gtk3 if your project requires Gtk2 instead of Gtk3.

macOS up through 10.15 provides only Python-2.7.10; macOS 11 and 12 /usr/bin/python is also Python-2.7.10 but Xcode 12 and later have a limited version of Python-3.8.9 which will be put in /usr/bin. Unfortunately it doesn't include headers so it's not useable to make the python bindings for libxml2 that ITS requires, but it is sufficient to run jhbuild. The Gnome project has migrated most of its projects to the Meson Build System which requires Python 3, so gtk-osx-setup.sh creates a Python3 Virtual Environment using Pipenv.

How It Works

Gtk is a unix graphical framework originally designed to run on Unix systems with X-Windows... but it provides multiple backends including one for MacOS's Quartz graphics engine, so it can be built to run on MacOS without enabling the X11 subsystem.

But there is a lot to Gtk, including a lot of dependencies, that are included as part of most Linux distributions and either aren't included or aren't sufficiently up to date on MacOS. End applications may have even more dependencies falling into one of those categories. GTK-OSX provides an easy way to download, build, and install all of those various dependencies.

The actual work is done by a Python program called `jhbuild` which takes its instructions from a collection of XML files called modulesets.

GTK-OSX's installation script installs jhbuild along with some extra code, a configuration file, and a set of modulesets which have been tailored and tested for OSX.

Prerequisites

Gtk-OSX requires MacOS X 10.10 or later and the command-line compile tools from Xcode 6.3 or later.

Simply running gtk-osx-setup.sh should offer to install the command-line tools for you if they're not already there. Failing that you can download them from Apple's developer site(you'll need to sign in with your Apple ID and accept some terms and conditions if you don't already have an Apple developer account) or you can get Xcode from the App Store. If you have HomeBrew, MacPorts, or Fink installed, you must remove all traces of them from your environment before you try to run jhbuild. The easiest way is to create a new user and run jhbuild while logged in as that user. Mixing HomeBrew, MacPorts, or Fink and GTK-OSX will fail. Note that more packages are moving from autotools to Cmake and that Cmake has hardcoded all three into its default search paths and that those paths are global so they apply to all users. Add

cmakeargs = '-DCMAKE_SYSTEM_IGNORE_PATH="/opt/homebrew:/opt/macports:/sw:/usr/local"'

to your jhbuildrc-custom to protect your Gtk-OSX build from Cmake searching those folders.

Note that there are other ways to deal with these "Linux on your Mac" package systems: You can write a shell script or a special .bashrc to add a special environment (removing those settings from your default profile/bashrc) and start a new shell to use the software they've built just as you do for jhbuild, or you can add python os.environ commands to your .jhbuildrc-custom which remove the package system's environment settings. What's important is that nothing from the package system is visible to programs running under jhbuild: Getting m4 files from the wrong aclocal will be immediately catastrophic (the build will break), installing a python module to the wrong place could lose you an afternoon trying to figure out why your program doesn't work, and linking the wrong shared library might work on your computer (until the next time you run fink selfupdate -- there goes another afternoon figuring out what changed) but fail for other users when you distribute a bundled application.

Procedure

Download gtk-osx-setup.sh to your home directory and run it:

$ sh gtk-osx-setup.sh

gtk-osx-setup.sh creates a Python3 virtual environment using Pipenv as noted under [Quick Start]. If you don't have or it can't find Python3 already installed on your system it will offer to build and install the latest Python 3.6 version for you. It will clone and install jhbuild and create a shell script to launch jhbuild inside the new virtualenv.

The installation directories gtk-osx-setup.sh uses are controlled by several environment variables, given here with their defaults:

  • DEVROOT "$HOME"
  • DEVPREFIX "$DEVROOT/.new_local"
  • PYTHONUSERBASE "$DEVROOT/.new_local"
  • DEV_SRC_ROOT "$DEVROOT/Source"
  • PYENV_INSTALL_ROOT "$DEV_SRC_ROOT/pyenv"
  • PYENV_ROOT "$DEVPREFIX/share/pyenv"
  • PIP_CONFIG_DIR "$HOME/.config/pip"
  • XDG_CONFIG_HOME "$HOME/.config"
  • JHBUILDRC "$XDG_CONFIG_HOME/jhbuildrc"
  • JHBUILDRC_CUSTOM "$XDG_CONFIG_HOME/jhbuildrc-custom"

jhbuild is installed in $DEVPREFIX/bin. You must either add that to your path, alias jhbuild, or call jhbuild with that path, e.g.

$ ~/.new_local/bin/jhbuild shell

$ alias jhbuild="PATH=.new_local/bin:$PATH jhbuild"

Now run

$ jhbuild bootstrap-gtk-osx
$ jhbuild build meta-gtk-osx-bootstrap meta-gtk-osx-gtk3

And you have a reasonably complete Gtk installation ready for you to build the application that interests you. For example

$ jhbuild build gimp

will build The GIMP and all of its dependencies. Two things of note: First, it's best to do a whole build in one go by putting all of the required modules and meta-modules in one command. That allows jhbuild to figure out the best order to build everything. Second, The GIMP is an example of an application module that has everything it needs defined as hard dependencies, but not all applications do. N.B. The GIMP is a Gtk2 application, so it requires meta-gtk-osx-core instead of meta-gtk-osx-gtk3.

RC Files

Gtk-osx is controlled by two files, jhbuildrc and jhbuildrc-custom, see the respective environment variables above for their default locations. Note that if you're upgrading an existing Gtk-osx installation that they'll be $HOME/.jhbuildrc and $HOME/.jhbuildrc-custom respectively.

jhbuildrc contains additional code to configure the build for MacOS, so it will be updated every time you run gtk-osx-setup.sh. jhbuildrc-custom, on the other hand, is for you to customize your builds to meet your needs. A sample one will be installed only if one isn't found so if you change or replace it your customizations are preserved. Look at the sample and read the jhbuild configuration file manual to see what's possible.

You may want to set

build_policy = "updated-deps"

in your jhbuildrc-custom to avoid rebuilding packages which haven't changed since the last build.

Read the jhbuild manual for details and many more options.

Developing and Running your program

A library or program built with Gtk-OSX requires a properly configured environment: In addition to the execution path GNOME programs rely heavily on environment variables to find their configuration files and resources. The simplest way to ensure that the environment is set up is to run jhbuild shell in a terminal and then run your program from the command line. If you use an IDE or an editor that integrates with tools like the debugger then you'll need to re-create the environment for your tool.

To make a program executable with LaunchServices, that is using the open command or double-clicking it in Finder you must Bundle it.

When running jhbuild shell, note that it sets $PATH before running your .bashrc, so depending on how you set $PATH, you may want to

$ export PATH=$PREFIX/bin:$PATH

after starting the jhbuild shell and before you try to do anything. We don't recommend making this your ordinary $PATH because some of the programs installed in the gtk directory work differently from the ones which Apple provides, which could cause trouble elsewhere. ($PREFIX won't work right outside of the jhbuild shell anyway.)

Parts of Gtk-OSX

The Setup Function

setup_sdk(target='native', sdk=None, architecture=[_default_arch]) The main configuration function in .jhbuildrc is setup_sdk(). .jhbuildrc-custom must call setup_sdk() to set up the build with the right compiler path and flags including the path to the Apple Software Development Kit (SDK) that contains all of the system headers and library stubs. setup_sdk() takes the three arguments noted above: target, also either 'native' or a MacOS version number, sets the minimum version of MacOS on which the resulting code will run; SDK used to set the SDK to use but modern Xcode versions have made that unnecessary so it's ignored;and architecture, which takes an array with one or both of i386 and x86_64. _default_arch will build whatever is the default for the current running system. If left to itself Gtk-Osx will build for the currently-running version of MacOS. If you want the result to run on earlier versions see #Building for Older Versions of MacOS. NOTE: Passing multiple arguments to architectures will cause the compiler to build universal binaries. This may or may not work. All supported (see Older Versions) versions of MacOS X run only on 64-bit Intel so it nearly always makes the most sense to accept the default.

Modulesets

GTK-OSX supplies 3 modulesets: modulesets-stable, modulesets, and modulesets-unstable. The default is modulesets-stable. You can switch to one of the others by putting the line

moduleset="https://gitlab.gnome.org/GNOME/gtk-osx/blob/master/MODULESETS/gtk-osx.modules"

to your ~/.jhbuildrc-custom, substituting one of the modulesets listed above for MODULESETS.

Modulesets-stable uses mostly tar archives ("mostly" because there are a couple of modules for which tar archives aren't available). Modulesets pulls mostly from development version control repositories (git, subversion, or cvs, mostly git) with revision or tag attributes designating a stable release, with a few tarballs where version control access isn't available, the project is dormant (no updates in over a year), or there is no suitable tag, revision id, or stable branch to ensure a stable release. Modulesets-unstable pulls from the same repositories as modulesets, but has the revision and tag attributes removed so that you get the latest development work for almost all modules.

NB: Modulesets-unstable is really unstable. Any one of the modules can be broken by a developer checking in a change that causes the module to not compile. If it won't compile at all, it will in most cases be fixed within a few hours, but if it just won't compile on OSX, then it may take some work on your part to get it going again. We don't regard this as our problem, and won't provide support. Don't use modulesets-unstable unless you are comfortable with autotools, C programming, glib (and particularly GObject), Cocoa, and Gitlab! In general, if you want to work on a module under version control, the best plan is to use modulesets and override the branch on that specific module in ~/.jhbuildrc-custom: (Git; consult the jhbuild documentation for other repository types.)

branches["foo"]=[None, "master"]

This may not work if the module specifies a tag instead of a branch.

Offline Building

Sometimes you may need to build a tree when you don't have network connectivity. For this to work, you'll obviously need to have all of the sources already downloaded. You also need a local set of modulesets; when you run gtk-osx-build-setup.sh, the stable modulesets are installed in ~/Source/jhbuild/modulesets alongside the modulesets that come with jhbuild. To use them, add a line

moduleset=os.path.join(os.environ["HOME"], "Source", "jhbuild", "modulesets", "gtk-osx.modules")

to your .jhbuildrc-custom. If you are using one of the other modulesets (i.e. modulesets or modulesets-unstable) you'll need to retrieve them from gitlab.gnome.org (the easiest way is to clone the project) and adapt the line above to point to the right place.

If you're using tarball modules (and the bootstrap modules always are tarballs) and building multiple trees, you'll want to set a single directory for downloading:

tarballdir=os.path.join(os.environ["HOME"], "Source", "pkgs")

To prevent jhbuild from trying to get updates from source control repositories, you can pass --no-network on the command line or add

nonetwork=True

to .jhbuildrc-custom, but that will risk not updating when you are connected, so it's probably better to use the command line option.

Download now, build later

If you want to pull everything in in one go so you can build offline, here's how:

After running gtk-osx-setup.sh and setting up your .jhbuildrc-custom file, you can make jhbuild download the modulesets as follows:

jhbuild -m bootstrap update meta-bootstrap
jhbuild update meta-gtk-osx-bootstrap
jhbuild update meta-gtk-osx-core

If you want to target multiple architectures, set up your .jhbuildrc-custom for each architecture and then rerun the above commands, because different architectures call in different modules.

Once jhbuild has downloaded the modulesets, you can build them offline later like this:

jhbuild bootstrap --no-network
jhbuild build meta-gtk-osx-bootstrap --no-network
jhbuild build meta-gtk-osx-core --no-network

Building for Older Versions of MacOS

Gtk-OSX is maintained mostly for the current stable release of MacOS and Xcode, but it can be used to build applications which run on older systems as well. The oldest supported version is the one that Apple considered current 5 years ago; at the time of this writing that is 10.10 Yosemite and Xcode 6.3. At the 5th anniversary of the release of 10.11 El Capitan that and its attendant Xcode version will become the oldest supported version, and so on.

If you're building with Xcode 7.0 (provided with MacOS X 10.10) or later and MacOSX10.10.sdk or later, just set the target argument of setup_sdk() to the earliest version you want to support. This is known to work for versions as early as SnowLeopard, but Apples stub-linking facility isn't perfect so some modules may not compile cleanly or work properly on MacOS versions older than the one on which they're built. Note that SnowLeopard is also the last version of MacOS to support 32-bit hardware, so if you're building for it you may want to set architecture=['i386'].

GObject-Introspection needs to link against libpython. If set a target older than the MacOS version on which you're building the mac-os-x-version-min will differ for that link step from the virtual environment's libpython and the build will fail. In that case you must include python3 in your list of modules. While it's not really necessary to have GObject-Introspection unless you're building a project that requires non-C/C++ language bindings several of the core modules default to requiring it.

The last commit of Gtk-OSX that supported building on systems older than MountainLion is tagged "LastLion". You can clone the Gtk-OSX repository and check out that tag to build with earlier SDKs; the modulesets in that commit are also known to work with MacOS X 10.5 and may have worked with MacOS X 10.4.

Building Other Programs

The procedure for building other programs is the same as for included programs, once you have set up a moduleset. Before you dive in, though, look through (or grep) the gtk-osx modulesets in ~/Source/jhbuild/modulesets. If the program you need is already there, then you just have to tell jhbuild to build it: jhbuild build theprogram

If it's not there, the first step is to open ~/.jhbuildrc-custom and ~/mymodules.modules in your favorite editor. .jhbuildrc-custom needs only one line for now:

moduleset=os.path.join(os.environ['HOME'], 'mymodules.modules')

mymodules.modules (which can be any name you like, but must use the "modules" extension) will start off like this:

<?xml version="1.0"?>
<!DOCTYPE moduleset SYSTEM "moduleset.dtd">
<?xml-stylesheet type="text/xsl" href="moduleset.xsl"?>
<moduleset>
    <include href="https://gitlab.gnome.org/GNOME/gtk-osx/raw/master/modulesets-stable/gtk-osx.modules"/>

</moduleset>

You'll insert any new modules and repositories after the <include/> element.

Before writing a module yourself, be sure to look through the modules in ~/Source/jhbuild/modulesets and see if there's one that you can copy into mymodules.modules. Be sure to copy the repository as well as the module.

Writing modules is beyond the scope of this page, but it's pretty easy to do. Read through jhbuild's documentation and look at the provided modulesets for examples. The hard work is in figuring out the dependencies.

A Note About Building Your Own Programs

It is common for people new to Unix programming to think that they can distribute their sources with just a Makefile (or even a bunch of Makefiles). This is unfortunately naive, because there are a number of different sorts of Unix and Unix-like operating systems, and the way that they provide services to programs varies greatly. This is true even among different versions of Linux.

To get around this problem, there have arisen a variety of different build systems which are able to explore the operating system hosting the build and make appropriate adjustments to the build files so that the program builds and runs correctly. The most commonly encountered is the GNU Autotools suite, which provides the familiar ./configure && make && sudo make install sequence. You'll find several choices supported by jhbuild in the documentation. Make sure that you use one for your program!

Bundling

When you have GTK+ built as above, and once you have built your own application, investigate bundling your application for distribution.

Support

Bugs and other issues should be reported on the gtk-osx issue tracker; propose updates and fixes as merge requests at gtk-osx merge requests. Support questions, general suggestions, and so on can be posted to discussion area at the maintainer's Github repo.

Projects/GTK/OSX/Building (last edited 2022-09-29 17:26:04 by jralls)