[chronojump: 2/3] Add a new script to package macOS



commit a3c2528ea3c22ea40267f42d2ae4e64339c4256a
Author: Andoni Morales Alastruey <ylatuya gmail com>
Date:   Fri May 15 18:41:15 2020 +0200

    Add a new script to package macOS

 .gitignore                                         |   4 +
 package/macos/Makefile                             |  60 +++++++++
 package/macos/app/Applications                     |   1 +
 .../app/Chronojump.app/Contents/MacOS/Chronojump   |   1 +
 .../Chronojump.app/Contents/Resources/icon.icns    | Bin 0 -> 88060 bytes
 package/macos/resources/Info.plist                 |  26 ++++
 package/macos/resources/config                     |  54 ++++++++
 package/macos/resources/osxrelocator.py            | 139 +++++++++++++++++++++
 8 files changed, 285 insertions(+)
---
diff --git a/.gitignore b/.gitignore
index fe30cea4..76016a11 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,7 @@ chronojump.userprefs
 # Files generated by "make dist" on Linux
 ChangeLog
 chronojump-*.tar.gz
+
+#MacOS package
+package/macos/Chronojump.app/Contents/Home
+package/macos/Chronojump.app/Contents/Info.plist
\ No newline at end of file
diff --git a/package/macos/Makefile b/package/macos/Makefile
new file mode 100644
index 00000000..c714a7a3
--- /dev/null
+++ b/package/macos/Makefile
@@ -0,0 +1,60 @@
+MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+CUR_DIR := $(dir $(MKFILE_PATH))
+BASE_DIR = "$(CUR_DIR)/../../"
+SOURCES_DIR = "$(CUR_DIR)/../../src"
+TOOLS_DIR = "$(CUR_DIR)/../../tools"
+PACKAGE_DIR = "$(CUR_DIR)/../../package/macos"
+APP_DIR = "$(PACKAGE_DIR)/app/Chronojump.app/Contents/"
+RESOURCES_DIR = "$(PACKAGE_DIR)/resources"
+SIGNING = "$(PACKAGE_DIR)/../signing"
+USER = $(shell whoami)
+PACKAGE_VERSION = $(shell git describe --abbrev=0)
+PACKAGE_NAME = "chronojump-$(PACKAGE_VERSION)-x86_64"
+BUILD_DIR = "$(SOURCES_DIR)/build"
+MONO_DIR = "/Library/Frameworks/Mono.framework/Versions/Current/"
+MONO_PKG_CONFIG = "$(MONO_DIR)/lib/pkgconfig"
+PATH := $(PATH):/usr/local/Cellar/gettext/0.20.1/bin
+
+all: configure chronojump install_chronojump install_mono bundle package
+
+
+configure:
+       cd $(BASE_DIR) && PKG_CONFIG_PATH=/Library/Frameworks/Mono.framework/Versions/Current/lib/pkgconfig 
sh autogen.sh --prefix=$(APP_DIR)/Home/
+
+chronojump:
+       cd $(BASE_DIR) && make
+
+install_chronojump:
+       cd $(BASE_DIR) && make install
+
+install_mono:
+       # Copy the files that will not be embedded in the bundle
+       cp -r $(MONO_DIR)/lib/lib*dylib $(APP_DIR)/Home/lib/
+       cp $(MONO_DIR)/lib/*sharpglue*.so $(APP_DIR)/Home/lib/
+       codesign --remove-signature $(APP_DIR)/Home/lib/lib*dylib
+       python $(RESOURCES_DIR)/osxrelocator.py lib/ $(APP_DIR)/Home/ 
/Library/Frameworks/Mono.framework/Versions/6.8.0/ --recursive
+
+bundle:
+       # Generate Info.plist file
+       cp $(PACKAGE_DIR)/resources/Info.plist $(APP_DIR)
+       sed -e s/{version}/$(PACKAGE_VERSION)/ $(PACKAGE_DIR)/resources/Info.plist > $(APP_DIR)/Info.plist
+       # The mono binary is signed, we need to remove the signature to add the rpath's
+       # otherwise modifying the binary invalidates the signature
+       cp $(MONO_DIR)/bin/mono $(PACKAGE_DIR)
+       codesign --remove-signature $(PACKAGE_DIR)/mono
+       install_name_tool -add_rpath @executable_path/../lib -add_rpath @loader_path $(PACKAGE_DIR)/mono
+       mkbundle \
+               $(APP_DIR)/Home/lib/chronojump/Chronojump.exe \
+               -o $(PACKAGE_DIR)/Chronojump \
+               -L $(MONO_DIR)/lib/mono/4.5/ \
+               -L $(MONO_DIR)/lib/mono/4.5/Facades \
+               -L $(MONO_DIR)/lib/mono/gtk-sharp-2.0 \
+               --runtime $(PACKAGE_DIR)/mono \
+               --config $(RESOURCES_DIR)/config \
+           --simple \
+               --deps
+       cp $(PACKAGE_DIR)/Chronojump $(APP_DIR)/Home/bin/
+
+
+package:
+       hdiutil create -volname Chronojump -srcfolder app -ov -format UDZO Chronojump-$(PACKAGE_VERSION).dmg
\ No newline at end of file
diff --git a/package/macos/app/Applications b/package/macos/app/Applications
new file mode 120000
index 00000000..bd2d47fa
--- /dev/null
+++ b/package/macos/app/Applications
@@ -0,0 +1 @@
+/Applications
\ No newline at end of file
diff --git a/package/macos/app/Chronojump.app/Contents/MacOS/Chronojump 
b/package/macos/app/Chronojump.app/Contents/MacOS/Chronojump
new file mode 120000
index 00000000..b0d18d41
--- /dev/null
+++ b/package/macos/app/Chronojump.app/Contents/MacOS/Chronojump
@@ -0,0 +1 @@
+../Home/bin/Chronojump
\ No newline at end of file
diff --git a/package/macos/app/Chronojump.app/Contents/Resources/icon.icns 
b/package/macos/app/Chronojump.app/Contents/Resources/icon.icns
new file mode 100644
index 00000000..026086dc
Binary files /dev/null and b/package/macos/app/Chronojump.app/Contents/Resources/icon.icns differ
diff --git a/package/macos/resources/Info.plist b/package/macos/resources/Info.plist
new file mode 100644
index 00000000..f778bc82
--- /dev/null
+++ b/package/macos/resources/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd";>
+<plist version="1.0">
+<dict>
+<key>CFBundleIdentifier</key>
+<string>org.chronojump</string>
+<key>CFBundleName</key>
+<string>Chronojump</string>
+<key>CFBundleExecutable</key>
+<string>Chronojump</string>
+<key>CFBundlePackageGetInfoString</key>
+<string>Chronojump</string>
+<key>CFBundlePackageType</key>
+<string>APPL</string>
+<key>CFBundleVersion</key>
+<string>{version}</string>
+<key>CFBundleShortVersionString</key>
+<string>{version}</string>
+<key>LSMinimumSystemVersion</key>
+<string>10.7</string>
+<key>CFBundleInfoDictionaryVersion</key>
+<string>6.0</string>
+<key>CFBundleIconFile</key>
+<string>icon.icns</string>
+</dict>
+</plist>
diff --git a/package/macos/resources/config b/package/macos/resources/config
new file mode 100644
index 00000000..f993009b
--- /dev/null
+++ b/package/macos/resources/config
@@ -0,0 +1,54 @@
+<configuration>
+       <dllmap dll="i:cygwin1.dll" target="libc.dylib" os="!windows" />
+       <dllmap dll="libc" target="libc.dylib" os="!windows"/>
+       <dllmap dll="intl" target="libintl.dylib" os="!windows"/>
+       <dllmap dll="intl" name="bind_textdomain_codeset" target="libc.dylib" os="solaris"/>
+       <dllmap dll="libintl" name="bind_textdomain_codeset" target="libc.dylib" os="solaris"/>
+       <dllmap dll="libintl" target="libintl.dylib" os="!windows"/>
+       <dllmap dll="i:libxslt.dll" target="libxslt.dylib" os="!windows"/>
+       <dllmap dll="i:odbc32.dll" target="libodbc.so.2" os="!windows"/>
+       <dllmap dll="i:odbc32.dll" target="libiodbc.dylib" os="osx"/>
+       <dllmap dll="oci" target="libclntsh.dylib" os="!windows"/>
+       <dllmap dll="db2cli" target="libdb2_36.dylib" os="!windows"/>
+       <dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.dylib" os="!windows" />
+       <dllmap dll="System.Native" target="libmono-native-compat.dylib" os="!windows" />
+       <dllmap dll="System.Net.Security.Native" target="libmono-native-compat.dylib" os="!windows" />
+       <dllmap dll="System.Security.Cryptography.Native.Apple" target="libmono-native-compat.dylib" os="osx" 
/>
+       <dllmap dll="libmono-btls-shared" target="libmono-btls-shared.dylib" os="!windows" />
+       <dllmap dll="i:msvcrt" target="libc.dylib" os="!windows"/>
+       <dllmap dll="i:msvcrt.dll" target="libc.dylib" os="!windows"/>
+       <dllmap dll="sqlite" target="libsqlite.0.dylib" os="!windows"/>
+       <dllmap dll="sqlite3" target="libsqlite3.0.dylib" os="!windows"/>
+       <dllmap dll="libX11" target="libX11.dylib" os="!windows" />
+       <dllmap dll="libgdk-x11-2.0" target="libgdk-x11-2.0.dylib" os="!windows"/>
+       <dllmap dll="libgdk_pixbuf-2.0" target="libgdk_pixbuf-2.0.so.0" os="!windows"/>
+       <dllmap dll="libgtk-x11-2.0" target="libgtk-x11-2.0.dylib" os="!windows"/>
+       <dllmap dll="libglib-2.0" target="libglib-2.0.so.0" os="!windows"/>
+       <dllmap dll="libgobject-2.0" target="libgobject-2.0.so.0" os="!windows"/>
+       <dllmap dll="libgnomeui-2" target="libgnomeui-2.so.0" os="!windows"/>
+       <dllmap dll="librsvg-2" target="librsvg-2.so.2" os="!windows"/>
+       <dllmap dll="libXinerama" target="libXinerama.so.1" os="!windows" />
+       <dllmap dll="libasound" target="libasound.so.2" os="!windows" />
+       <dllmap dll="libcairo-2.dll" target="libcairo.so.2" os="!windows"/>
+       <dllmap dll="libcairo-2.dll" target="libcairo.2.dylib" os="osx"/>
+       <dllmap dll="libcups" target="libcups.so.2" os="!windows"/>
+       <dllmap dll="libcups" target="libcups.dylib" os="osx"/>
+       <dllmap dll="i:kernel32.dll">
+               <dllentry dll="__Internal" name="CopyMemory" target="mono_win32_compat_CopyMemory"/>
+               <dllentry dll="__Internal" name="FillMemory" target="mono_win32_compat_FillMemory"/>
+               <dllentry dll="__Internal" name="MoveMemory" target="mono_win32_compat_MoveMemory"/>
+               <dllentry dll="__Internal" name="ZeroMemory" target="mono_win32_compat_ZeroMemory"/>
+       </dllmap>
+       <dllmap dll="gdiplus" target="/Library/Frameworks/Mono.framework/Versions/6.8.0/lib/libgdiplus.dylib" 
os="!windows"/>
+       <dllmap dll="gdiplus.dll" 
target="/Library/Frameworks/Mono.framework/Versions/6.8.0/lib/libgdiplus.dylib"  os="!windows"/>
+       <dllmap dll="gdi32" target="/Library/Frameworks/Mono.framework/Versions/6.8.0/lib/libgdiplus.dylib" 
os="!windows"/>
+       <dllmap dll="gdi32.dll" 
target="/Library/Frameworks/Mono.framework/Versions/6.8.0/lib/libgdiplus.dylib" os="!windows"/>
+
+       <dllmap dll="libglib-2.0-0.dll" target="libglib-2.0.0.dylib"/>
+       <dllmap dll="libgobject-2.0-0.dll" target="libgobject-2.0.0.dylib"/>
+       <dllmap dll="libatk-1.0-0.dll" target="libatk-1.0.0.dylib"/>
+       <dllmap dll="libgtk-win32-2.0-0.dll" target="libgtk-quartz-2.0.0.dylib"/>
+       <dllmap dll="libgdk-win32-2.0-0.dll" target="libgdk-quartz-2.0.0.dylib"/>
+       <dllmap dll="libgdk_pixbuf-2.0-0.dll" target="libgdk_pixbuf-2.0.0.dylib"/>
+       <dllmap dll="libglade-2.0-0.dll" target="libglade-2.0.0.dylib"/>
+</configuration>
diff --git a/package/macos/resources/osxrelocator.py b/package/macos/resources/osxrelocator.py
new file mode 100644
index 00000000..864c6152
--- /dev/null
+++ b/package/macos/resources/osxrelocator.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+# 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'
+
+
+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, recursive, logfile=None):
+        self.root = root
+        self.lib_prefix = self._fix_path(lib_prefix)
+        self.recursive = recursive
+        self.use_relative_paths = True
+        self.logfile = None
+
+    def relocate(self):
+        self.parse_dir(self.root)
+
+    def relocate_dir(self, dirname):
+        self.parse_dir(os.path.join(self.root, dirname))
+
+    def relocate_file(self, object_file):
+        self.change_libs_path(object_file)
+
+    def change_id(self, object_file, id=None):
+        id = id or object_file.replace(self.lib_prefix, '@rpath')
+        filename = os.path.basename(object_file)
+        if not (filename.endswith('so') or filename.endswith('dylib')):
+            return
+        subprocess.call([INT_CMD, "-id", id, object_file])
+
+    def change_libs_path(self, object_file):
+        depth = len(object_file.split('/')) - len(self.root.split('/')) - 1
+        p_depth = '/..' * depth
+        rpaths = ['.']
+        rpaths += ['@loader_path' + p_depth, '@executable_path' + p_depth]
+        rpaths += ['@loader_path' + '/../lib', '@executable_path' + '/../lib']
+        if not (object_file.endswith('so') or object_file.endswith('dylib')):
+            return
+        if depth > 1:
+            rpaths += ['@loader_path/..', '@executable_path/..']
+        for p in rpaths:
+            subprocess.call([INT_CMD, "-add_rpath", p, object_file])
+        for lib in self.list_shared_libraries(object_file):
+            if self.lib_prefix in lib:
+                new_lib = lib.replace(self.lib_prefix, '@rpath')
+                subprocess.call([INT_CMD, '-change', lib, new_lib, object_file])
+
+    def change_lib_path(self, object_file, old_path, new_path):
+        for lib in self.list_shared_libraries(object_file):
+            if old_path in lib:
+                new_path = lib.replace(old_path, new_path)
+                cmd = '%s -change "%s" "%s" "%s"' % (INT_CMD, lib, new_path,
+                                               object_file)
+                subprocess.call(cmd, fail=True, logfile=self.logfile)
+
+    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
+                self.change_libs_path(os.path.join(dirpath, f))
+            if not self.recursive:
+                break
+
+    @staticmethod
+    def list_shared_libraries(object_file):
+        res = subprocess.getoutput("{} -L {}".format(OTOOL_CMD, object_file)).splitlines()
+        # We don't use the first line
+        libs = res[1:]
+        # Remove the first character tabulation
+        libs = [str(x[1:]) for x in libs]
+        # Remove the version info
+        libs = [x.split(' ', 1)[0] for x in libs]
+        return libs
+
+    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] library_path old_prefix new_prefix"
+        description = 'Rellocates object files changing the dependant '\
+                      ' 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[1], args[2], options.recursive)
+        relocator.relocate_dir(args[0])
+        exit(0)
+
+
+if __name__ == "__main__":
+    main = Main()
+    main.run()


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]