Created
June 9, 2012 22:05
-
-
Save dakcarto/2902743 to your computer and use it in GitHub Desktop.
Bash script for local builds of QGIS on Mac OS X Snow Leopard and Lion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# -*- coding: utf-8 -*- | |
# | |
# Build Mac OS X QGIS.app locally from github.com QGIS repository. | |
# Keep a number of dmg archives of previous builds as backups. | |
# | |
# **Run via sudo, if not installing under home directory** | |
# | |
# YOU MUST HAVE FORKED QGIS GITHUB.COM REPOSITORY AND CLONED IT TO YOUR MAC. | |
# | |
# Script will optional stash any uncommitted changes in your working tree, | |
# switch to master branch (you're not working in master branch, right?), merge | |
# any changes in master branch from QGIS's github.com repository to local | |
# master branch, apply any build-specific patch file to master, build master, | |
# reverse any applied patch file, switch back to previous branch and finally | |
# apply/delete any stash to your working tree, drop you back to your | |
# previous work state in the branch you were working on, and finally archive | |
# any built QGIS.app for later reference and comparison to later builds. | |
# | |
# Archived builds can be run from disk images later. No need to reinstall. | |
# | |
# Tested under Lion 10.7.4 and Snow Leopard 10.6.8 | |
# Requires same kyngchaos.com framework setup and build environment as per | |
# https://github.com/qgis/Quantum-GIS/blob/master/INSTALL | |
############################################################################### | |
## Run settings | |
############################################################################### | |
# Mac system python version (use 2.6 for Snow Leopard) | |
PYVER="2.7" | |
# Git setup | |
GIT=/usr/local/git/bin/git | |
GITURL="git://github.com/qgis/Quantum-GIS.git" | |
# Working directory and output locations | |
USERDIR=$(echo ~) | |
QGISDIR="$USERDIR/QGIS" | |
GITHUBDIR="$QGISDIR/github.com" | |
QGISGITDIR="$GITHUBDIR/Quantum-GIS" | |
BUILDDIR="$GITHUBDIR/build" | |
TARGETDIR="QGIS_Apps" | |
APPTARGETDIR="$GITHUBDIR/$TARGETDIR" | |
APPTARGET="$APPTARGETDIR/QGIS.app" | |
BROWSER="QGIS Browser.app" | |
BROWSERLINK="QGIS Browser(symlink).app" | |
APPARCHIVES="$GITHUBDIR/archives" | |
# Name for remote to pull from: qgisupstream | |
# Remote 'qgisupstream' for QGIS and 'origin' for fork cloned from github.com. | |
# Script will add 'qgisupstream' remote to your local repository, if missing. | |
# Stashing will happen if there are uncommited changes in current branch. | |
# GITSTASH=0 will cause script to exit if there are uncommited changes instead. | |
# TODO: add stashing for current branch, even if not pulling ? | |
GITPULL=0 | |
GITSTASH=1 | |
BUILDBRANCH='master' | |
REMOTEBRANCH='master' | |
# Path to git patch file to apply to branch after switching to it. | |
# This patch will be applied to branch, branch built, then patch reversed. | |
# This is useful for getting a branch to build if it won't, yet, | |
# but reverses the fix and leaves the branch clean after build. | |
# If QGISTESTS=TRUE, the patch will not be removed, so tests can recompile. | |
# In such a case, patch will have to be manually removed before next compile. | |
#GITPATCH="$GITHUBDIR/patches/qgsspatialindex_patch.diff" | |
GITPATCH="" | |
# Use maximum number of CPU cores for compiling QGIS. | |
CPUCORES=$(/usr/sbin/sysctl -n hw.ncpu) | |
#CPUCORES=4 | |
BUILDQGIS=1 | |
# Whether to clean (rm -R dir && mkdir) build dir before building | |
CLEANBUILD=0 | |
QGISDEBUG=1 | |
QGISSERVER="TRUE" | |
QGISAPIDOCS="FALSE" | |
INSTALLQGIS=1 | |
# Other config build options. | |
QGISTESTS="TRUE" | |
BUILDTYPE="MinSizeRel" | |
MACBUNDLE="2" | |
if [ $QGISDEBUG -gt 0 ]; then | |
BUILDTYPE="RelWithDebInfo" | |
# TODO: test if lesser bundling setting helps debugging | |
MACBUNDLE="2" | |
fi | |
# This is an optional Python module and .dylib to support QScintilla. | |
# It must be installed to be included. I use it to support my plugin work. | |
ADDQSCI=1 | |
# Launch the app, archiving happens after launch | |
LAUNCHAPP=1 | |
ARCHIVEAPP=0 | |
KEEPARCHIVES=5 | |
############################################################################### | |
## Functions | |
############################################################################### | |
REVPATCH () { | |
cd "$QGISGITDIR" | |
echo -e "\nReversing patch to branch..." | |
$GIT apply --reverse "$GITPATCH" | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR reversing patch to branch!" | |
exit 1 | |
fi | |
} | |
############################################################################### | |
## Start Build | |
############################################################################### | |
echo -e "QGIS Build @ $(date)" | |
############################################################################### | |
## Update QGIS Working Tree | |
############################################################################### | |
cd "$GITHUBDIR" | |
if [ $GITPULL -gt 0 ]; then | |
# check for existing repo dir | |
if [ ! -d "$QGISGITDIR" ]; then | |
echo -e "\nRepository missing. Using your github.com account, fork and/or clone QGIS repository.\n" | |
exit 1 | |
else | |
cd "$QGISGITDIR" | |
echo -e "\nRepository exists, checking for 'qgisupstream' remote..." | |
if [ $($GIT remote | egrep -c '^qgisupstream$') -gt 0 ]; then | |
echo "found remote" | |
else | |
$GIT remote add qgisupstream "$GITURL" | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR adding remote qgisupstream to local repository!" | |
exit 1 | |
fi | |
echo "added remote" | |
fi | |
echo -e "\nSaving name of current branch..." | |
CURBRANCH=$($GIT branch | grep '*' | sed 's/* //') | |
echo -e "\nChecking for uncommited changes in working tree...\n" | |
HASSTASH=0 | |
GITSTAT=$($GIT diff --shortstat | grep -c '.') | |
if [ $GITSTAT -gt 0 ]; then | |
if [ $GITSTASH -eq 0 ]; then | |
echo -e "\nERROR uncommited changes in current branch!" | |
echo -e "Can't switch to master until changes are committed, stashed or otherwise handled." | |
exit 1 | |
else | |
echo -e "Stashing found uncommited changes..." | |
STASHED=$($GIT stash | egrep -c '^No local changes') | |
if [ $STASHED -gt 0 ]; then | |
echo -e "Nothing stashed" | |
else | |
echo -e "Stash created" | |
HASSTASH=1 | |
fi | |
fi | |
else | |
echo -e "Nothing found" | |
fi | |
if [ $CURBRANCH != $BUILDBRANCH ]; then | |
echo -e "\nChecking out local '$BUILDBRANCH' branch..." | |
# this will DELETE ANY CHANGES to current working tree! | |
ISBUILDBRANCH=$($GIT branch | grep -c '^* $BUILDBRANCH') | |
if [ $ISBUILDBRANCH -eq 0 ]; then | |
$GIT checkout -f $BUILDBRANCH | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR checking out local '$BUILDBRANCH' branch!" | |
exit 1 | |
fi | |
fi | |
fi | |
# update build branch | |
echo -e "\nFetching 'qgisupstream' and pulling '$REMOTEBRANCH' branch..." | |
$GIT fetch qgisupstream | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR fetching git repository!" | |
exit 1 | |
fi | |
$GIT pull qgisupstream $REMOTEBRANCH | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR pulling qgisupstream git repo '$REMOTEBRANCH' branch!" | |
exit 1 | |
fi | |
fi | |
fi | |
if [ "$GITPATCH" != "" ]; then | |
cd "$QGISGITDIR" | |
echo -e "\nPatching branch, checking patch validity...\n" | |
$GIT apply --check "$GITPATCH" | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR patch cannot be applied to branch!" | |
exit 1 | |
else | |
echo -e "Patch can be applied, applying..." | |
fi | |
$GIT apply "$GITPATCH" | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR applying patch to branch!" | |
exit 1 | |
fi | |
fi | |
if [ "$SUDO_USER" != "" ]; then | |
chown -R $SUDO_USER "$QGISGITDIR" | |
fi | |
############################################################################### | |
## Build QGIS.app | |
############################################################################### | |
if [ $BUILDQGIS -gt 0 ]; then | |
if [ $CLEANBUILD -gt 0 ]; then | |
# remove any previous build dir | |
if [ -d "$BUILDDIR" ]; then | |
echo -e "\nRemoving existing build directory ..." | |
rm -R "$BUILDDIR" | |
fi | |
fi | |
if [ ! -d "$BUILDDIR" ]; then | |
# QGIS CMake setup supports out-of-source dir builds | |
# make build directory and configure | |
mkdir -p "$BUILDDIR" | |
fi | |
cd "$BUILDDIR" | |
# use ccache symlinked compilers | |
export PATH=/usr/local/bin/compilers:$PATH | |
# force llvm instead of clang under XCode 4.4.1 | |
export CC=/usr/bin/llvm-gcc | |
export CXX=/usr/bin/llvm-g++ | |
echo -e "\nConfiguring build ...\n" | |
/usr/local/bin/cmake -D CMAKE_INSTALL_PREFIX="$APPTARGETDIR" \ | |
-D CMAKE_BUILD_TYPE="$BUILDTYPE" \ | |
-D WITH_ASTYLE=TRUE \ | |
-D ENABLE_TESTS="$QGISTESTS" \ | |
-D WITH_INTERNAL_SPATIALITE=FALSE \ | |
-D WITH_PYSPATIALITE=FALSE \ | |
-D SPATIALINDEX_INCLUDE_DIR=/usr/local/include/spatialindex \ | |
-D SPATIALINDEX_LIBRARY=/usr/local/lib/libspatialindex.dylib \ | |
-D QWT_LIBRARY=/usr/local/qwt-5.2.2/lib/libqwt.dylib \ | |
-D QWT_INCLUDE_DIR=/usr/local/qwt-5.2.2/include \ | |
-D BISON_EXECUTABLE=/usr/local/bin/bison \ | |
-D WITH_MAPSERVER="$QGISSERVER" \ | |
-D WITH_APIDOC="$QGISAPIDOCS" \ | |
-D QGIS_MACAPP_BUNDLE="$MACBUNDLE" \ | |
"$QGISGITDIR" | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR configuring QGIS build!" | |
if [ "$GITPATCH" != "" ]; then REVPATCH; fi | |
exit 1 | |
fi | |
# exit 0 | |
#Build and install target QGIS.app | |
echo -e "\nMaking build ...\n" | |
make -j $CPUCORES | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR building QGIS!" | |
if [ "$GITPATCH" != "" ]; then REVPATCH; fi | |
exit 1 | |
fi | |
if [ "$SUDO_USER" != "" ]; then | |
chown -R $SUDO_USER "$BUILDDIR" | |
fi | |
if [ $INSTALLQGIS -gt 0 ]; then | |
if [ ! -d "$APPTARGETDIR" ]; then | |
mkdir -p "$APPTARGETDIR" | |
fi | |
# remove last app built if current repo built | |
if [ -d "$APPTARGET" ]; then | |
echo -e "\nRemoving previously installed app ..." | |
rm -R "$APPTARGET" | |
fi | |
# # remove existing alias to QGIS Broswer | |
# if [ -e "$APPTARGETDIR/$BROWSER" ]; then | |
# echo -e "\nRemoving previous browser app alias ..." | |
# rm "$APPTARGETDIR/$BROWSER" | |
# fi | |
echo -e "\nInstalling build to target app bundle...\n" | |
# only show errors | |
make install 1>/dev/null | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR installing QGIS!" | |
if [ "$GITPATCH" != "" ]; then REVPATCH; fi | |
exit 1 | |
fi | |
# # remove build alias to QGIS Broswer | |
# echo -e "\nRemoving browser app alias, in favor of symlink for archive..." | |
# /usr/bin/find "$APPTARGETDIR" -type f -name "$BROWSER*" -delete | |
# make link to QGIS Browser.app | |
# link is better than alias, when launching from dmg | |
# if [ -e "$APPTARGETDIR/$BROWSERLINK" ]; then | |
# echo -e "\nRemoving previous browser app link ..." | |
# rm "$APPTARGETDIR/$BROWSERLINK" | |
# fi | |
echo -e "\nCreating symbolic link to QGIS Browser.app ..." | |
# /usr/bin/osascript > /dev/null <<EOT | |
# tell application "Finder" | |
# set srcPath to POSIX file "$APPTARGET/Contents/MacOS/bin/QGIS Browser.app" as string | |
# set trgtPath to POSIX file "$APPTARGETDIR" as string | |
# make new alias file to application file srcPath at folder trgtPath | |
# end tell | |
# EOT | |
cd "$APPTARGETDIR" | |
ln -fs "QGIS.app/Contents/MacOS/bin/$BROWSER" "./$BROWSERLINK" | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR creating link!" | |
fi | |
echo -e "\nCompiling app's python modules ..." | |
PY="/usr/bin/python$PYVER" | |
PYDIR="$APPTARGET/Contents/Resources/python" | |
PYCOMPILE=/System/Library/Frameworks/Python.framework/Versions/$PYVER/lib/python$PYVER/compileall.pyc | |
# show only errors | |
$PY $PYCOMPILE $PYDIR 1>/dev/null | |
fi | |
fi | |
############################################################################### | |
## Reset repository | |
############################################################################### | |
# Reverse any patch file applied to current or master branch. | |
# Unless building for tests, in which case leave it, then manually removed. | |
if [ "$GITPATCH" != "" ] && [ "$QGISTESTS" != "TRUE" ]; then | |
REVPATCH | |
fi | |
if [ $GITPULL -gt 0 ]; then | |
# Checkout previous branch, unless running tests. | |
if [ $CURBRANCH != $BUILDBRANCH ]; then | |
echo -e "\nChecking out $CURBRANCH branch..." | |
$GIT checkout -f $CURBRANCH | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR checking out local $CURBRANCH branch!" | |
exit 1 | |
fi | |
fi | |
# Apply and delete any stash to previous branch | |
if [ $GITSTASH -gt 0 ] && [ $HASSTASH -gt 0 ]; then | |
echo -e "\nApplying and removing stash..." | |
$GIT stash pop | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR applying and removing stash!" | |
exit 1 | |
fi | |
fi | |
fi | |
if [ "$SUDO_USER" != "" ]; then | |
chown -R $SUDO_USER "$QGISGITDIR" | |
fi | |
############################################################################### | |
## Add QScintilla Python module to QGIS.app | |
############################################################################### | |
if [ $ADDQSCI -gt 0 ] && [ $INSTALLQGIS -gt 0 ]; then | |
echo -e "\nAdding Qsci.so module to target..." | |
# cd $QSCIPATH | |
# QSCIPATH="$QGISDIR/Qsci_Lion/4.8.0" | |
# /bin/cp -a Qsci.so "$APPTARGET/Contents/Resources/python/PyQt4/"; | |
# /bin/cp -a libqscintilla2.* "$APPTARGET/Contents/MacOS/lib/"; | |
# /usr/bin/install_name_tool -add_rpath @executable_path/lib/libqscintilla2.8.dylib "$APPTARGET/Contents/MacOS/QGIS" | |
QSCI='' | |
if [ -e /Library/Python/$PYVER/site-packages/PyQt4/Qsci.so ]; then | |
QSCI=/Library/Python/$PYVER/site-packages/PyQt4/Qsci.so | |
fi | |
if [ -n $QSCI ]; then | |
cd "$APPTARGET/Contents/Resources/python/PyQt4" | |
cp -a $QSCI . | |
install_name_tool -change /Library/Frameworks/libqscintilla2.8.dylib \ | |
@loader_path/../../../MacOS/lib/libqscintilla2.8.dylib Qsci.so | |
install_name_tool -change QtCore.framework/Versions/4/QtCore \ | |
@loader_path/../../../MacOS/../Frameworks/QtCore.framework/QtCore Qsci.so | |
install_name_tool -change QtGui.framework/Versions/4/QtGui \ | |
@loader_path/../../../MacOS/../Frameworks/QtGui.framework/QtGui Qsci.so | |
cd "$APPTARGET/Contents/MacOS/lib" | |
cp -a /Library/Frameworks/libqscintilla*.dylib . | |
install_name_tool -change QtCore.framework/Versions/4/QtCore \ | |
@loader_path/../../Frameworks/QtCore.framework/QtCore libqscintilla2.8.dylib | |
install_name_tool -change QtGui.framework/Versions/4/QtGui \ | |
@loader_path/../../Frameworks/QtGui.framework/QtGui libqscintilla2.8.dylib | |
install_name_tool -add_rpath @executable_path/lib/libqscintilla2.8.dylib \ | |
"$APPTARGET/Contents/MacOS/QGIS" | |
else | |
echo -e "\nQsci.so module not found..." | |
fi | |
fi | |
if [ "$SUDO_USER" != "" ] && [ $BUILDQGIS -gt 0 ] && [ $INSTALLQGIS -gt 0 ]; then | |
# set permissions on app | |
chown -R $SUDO_USER "$APPTARGETDIR" | |
fi | |
############################################################################### | |
## Launch app before any archive operation | |
############################################################################### | |
if [ $LAUNCHAPP -gt 0 ]; then | |
echo -e "\nLaunching app..." | |
sleep 3 | |
/usr/bin/open "$APPTARGET" | |
fi | |
############################################################################### | |
## Archive QGIS.app to DMG Archives | |
############################################################################### | |
if [ $ARCHIVEAPP -gt 0 ]; then | |
sleep 5 | |
# make sure dir structure exists | |
if [ ! -d "$APPARCHIVES" ]; then | |
mkdir -p "$APPARCHIVES" | |
fi | |
# congifure archive file | |
cd "$QGISGITDIR" | |
# this creates a short sha hash for last commit on current local branch built | |
REVHASH=$($GIT log --no-color --pretty=format:%H $BUILDBRANCH^..$BUILDBRANCH) | |
REVSHORTHASH=$(echo ${REVHASH:0:7}) | |
REVMAJOR=$(grep 'SET(CPACK_PACKAGE_VERSION_MAJOR' CMakeLists.txt | egrep -o '[0-9]+') | |
REVMINOR=$(grep 'SET(CPACK_PACKAGE_VERSION_MINOR' CMakeLists.txt | egrep -o '[0-9]+') | |
REVPATCH=$(grep 'SET(CPACK_PACKAGE_VERSION_PATCH' CMakeLists.txt | egrep -o '[0-9]+') | |
VERNUM="${REVMAJOR}-${REVMINOR}-${REVPATCH}" | |
TODAY=$(date -u +"%Y-%m-%d") | |
if [ "$BUILDBRANCH" != "master" ]; then | |
APPNAME="QGIS_${VERNUM}_${TODAY}_${REVSHORTHASH}($BUILDBRANCH)" | |
else | |
APPNAME="QGIS_${VERNUM}_${TODAY}_${REVSHORTHASH}" | |
fi | |
APPARCHIVE="$APPNAME.dmg" | |
OLDARCHIVE="$APPNAME_old.dmg" | |
# archive built app to compressed dmg | |
echo -e "\nArchiving $TARGETDIR target directory...\n" | |
cd "$GITHUBDIR" | |
# keep at least 1 archive with same hash, in case build fails | |
if [ -e "$APPARCHIVES/$APPARCHIVE" ]; then | |
if [ -e "$APPARCHIVES/$OLDARCHIVE" ]; then | |
rm "$APPARCHIVES/$OLDARCHIVE" | |
fi | |
mv "$APPARCHIVES/$APPARCHIVE" "$APPARCHIVES/$OLDARCHIVE" | |
fi | |
# create dmg archive | |
hdiutil create -nospotlight -skipunreadable -noanyowners \ | |
-srcfolder "$APPTARGETDIR" "$APPARCHIVES/$APPARCHIVE" | |
if [ $? -gt 0 ]; then | |
echo -e "\nERROR archiving QGIS to dmg!" | |
exit 1 | |
fi | |
if [ "$SUDO_USER" != "" ]; then | |
chown -R $SUDO_USER "$APPARCHIVES" | |
fi | |
# prune dmg archives | |
CURFILE=1 | |
for f in $( ls -1t "$APPARCHIVES" ); do | |
if [ $CURFILE -gt $KEEPARCHIVES ]; then | |
rm "$APPARCHIVES/$f" | |
fi | |
let CURFILE=CURFILE+1 | |
done | |
fi | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment