diff --git Changelog Changelog index 07dfe16..47bbfef 100644 --- Changelog +++ Changelog @@ -1,5 +1,21 @@ Changes in version 0.7.4: + - Release gtk-mac-bundler 0.7.4 (John Ralls) + - Make sure to check for .symbolic icon extension (Jesse van den Kieboom) + - Fix pixbuf loader cache path (Jesse van den Kieboom) + - Make sure that libraries are writeable before using otool. (Julien Woillez) + - Update doap to reflect correct bug tracker. (John Ralls) + - Bug 698196: Fix typo (John Ralls) + - Bug #698916: Update versions, fix pixbuf (John Ralls) + - Update all launchers to reflect new pango environment variables (John Ralls) + - launcher: Update for pango changes (Dave Vasilevsky) + - Correct gdk-pixbuf path in the pygtk example (John Ralls) + - Bring example bundle files and launchers up to date (John Ralls) + - The *correct* fix for pango modules. (John Ralls) + - Fix another typo in pango module path (John Ralls) + +Changes in version 0.7.4: + - Make sure to check for .symbolic icon extension (Jesse van den Kieboom) - Fix pixbuf loader cache path (Jesse van den Kieboom) - Make sure that libraries are writeable before using otool. (Julien Woillez) diff --git bundler/bundler.py bundler/bundler.py index 8061ac1..65b8cc0 100644 --- bundler/bundler.py +++ bundler/bundler.py @@ -1,12 +1,12 @@ import sys import os, errno, glob -import dircache, shutil +import shutil import re from plistlib import Plist from distutils import dir_util, file_util -from project import * -import utils +from .project import * +from . import utils class Bundler: def __init__(self, project): @@ -32,13 +32,13 @@ class Bundler: def recursive_rm(self, dirname): # Extra safety ;) if dirname in [ "/", os.getenv("HOME"), os.path.join(os.getenv("HOME"), "Desktop"), self.meta.dest ]: - print "Eek, trying to remove a bit much, eh? (%s)" % (dirname) + print("Eek, trying to remove a bit much, eh? (%s)" % (dirname)) sys.exit(1) if not os.path.exists(dirname): return - files = dircache.listdir(dirname) + files = os.listdir(dirname) for file in files: path = os.path.join (dirname, file) if os.path.isdir(path): @@ -68,6 +68,12 @@ class Bundler: self.copy_path(path) def create_pango_setup(self): + if utils.has_pkgconfig_module("pango") and \ + not utils.has_pkgconfig_variable("pango", "pango_module_version"): + # Newer pango (>= 1.38) no longer has modules, skip this + # step in that case. + return + # Create a temporary pangorc file just for creating the # modules file with the right modules. module_version = utils.evaluate_pkgconfig_variables("${pkg:pango:pango_module_version}") @@ -227,7 +233,7 @@ class Bundler: # Clean out any libtool (*.la) files and static libraries if os.path.isdir(dest): for root, dirs, files in os.walk(dest): - for name in filter(lambda l: l.endswith(".la") or l.endswith(".a"), files): + for name in [l for l in files if l.endswith(".la") or l.endswith(".a")]: os.remove(os.path.join(root, name)) # Copies from Path.source to Path.dest, evaluating any variables @@ -247,7 +253,7 @@ class Bundler: relative_dest = self.project.evaluate_path(Path.source[m.end():]) dest = self.project.get_bundle_path("Contents/Resources", relative_dest) else: - print "Invalid bundle file, missing or invalid 'dest' property: " + Path.dest + print("Invalid bundle file, missing or invalid 'dest' property: " + Path.dest) sys.exit(1) (dest_parent, dest_tail) = os.path.split(dest) @@ -257,7 +263,7 @@ class Bundler: p = re.compile("[\*\?]") (source_parent, source_tail) = os.path.split(source) if p.search(source_parent): - print "Can't have wildcards except in the last path component: " + source + print("Can't have wildcards except in the last path component: " + source) sys.exit(1) if p.search(source_tail): @@ -267,7 +273,7 @@ class Bundler: else: source_check = source if not os.path.exists(source_check): - print "Cannot find source to copy: " + source + print("Cannot find source to copy: " + source) sys.exit(1) # If the destination has a wildcard as last component (copied @@ -285,13 +291,13 @@ class Bundler: try: # print "Copying %s to %s" % (globbed_source, destdir) shutil.copy(globbed_source, destdir) - except EnvironmentError, e: + except EnvironmentError as e: if e.errno == errno.ENOENT: - print "Warning, source file missing: " + globbed_source + print("Warning, source file missing: " + globbed_source) elif e.errno == errno.EEXIST: - print "Warning, path already exits: " + dest + print("Warning, path already exits: " + dest) else: - print "Error %s when copying file: %s" % ( str(e), globbed_source ) + print("Error %s when copying file: %s" % ( str(e), globbed_source )) sys.exit(1) else: @@ -315,13 +321,13 @@ class Bundler: link=None, verbose=1, dry_run=0) - except EnvironmentError, e: + except EnvironmentError as e: if e.errno == errno.ENOENT: - print "Warning, source file missing: " + globbed_source + print("Warning, source file missing: " + globbed_source) elif e.errno == errno.EEXIST: - print "Warning, path already exits: " + dest + print("Warning, path already exits: " + dest) else: - print "Error %s when copying file: %s" %( str(e), globbed_source ) + print("Error %s when copying file: %s" %( str(e), globbed_source )) sys.exit(1) return dest @@ -338,11 +344,11 @@ class Bundler: for path in self.binary_paths: if os.path.isdir(path): for root, dirs, files in os.walk(path): - paths.extend(map(lambda l: os.path.join(root, l), files)) + paths.extend([os.path.join(root, l) for l in files]) else: paths.append(path) - paths = filter(filter_path, paths) + paths = list(filter(filter_path, paths)) return list(set(paths)) def resolve_library_dependencies(self): @@ -366,11 +372,11 @@ class Bundler: def relative_path_map(line): if not os.path.isabs(line): - for prefix in prefixes.values(): + for prefix in list(prefixes.values()): path = os.path.join(prefix, "lib", line) if os.path.exists(path): return path - print "Cannot find a matching prefix for %s" % (line) + print("Cannot find a matching prefix for %s" % (line)) return line def prefix_filter(line): @@ -378,24 +384,24 @@ class Bundler: return False if line.startswith("/usr/X11"): - print "Warning, found X11 library dependency, you most likely don't want that:", line.strip().split()[0] + print("Warning, found X11 library dependency, you most likely don't want that:", line.strip().split()[0]) if os.path.isabs(line): - for prefix in prefixes.values(): + for prefix in list(prefixes.values()): if prefix in line: return True if not line.startswith("/usr/lib") and not line.startswith("/System/Library"): - print "Warning, library not available in any prefix:", line.strip().split()[0] + print("Warning, library not available in any prefix:", line.strip().split()[0]) return False return True - lines = filter(prefix_filter, [line.strip() for line in f]) + lines = list(filter(prefix_filter, [line.strip() for line in f])) p = re.compile("(.*\.dylib\.?.*)\s\(compatibility.*$") lines = utils.filterlines(p, lines) - lines = map(relative_path_map, lines) + lines = list(map(relative_path_map, lines)) #When you need to track down errors, uncomment this blocK # for path in paths: # cmd = "otool -L %s" % path @@ -407,7 +413,7 @@ class Bundler: for library in set(lines): # Replace the real path with the right prefix so we can # create a Path object. - for (key, value) in prefixes.items(): + for (key, value) in list(prefixes.items()): if library.startswith(value): path = Path("${prefix:" + key + "}" + library[len(value):]) new_libraries.append(path) @@ -415,14 +421,14 @@ class Bundler: n_paths = len(paths) n_iterations += 1 if n_iterations > 10: - print "Too many tries to resolve library dependencies" + print("Too many tries to resolve library dependencies") sys.exit(1) self.copy_binaries(new_libraries) paths = self.list_copied_binaries() def run_install_name_tool(self): - print "Running install name tool" + print("Running install name tool") paths = self.list_copied_binaries() prefixes = self.meta.prefixes @@ -430,12 +436,12 @@ class Bundler: # First change all references in libraries. for prefix in prefixes: prefix_path = self.project.get_prefix(prefix) - print "Going through prefix: " + prefix_path + print("Going through prefix: " + prefix_path) for path in paths: cmd = os.path.join(os.path.dirname(__file__), "run-install-name-tool-change.sh") + " " + path + " " + prefix_path + " Resources" + " change" f = os.popen(cmd) for line in f: - print line + print(line) # Then change the id of all libraries. Skipping this part for now #for path in paths: @@ -448,18 +454,18 @@ class Bundler: for framework in self.frameworks: fw_name, ext = os.path.splitext(os.path.basename(framework)) fwl = os.path.join(framework, fw_name) - print "Importing Framework: " + fwl + print("Importing Framework: " + fwl) # Fix the framework IDs cmd = os.path.join(os.path.dirname(__file__), "run-install-name-tool-change.sh") + " " + fwl + " " + fw_name + " Frameworks" + " id" f = os.popen(cmd) for line in f: - print line + print(line) # Fix the dependencies in other libraries for path in paths: cmd = os.path.join(os.path.dirname(__file__), "run-install-name-tool-change.sh") + " " + path + " " + fw_name + " Frameworks/" + fw_name + " change" f = os.popen(cmd) for line in f: - print line + print(line) #fix the dependencies in frameworks for ufw in self.frameworks: ufw_name, ext = os.path.splitext(os.path.basename(ufw)) @@ -469,20 +475,20 @@ class Bundler: cmd = os.path.join(os.path.dirname(__file__), "run-install-name-tool-change.sh") + " " + ufwl + " " + fw_name + " Frameworks/" + fw_name + " change" f = os.popen(cmd) for line in f: - print line + print(line) def strip_debugging(self): paths = self.list_copied_binaries() for path in paths: if path.endswith(".dylib") or path.endswith(".so"): - os.chmod(path, 0644) + os.chmod(path, 0o644) os.system("strip -x " + path + " 2>/dev/null") - os.chmod(path, 0444) + os.chmod(path, 0o444) else: - os.chmod(path, 0755) + os.chmod(path, 0o755) os.system("strip -ur " + path + " 2>/dev/null") - os.chmod(path, 0555) + os.chmod(path, 0o555) # # If you want to sign your application, set $APPLICATION_CERT with the @@ -491,7 +497,7 @@ class Bundler: # bundle's id string. # def sign_binaries(self): - if not os.environ.has_key("APPLICATION_CERT"): + if "APPLICATION_CERT" not in os.environ: return cert = os.getenv("APPLICATION_CERT") paths = self.list_copied_binaries() @@ -501,7 +507,7 @@ class Bundler: result = os.spawnvp(os.P_WAIT, 'codesign', cmdargs) if result: - raise OSError, '"' + " ".join(cmdargs) + '" failed %d' % result + raise OSError('"' + " ".join(cmdargs) + '" failed %d' % result) def copy_icon_themes(self): all_icons = set() @@ -604,7 +610,7 @@ class Bundler: final_path = self.project.evaluate_path(final_path) if not self.meta.overwrite and os.path.exists(final_path): - print "Bundle already exists: " + final_path + print("Bundle already exists: " + final_path) sys.exit(1) self.create_skeleton() @@ -618,7 +624,7 @@ class Bundler: path = self.project.get_main_binary() source = self.project.evaluate_path(path.source) if not os.path.exists(source): - print "Cannot find main binary: " + source + print("Cannot find main binary: " + source) sys.exit(1) dest = self.copy_path(path) @@ -658,24 +664,24 @@ class Bundler: launcher_script = self.project.get_launcher_script() if launcher_script: path = self.copy_path(launcher_script) - if os.environ.has_key("APPLICATION_CERT"): + if "APPLICATION_CERT" in os.environ: cert = os.environ["APPLICATION_CERT"] ident = self.project.get_bundle_id() cmdargs = ['codesign', '-s', cert, '-i', ident, "-f", path] result = os.spawnvp(os.P_WAIT, 'codesign', cmdargs) if result: - raise OSError, '"'+ " ".join(cmdargs) + '" failed %d' % result + raise OSError('"'+ " ".join(cmdargs) + '" failed %d' % result) if self.meta.overwrite: self.recursive_rm(final_path) shutil.move(self.project.get_bundle_path(), final_path) if __name__ == '__main__': if len(sys.argv) != 2: - print "Usage: %s " % (sys.argv[0]) + print("Usage: %s " % (sys.argv[0])) sys.exit(2) if not os.path.exists(sys.argv[1]): - print "File %s does not exist" % (sys.argv[1]) + print("File %s does not exist" % (sys.argv[1])) sys.exit(2) project = Project(sys.argv[1]) diff --git bundler/main.py bundler/main.py index bcd7dda..b9b2fc5 100644 --- bundler/main.py +++ bundler/main.py @@ -1,18 +1,20 @@ import sys, os -from project import * -from bundler import * +from .project import * +from .bundler import * def main(argv): if len(argv) != 1: - print "Usage: %s " % (sys.argv[0]) + print("Usage: %s " % (sys.argv[0])) sys.exit(2) if not os.path.exists(argv[0]): - print "File %s does not exist" % (argv[0]) + print("File %s does not exist" % (argv[0])) sys.exit(2) project = Project(argv[0]) bundler = Bundler(project) - - bundler.run() + try: + bundler.run() + except Exception as err: + print("Bundler encountered an error %s" % str(err)) diff --git bundler/project.py bundler/project.py index c98b65a..60f0541 100644 --- bundler/project.py +++ bundler/project.py @@ -5,7 +5,7 @@ import os import xml.dom.minidom from xml.dom.minidom import Node from plistlib import Plist -import utils +from . import utils # Base class for anything that can be copied into a bundle with a # source and dest. @@ -184,7 +184,7 @@ class Data(Path): pass class IconTheme: - ICONS_NONE, ICONS_ALL, ICONS_AUTO = range(3) + ICONS_NONE, ICONS_ALL, ICONS_AUTO = list(range(3)) def __init__(self, name, icons=ICONS_AUTO): self.name = name @@ -221,7 +221,7 @@ class Project: # Get the first app-bundle tag and ignore any others. self.root = utils.node_get_element_by_tag_name(doc, "app-bundle") except: - print "Could not load project %s:" % (project_path) + print("Could not load project %s:" % (project_path)) raise # The directory the project file is in (as opposed to @@ -232,14 +232,14 @@ class Project: plist_path = self.get_plist_path() try: plist = Plist.fromFile(plist_path) - except EnvironmentError, e: + except EnvironmentError as e: if e.errno == errno.ENOENT: - print "Info.plist file not found: " + plist_path + print("Info.plist file not found: " + plist_path) sys.exit(1) else: raise self.name = plist.CFBundleExecutable - if plist.has_key("CFBundleName"): + if "CFBundleName" in plist: self.bundle_name = plist.CFBundleName else: self.bundle_name = plist.CFBundleExecutable @@ -340,7 +340,7 @@ class Project: themes.append(IconTheme.from_node(node)) # The hicolor theme is mandatory. - if not filter(lambda l: l.name == "hicolor", themes): + if not [l for l in themes if l.name == "hicolor"]: themes.append(IconTheme("hicolor")) return themes @@ -412,33 +412,33 @@ class Project: if __name__ == '__main__': project = Project(os.path.join(os.getcwd(), 'giggle.bundle')) - print "General:" - print " Project path: %s" % (project.get_project_path()) - print " Plist path: %s" % (project.get_plist_path()) - print " App name: %s" % (project.name) - print " Destination: %s" % (project.get_meta().dest) - print " Overwrite: %s" % (str(project.get_meta().overwrite)) + print("General:") + print(" Project path: %s" % (project.get_project_path())) + print(" Plist path: %s" % (project.get_plist_path())) + print(" App name: %s" % (project.name)) + print(" Destination: %s" % (project.get_meta().dest)) + print(" Overwrite: %s" % (str(project.get_meta().overwrite))) environment = project.get_environment() - print "Environment:" + print("Environment:") for variable in environment.runtime_variables: - print " %s=%s" % (variable.name, variable.value) + print(" %s=%s" % (variable.name, variable.value)) for script in environment.scripts: - print " %s => %s" % (script.source, script.dest) + print(" %s => %s" % (script.source, script.dest)) - print "Frameworks:" + print("Frameworks:") for framework in project.get_frameworks(): - print " ", framework + print(" ", framework) - print "Main binary:" + print("Main binary:") binary = project.get_main_binary() - print " %s => %s" % (binary.source, binary.dest) + print(" %s => %s" % (binary.source, binary.dest)) - print "Launcher:" + print("Launcher:") launcher_script = project.get_launcher_script() - print " %s => %s" % (launcher_script.source, launcher_script.dest) + print(" %s => %s" % (launcher_script.source, launcher_script.dest)) - print "Binaries:" + print("Binaries:") for binary in project.get_binaries(): - print " %s => %s" % (binary.source, binary.dest) + print(" %s => %s" % (binary.source, binary.dest)) diff --git bundler/project_test.py bundler/project_test.py index 4c3d583..0ff9179 100644 --- bundler/project_test.py +++ bundler/project_test.py @@ -5,8 +5,8 @@ import unittest import xml.dom.minidom from xml.dom.minidom import Node from plistlib import Plist -from project import Project -import utils +from .project import Project +from . import utils class Mock_Project(Project): @@ -20,9 +20,9 @@ class Mock_Project(Project): try: plist_path = os.path.join(self.project_dir, "test.plist") plist = Plist.fromFile(plist_path) - except EnvironmentError, e: + except EnvironmentError as e: if e.errno == errno.ENOENT: - print "Info.plist file not found: " + plist_path + print("Info.plist file not found: " + plist_path) sys.exit(1) else: raise diff --git bundler/runtest.py bundler/runtest.py index 309643a..2450f2d 100755 --- bundler/runtest.py +++ bundler/runtest.py @@ -1,7 +1,7 @@ #!/usr/bin/python import unittest import os -from project_test import Project_Test +from .project_test import Project_Test def setProjects( goodpath, badpath): if not os.path.isabs(goodpath): diff --git bundler/utils.py bundler/utils.py index 78d4c47..76c83d1 100644 --- bundler/utils.py +++ bundler/utils.py @@ -15,6 +15,21 @@ def evaluate_environment_variables(string): return string +def has_pkgconfig_module(module): + """Returns True if the pkg-config module exists""" + f = os.popen("pkg-config --exists " + module) + f.read().strip() + return f.close() is None + +def has_pkgconfig_variable(module, key): + """Returns True if the pkg-config variable exists for the given + module + """ + f = os.popen("pkg-config --variable=" + key + " " + module) + status = bool(f.read().strip()) + f.close() + return status + def evaluate_pkgconfig_variables(string): p = re.compile("\${pkg:(.*?):(.*?)}") m = p.search(string) @@ -24,6 +39,17 @@ def evaluate_pkgconfig_variables(string): f = os.popen("pkg-config --variable=" + key + " " + module) value = f.read().strip() if not value: + # pango 1.38 removed modules, try to give a helpful + # message in case something tries to reference the no + # longer existing variable (most likely from old bundle + # xml files) when using a newer pango build. + if module == "pango" and key == "pango_module_version": + if has_pkgconfig_module("pango"): + raise Exception( + "'%s' got removed in '%s' " + "1.38. Remove any reference to pango " + "modules in your bundle xml." % ( + key, module)) raise Exception("pkg-config variable '%s %s' is undefined" % (key, module)) string = p.sub(value, string, 1) m = p.search(string) @@ -33,7 +59,7 @@ def evaluate_pkgconfig_variables(string): def makedirs(path): try: os.makedirs(path) - except EnvironmentError, e: + except EnvironmentError as e: if e.errno != errno.EEXIST: raise diff --git examples/gtk-demo.bundle examples/gtk-demo.bundle index 4b32d6b..422126e 100644 --- examples/gtk-demo.bundle +++ examples/gtk-demo.bundle @@ -79,9 +79,11 @@ ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/*.so + ${prefix}/etc/pango/ diff --git examples/gtk3-demo.bundle examples/gtk3-demo.bundle index ba15cd3..ec0eb4c 100644 --- examples/gtk3-demo.bundle +++ examples/gtk3-demo.bundle @@ -69,13 +69,11 @@ ${prefix}/lib/gdk-pixbuf-2.0/${pkg:gdk-pixbuf-2.0:gdk_pixbuf_binary_version}/loaders/*.so + - - - - ${project}/gtkrc - - - - - + diff --git examples/gtk3-launcher.sh examples/gtk3-launcher.sh index 4aed833..9137ccb 100755 --- examples/gtk3-launcher.sh +++ examples/gtk3-launcher.sh @@ -29,11 +29,15 @@ export GTK_DATA_PREFIX="$bundle_res" export GTK_EXE_PREFIX="$bundle_res" export GTK_PATH="$bundle_res" -export GTK2_RC_FILES="$bundle_etc/gtk-3.0/gtkrc" -export GTK_IM_MODULE_FILE="$bundle_etc/gtk-3.0/gtk.immodules" -export GDK_PIXBUF_MODULE_FILE="$bundle_etc/gtk-3.0/gdk-pixbuf.loaders" -export PANGO_LIBDIR="$bundle_lib" +# PANGO_* is no longer needed for pango >= 1.38 +export PANGO_RC_FILE="$bundle_etc/pango/pangorc" export PANGO_SYSCONFDIR="$bundle_etc" +export PANGO_LIBDIR="$bundle_lib" + +export GDK_PIXBUF_MODULE_FILE="$bundle_lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" +if [ `uname -r | cut -d . -f 1` -ge 10 ]; then + export GTK_IM_MODULE_FILE="$bundle_etc/gtk-3.0/gtk.immodules" +fi APP=name diff --git examples/launcher.sh examples/launcher.sh index a1dfd1b..11cd019 100755 --- examples/launcher.sh +++ examples/launcher.sh @@ -31,7 +31,11 @@ export GTK_PATH="$bundle_res" export GTK2_RC_FILES="$bundle_etc/gtk-2.0/gtkrc" export GTK_IM_MODULE_FILE="$bundle_etc/gtk-2.0/gtk.immodules" -export GDK_PIXBUF_MODULE_FILE="$bundle_etc/gtk-2.0/gdk-pixbuf.loaders" +#N.B. When gdk-pixbuf was separated from Gtk+ the location of the +#loaders cache changed as well. Depending on the version of Gtk+ that +#you built with you may still need to use the old location: +#export GDK_PIXBUF_MODULE_FILE="$bundle_etc/gtk-2.0/gdk-pixbuf.loaders" +export GDK_PIXBUF_MODULE_FILE="$bundle_lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" export PANGO_LIBDIR="$bundle_lib" export PANGO_SYSCONFDIR="$bundle_etc"