[gitg] Add initial OS X app bundle generator
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg] Add initial OS X app bundle generator
- Date: Thu, 24 Dec 2015 11:30:42 +0000 (UTC)
commit 393e7c04d9943520769fcc4bfaa037da236a6cfe
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Thu Dec 24 12:30:18 2015 +0100
Add initial OS X app bundle generator
configure.ac | 1 +
osx/.gitignore | 1 +
osx/bundle.json.in | 22 ++++
osx/data/Info.plist | 36 ++++++
osx/scripts/launcher | 65 +++++++++++
osx/scripts/make-bundle | 290 +++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 415 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index dc8c29c..016d988 100644
--- a/configure.ac
+++ b/configure.ac
@@ -357,6 +357,7 @@ libgitg/libgitg-1.0.pc
libgitg-ext/libgitg-ext-1.0.pc
data/gitg.desktop.in
data/org.gnome.gitg.gschema.xml.in
+osx/bundle.json
po/Makefile.in
])
diff --git a/osx/.gitignore b/osx/.gitignore
new file mode 100644
index 0000000..89316c0
--- /dev/null
+++ b/osx/.gitignore
@@ -0,0 +1 @@
+/Gitg.app
diff --git a/osx/bundle.json.in b/osx/bundle.json.in
new file mode 100644
index 0000000..08c6d3b
--- /dev/null
+++ b/osx/bundle.json.in
@@ -0,0 +1,22 @@
+{
+ "name": "Gitg",
+
+ "variables": {
+ "version": "@VERSION@",
+ "prefix": "@prefix@"
+ },
+
+ "main": "${resources}/bin/gitg",
+
+ "binaries": {
+ "${prefix}/bin/gitg": "${resources}/bin/gitg"
+ },
+
+ "data": {
+ "${prefix}/lib/girepository-1.0/*.typelib": "${resources}/lib/girepository-1.0/"
+ },
+
+ "data_interpolated": {
+ "${rootdir}/data/Info.plist": "${contents}/Info.plist"
+ }
+}
diff --git a/osx/data/Info.plist b/osx/data/Info.plist
new file mode 100644
index 0000000..ce9dd0c
--- /dev/null
+++ b/osx/data/Info.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>Gitg</string>
+ <key>CFBundleGetInfoString</key>
+ <string>${version} Copyright 2015, ${name}</string>
+ <key>CFBundleIconFile</key>
+ <string>Gitg.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.gnome.Gitg</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>${version}</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>${version}</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright 2015 ${name}, GNU General Public License.</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.7</string>
+ <key>CFBundleName</key>
+ <string>${name}</string>
+ <key>CFBundleDisplayName</key>
+ <string>${name}</string>
+ <key>NSHighResolutionCapable</key>
+ <string>True</string>
+</dict>
+</plist>
diff --git a/osx/scripts/launcher b/osx/scripts/launcher
new file mode 100644
index 0000000..1e7ec5d
--- /dev/null
+++ b/osx/scripts/launcher
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+if test "x$GTK_DEBUG_LAUNCHER" != x; then
+ set -x
+fi
+
+if test "x$GTK_DEBUG_GDB" != x; then
+ EXEC="lldb --"
+elif test "x$GTK_DEBUG_DTRUSS" != x; then
+ EXEC="sudo dtruss sudo -u $USER"
+else
+ EXEC=exec
+fi
+
+name=$(basename "$0")
+dirn=$(dirname "$0")
+
+pushd "$dirn/../../" > /dev/null
+bundle=$(pwd -P)
+popd > /dev/null
+
+bundle_contents="$bundle"/Contents
+bundle_res="$bundle_contents"/Resources
+bundle_lib="$bundle_res"/lib
+bundle_bin="$bundle_res"/bin
+bundle_data="$bundle_res"/share
+bundle_etc="$bundle_res"/etc
+
+export PATH="$bundle_bin:$PATH"
+export DYLD_LIBRARY_PATH="$bundle_lib:$DYLD_LIBRARY_PATH"
+export XDG_CONFIG_DIRS="$bundle_etc:$XDG_CONFIG_DIRS"
+export XDG_DATA_DIRS="$bundle_data:$XDG_DATA_DIRS"
+export GTK_DATA_PREFIX="$bundle_res"
+export GTK_EXE_PREFIX="$bundle_res"
+export GTK_PATH="$bundle_res"
+export GDK_PIXBUF_MODULE_FILE="$bundle_lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
+#export GIO_EXTRA_MODULES="$bundle_lib/gio/modules"
+export GI_TYPELIB_PATH="$bundle_lib/girepository-1.0"
+export PANGO_LIBDIR="$bundle_lib"
+export PANGO_SYSCONFDIR="$bundle_etc"
+export PEAS_PLUGIN_LOADERS_DIR="$bundle_lib/libpeas-1.0/loaders"
+export ENCHANT_MODULES_DIR="$bundle_lib/enchant"
+export ENCHANT_DATA_DIR="$bundle_data/enchant"
+
+if test -f "$bundle_lib/charset.alias"; then
+ export CHARSETALIASDIR="$bundle_lib"
+fi
+
+# Extra arguments can be added in environment.sh.
+EXTRA_ARGS=
+
+if test -f "$bundle_res/environment.sh"; then
+ source "$bundle_res/environment.sh"
+fi
+
+# Strip out the argument added by the OS.
+if [ x`echo "x$1" | sed -e "s/^x-psn_.*//"` == x ]; then
+ shift 1
+fi
+
+if [ "x$GTK_DEBUG_SHELL" != "x" ]; then
+ exec bash
+else
+ $EXEC "$bundle_contents/MacOS/${name}-bin" "$@" $EXTRA_ARGS
+fi
diff --git a/osx/scripts/make-bundle b/osx/scripts/make-bundle
new file mode 100755
index 0000000..2936e9a
--- /dev/null
+++ b/osx/scripts/make-bundle
@@ -0,0 +1,290 @@
+#!/usr/bin/python
+
+import inspect, os, shutil, subprocess, glob, sys, re, argparse, json
+
+scriptdir = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe())))
+rootdir = os.path.dirname(scriptdir)
+
+import argparse
+
+parser = argparse.ArgumentParser(description='gitg osx bundler')
+
+parser.add_argument('-d', '--debug', type=bool, help='enable debugging')
+parser.add_argument('bundle', type=str, metavar='FILE', help='bundle json config')
+
+args = parser.parse_args()
+
+class Application:
+ def __init__(self, name, variables):
+ self.name = name
+ self.path = os.path.join(rootdir, name + '.app')
+
+ self.install_path = os.path.join('/Applications', self.path)
+
+ self.variables = dict(variables)
+
+ self.variables['name'] = name
+ self.variables['path'] = self.path
+ self.variables['rootdir'] = rootdir
+ self.variables['contents'] = os.path.join(self.path, 'Contents')
+ self.variables['resources'] = os.path.join(self.variables['contents'], 'Resources')
+ self.variables['lib'] = os.path.join(self.variables['resources'], 'lib')
+ self.variables['macos'] = os.path.join(self.variables['contents'], 'MacOS')
+
+ shutil.rmtree(self.path, ignore_errors=True)
+
+ for p in (self.variables['contents'], self.variables['resources'], self.variables['macos']):
+ try:
+ os.makedirs(p)
+ except:
+ pass
+
+ self._resolved_libs = {}
+ self._pkg_cache = {}
+
+ def repl(self, s):
+ def replace(x):
+ m = re.match('^pkg:([^:]+):([^:]+)$', x.group(1))
+
+ if m:
+ cachename = m.group(1) + ':' + m.group(2)
+
+ if cachename in self._pkg_cache:
+ return self._pkg_cache[cachename]
+
+ out = subprocess.Popen(['pkg-config', '--variable', m.group(2), m.group(1)],
stdout=subprocess.PIPE).communicate()[0].strip()
+ self._pkg_cache[cachename] = out
+
+ return out
+ else:
+ return self.variables[x.group(1)]
+
+ return re.sub("\\${([^}]+)}", replace, s)
+
+ def needs_copy(self, p):
+ prefixes = [self.variables['prefix'], '/usr/local'];
+
+ for prefix in prefixes:
+ if p.startswith(prefix):
+ return True
+
+ return False
+
+ def future_path(self, p):
+ if not os.path.isabs(p):
+ return os.path.join(self.install_path, p)
+
+ prefixes = [self.variables['prefix'], '/usr/local'];
+
+ for prefix in prefixes:
+ if p.startswith(prefix):
+ return os.path.join(self.install_path, p[len(prefix) + 1:])
+
+ return p
+
+ def copy_binary(self, binary, target):
+ binary = self.repl(binary)
+ target = self.repl(target)
+
+ target = self._copy(binary, target)
+
+ future = self.future_path(target)
+ self._resolved_libs[os.path.realpath(binary)] = future
+
+ os.chmod(target, 0755)
+
+ # Set the new id of the library
+ if binary.endswith('.so') or binary.endswith('.dylib'):
+ if not args.debug:
+ subprocess.call(['strip', '-x', target])
+
+ # Set the new id
+ subprocess.call(['install_name_tool', '-id', future, target])
+ else:
+ if not args.debug:
+ subprocess.call(['strip', '-u', '-r', target])
+
+ # Resolve and copy external dependencies
+ self.resolve_deps(target)
+
+ def otool_deps(self, path):
+ out = subprocess.Popen(['otool', '-L', path], stdout=subprocess.PIPE).communicate()[0]
+ return [x.strip().split(' ')[0] for x in out.splitlines()[1:]]
+
+ def resolve_deps(self, libname):
+ # Run otool to get the deps
+ deps = self.otool_deps(libname)
+
+ for dep in deps:
+ rdep = os.path.realpath(dep)
+
+ if not self.needs_copy(rdep) or rdep == libname:
+ continue
+
+ if not rdep in self._resolved_libs and rdep != libname:
+ # Copy the dependency
+ name = os.path.basename(rdep)
+ target = os.path.join(self.variables['lib'], name)
+
+ # Go deep
+ self.copy_binary(rdep, target)
+
+ newname = self._resolved_libs[rdep].replace(self.variables['contents'], '@executable_path/..')
+ subprocess.call(['install_name_tool', '-change', dep, newname, libname])
+
+ def _copy_file_name(self, source, target):
+ if target.endswith('/'):
+ return target + os.path.basename(source)
+ else:
+ return target
+
+ def _copy(self, source, target):
+ target = self._copy_file_name(source, target)
+
+ try:
+ os.makedirs(os.path.dirname(target))
+ except:
+ pass
+
+ if os.path.isdir(source):
+ shutil.copytree(source, target)
+ else:
+ shutil.copyfile(source, target)
+
+ return target
+
+ def copy_data(self, data, target):
+ self._copy(data, target)
+
+ def _interpolate_file(self, filename):
+ data = open(filename).read()
+ newdata = self.repl(data)
+
+ if newdata != data:
+ f = open(filename, 'w')
+ f.write(newdata)
+ f.flush()
+ f.close()
+
+ def copy_data_interpolated(self, data, target):
+ target = self._copy(data, target)
+
+ if os.path.isdir(target):
+ for root, dirnames, filenames in os.walk(target):
+ for filename in filenames:
+ fullname = os.path.join(root, filename)
+ self._interpolate_file(fullname)
+ else:
+ self._interpolate_file(target)
+
+ def copy_script(self, script, target):
+ target = self._copy(script, target)
+ os.chmod(target, 0755)
+
+ def copy_glob(self, items, fn):
+ for k in items:
+ g = self.repl(k)
+ files = glob.glob(g)
+
+ if len(files) == 0:
+ print('Warning: The glob `{0}\' did not result in any files'.format(g))
+ continue
+
+ target = items[k]
+
+ if not isinstance(target, list):
+ target = [target]
+
+ for t in target:
+ t = self.repl(t)
+
+ for f in files:
+ fn(f, t)
+
+ def link_main(self, main):
+ launcher = open(os.path.join(scriptdir, 'launcher'), 'r').read()
+
+ launcher = self.repl(launcher)
+ main = self.repl(main)
+
+ lpath = os.path.join(self.variables['macos'], self.name)
+
+ with open(lpath, 'w') as f:
+ f.write(launcher)
+
+ os.chmod(lpath, 0755)
+
+ relpath = os.path.relpath(main, os.path.join(self.variables['macos']))
+ os.symlink(relpath, os.path.join(self.variables['macos'], self.name + '-bin'))
+
+ def link_binaries(self, binaries):
+ p = os.path.join(self._root, 'bin')
+ shutil.rmtree(p, ignore_errors=True)
+
+ try:
+ os.makedirs(p)
+ except:
+ pass
+
+ for b in binaries:
+ files = glob.glob(self.repl(b))
+
+ for f in files:
+ os.symlink(self.future_path(f), os.path.join(p, os.path.basename(f)))
+
+ def copy_pixbuf_loaders(self):
+ moduledir = self.repl('${pkg:gdk-pixbuf-2.0:gdk_pixbuf_moduledir}')
+ loaders = glob.glob(os.path.join(moduledir, '*.so'))
+
+ target_moduledir = self.repl('gdk-pixbuf-2.0/${pkg:gdk-pixbuf-2.0:gdk_pixbuf_binary_version}')
+
+ for loader in loaders:
+ self.copy_binary(loader, os.path.join(self.variables['lib'], target_moduledir, 'loaders',
os.path.basename(loader)))
+
+ args = ['gdk-pixbuf-query-loaders']
+ args.extend(loaders)
+
+ cache = subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
+ cache = cache.replace(moduledir, os.path.join('@executable_path/../Resources/lib', target_moduledir,
'loaders'))
+
+ with open(os.path.join(self.variables['lib'], target_moduledir, 'loaders.cache'), 'w') as f:
+ f.write(cache)
+
+bundle = json.load(open(args.bundle, 'r'))
+
+# Create the framework
+application = Application(bundle['name'], bundle['variables'])
+
+# Copy binaries
+application.copy_glob(bundle['binaries'], application.copy_binary)
+
+# Link main
+application.link_main(bundle['main'])
+
+# Copy pixbuf loaders
+application.copy_pixbuf_loaders()
+
+# Copy data
+application.copy_glob(bundle['data'], application.copy_data)
+
+# Copy data interpolated
+application.copy_glob(bundle['data_interpolated'], application.copy_data_interpolated)
+
+# # Copy scripts
+# framework.copy_glob(config['scripts'], framework.copy_script)
+
+# # Copy headers
+# framework.copy_glob(config['headers'], framework.copy_header)
+
+# # Rewrite header includes
+# framework.rewrite_headers(config['header_rewrites'])
+
+# # Create applications
+# framework.create_applications(config['applications'])
+
+# # Link bin
+# framework.link_binaries(config['linked-binaries'])
+
+print('Application created in {0}.app'.format(bundle['name']))
+
+# vi:ts=4:et
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]