This repository has been archived by the owner on Jul 21, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
/
build_app.py
211 lines (174 loc) · 6.19 KB
/
build_app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/usr/bin/env python3.3
candidate_paths = "bin obs-plugins data".split()
plist_path = "../cmake/osxbundle/Info.plist"
icon_path = "../cmake/osxbundle/tachyon.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
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://github.com')
parser.add_argument('-u', '--user', dest='user', default='WatchBeam')
parser.add_argument('-c', '--channel', dest='channel', default='ftl-studio')
parser.add_argument('-s', '--stable', dest='stable', required=False, action='store_true', 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:
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:
pass
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")
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/tachyon'.format(
args.prefix, actual_sparkle_path, sparkle_path.format('../..'), prefix))
for path, external, copy_as in inspected:
id_ = ""
filename = path
rpath = ""
if external:
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")