1#!/usr/bin/env python 2# encoding: utf-8 3# Thomas Nagy 2008-2018 (ita) 4 5""" 6MacOSX related tools 7""" 8 9import os, shutil, platform 10from waflib import Task, Utils 11from waflib.TaskGen import taskgen_method, feature, after_method, before_method 12 13app_info = ''' 14<?xml version="1.0" encoding="UTF-8"?> 15<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> 16<plist version="0.9"> 17<dict> 18 <key>CFBundlePackageType</key> 19 <string>APPL</string> 20 <key>CFBundleGetInfoString</key> 21 <string>Created by Waf</string> 22 <key>CFBundleSignature</key> 23 <string>????</string> 24 <key>NOTE</key> 25 <string>THIS IS A GENERATED FILE, DO NOT MODIFY</string> 26 <key>CFBundleExecutable</key> 27 <string>{app_name}</string> 28</dict> 29</plist> 30''' 31""" 32plist template 33""" 34 35@feature('c', 'cxx') 36def set_macosx_deployment_target(self): 37 """ 38 see WAF issue 285 and also and also http://trac.macports.org/ticket/17059 39 """ 40 if self.env.MACOSX_DEPLOYMENT_TARGET: 41 os.environ['MACOSX_DEPLOYMENT_TARGET'] = self.env.MACOSX_DEPLOYMENT_TARGET 42 elif 'MACOSX_DEPLOYMENT_TARGET' not in os.environ: 43 if Utils.unversioned_sys_platform() == 'darwin': 44 os.environ['MACOSX_DEPLOYMENT_TARGET'] = '.'.join(platform.mac_ver()[0].split('.')[:2]) 45 46@taskgen_method 47def create_bundle_dirs(self, name, out): 48 """ 49 Creates bundle folders, used by :py:func:`create_task_macplist` and :py:func:`create_task_macapp` 50 """ 51 dir = out.parent.find_or_declare(name) 52 dir.mkdir() 53 macos = dir.find_or_declare(['Contents', 'MacOS']) 54 macos.mkdir() 55 return dir 56 57def bundle_name_for_output(out): 58 name = out.name 59 k = name.rfind('.') 60 if k >= 0: 61 name = name[:k] + '.app' 62 else: 63 name = name + '.app' 64 return name 65 66@feature('cprogram', 'cxxprogram') 67@after_method('apply_link') 68def create_task_macapp(self): 69 """ 70 To compile an executable into a Mac application (a .app), set its *mac_app* attribute:: 71 72 def build(bld): 73 bld.shlib(source='a.c', target='foo', mac_app=True) 74 75 To force *all* executables to be transformed into Mac applications:: 76 77 def build(bld): 78 bld.env.MACAPP = True 79 bld.shlib(source='a.c', target='foo') 80 """ 81 if self.env.MACAPP or getattr(self, 'mac_app', False): 82 out = self.link_task.outputs[0] 83 84 name = bundle_name_for_output(out) 85 dir = self.create_bundle_dirs(name, out) 86 87 n1 = dir.find_or_declare(['Contents', 'MacOS', out.name]) 88 89 self.apptask = self.create_task('macapp', self.link_task.outputs, n1) 90 inst_to = getattr(self, 'install_path', '/Applications') + '/%s/Contents/MacOS/' % name 91 self.add_install_files(install_to=inst_to, install_from=n1, chmod=Utils.O755) 92 93 if getattr(self, 'mac_files', None): 94 # this only accepts files; they will be installed as seen from mac_files_root 95 mac_files_root = getattr(self, 'mac_files_root', None) 96 if isinstance(mac_files_root, str): 97 mac_files_root = self.path.find_node(mac_files_root) 98 if not mac_files_root: 99 self.bld.fatal('Invalid mac_files_root %r' % self.mac_files_root) 100 res_dir = n1.parent.parent.make_node('Resources') 101 inst_to = getattr(self, 'install_path', '/Applications') + '/%s/Resources' % name 102 for node in self.to_nodes(self.mac_files): 103 relpath = node.path_from(mac_files_root or node.parent) 104 self.create_task('macapp', node, res_dir.make_node(relpath)) 105 self.add_install_as(install_to=os.path.join(inst_to, relpath), install_from=node) 106 107 if getattr(self.bld, 'is_install', None): 108 # disable regular binary installation 109 self.install_task.hasrun = Task.SKIP_ME 110 111@feature('cprogram', 'cxxprogram') 112@after_method('apply_link') 113def create_task_macplist(self): 114 """ 115 Creates a :py:class:`waflib.Tools.c_osx.macplist` instance. 116 """ 117 if self.env.MACAPP or getattr(self, 'mac_app', False): 118 out = self.link_task.outputs[0] 119 120 name = bundle_name_for_output(out) 121 122 dir = self.create_bundle_dirs(name, out) 123 n1 = dir.find_or_declare(['Contents', 'Info.plist']) 124 self.plisttask = plisttask = self.create_task('macplist', [], n1) 125 plisttask.context = { 126 'app_name': self.link_task.outputs[0].name, 127 'env': self.env 128 } 129 130 plist_ctx = getattr(self, 'plist_context', None) 131 if (plist_ctx): 132 plisttask.context.update(plist_ctx) 133 134 if getattr(self, 'mac_plist', False): 135 node = self.path.find_resource(self.mac_plist) 136 if node: 137 plisttask.inputs.append(node) 138 else: 139 plisttask.code = self.mac_plist 140 else: 141 plisttask.code = app_info 142 143 inst_to = getattr(self, 'install_path', '/Applications') + '/%s/Contents/' % name 144 self.add_install_files(install_to=inst_to, install_from=n1) 145 146@feature('cshlib', 'cxxshlib') 147@before_method('apply_link', 'propagate_uselib_vars') 148def apply_bundle(self): 149 """ 150 To make a bundled shared library (a ``.bundle``), set the *mac_bundle* attribute:: 151 152 def build(bld): 153 bld.shlib(source='a.c', target='foo', mac_bundle = True) 154 155 To force *all* executables to be transformed into bundles:: 156 157 def build(bld): 158 bld.env.MACBUNDLE = True 159 bld.shlib(source='a.c', target='foo') 160 """ 161 if self.env.MACBUNDLE or getattr(self, 'mac_bundle', False): 162 self.env.LINKFLAGS_cshlib = self.env.LINKFLAGS_cxxshlib = [] # disable the '-dynamiclib' flag 163 self.env.cshlib_PATTERN = self.env.cxxshlib_PATTERN = self.env.macbundle_PATTERN 164 use = self.use = self.to_list(getattr(self, 'use', [])) 165 if not 'MACBUNDLE' in use: 166 use.append('MACBUNDLE') 167 168app_dirs = ['Contents', 'Contents/MacOS', 'Contents/Resources'] 169 170class macapp(Task.Task): 171 """ 172 Creates mac applications 173 """ 174 color = 'PINK' 175 def run(self): 176 self.outputs[0].parent.mkdir() 177 shutil.copy2(self.inputs[0].srcpath(), self.outputs[0].abspath()) 178 179class macplist(Task.Task): 180 """ 181 Creates plist files 182 """ 183 color = 'PINK' 184 ext_in = ['.bin'] 185 def run(self): 186 if getattr(self, 'code', None): 187 txt = self.code 188 else: 189 txt = self.inputs[0].read() 190 context = getattr(self, 'context', {}) 191 txt = txt.format(**context) 192 self.outputs[0].write(txt) 193 194