5 """MacOSX related tools
7 To compile an executable into a Mac application bundle (a .app), set its 'mac_app' attribute
10 To make a bundled shared library (a .bundle), set the 'mac_bundle' attribute:
14 import os, shutil, sys, platform
15 import TaskGen, Task, Build, Options, Utils
16 from TaskGen import taskgen, feature, after, before
17 from Logs import error, debug
21 <?xml version="1.0" encoding="UTF-8"?>
22 <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
25 <key>CFBundlePackageType</key>
27 <key>CFBundleGetInfoString</key>
28 <string>Created by Waf</string>
29 <key>CFBundleSignature</key>
32 <string>THIS IS A GENERATED FILE, DO NOT MODIFY</string>
33 <key>CFBundleExecutable</key>
40 # and also http://trac.macports.org/ticket/17059
42 @before('apply_lib_vars')
43 def set_macosx_deployment_target(self):
44 if self.env['MACOSX_DEPLOYMENT_TARGET']:
45 os.environ['MACOSX_DEPLOYMENT_TARGET'] = self.env['MACOSX_DEPLOYMENT_TARGET']
46 elif 'MACOSX_DEPLOYMENT_TARGET' not in os.environ:
47 if sys.platform == 'darwin':
48 os.environ['MACOSX_DEPLOYMENT_TARGET'] = '.'.join(platform.mac_ver()[0].split('.')[:2])
51 @after('apply_lib_vars')
52 def apply_framework(self):
53 for x in self.to_list(self.env['FRAMEWORKPATH']):
54 frameworkpath_st = '-F%s'
55 self.env.append_unique('CXXFLAGS', frameworkpath_st % x)
56 self.env.append_unique('CCFLAGS', frameworkpath_st % x)
57 self.env.append_unique('LINKFLAGS', frameworkpath_st % x)
59 for x in self.to_list(self.env['FRAMEWORK']):
60 self.env.append_value('LINKFLAGS', ['-framework', x])
63 def create_bundle_dirs(self, name, out):
65 dir = out.parent.get_dir(name)
68 dir = out.__class__(name, out.parent, 1)
70 contents = out.__class__('Contents', dir, 1)
72 macos = out.__class__('MacOS', contents, 1)
76 def bundle_name_for_output(out):
80 name = name[:k] + '.app'
88 def create_task_macapp(self):
89 """Use env['MACAPP'] to force *all* executables to be transformed into Mac applications
90 or use obj.mac_app = True to build specific targets as Mac apps"""
91 if self.env['MACAPP'] or getattr(self, 'mac_app', False):
92 apptask = self.create_task('macapp')
93 apptask.set_inputs(self.link_task.outputs)
95 out = self.link_task.outputs[0]
97 name = bundle_name_for_output(out)
98 dir = self.create_bundle_dirs(name, out)
100 n1 = dir.find_or_declare(['Contents', 'MacOS', out.name])
102 apptask.set_outputs([n1])
104 apptask.install_path = os.path.join(self.install_path, name, 'Contents', 'MacOS')
105 self.apptask = apptask
109 def create_task_macplist(self):
110 """Use env['MACAPP'] to force *all* executables to be transformed into Mac applications
111 or use obj.mac_app = True to build specific targets as Mac apps"""
112 if self.env['MACAPP'] or getattr(self, 'mac_app', False):
113 # check if the user specified a plist before using our template
114 if not getattr(self, 'mac_plist', False):
115 self.mac_plist = app_info
117 plisttask = self.create_task('macplist')
118 plisttask.set_inputs(self.link_task.outputs)
120 out = self.link_task.outputs[0]
121 self.mac_plist = self.mac_plist % (out.name)
123 name = bundle_name_for_output(out)
124 dir = self.create_bundle_dirs(name, out)
126 n1 = dir.find_or_declare(['Contents', 'Info.plist'])
128 plisttask.set_outputs([n1])
129 plisttask.mac_plist = self.mac_plist
130 plisttask.install_path = os.path.join(self.install_path, name, 'Contents')
131 self.plisttask = plisttask
135 def apply_link_osx(self):
136 name = self.link_task.outputs[0].name
137 if not self.install_path:
139 if getattr(self, 'vnum', None):
140 name = name.replace('.dylib', '.%s.dylib' % self.vnum)
142 path = os.path.join(Utils.subst_vars(self.install_path, self.env), name)
143 if '-dynamiclib' in self.env['LINKFLAGS']:
144 self.env.append_value('LINKFLAGS', '-install_name')
145 self.env.append_value('LINKFLAGS', path)
147 @before('apply_link', 'apply_lib_vars')
148 @feature('cc', 'cxx')
149 def apply_bundle(self):
150 """use env['MACBUNDLE'] to force all shlibs into mac bundles
151 or use obj.mac_bundle = True for specific targets only"""
152 if not ('cshlib' in self.features or 'shlib' in self.features): return
153 if self.env['MACBUNDLE'] or getattr(self, 'mac_bundle', False):
154 self.env['shlib_PATTERN'] = self.env['macbundle_PATTERN']
155 uselib = self.uselib = self.to_list(self.uselib)
156 if not 'MACBUNDLE' in uselib: uselib.append('MACBUNDLE')
160 def apply_bundle_remove_dynamiclib(self):
161 if self.env['MACBUNDLE'] or getattr(self, 'mac_bundle', False):
162 if not getattr(self, 'vnum', None):
164 self.env['LINKFLAGS'].remove('-dynamiclib')
165 self.env['LINKFLAGS'].remove('-single_module')
169 # TODO REMOVE IN 1.6 (global variable)
170 app_dirs = ['Contents', 'Contents/MacOS', 'Contents/Resources']
174 shutil.copy2(task.inputs[0].srcpath(env), task.outputs[0].abspath(env))
178 def plist_build(task):
180 f = open(task.outputs[0].abspath(env), "w")
181 f.write(task.mac_plist)
186 Task.task_type_from_func('macapp', vars=[], func=app_build, after="cxx_link cc_link static_link")
187 Task.task_type_from_func('macplist', vars=[], func=plist_build, after="cxx_link cc_link static_link")