Skip to content

Commit

Permalink
CMake: Improve MacOS Deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
HTRamsey committed Jan 15, 2025
1 parent 7c64ca3 commit d55b615
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 26 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
55 changes: 31 additions & 24 deletions cmake/CreateMacDMG.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
160 changes: 160 additions & 0 deletions deploy/mac/osxrelocator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/usr/bin/env python
# cerbero - a multi-platform build system for Open Source software
# Copyright (C) 2012 Andoni Morales Alastruey <ylatuya@gmail.com>
#
# 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()
84 changes: 84 additions & 0 deletions deploy/mac/prepare_gstreamer_framework.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/bin/bash
#
# Author: Gus Grubba <mavlink@grubba.com>
#
# 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"



0 comments on commit d55b615

Please sign in to comment.