Homebrew is the de facto standard package manager of OS X. It is also available on Linux as Linuxbrew.
By default homebrew installs packages system-wide into /usr/local/
(on OS X). But you can actually clone the brew repository to any place in the filesystem. brew
will then install its packages into that place. This is a convenient way to create your own playground development environment of tools and libraries. mulle-brew helps you set up and share such playgrounds.
- Keep the runtime and your system clean from temporary downloads.
- Updating brew libraries for one project, doesn't necessarily impact parallel projects
- An easy way to share the required build environments
- A uniform way to access those dependencies via a known folder name
addictions
- Makes homebrew dylibs and frameworks embeddable
Install mulle-brew with brew, it makes sense :) :
brew install mulle-kybernetik/alpha/mulle-brew
Install hello:
mulle-brew init
mulle-brew setting -g -r brews hello
mulle-brew
Run hello:
mulle-brew run hello
mulle-brew run
adds addictions/bin
to your PATH
shell variable so that hello is found for the duration of this command.
You could as well have used ./addictions/bin/hello
. The use of
mulle-brew run
becomes more useful in more complex scenarios.
Install libpng and zlib:
mulle-brew init
mulle-brew setting -g -r brews libpng
mulle-brew setting -g -r -a brews zlib
mulle-brew
Now let's create a small test program x
. Here is the C Source:
cat <<EOF > x.c
#include <png.h>
#include <zlib.h>
main()
{
printf( "png version: %u\n", png_access_version_number());
printf( "zlib version: %s\n", zlibVersion());
return( 0);
}
EOF
Build x.c
as x
with cc and run it:
flags="`mulle-brew paths -l cflags`"
mulle-brew run cc ${flags} -o x x.c
mulle-brew run ./x
If you are running mulle-brew paths -l cflags
in /tmp
it will produce this
line:
-I/tmp/addictions/include -F/tmp/addictions/Frameworks -L/tmp/addictions/lib -lpng -lpng16 -lz
mulle-brew paths cflags
on its own produces the -I, -F and -L flags. The -l
option collects all the available libraries from addictions/lib
and adds them as commandline options.
If you don't have cmake:
brew install cmake
.
Create a CMakeLists.txt
:
cat <<EOF > CMakeLists.txt
cmake_minimum_required (VERSION 3.0)
project (x)
find_library( PNG_LIBRARY NAMES png)
find_library( ZLIB_LIBRARY NAMES z)
add_executable( x
x.c)
target_link_libraries( x
\${PNG_LIBRARY}
\${ZLIB_LIBRARY}
)
EOF
Build it with cmake
and make
:
mulle-brew run mkdir build
mulle-brew run cd build
mulle-brew run cmake `mulle-brew paths cmake` ..
mulle-brew run make
mulle-brew run ./x
Check with
otool -L x
that indeed the libraries fromaddictions/lib
have been picked up.
If you install libraries and frameworks with homebrew, you will notice that you can not embed them into your own applications, because homebrew does not build them with @rpath.
mulle-brew install -r
will fix this for you.
Since
mulle-brew run
not only changes thePATH
but also sets upDYLD_FALLBACK_LIBRARY_PATH
andDYLD_FALLBACK_FRAMEWORK_PATH
, binaries inaddictions/bin
will continue to run when executed withmulle-brew run
.
Assume you already have an app set up, and you want to embed a framework int it:
- Make sure you have run
mulle-brew install -r
so all frameworks are relocatable - Link against the framework in the
addictions/Frameworks
folder. In Xcode locate the "Embed Frameworks" build phase. Press the + button, then choose "Add Other...". Then navigate to../addictions/Frameworks
and add the framework. - Build and it should work. Test the relocatability of your app by copying it to the Desktop and starting it from there.
Assume you already have an app set up, and you want to embed a shared library in it:
- Make sure you have run
mulle-brew install -r
so all shared libraries are relocatable - Link against the shared library in the
addictions/lib
folder. In Xcode locate the "Link Libraries with Binary" build phase. Press the + button, then choose "Add Other...". Then navigate to../addictions/lib
and add the library (don't copy). Use the most qualified version. E.g. if you have a choice betweenlibz.dylib
,libz.1.dylib
,libz.1.2.11.dylib
chooselibz.1.2.11.dylib
. - The library should now appear in the "Project navigator". Drag it into the "Copy Bundle Resources" build phase of your app.
- Add the header directory
addictions/include
to the Header Search Paths" " (HEADER_SEARCH_PATHS
) build setting of your project or target. As an alternative you can runmulle-brew xcode -a
to do this step for you. - Build the app and it should work. Test the relocatability of your app by copying it to your Desktop and starting it from there.
MulleBrewApp is an example that shows an App embedding the Python framework and the zlib shared library using the techniques above.
The initial setup was done with:
mulle-brew init
mulle-brew setting -g -r brews zlib
mulle-brew setting -g -r -a brews python
mulle-brew install -r
The App is a stock Xcode app for Objective-C without any specialties added.
Only AppDelegate.m
and MainMenu.nib
have been modified to retrieve zlib and
python version strings and place them into NSTextFields
:
#import "AppDelegate.h"
#import <Python/Python.h>
#include <zlib.h>
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@property (weak) IBOutlet NSTextField *zlibVersion;
@property (weak) IBOutlet NSTextField *pythonVersion;
@end
@implementation AppDelegate
- (void) applicationDidFinishLaunching:(NSNotification *) aNotification
{
[self.zlibVersion setStringValue:[NSString stringWithUTF8String:zlibVersion()]];
[self.pythonVersion setStringValue:[NSString stringWithUTF8String:Py_GetVersion()]];
}
@end
Then the library and framework have been added as explained above:
And last but not least the search paths for the headers were set. Note that Xcode does updates the "Framework Search Paths" for you automatically.
And it just worked.
Cloning brew from GitHub for every project can get tedious. You can use a local cache with:
mulle-brew config -u "clone_cache" "${HOME}/Library/Caches/mulle-brew"
Prefixing everything with mulle-brew can be tedious. Use a subshell:
mulle-brew run "${SHELL}"
If Xcode uses tools in your binary folder, it can be useful to start Xcode in the proper environment, so open your project like so:
mulle-brew run open Foo.xcodeproj
You might find that you have multiple projects with overlapping dependencies on brew formula and the duplication becomes tedious. You can create a "master" playground in the common parent directory with:
mulle-brew defer
And revert back to a private playground with
mulle-brew emancipate
Here the use of mulle-brew paths
comes in handy, as it adapts to the
new position of the addictions
folder in the filesystem.
As this simplifies things a lot. If your home directory is "My Home" though,
you should note that you can give quoting options to mulle-brew paths
.
To properly use quoted paths you will then need to use eval
to run commands:
CFLAGS="`mulle-brew paths -1 -q "'" cflags`"
mulle-bootstrao run eval cc ${CFLAGS} x.c
The development is done on Mulle kybernetiK. Releases and bug-tracking are on GitHub.