mumble/macx/scripts/osxdist.py
Davide Beatrici e9029a904e Remove "compat", i386 is not supported anymore
"compat" was a very small and simple program; its purpose was to show a message informing the user that the Mumble binary is only for x86_64 processors.

The i386 architecture is not supported in Xcode 10, meaning that we cannot compile it anymore.

https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes
2019-10-10 00:11:16 +02:00

389 lines
12 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2005-2019 The Mumble Developers. All rights reserved.
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file at the root of the
# Mumble source tree or at <https://www.mumble.info/LICENSE>.
#
# Simple Mac OS X Application Bundler for Mumble
#
# Loosely based on original bash-version by Sebastian Schlingmann (based, again, on a OSX application bundler
# by Thomas Keller).
#
import sys, os, string, re, shutil, plistlib, tempfile, exceptions, datetime, tarfile
from subprocess import Popen, PIPE
from optparse import OptionParser
options = None
def gitrev():
'''Get git revision of the current Mumble build.'''
return os.popen('git describe').read()[:-1]
def certificate_subject_OU(identity):
'''Extract the subject OU from the certificate matching the identity parameter in the specified keychain.'''
findCert = Popen(('/usr/bin/security', 'find-certificate', '-c', identity, '-p', options.keychain), stdout=PIPE)
pem, _ = findCert.communicate()
openssl = Popen(('/usr/bin/openssl', 'x509', '-subject', '-noout'), stdout=PIPE, stdin=PIPE)
subject, _ = openssl.communicate(pem)
attr = '/OU='
begin = subject.index(attr) + len(attr)
tmp = subject[begin:]
end = tmp.index('/')
return tmp[:end]
def lookup_file_identifier(path):
try:
d = plistlib.readPlist(os.path.join(path, 'Contents', 'Info.plist'))
return d['CFBundleIdentifier']
except:
return os.path.basename(path)
def codesign(path):
'''Call the codesign executable on the signable object at path.'''
certname = 'Developer ID Application: %s' % options.developer_id
OU = certificate_subject_OU(certname)
if hasattr(path, 'isalpha'):
path = (path,)
for p in path:
identifier = lookup_file_identifier(p)
reqs = None
with open('macx/scripts/codesign-requirements.tmpl', 'r') as f:
tmplReqs = f.read()
reqs = string.Template(tmplReqs).substitute({
'identifier': identifier,
'subject_OU': OU,
})
p = Popen(('codesign', '--keychain', options.keychain, '-vvvv', '-i', identifier, '-r='+reqs, '-s', certname, p))
retval = p.wait()
if retval != 0:
return retval
return 0
def prodsign(inf, outf):
'''Call the prodsign executable.'''
certname = 'Developer ID Installer: %s' % options.developer_id
p = Popen(('productsign', '--keychain', options.keychain, '--sign', certname, inf, outf))
retval = p.wait()
if retval != 0:
return retval
return 0
def create_overlay_package():
print '* Creating overlay installer'
bundle = os.path.join('release', 'MumbleOverlay.osax')
overlaylib = os.path.join('release', 'libmumbleoverlay.dylib')
if options.developer_id:
codesign(bundle)
codesign(overlaylib)
p = Popen(('./macx/scripts/build-overlay-installer',))
retval = p.wait()
if retval != 0:
raise Exception('build-overlay-installer failed')
if options.developer_id:
os.rename('release/MumbleOverlay.pkg', 'release/MumbleOverlayUnsigned.pkg')
prodsign('release/MumbleOverlayUnsigned.pkg', 'release/MumbleOverlay.pkg')
class AppBundle(object):
def copy_helper(self, fn):
'''
Copy a helper binary into the Mumble app bundle.
'''
if os.path.exists(os.path.join(self.bundle, '..', fn)):
print ' * Copying helper binary: %s' % fn
src = os.path.join(self.bundle, '..', fn)
dst = os.path.join(self.bundle, 'Contents', 'MacOS', fn)
shutil.copy(src, dst)
def copy_resources(self, rsrcs):
'''
Copy needed resources into our bundle.
'''
print ' * Copying needed resources'
rsrcpath = os.path.join(self.bundle, 'Contents', 'Resources')
if not os.path.exists(rsrcpath):
os.mkdir(rsrcpath)
# Copy resources already in the bundle
for rsrc in rsrcs:
b = os.path.basename(rsrc)
if os.path.isdir(rsrc):
shutil.copytree(rsrc, os.path.join(rsrcpath, b), symlinks=True)
elif os.path.isfile(rsrc):
shutil.copy(rsrc, os.path.join(rsrcpath, b))
# Extras
shutil.copy('release/MumbleOverlay.pkg', os.path.join(rsrcpath, 'MumbleOverlay.pkg'))
def copy_codecs(self):
'''
Try to copy the dynamic audio codec libraries into the App Bundle.
'''
print ' * Attempting to copy audio codec libraries into App Bundle'
dst = os.path.join(self.bundle, 'Contents', 'Codecs')
os.makedirs(dst)
codecs = ('release/libcelt0.0.7.0.dylib', 'release/libcelt0.0.11.0.dylib', 'release/libopus.dylib')
for codec in codecs:
if os.path.exists(codec):
shutil.copy(codec, dst)
def copy_plugins(self):
'''
Copy over any built Mumble plugins.
'''
print ' * Copying positional audio plugins'
dst = os.path.join(self.bundle, 'Contents', 'Plugins')
if os.path.exists(dst):
shutil.rmtree(dst)
shutil.copytree('release/plugins/', dst, symlinks=True)
def update_plist(self):
'''
Modify our bundle's Info.plist to make it ready for release.
'''
if self.version is not None:
print ' * Changing version in Info.plist'
p = self.infoplist
p['CFBundleVersion'] = self.version
plistlib.writePlist(p, self.infopath)
def set_min_macosx_version(self, version):
'''
Set the minimum version of Mac OS X version that this App will run on.
'''
print ' * Setting minimum Mac OS X version to: %s' % (version)
self.infoplist['LSMinimumSystemVersion'] = version
def done(self):
plistlib.writePlist(self.infoplist, self.infopath)
print ' * Done!'
print ''
def __init__(self, bundle, version=None):
self.framework_path = ''
self.handled_libs = {}
self.bundle = bundle
self.version = version
self.infopath = os.path.join(os.path.abspath(bundle), 'Contents', 'Info.plist')
self.infoplist = plistlib.readPlist(self.infopath)
self.binary = os.path.join(os.path.abspath(bundle), 'Contents', 'MacOS', self.infoplist['CFBundleExecutable'])
print ' * Preparing AppBundle'
class FolderObject(object):
class Exception(exceptions.Exception):
pass
def __init__(self):
self.tmp = tempfile.mkdtemp()
def copy(self, src, dst='/'):
'''
Copy a file or directory into the folder.
'''
asrc = os.path.abspath(src)
if dst[0] != '/':
raise self.Exception
# Determine destination
if dst[-1] == '/':
adst = os.path.abspath(self.tmp + '/' + dst + os.path.basename(src))
else:
adst = os.path.abspath(self.tmp + '/' + dst)
if os.path.isdir(asrc):
print ' * Copying directory: %s' % os.path.basename(asrc)
shutil.copytree(asrc, adst, symlinks=True)
elif os.path.isfile(asrc):
print ' * Copying file: %s' % os.path.basename(asrc)
shutil.copy(asrc, adst)
def symlink(self, src, dst):
'''
Create a symlink inside the folder.
'''
asrc = os.path.abspath(src)
adst = self.tmp + '/' + dst
print ' * Creating symlink %s' % os.path.basename(asrc)
os.symlink(asrc, adst)
def mkdir(self, name):
'''
Create a directory inside the folder.
'''
print ' * Creating directory %s' % os.path.basename(name)
adst = self.tmp + '/' + name
os.makedirs(adst)
class DiskImage(FolderObject):
def __init__(self, filename, volname):
FolderObject.__init__(self)
print ' * Preparing to create diskimage'
self.filename = filename
self.volname = volname
def create(self):
'''
Create the disk image itself.
'''
print ' * Creating diskimage. Please wait...'
if os.path.exists(self.filename):
shutil.rmtree(self.filename)
p = Popen(['hdiutil', 'create',
'-srcfolder', self.tmp,
'-format', 'UDBZ',
'-volname', self.volname,
self.filename])
retval = p.wait()
print ' * Removing temporary directory.'
shutil.rmtree(self.tmp)
print ' * Done!'
def package_client():
if options.version is not None:
ver = options.version
else:
ver = gitrev()
if options.universal:
fn = 'release/Mumble-Universal-%s.dmg' % ver
title = 'Mumble %s (Universal)' % ver
else:
fn = 'release/Mumble-%s.dmg' % ver
title = 'Mumble %s' % ver
if not os.path.exists('release'):
print 'This script needs to be run from the root of the Mumble source tree.'
sys.exit(1)
# Fix overlay installer package
create_overlay_package()
if options.only_overlay:
sys.exit(0)
# Do the finishing touches to our Application bundle before release
a = AppBundle('release/Mumble.app', ver)
a.copy_helper('mumble-g15-helper')
a.copy_helper('sbcelt-helper')
a.copy_codecs()
a.copy_plugins()
a.copy_resources(['icons/mumble.icns'])
a.update_plist()
if not options.universal:
a.set_min_macosx_version('10.9.0')
else:
a.set_min_macosx_version('10.4.8')
a.done()
# Sign our binaries, etc.
if options.developer_id:
print ' * Signing binaries with Developer ID `%s\'' % options.developer_id
binaries = (
'release/Mumble.app',
'release/Mumble.app/Contents/Plugins/liblink.dylib',
'release/Mumble.app/Contents/Plugins/libmanual.dylib',
'release/Mumble.app/Contents/Codecs/libcelt0.0.7.0.dylib',
'release/Mumble.app/Contents/Codecs/libcelt0.0.11.0.dylib',
'release/Mumble.app/Contents/Codecs/libopus.dylib',
'release/Mumble.app/Contents/MacOS/mumble-g15-helper',
'release/Mumble.app/Contents/MacOS/sbcelt-helper',
)
availableBinaries = [bin for bin in binaries if os.path.exists(bin)]
codesign(availableBinaries)
print ''
if options.only_appbundle:
sys.exit(0)
# Create diskimage
d = DiskImage(fn, title)
d.copy('macx/scripts/DS_Store', '/.DS_Store')
d.symlink('/Applications', '/Applications')
d.copy('release/Mumble.app')
d.create()
def package_server():
if options.version is not None:
ver = options.version
else:
ver = gitrev()
name = 'Murmur-OSX-Static-%s' % ver
# Fix .ini files
p = Popen(('bash', 'mkini.sh'), cwd='scripts')
retval = p.wait()
if retval != 0:
raise Exception('build-overlay-installer failed')
destdir = os.path.join('release', name)
if os.path.exists(destdir):
shutil.rmtree(destdir)
os.mkdir(destdir)
shutil.copy('installer/gpl.txt', os.path.join(destdir, 'LICENSE'))
shutil.copy('README.static.osx', os.path.join(destdir, 'README'))
shutil.copy('CHANGES', os.path.join(destdir, 'CHANGES'))
shutil.copy('scripts/murmur.pl', os.path.join(destdir, 'murmur.pl'))
shutil.copy('scripts/weblist.pl', os.path.join(destdir, 'weblist.pl'))
shutil.copy('scripts/icedemo.php', os.path.join(destdir, 'icedemo.php'))
shutil.copy('scripts/weblist.php', os.path.join(destdir, 'weblist.php'))
shutil.copy('scripts/murmur.ini.osx', os.path.join(destdir, 'murmur.ini'))
shutil.copy('src/murmur/Murmur.ice', os.path.join(destdir, 'Murmur.ice'))
shutil.copy('release/murmurd', os.path.join(destdir, 'murmurd'))
codesign(os.path.join(destdir, 'murmurd'))
certname = 'Developer ID Installer: %s' % options.developer_id
p = Popen(('xip', '--keychain', options.keychain, '-s', certname, '--timestamp', destdir, os.path.join('release', name+'.xip')))
retval = p.wait()
if retval != 0:
print 'Failed to build Murmur XIP package'
sys.exit(1)
absrelease = os.path.join(os.getcwd(), 'release')
p = Popen(('tar', '-cjpf', name+'.tar.bz2', name), cwd=absrelease)
retval = p.wait()
if retval != 0:
print 'Failed to build Murmur tar.bz2 package'
sys.exit(1)
p = Popen(('gpg', '--detach-sign', '--armor', '-u', options.developer_id, '-o', name+'.tar.bz2.sig', name+'.tar.bz2'), cwd=absrelease)
retval = p.wait()
if retval != 0:
print 'Failed to sign Murmur tar.bz2 package'
sys.exit(1)
if __name__ == '__main__':
parser = OptionParser()
parser.add_option('', '--version', dest='version', help='This overrides the version number of the build.')
parser.add_option('', '--universal', dest='universal', help='Build an universal snapshot.', action='store_true', default=False)
parser.add_option('', '--only-appbundle', dest='only_appbundle', help='Only prepare the appbundle. Do not package.', action='store_true', default=False)
parser.add_option('', '--only-overlay', dest='only_overlay', help='Only create the overlay installer.', action='store_true', default=False)
parser.add_option('', '--developer-id', dest='developer_id', help='Identity (Developer ID) to use for code signing. The name is also used for GPG signing. (If not set, no code signing will occur)')
parser.add_option('', '--keychain', dest='keychain', help='The keychain to use when invoking code signing utilities. (Defaults to login.keychain', default='login.keychain')
parser.add_option('', '--server', dest='server', help='Build a Murmur package.', action='store_true', default=False)
options, args = parser.parse_args()
if not options.server:
package_client()
else:
package_server()