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")