| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- #!/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://obsproject.com/osx_update')
- 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")
|