macOS: Fix signing issues with notarization

Notarization requires signing with hardened runtime, but this added
requirements to the additional plugins we copy into Qt Creator for the
commercial package.

This patch fixes an issue with an absolute RPATH still being left in
extra plugins, and avoids copying plugins into an already signed
application by not signing the 7zips, but only the contents in the open
source disk image (and the installers are signed by the installer jobs
anyhow).

Change-Id: I8c945a0ad9df610b20a8ee110320875f255c65b4
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Eike Ziller
2020-02-13 14:50:15 +01:00
parent e85d6b363b
commit cde9f31068
3 changed files with 40 additions and 29 deletions

View File

@@ -91,7 +91,8 @@ def copytree(src, dst, symlinks=False, ignore=None):
def get_qt_install_info(qmake_bin): def get_qt_install_info(qmake_bin):
output = subprocess.check_output([qmake_bin, '-query']) output = subprocess.check_output([qmake_bin, '-query'])
lines = output.decode(encoding).strip().split('\n') decoded_output = output.decode(encoding) if encoding else output
lines = decoded_output.strip().split('\n')
info = {} info = {}
for line in lines: for line in lines:
(var, sep, value) = line.partition(':') (var, sep, value) = line.partition(':')
@@ -178,28 +179,37 @@ def is_not_debug(path, filenames):
files = [fn for fn in filenames if os.path.isfile(os.path.join(path, fn))] files = [fn for fn in filenames if os.path.isfile(os.path.join(path, fn))]
return [fn for fn in files if not is_debug_file(os.path.join(path, fn))] return [fn for fn in files if not is_debug_file(os.path.join(path, fn))]
def codesign(app_path): def codesign_call():
signing_identity = os.environ.get('SIGNING_IDENTITY') signing_identity = os.environ.get('SIGNING_IDENTITY')
if is_mac_platform() and signing_identity: if not signing_identity:
codesign_call = ['codesign', '-o', 'runtime', '--force', '-s', signing_identity, return None
'-v'] codesign_call = ['codesign', '-o', 'runtime', '--force', '-s', signing_identity,
signing_flags = os.environ.get('SIGNING_FLAGS') '-v']
if signing_flags: signing_flags = os.environ.get('SIGNING_FLAGS')
codesign_call.extend(signing_flags.split()) if signing_flags:
codesign_call.extend(signing_flags.split())
return codesign_call
def conditional_sign_recursive(path, filter): def os_walk(path, filter, function):
for r, _, fs in os.walk(path): for r, _, fs in os.walk(path):
for f in fs: for f in fs:
ff = os.path.join(r, f) ff = os.path.join(r, f)
if filter(ff): if filter(ff):
print('codesign "' + ff + '"') function(ff)
subprocess.check_call(codesign_call + [ff])
# sign all executables in Resources def conditional_sign_recursive(path, filter):
conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Resources'), codesign = codesign_call()
lambda ff: os.access(ff, os.X_OK)) if is_mac_platform() and codesign:
# sign all libraries in Imports os_walk(path, filter, lambda fp: subprocess.check_call(codesign + [fp]))
conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Imports'),
lambda ff: ff.endswith('.dylib')) def codesign(app_path):
# sign all executables in Resources
conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Resources'),
lambda ff: os.access(ff, os.X_OK))
# sign all libraries in Imports
conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Imports'),
lambda ff: ff.endswith('.dylib'))
codesign = codesign_call()
if is_mac_platform() and codesign:
# sign the whole bundle # sign the whole bundle
subprocess.check_call(codesign_call + ['--deep', app_path]) subprocess.check_call(codesign + ['--deep', app_path])

View File

@@ -33,8 +33,7 @@ import tempfile
import common import common
def parse_arguments(): def parse_arguments():
parser = argparse.ArgumentParser(description="Create Qt Creator package, filtering out debug information files.", parser = argparse.ArgumentParser(description="Create Qt Creator package, filtering out debug information files.")
epilog="To sign the contents before packaging on macOS, set the SIGNING_IDENTITY and optionally the SIGNING_FLAGS environment variables.")
parser.add_argument('--7z', help='path to 7z binary', parser.add_argument('--7z', help='path to 7z binary',
default='7z.exe' if common.is_windows_platform() else '7z', default='7z.exe' if common.is_windows_platform() else '7z',
metavar='<7z_binary>', dest='sevenzip') metavar='<7z_binary>', dest='sevenzip')
@@ -53,9 +52,6 @@ def main():
try: try:
common.copytree(arguments.source_directory, tempdir, symlinks=True, common.copytree(arguments.source_directory, tempdir, symlinks=True,
ignore=(common.is_not_debug if arguments.debug else common.is_debug)) ignore=(common.is_not_debug if arguments.debug else common.is_debug))
# on macOS we might have to codesign (again) to account for removed debug info
if not arguments.debug:
common.codesign(tempdir)
# package # package
zip_source = os.path.join(tempdir, '*') if arguments.exclude_toplevel else tempdir zip_source = os.path.join(tempdir, '*') if arguments.exclude_toplevel else tempdir
subprocess.check_call([arguments.sevenzip, 'a', '-mmt2', subprocess.check_call([arguments.sevenzip, 'a', '-mmt2',

View File

@@ -28,7 +28,6 @@
import argparse import argparse
import os import os
import subprocess import subprocess
import sys
import common import common
@@ -45,10 +44,16 @@ def parse_arguments():
if __name__ == "__main__": if __name__ == "__main__":
arguments = parse_arguments() arguments = parse_arguments()
qt_install_info = common.get_qt_install_info(arguments.qmake_binary)
if common.is_linux_platform(): if common.is_linux_platform():
qt_install_info = common.get_qt_install_info(arguments.qmake_binary)
common.fix_rpaths(arguments.source_directory, common.fix_rpaths(arguments.source_directory,
os.path.join(arguments.source_directory, 'lib', 'Qt', 'lib'), os.path.join(arguments.source_directory, 'lib', 'Qt', 'lib'),
qt_install_info) qt_install_info)
if common.is_mac_platform():
# remove Qt rpath
lib_path = qt_install_info['QT_INSTALL_LIBS']
common.os_walk(arguments.source_directory,
lambda fp: fp.endswith('.dylib'),
lambda fp: subprocess.call(['install_name_tool', '-delete_rpath', lib_path, fp]))
subprocess.check_call([arguments.sevenzip, 'a', '-mx9', arguments.target_file, subprocess.check_call([arguments.sevenzip, 'a', '-mx9', arguments.target_file,
os.path.join(arguments.source_directory, '*')]) os.path.join(arguments.source_directory, '*')])