diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d22793042e..0f4a4270eb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -541,8 +541,8 @@ install( ) set(deploy_tool_options_arg "") -if(APPLE) - set(deploy_tool_options_arg -qmldir=${CMAKE_SOURCE_DIR}/src -hardened-runtime -timestamp -appstore-compliant) +if(MACOS) + set(deploy_tool_options_arg -qmldir=${CMAKE_SOURCE_DIR}/src -hardened-runtime -timestamp -appstore-compliant) # -dmg endif() qt_generate_deploy_qml_app_script( diff --git a/cmake/CreateMacDMG.cmake b/cmake/CreateMacDMG.cmake index 672f3c3b9f5..6f498c8d857 100644 --- a/cmake/CreateMacDMG.cmake +++ b/cmake/CreateMacDMG.cmake @@ -4,32 +4,39 @@ set(TARGET_NAME QGroundControl) set(SYSTEM_FRAMEWORK_PATH /Library/Frameworks) set(BUNDLE_FRAMEWORK_PATH staging/${TARGET_NAME}.app/Contents/Frameworks) -message(STATUS "Copy GStreamer framework into bundle") -file(COPY ${SYSTEM_FRAMEWORK_PATH}/GStreamer.framework DESTINATION ${BUNDLE_FRAMEWORK_PATH}) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/bin) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/etc) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/share) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/Headers) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/include) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/Commands) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/pkgconfig) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/glib-2.0) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/graphene-1.0) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gst-validate-launcher) -file(GLOB REMOVE_LIB_FILES ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/*.a) -file(REMOVE ${REMOVE_LIB_FILES}) -file(GLOB REMOVE_LIB_FILES ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/*.la) -file(REMOVE ${REMOVE_LIB_FILES}) -file(GLOB REMOVE_LIB_FILES ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/*.a) -file(REMOVE ${REMOVE_LIB_FILES}) -file(GLOB REMOVE_LIB_FILES ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/*.la) -file(REMOVE ${REMOVE_LIB_FILES}) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/include) -file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/pkgconfig) +execute_process(COMMAND ${CMAKE_SOURCE_DIR}/deploy/mac/prepare_gstreamer_framework.sh ${CMAKE_BINARY_DIR}/gstwork/ ${TARGET_NAME}.app ${TARGET_NAME}) + +# message(STATUS "Copy GStreamer framework into bundle") +# file(COPY ${SYSTEM_FRAMEWORK_PATH}/GStreamer.framework DESTINATION ${BUNDLE_FRAMEWORK_PATH}) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/bin) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/etc) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/share) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/Headers) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/include) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/Commands) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/pkgconfig) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/glib-2.0) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/graphene-1.0) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gst-validate-launcher) +# file(GLOB REMOVE_LIB_FILES ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/*.a) +# file(REMOVE ${REMOVE_LIB_FILES}) +# file(GLOB REMOVE_LIB_FILES ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/*.la) +# file(REMOVE ${REMOVE_LIB_FILES}) +# file(GLOB REMOVE_LIB_FILES ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/*.a) +# file(REMOVE ${REMOVE_LIB_FILES}) +# file(GLOB REMOVE_LIB_FILES ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/*.la) +# file(REMOVE ${REMOVE_LIB_FILES}) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/include) +# file(REMOVE_RECURSE ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/pkgconfig) + +# execute_process(COMMAND otool -L ${SYSTEM_FRAMEWORK_PATH}/GStreamer.framework/GStreamer) # Fix up library paths to point into bundle -execute_process(COMMAND ln -sf ${BUNDLE_FRAMEWORK_PATH} ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/libexec/Frameworks) -execute_process(COMMAND install_name_tool -change ${SYSTEM_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/GStreamer @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer staging/${TARGET_NAME}.app/Contents/MacOS/${TARGET_NAME}) +# execute_process(COMMAND ln -sf ${BUNDLE_FRAMEWORK_PATH} ${BUNDLE_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/libexec) +# execute_process(COMMAND install_name_tool -id @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer staging/${TARGET_NAME}.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/GStreamer) +# execute_process(COMMAND install_name_tool -change ${SYSTEM_FRAMEWORK_PATH}/GStreamer.framework/Versions/1.0/lib/GStreamer @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer staging/${TARGET_NAME}.app/Contents/MacOS/${TARGET_NAME}) + +execute_process(COMMAND otool -L staging/${TARGET_NAME}.app/Contents/MacOS/${TARGET_NAME}) # include(BundleUtilities) # include(CMakePrintHelpers) diff --git a/deploy/mac/osxrelocator.py b/deploy/mac/osxrelocator.py new file mode 100755 index 00000000000..9a6999b5dfb --- /dev/null +++ b/deploy/mac/osxrelocator.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# cerbero - a multi-platform build system for Open Source software +# Copyright (C) 2012 Andoni Morales Alastruey +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +import os +import subprocess + + +INT_CMD = 'install_name_tool' +OTOOL_CMD = 'otool' + + +def shell_call(cmd, cmd_dir='.', fail=True): + #print("call", cmd) + try: + ret = subprocess.check_call( + cmd, cwd=cmd_dir, + env=os.environ.copy()) + except subprocess.CalledProcessError: + if fail: + raise SystemError("Error running command: {}".format(cmd)) + else: + ret = 0 + return ret + + +def shell_check_call(cmd): + #print("ccall", cmd) + try: + process = subprocess.Popen( + cmd, stdout=subprocess.PIPE) + output, _ = process.communicate() + except Exception: + raise SystemError("Error running command: {}".format(cmd)) + return output + + +class OSXRelocator(object): + ''' + Wrapper for OS X's install_name_tool and otool commands to help + relocating shared libraries. + + It parses lib/ /libexec and bin/ directories, changes the prefix path of + the shared libraries that an object file uses and changes it's library + ID if the file is a shared library. + ''' + + def __init__(self, root, lib_prefix, new_lib_prefix, recursive): + self.root = root + self.lib_prefix = self._fix_path(lib_prefix).encode('utf-8') + self.new_lib_prefix = self._fix_path(new_lib_prefix).encode('utf-8') + self.recursive = recursive + + def relocate(self): + self.parse_dir(self.root, filters=['', '.dylib', '.so']) + + def relocate_file(self, object_file, id=None): + self.change_libs_path(object_file) + self.change_id(object_file, id) + + def change_id(self, object_file, id=None): + id = id or object_file.replace(self.lib_prefix.decode('utf-8'), self.new_lib_prefix.decode('utf-8')) + filename = os.path.basename(object_file) + if not (filename.endswith('so') or filename.endswith('dylib')): + return + cmd = [INT_CMD, "-id", id, object_file] + shell_call(cmd, fail=False) + + def change_libs_path(self, object_file): + for lib in self.list_shared_libraries(object_file): + if self.lib_prefix in lib: + new_lib = lib.replace(self.lib_prefix, self.new_lib_prefix) + cmd = [INT_CMD, "-change", lib, new_lib, object_file] + shell_call(cmd) + + def parse_dir(self, dir_path, filters=None): + for dirpath, dirnames, filenames in os.walk(dir_path): + for f in filenames: + if filters is not None and \ + os.path.splitext(f)[1] not in filters: + continue + fn = os.path.join(dirpath, f) + if os.path.islink(fn): + continue + if not os.path.isfile(fn): + continue + self.relocate_file(fn) + if not self.recursive: + break + + @staticmethod + def list_shared_libraries(object_file): + cmd = [OTOOL_CMD, "-L", object_file] + res = shell_check_call(cmd).split(b'\n') + # We don't use the first line + libs = res[1:] + # Remove the first character tabulation + libs = [x[1:] for x in libs] + # Remove the version info + libs = [x.split(b' ', 1)[0] for x in libs] + return libs + + @staticmethod + def library_id_name(object_file): + cmd = [OTOOL_CMD, "-D", object_file] + res = shell_check_call(cmd).split('\n')[0] + # the library name ends with ':' + lib_name = res[:-1] + return lib_name + + def _fix_path(self, path): + if path.endswith('/'): + return path[:-1] + return path + + +class Main(object): + + def run(self): + # We use OptionParser instead of ArgumentsParse because this script + # might be run in OS X 10.6 or older, which do not provide the argparse + # module + import optparse + usage = "usage: %prog [options] directory old_prefix new_prefix" + description = 'Rellocates object files changing the dependent '\ + ' dynamic libraries location path with a new one' + parser = optparse.OptionParser(usage=usage, description=description) + parser.add_option('-r', '--recursive', action='store_true', + default=False, dest='recursive', + help='Scan directories recursively') + + options, args = parser.parse_args() + if len(args) != 3: + parser.print_usage() + exit(1) + relocator = OSXRelocator(args[0], args[1], args[2], options.recursive) + relocator.relocate() + exit(0) + +def main(): + main = Main() + main.run() + +if __name__ == "__main__": + main() diff --git a/deploy/mac/prepare_gstreamer_framework.sh b/deploy/mac/prepare_gstreamer_framework.sh new file mode 100755 index 00000000000..f59f4c93bbd --- /dev/null +++ b/deploy/mac/prepare_gstreamer_framework.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# +# Author: Gus Grubba +# +# Copies the regular distribution of the gstreamer framework +# into the libs directory (to be used for creating the Mac OS +# bundle). It also prunes unnecessary files and relocates the +# dynamic libraries. +# +# This script is run by the build process and should not be +# executed manually. +# +# Usage: $script [framework destination path] [app bundle path] [qgc executable name] +# +# destination is usually: ../libs/lib/Frameworks/ +# app bundle is something like: /path/qgroundcontrol.app +# executable name is usually: qgroundcontrol + +die () { + echo "$@" 1>&2 + exit 1 +} + +GST_VER=1.0 +GST_ROOT=/Library/Frameworks/GStreamer.framework +GST_BASE=$GST_ROOT/Versions/$GST_VER +RELOC=$(dirname $0)/osxrelocator.py + +OLDDLPATH=/Library/Frameworks/GStreamer.framework/ +NEWDLPATH=@executable_path/../Frameworks/GStreamer.framework/ + +echo "GST Installer" +[ "$#" -eq 3 ] || die "3 arguments required, $# provided" +[ -d "$2" ] || die "Could not find $2" + +FMWORK_TARGET=$1 +BUNDLE_TARGET=$2 +GST_SOURCE=${1%/}/GStreamer.framework +GST_TARGET=$GST_SOURCE/Versions/$GST_VER +QGC_BINARY=$BUNDLE_TARGET/Contents/MacOS/$3 +[ -e "$QGC_BINARY" ] || die "Could not find $QGC_BINARY" + +process_framework() { + #-- Start looking for the source framework + [ -d "$GST_ROOT" ] || die "Could not find gstreamer framework in $GST_ROOT" + [ -d "$GST_BASE" ] || die "Wrong version of gstreamer framework found in $GST_ROOT" + [ -e $RELOC ] || die "Cannot find $RELOC" + echo "GST Installer: Copying $GST_ROOT to $FMWORK_TARGET" + rsync -a --delete "$GST_ROOT" "$FMWORK_TARGET" || die "Error copying $GST_ROOT to $FMWORK_TARGET" + #-- Prune unused stuff + rm -rf $GST_TARGET/bin + rm -rf $GST_TARGET/etc + rm -rf $GST_TARGET/share + rm -rf $GST_TARGET/Headers + rm -rf $GST_TARGET/include + rm -rf $GST_TARGET/lib/*.a + rm -rf $GST_TARGET/lib/*.la + rm -rf $GST_TARGET/lib/gio/modules/static + rm -rf $GST_TARGET/lib/glib-2.0 + rm -rf $GST_TARGET/lib/gst-validate-launcher + rm -rf $GST_TARGET/lib/gstreamer-1.0/static + rm -rf $GST_TARGET/lib/libffi-3.0.13 + rm -rf $GST_TARGET/lib/pkgconfig + rm $GST_TARGET/Commands + #-- Now relocate the embedded paths + echo "GST Installer: Relocating" + python $RELOC -r "$GST_TARGET" "$OLDDLPATH" "$NEWDLPATH" > /dev/null || die "Error relocating binaries in $GST_TARGET/lib" +} + +#-- Check and see if we've already processed the framework +echo "GST Installer: Checking $GST_TARGET" +[ -d $GST_TARGET ] || process_framework +#-- Now copy the framework to the app bundle +echo "GST Installer: Copying $GST_SOURCE to $BUNDLE_TARGET/Contents/Frameworks/" +rsync -a --delete $GST_SOURCE $BUNDLE_TARGET/Contents/Frameworks/ || die "Error copying framework into app bundle" +#-- The plugin scanner needs to find the GStreamer libraries +GSTINBUNDLE=$BUNDLE_TARGET/Contents/Frameworks/GStreamer.framework/Versions/$GST_VER +pushd $GSTINBUNDLE/libexec && ln -sf ../../../../../Frameworks . && popd || die "Error creating Frameworks symlink in $GST_TARGET/libexec" +#-- Fix main binary +install_name_tool -change /Library/Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer "$QGC_BINARY" > /dev/null || die "Error relocating $QGC_BINARY" +pushd $GSTINBUNDLE && install_name_tool -id @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer GStreamer && popd || die "Error relocating GStreamer" + + +