mirror of
https://gitlab.gnome.org/GNOME/gitg
synced 2024-11-04 21:16:52 +00:00
Add initial OS X app bundle generator
This commit is contained in:
parent
c2282eb13e
commit
393e7c04d9
6 changed files with 415 additions and 0 deletions
|
@ -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
|
||||
])
|
||||
|
||||
|
|
1
osx/.gitignore
vendored
Normal file
1
osx/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/Gitg.app
|
22
osx/bundle.json.in
Normal file
22
osx/bundle.json.in
Normal file
|
@ -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"
|
||||
}
|
||||
}
|
36
osx/data/Info.plist
Normal file
36
osx/data/Info.plist
Normal file
|
@ -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>
|
65
osx/scripts/launcher
Normal file
65
osx/scripts/launcher
Normal file
|
@ -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
|
290
osx/scripts/make-bundle
Executable file
290
osx/scripts/make-bundle
Executable file
|
@ -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
|
Loading…
Reference in a new issue