c_osx.py 5.7 KB

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