obs-studio/CI/install/osx/build_app.py
jp9000 89a5235ac0 CI: Reduce travis output verbosity
The log output of the travis CI for macOS is unreasonably large.  This
reduces the verbosity of the output and reduces the size of the build
logs significantly.
2018-04-13 22:25:05 -07:00

239 lines
7.0 KiB
Python

#!/usr/bin/env python
candidate_paths = "bin obs-plugins data".split()
plist_path = "../cmake/osxbundle/Info.plist"
icon_path = "../cmake/osxbundle/obs.icns"
run_path = "../cmake/osxbundle/obslaunch.sh"
#not copied
blacklist = """/usr /System""".split()
#copied
whitelist = """/usr/local""".split()
#
#
#
from sys import argv
from glob import glob
from subprocess import check_output, call
from collections import namedtuple
from shutil import copy, copytree, rmtree
from os import makedirs, rename, walk, path as ospath
import plistlib
import argparse
def _str_to_bool(s):
"""Convert string to bool (in argparse context)."""
if s.lower() not in ['true', 'false']:
raise ValueError('Need bool; got %r' % s)
return {'true': True, 'false': False}[s.lower()]
def add_boolean_argument(parser, name, default=False):
"""Add a boolean argument to an ArgumentParser instance."""
group = parser.add_mutually_exclusive_group()
group.add_argument(
'--' + name, nargs='?', default=default, const=True, type=_str_to_bool)
group.add_argument('--no' + name, dest=name, action='store_false')
parser = argparse.ArgumentParser(description='obs-studio package util')
parser.add_argument('-d', '--base-dir', dest='dir', default='rundir/RelWithDebInfo')
parser.add_argument('-n', '--build-number', dest='build_number', default='0')
parser.add_argument('-k', '--public-key', dest='public_key', default='OBSPublicDSAKey.pem')
parser.add_argument('-f', '--sparkle-framework', dest='sparkle', default=None)
parser.add_argument('-b', '--base-url', dest='base_url', default='https://builds.catchexception.org/obs-studio')
parser.add_argument('-u', '--user', dest='user', default='jp9000')
parser.add_argument('-c', '--channel', dest='channel', default='master')
add_boolean_argument(parser, 'stable', default=False)
parser.add_argument('-p', '--prefix', dest='prefix', default='')
args = parser.parse_args()
def cmd(cmd):
import subprocess
import shlex
return subprocess.check_output(shlex.split(cmd)).rstrip('\r\n')
LibTarget = namedtuple("LibTarget", ("path", "external", "copy_as"))
inspect = list()
inspected = set()
build_path = args.dir
build_path = build_path.replace("\\ ", " ")
def add(name, external=False, copy_as=None):
if external and copy_as is None:
copy_as = name.split("/")[-1]
if name[0] != "/":
name = build_path+"/"+name
t = LibTarget(name, external, copy_as)
if t in inspected:
return
inspect.append(t)
inspected.add(t)
for i in candidate_paths:
print("Checking " + i)
for root, dirs, files in walk(build_path+"/"+i):
for file_ in files:
if ".ini" in file_:
continue
if ".png" in file_:
continue
if ".effect" in file_:
continue
if ".py" in file_:
continue
if ".json" in file_:
continue
path = root + "/" + file_
try:
out = check_output("{0}otool -L '{1}'".format(args.prefix, path), shell=True,
universal_newlines=True)
if "is not an object file" in out:
continue
except:
continue
rel_path = path[len(build_path)+1:]
print(repr(path), repr(rel_path))
add(rel_path)
def add_plugins(path, replace):
for img in glob(path.replace(
"lib/QtCore.framework/Versions/5/QtCore",
"plugins/%s/*"%replace).replace(
"Library/Frameworks/QtCore.framework/Versions/5/QtCore",
"share/qt5/plugins/%s/*"%replace)):
if "_debug" in img:
continue
add(img, True, img.split("plugins/")[-1])
actual_sparkle_path = '@loader_path/Frameworks/Sparkle.framework/Versions/A/Sparkle'
while inspect:
target = inspect.pop()
print("inspecting", repr(target))
path = target.path
if path[0] == "@":
continue
out = check_output("{0}otool -L '{1}'".format(args.prefix, path), shell=True,
universal_newlines=True)
if "QtCore" in path:
add_plugins(path, "platforms")
add_plugins(path, "imageformats")
add_plugins(path, "accessible")
add_plugins(path, "styles")
for line in out.split("\n")[1:]:
new = line.strip().split(" (")[0]
if '@' in new and "sparkle.framework" in new.lower():
actual_sparkle_path = new
print "Using sparkle path:", repr(actual_sparkle_path)
if not new or new[0] == "@" or new.endswith(path.split("/")[-1]):
continue
whitelisted = False
for i in whitelist:
if new.startswith(i):
whitelisted = True
if not whitelisted:
blacklisted = False
for i in blacklist:
if new.startswith(i):
blacklisted = True
break
if blacklisted:
continue
add(new, True)
changes = list()
for path, external, copy_as in inspected:
if not external:
continue #built with install_rpath hopefully
changes.append("-change '%s' '@rpath/%s'"%(path, copy_as))
changes = " ".join(changes)
info = plistlib.readPlist(plist_path)
latest_tag = cmd('git describe --tags --abbrev=0')
log = cmd('git log --pretty=oneline {0}...HEAD'.format(latest_tag))
from os import path
# set version
if args.stable:
info["CFBundleVersion"] = latest_tag
info["CFBundleShortVersionString"] = latest_tag
info["SUFeedURL"] = '{0}/stable/updates.xml'.format(args.base_url)
else:
info["CFBundleVersion"] = args.build_number
info["CFBundleShortVersionString"] = '{0}.{1}'.format(latest_tag, args.build_number)
info["SUFeedURL"] = '{0}/{1}/{2}/updates.xml'.format(args.base_url, args.user, args.channel)
info["SUPublicDSAKeyFile"] = path.basename(args.public_key)
info["OBSFeedsURL"] = '{0}/feeds.xml'.format(args.base_url)
app_name = info["CFBundleName"]+".app"
icon_file = "tmp/Contents/Resources/%s"%info["CFBundleIconFile"]
copytree(build_path, "tmp/Contents/Resources/", symlinks=True)
copy(icon_path, icon_file)
plistlib.writePlist(info, "tmp/Contents/Info.plist")
makedirs("tmp/Contents/MacOS")
copy(run_path, "tmp/Contents/MacOS/%s"%info["CFBundleExecutable"])
try:
copy(args.public_key, "tmp/Contents/Resources")
except:
pass
if args.sparkle is not None:
copytree(args.sparkle, "tmp/Contents/Frameworks/Sparkle.framework", symlinks=True)
prefix = "tmp/Contents/Resources/"
sparkle_path = '@loader_path/{0}/Frameworks/Sparkle.framework/Versions/A/Sparkle'
cmd('{0}install_name_tool -change {1} {2} {3}/bin/obs'.format(
args.prefix, actual_sparkle_path, sparkle_path.format('../..'), prefix))
for path, external, copy_as in inspected:
id_ = ""
filename = path
rpath = ""
if external:
if copy_as == "Python":
continue
id_ = "-id '@rpath/%s'"%copy_as
filename = prefix + "bin/" +copy_as
rpath = "-add_rpath @loader_path/ -add_rpath @executable_path/"
if "/" in copy_as:
try:
dirs = copy_as.rsplit("/", 1)[0]
makedirs(prefix + "bin/" + dirs)
except:
pass
copy(path, filename)
else:
filename = path[len(build_path)+1:]
id_ = "-id '@rpath/../%s'"%filename
if not filename.startswith("bin"):
print(filename)
rpath = "-add_rpath '@loader_path/{}/'".format(ospath.relpath("bin/", ospath.dirname(filename)))
filename = prefix + filename
cmd = "{0}install_name_tool {1} {2} {3} '{4}'".format(args.prefix, changes, id_, rpath, filename)
call(cmd, shell=True)
try:
rename("tmp", app_name)
except:
print("App already exists")
rmtree("tmp")