jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

657 lines
22KB

  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # XCode 3/XCode 4 generator for Waf
  4. # Based on work by Nicolas Mercier 2011
  5. # Extended by Simon Warg 2015, https://github.com/mimon
  6. # XCode project file format based on http://www.monobjc.net/xcode-project-file-format.html
  7. """
  8. Usage:
  9. See also demos/xcode6/ folder
  10. def options(opt):
  11. opt.load('xcode6')
  12. def configure(cnf):
  13. # <do your stuff>
  14. # For example
  15. cnf.env.SDKROOT = 'macosx10.9'
  16. # Use cnf.PROJ_CONFIGURATION to completely set/override
  17. # global project settings
  18. # cnf.env.PROJ_CONFIGURATION = {
  19. # 'Debug': {
  20. # 'SDKROOT': 'macosx10.9'
  21. # }
  22. # 'MyCustomReleaseConfig': {
  23. # 'SDKROOT': 'macosx10.10'
  24. # }
  25. # }
  26. # In the end of configure() do
  27. cnf.load('xcode6')
  28. def build(bld):
  29. # Make a Framework target
  30. bld.framework(
  31. source_files={
  32. 'Include': bld.path.ant_glob('include/MyLib/*.h'),
  33. 'Source': bld.path.ant_glob('src/MyLib/*.cpp')
  34. },
  35. includes='include',
  36. export_headers=bld.path.ant_glob('include/MyLib/*.h'),
  37. target='MyLib',
  38. )
  39. # You can also make bld.dylib, bld.app, bld.stlib ...
  40. $ waf configure xcode6
  41. """
  42. # TODO: support iOS projects
  43. from waflib import Context, TaskGen, Build, Utils, ConfigSet, Configure, Errors
  44. from waflib.Build import BuildContext
  45. import os, sys, random, time
  46. HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)'
  47. MAP_EXT = {
  48. '': "folder",
  49. '.h' : "sourcecode.c.h",
  50. '.hh': "sourcecode.cpp.h",
  51. '.inl': "sourcecode.cpp.h",
  52. '.hpp': "sourcecode.cpp.h",
  53. '.c': "sourcecode.c.c",
  54. '.m': "sourcecode.c.objc",
  55. '.mm': "sourcecode.cpp.objcpp",
  56. '.cc': "sourcecode.cpp.cpp",
  57. '.cpp': "sourcecode.cpp.cpp",
  58. '.C': "sourcecode.cpp.cpp",
  59. '.cxx': "sourcecode.cpp.cpp",
  60. '.c++': "sourcecode.cpp.cpp",
  61. '.l': "sourcecode.lex", # luthor
  62. '.ll': "sourcecode.lex",
  63. '.y': "sourcecode.yacc",
  64. '.yy': "sourcecode.yacc",
  65. '.plist': "text.plist.xml",
  66. ".nib": "wrapper.nib",
  67. ".xib": "text.xib",
  68. }
  69. # Used in PBXNativeTarget elements
  70. PRODUCT_TYPE_APPLICATION = 'com.apple.product-type.application'
  71. PRODUCT_TYPE_FRAMEWORK = 'com.apple.product-type.framework'
  72. PRODUCT_TYPE_EXECUTABLE = 'com.apple.product-type.tool'
  73. PRODUCT_TYPE_LIB_STATIC = 'com.apple.product-type.library.static'
  74. PRODUCT_TYPE_LIB_DYNAMIC = 'com.apple.product-type.library.dynamic'
  75. PRODUCT_TYPE_EXTENSION = 'com.apple.product-type.kernel-extension'
  76. PRODUCT_TYPE_IOKIT = 'com.apple.product-type.kernel-extension.iokit'
  77. # Used in PBXFileReference elements
  78. FILE_TYPE_APPLICATION = 'wrapper.cfbundle'
  79. FILE_TYPE_FRAMEWORK = 'wrapper.framework'
  80. FILE_TYPE_LIB_DYNAMIC = 'compiled.mach-o.dylib'
  81. FILE_TYPE_LIB_STATIC = 'archive.ar'
  82. FILE_TYPE_EXECUTABLE = 'compiled.mach-o.executable'
  83. # Tuple packs of the above
  84. TARGET_TYPE_FRAMEWORK = (PRODUCT_TYPE_FRAMEWORK, FILE_TYPE_FRAMEWORK, '.framework')
  85. TARGET_TYPE_APPLICATION = (PRODUCT_TYPE_APPLICATION, FILE_TYPE_APPLICATION, '.app')
  86. TARGET_TYPE_DYNAMIC_LIB = (PRODUCT_TYPE_LIB_DYNAMIC, FILE_TYPE_LIB_DYNAMIC, '.dylib')
  87. TARGET_TYPE_STATIC_LIB = (PRODUCT_TYPE_LIB_STATIC, FILE_TYPE_LIB_STATIC, '.a')
  88. TARGET_TYPE_EXECUTABLE = (PRODUCT_TYPE_EXECUTABLE, FILE_TYPE_EXECUTABLE, '')
  89. # Maps target type string to its data
  90. TARGET_TYPES = {
  91. 'framework': TARGET_TYPE_FRAMEWORK,
  92. 'app': TARGET_TYPE_APPLICATION,
  93. 'dylib': TARGET_TYPE_DYNAMIC_LIB,
  94. 'stlib': TARGET_TYPE_STATIC_LIB,
  95. 'exe' :TARGET_TYPE_EXECUTABLE,
  96. }
  97. """
  98. Configuration of the global project settings. Sets an environment variable 'PROJ_CONFIGURATION'
  99. which is a dictionary of configuration name and buildsettings pair.
  100. E.g.:
  101. env.PROJ_CONFIGURATION = {
  102. 'Debug': {
  103. 'ARCHS': 'x86',
  104. ...
  105. }
  106. 'Release': {
  107. 'ARCHS' x86_64'
  108. ...
  109. }
  110. }
  111. The user can define a completely customized dictionary in configure() stage. Otherwise a default Debug/Release will be created
  112. based on env variable
  113. """
  114. def configure(self):
  115. if not self.env.PROJ_CONFIGURATION:
  116. self.to_log("A default project configuration was created since no custom one was given in the configure(conf) stage. Define your custom project settings by adding PROJ_CONFIGURATION to env. The env.PROJ_CONFIGURATION must be a dictionary with at least one key, where each key is the configuration name, and the value is a dictionary of key/value settings.\n")
  117. # Check for any added config files added by the tool 'c_config'.
  118. if 'cfg_files' in self.env:
  119. self.env.INCLUDES = Utils.to_list(self.env.INCLUDES) + [os.path.abspath(os.path.dirname(f)) for f in self.env.cfg_files]
  120. # Create default project configuration?
  121. if 'PROJ_CONFIGURATION' not in self.env:
  122. self.env.PROJ_CONFIGURATION = {
  123. "Debug": self.env.get_merged_dict(),
  124. "Release": self.env.get_merged_dict(),
  125. }
  126. # Some build settings are required to be present by XCode. We will supply default values
  127. # if user hasn't defined any.
  128. defaults_required = [('PRODUCT_NAME', '$(TARGET_NAME)')]
  129. for cfgname,settings in self.env.PROJ_CONFIGURATION.iteritems():
  130. for default_var, default_val in defaults_required:
  131. if default_var not in settings:
  132. settings[default_var] = default_val
  133. # Error check customization
  134. if not isinstance(self.env.PROJ_CONFIGURATION, dict):
  135. raise Errors.ConfigurationError("The env.PROJ_CONFIGURATION must be a dictionary with at least one key, where each key is the configuration name, and the value is a dictionary of key/value settings.")
  136. part1 = 0
  137. part2 = 10000
  138. part3 = 0
  139. id = 562000999
  140. def newid():
  141. global id
  142. id = id + 1
  143. return "%04X%04X%04X%012d" % (0, 10000, 0, id)
  144. class XCodeNode:
  145. def __init__(self):
  146. self._id = newid()
  147. self._been_written = False
  148. def tostring(self, value):
  149. if isinstance(value, dict):
  150. result = "{\n"
  151. for k,v in value.items():
  152. result = result + "\t\t\t%s = %s;\n" % (k, self.tostring(v))
  153. result = result + "\t\t}"
  154. return result
  155. elif isinstance(value, str):
  156. return "\"%s\"" % value
  157. elif isinstance(value, list):
  158. result = "(\n"
  159. for i in value:
  160. result = result + "\t\t\t%s,\n" % self.tostring(i)
  161. result = result + "\t\t)"
  162. return result
  163. elif isinstance(value, XCodeNode):
  164. return value._id
  165. else:
  166. return str(value)
  167. def write_recursive(self, value, file):
  168. if isinstance(value, dict):
  169. for k,v in value.items():
  170. self.write_recursive(v, file)
  171. elif isinstance(value, list):
  172. for i in value:
  173. self.write_recursive(i, file)
  174. elif isinstance(value, XCodeNode):
  175. value.write(file)
  176. def write(self, file):
  177. if not self._been_written:
  178. self._been_written = True
  179. for attribute,value in self.__dict__.items():
  180. if attribute[0] != '_':
  181. self.write_recursive(value, file)
  182. w = file.write
  183. w("\t%s = {\n" % self._id)
  184. w("\t\tisa = %s;\n" % self.__class__.__name__)
  185. for attribute,value in self.__dict__.items():
  186. if attribute[0] != '_':
  187. w("\t\t%s = %s;\n" % (attribute, self.tostring(value)))
  188. w("\t};\n\n")
  189. # Configurations
  190. class XCBuildConfiguration(XCodeNode):
  191. def __init__(self, name, settings = {}, env=None):
  192. XCodeNode.__init__(self)
  193. self.baseConfigurationReference = ""
  194. self.buildSettings = settings
  195. self.name = name
  196. if env and env.ARCH:
  197. settings['ARCHS'] = " ".join(env.ARCH)
  198. class XCConfigurationList(XCodeNode):
  199. def __init__(self, configlst):
  200. """ :param configlst: list of XCConfigurationList """
  201. XCodeNode.__init__(self)
  202. self.buildConfigurations = configlst
  203. self.defaultConfigurationIsVisible = 0
  204. self.defaultConfigurationName = configlst and configlst[0].name or ""
  205. # Group/Files
  206. class PBXFileReference(XCodeNode):
  207. def __init__(self, name, path, filetype = '', sourcetree = "SOURCE_ROOT"):
  208. XCodeNode.__init__(self)
  209. self.fileEncoding = 4
  210. if not filetype:
  211. _, ext = os.path.splitext(name)
  212. filetype = MAP_EXT.get(ext, 'text')
  213. self.lastKnownFileType = filetype
  214. self.name = name
  215. self.path = path
  216. self.sourceTree = sourcetree
  217. def __hash__(self):
  218. return (self.path+self.name).__hash__()
  219. def __eq__(self, other):
  220. return (self.path, self.name) == (other.path, other.name)
  221. class PBXBuildFile(XCodeNode):
  222. """ This element indicate a file reference that is used in a PBXBuildPhase (either as an include or resource). """
  223. def __init__(self, fileRef, settings={}):
  224. XCodeNode.__init__(self)
  225. # fileRef is a reference to a PBXFileReference object
  226. self.fileRef = fileRef
  227. # A map of key/value pairs for additionnal settings.
  228. self.settings = settings
  229. def __hash__(self):
  230. return (self.fileRef).__hash__()
  231. def __eq__(self, other):
  232. return self.fileRef == other.fileRef
  233. class PBXGroup(XCodeNode):
  234. def __init__(self, name, sourcetree = "<group>"):
  235. XCodeNode.__init__(self)
  236. self.children = []
  237. self.name = name
  238. self.sourceTree = sourcetree
  239. def add(self, sources):
  240. """ sources param should be a list of PBXFileReference objects """
  241. self.children.extend(sources)
  242. class PBXContainerItemProxy(XCodeNode):
  243. """ This is the element for to decorate a target item. """
  244. def __init__(self, containerPortal, remoteGlobalIDString, remoteInfo='', proxyType=1):
  245. XCodeNode.__init__(self)
  246. self.containerPortal = containerPortal # PBXProject
  247. self.remoteGlobalIDString = remoteGlobalIDString # PBXNativeTarget
  248. self.remoteInfo = remoteInfo # Target name
  249. self.proxyType = proxyType
  250. class PBXTargetDependency(XCodeNode):
  251. """ This is the element for referencing other target through content proxies. """
  252. def __init__(self, native_target, proxy):
  253. XCodeNode.__init__(self)
  254. self.target = native_target
  255. self.targetProxy = proxy
  256. class PBXFrameworksBuildPhase(XCodeNode):
  257. """ This is the element for the framework link build phase, i.e. linking to frameworks """
  258. def __init__(self, pbxbuildfiles):
  259. XCodeNode.__init__(self)
  260. self.buildActionMask = 2147483647
  261. self.runOnlyForDeploymentPostprocessing = 0
  262. self.files = pbxbuildfiles #List of PBXBuildFile (.o, .framework, .dylib)
  263. class PBXHeadersBuildPhase(XCodeNode):
  264. """ This is the element for adding header files to be packaged into the .framework """
  265. def __init__(self, pbxbuildfiles):
  266. XCodeNode.__init__(self)
  267. self.buildActionMask = 2147483647
  268. self.runOnlyForDeploymentPostprocessing = 0
  269. self.files = pbxbuildfiles #List of PBXBuildFile (.o, .framework, .dylib)
  270. class PBXCopyFilesBuildPhase(XCodeNode):
  271. """
  272. Represents the PBXCopyFilesBuildPhase section. PBXBuildFile
  273. can be added to this node to copy files after build is done.
  274. """
  275. def __init__(self, pbxbuildfiles, dstpath, dstSubpathSpec=0, *args, **kwargs):
  276. XCodeNode.__init__(self)
  277. self.files = pbxbuildfiles
  278. self.dstPath = dstpath
  279. self.dstSubfolderSpec = dstSubpathSpec
  280. class PBXSourcesBuildPhase(XCodeNode):
  281. """ Represents the 'Compile Sources' build phase in a Xcode target """
  282. def __init__(self, buildfiles):
  283. XCodeNode.__init__(self)
  284. self.files = buildfiles # List of PBXBuildFile objects
  285. class PBXLegacyTarget(XCodeNode):
  286. def __init__(self, action, target=''):
  287. XCodeNode.__init__(self)
  288. self.buildConfigurationList = XCConfigurationList([XCBuildConfiguration('waf', {})])
  289. if not target:
  290. self.buildArgumentsString = "%s %s" % (sys.argv[0], action)
  291. else:
  292. self.buildArgumentsString = "%s %s --targets=%s" % (sys.argv[0], action, target)
  293. self.buildPhases = []
  294. self.buildToolPath = sys.executable
  295. self.buildWorkingDirectory = ""
  296. self.dependencies = []
  297. self.name = target or action
  298. self.productName = target or action
  299. self.passBuildSettingsInEnvironment = 0
  300. class PBXShellScriptBuildPhase(XCodeNode):
  301. def __init__(self, action, target):
  302. XCodeNode.__init__(self)
  303. self.buildActionMask = 2147483647
  304. self.files = []
  305. self.inputPaths = []
  306. self.outputPaths = []
  307. self.runOnlyForDeploymentPostProcessing = 0
  308. self.shellPath = "/bin/sh"
  309. self.shellScript = "%s %s %s --targets=%s" % (sys.executable, sys.argv[0], action, target)
  310. class PBXNativeTarget(XCodeNode):
  311. """ Represents a target in XCode, e.g. App, DyLib, Framework etc. """
  312. def __init__(self, target, node, target_type=TARGET_TYPE_APPLICATION, configlist=[], buildphases=[]):
  313. XCodeNode.__init__(self)
  314. product_type = target_type[0]
  315. file_type = target_type[1]
  316. self.buildConfigurationList = XCConfigurationList(configlist)
  317. self.buildPhases = buildphases
  318. self.buildRules = []
  319. self.dependencies = []
  320. self.name = target
  321. self.productName = target
  322. self.productType = product_type # See TARGET_TYPE_ tuples constants
  323. self.productReference = PBXFileReference(node.name, node.abspath(), file_type, '')
  324. def add_configuration(self, cf):
  325. """ :type cf: XCBuildConfiguration """
  326. self.buildConfigurationList.buildConfigurations.append(cf)
  327. def add_build_phase(self, phase):
  328. # Some build phase types may appear only once. If a phase type already exists, then merge them.
  329. if ( (phase.__class__ == PBXFrameworksBuildPhase)
  330. or (phase.__class__ == PBXSourcesBuildPhase) ):
  331. for b in self.buildPhases:
  332. if b.__class__ == phase.__class__:
  333. b.files.extend(phase.files)
  334. return
  335. self.buildPhases.append(phase)
  336. def add_dependency(self, depnd):
  337. self.dependencies.append(depnd)
  338. # Root project object
  339. class PBXProject(XCodeNode):
  340. def __init__(self, name, version, env):
  341. XCodeNode.__init__(self)
  342. if not isinstance(env.PROJ_CONFIGURATION, dict):
  343. raise Errors.WafError("Error: env.PROJ_CONFIGURATION must be a dictionary. This is done for you if you do not define one yourself. However, did you load the xcode module at the end of your wscript configure() ?")
  344. # Retreive project configuration
  345. configurations = []
  346. for config_name, settings in env.PROJ_CONFIGURATION.items():
  347. cf = XCBuildConfiguration(config_name, settings)
  348. configurations.append(cf)
  349. self.buildConfigurationList = XCConfigurationList(configurations)
  350. self.compatibilityVersion = version[0]
  351. self.hasScannedForEncodings = 1;
  352. self.mainGroup = PBXGroup(name)
  353. self.projectRoot = ""
  354. self.projectDirPath = ""
  355. self.targets = []
  356. self._objectVersion = version[1]
  357. def create_target_dependency(self, target, name):
  358. """ : param target : PXBNativeTarget """
  359. proxy = PBXContainerItemProxy(self, target, name)
  360. dependecy = PBXTargetDependency(target, proxy)
  361. return dependecy
  362. def write(self, file):
  363. # Make sure this is written only once
  364. if self._been_written:
  365. return
  366. w = file.write
  367. w("// !$*UTF8*$!\n")
  368. w("{\n")
  369. w("\tarchiveVersion = 1;\n")
  370. w("\tclasses = {\n")
  371. w("\t};\n")
  372. w("\tobjectVersion = %d;\n" % self._objectVersion)
  373. w("\tobjects = {\n\n")
  374. XCodeNode.write(self, file)
  375. w("\t};\n")
  376. w("\trootObject = %s;\n" % self._id)
  377. w("}\n")
  378. def add_target(self, target):
  379. self.targets.append(target)
  380. def get_target(self, name):
  381. """ Get a reference to PBXNativeTarget if it exists """
  382. for t in self.targets:
  383. if t.name == name:
  384. return t
  385. return None
  386. class xcode(Build.BuildContext):
  387. cmd = 'xcode6'
  388. fun = 'build'
  389. file_refs = dict()
  390. build_files = dict()
  391. def as_nodes(self, files):
  392. """ Returns a list of waflib.Nodes from a list of string of file paths """
  393. nodes = []
  394. for x in files:
  395. if not isinstance(x, str):
  396. d = x
  397. else:
  398. d = self.srcnode.find_node(x)
  399. nodes.append(d)
  400. return nodes
  401. def create_group(self, name, files):
  402. """
  403. Returns a new PBXGroup containing the files (paths) passed in the files arg
  404. :type files: string
  405. """
  406. group = PBXGroup(name)
  407. """
  408. Do not use unique file reference here, since XCode seem to allow only one file reference
  409. to be referenced by a group.
  410. """
  411. files = [(PBXFileReference(d.name, d.abspath())) for d in self.as_nodes(files)]
  412. group.add(files)
  413. return group
  414. def unique_filereference(self, fileref):
  415. """
  416. Returns a unique fileref, possibly an existing one if the paths are the same.
  417. Use this after you've constructed a PBXFileReference to make sure there is
  418. only one PBXFileReference for the same file in the same project.
  419. """
  420. if fileref not in self.file_refs:
  421. self.file_refs[fileref] = fileref
  422. return self.file_refs[fileref]
  423. def unique_buildfile(self, buildfile):
  424. """
  425. Returns a unique buildfile, possibly an existing one.
  426. Use this after you've constructed a PBXBuildFile to make sure there is
  427. only one PBXBuildFile for the same file in the same project.
  428. """
  429. if buildfile not in self.build_files:
  430. self.build_files[buildfile] = buildfile
  431. return self.build_files[buildfile]
  432. def execute(self):
  433. """
  434. Entry point
  435. """
  436. self.restore()
  437. if not self.all_envs:
  438. self.load_envs()
  439. self.recurse([self.run_dir])
  440. appname = getattr(Context.g_module, Context.APPNAME, os.path.basename(self.srcnode.abspath()))
  441. p = PBXProject(appname, ('Xcode 3.2', 46), self.env)
  442. # If we don't create a Products group, then
  443. # XCode will create one, which entails that
  444. # we'll start to see duplicate files in the UI
  445. # for some reason.
  446. products_group = PBXGroup('Products')
  447. p.mainGroup.children.append(products_group)
  448. for g in self.groups:
  449. for tg in g:
  450. if not isinstance(tg, TaskGen.task_gen):
  451. continue
  452. tg.post()
  453. target_group = PBXGroup(tg.name)
  454. p.mainGroup.children.append(target_group)
  455. # Determine what type to build - framework, app bundle etc.
  456. target_type = getattr(tg, 'target_type', 'app')
  457. if target_type not in TARGET_TYPES:
  458. raise Errors.WafError("Target type '%s' does not exists. Available options are '%s'. In target '%s'" % (target_type, "', '".join(TARGET_TYPES.keys()), tg.name))
  459. else:
  460. target_type = TARGET_TYPES[target_type]
  461. file_ext = target_type[2]
  462. # Create the output node
  463. target_node = tg.path.find_or_declare(tg.name+file_ext)
  464. target = PBXNativeTarget(tg.name, target_node, target_type, [], [])
  465. products_group.children.append(target.productReference)
  466. if hasattr(tg, 'source_files'):
  467. # Create list of PBXFileReferences
  468. sources = []
  469. if isinstance(tg.source_files, dict):
  470. for grpname,files in tg.source_files.items():
  471. group = self.create_group(grpname, files)
  472. target_group.children.append(group)
  473. sources.extend(group.children)
  474. elif isinstance(tg.source_files, list):
  475. group = self.create_group("Source", tg.source_files)
  476. target_group.children.append(group)
  477. sources.extend(group.children)
  478. else:
  479. self.to_log("Argument 'source_files' passed to target '%s' was not a dictionary. Hence, some source files may not be included. Please provide a dictionary of source files, with group name as key and list of source files as value.\n" % tg.name)
  480. supported_extensions = ['.c', '.cpp', '.m', '.mm']
  481. sources = filter(lambda fileref: os.path.splitext(fileref.path)[1] in supported_extensions, sources)
  482. buildfiles = [self.unique_buildfile(PBXBuildFile(fileref)) for fileref in sources]
  483. target.add_build_phase(PBXSourcesBuildPhase(buildfiles))
  484. # Create build settings which can override the project settings. Defaults to none if user
  485. # did not pass argument. However, this will be filled up further below with target specfic
  486. # search paths, libs to link etc.
  487. settings = getattr(tg, 'settings', {})
  488. # Check if any framework to link against is some other target we've made
  489. libs = getattr(tg, 'tmp_use_seen', [])
  490. for lib in libs:
  491. use_target = p.get_target(lib)
  492. if use_target:
  493. # Create an XCode dependency so that XCode knows to build the other target before this target
  494. target.add_dependency(p.create_target_dependency(use_target, use_target.name))
  495. target.add_build_phase(PBXFrameworksBuildPhase([PBXBuildFile(use_target.productReference)]))
  496. if lib in tg.env.LIB:
  497. tg.env.LIB = list(filter(lambda x: x != lib, tg.env.LIB))
  498. # If 'export_headers' is present, add files to the Headers build phase in xcode.
  499. # These are files that'll get packed into the Framework for instance.
  500. exp_hdrs = getattr(tg, 'export_headers', [])
  501. hdrs = self.as_nodes(Utils.to_list(exp_hdrs))
  502. files = [self.unique_filereference(PBXFileReference(n.name, n.abspath())) for n in hdrs]
  503. target.add_build_phase(PBXHeadersBuildPhase([PBXBuildFile(f, {'ATTRIBUTES': ('Public',)}) for f in files]))
  504. # Install path
  505. installpaths = Utils.to_list(getattr(tg, 'install', []))
  506. prodbuildfile = PBXBuildFile(target.productReference)
  507. for instpath in installpaths:
  508. target.add_build_phase(PBXCopyFilesBuildPhase([prodbuildfile], instpath))
  509. # Merge frameworks and libs into one list, and prefix the frameworks
  510. ld_flags = ['-framework %s' % lib.split('.framework')[0] for lib in Utils.to_list(tg.env.FRAMEWORK)]
  511. ld_flags.extend(Utils.to_list(tg.env.STLIB) + Utils.to_list(tg.env.LIB))
  512. # Override target specfic build settings
  513. bldsettings = {
  514. 'HEADER_SEARCH_PATHS': ['$(inherited)'] + tg.env['INCPATHS'],
  515. 'LIBRARY_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(tg.env.LIBPATH) + Utils.to_list(tg.env.STLIBPATH),
  516. 'FRAMEWORK_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(tg.env.FRAMEWORKPATH),
  517. 'OTHER_LDFLAGS': r'\n'.join(ld_flags)
  518. }
  519. # The keys represents different build configuration, e.g. Debug, Release and so on..
  520. # Insert our generated build settings to all configuration names
  521. keys = set(settings.keys() + self.env.PROJ_CONFIGURATION.keys())
  522. for k in keys:
  523. if k in settings:
  524. settings[k].update(bldsettings)
  525. else:
  526. settings[k] = bldsettings
  527. for k,v in settings.items():
  528. target.add_configuration(XCBuildConfiguration(k, v))
  529. p.add_target(target)
  530. node = self.bldnode.make_node('%s.xcodeproj' % appname)
  531. node.mkdir()
  532. node = node.make_node('project.pbxproj')
  533. p.write(open(node.abspath(), 'w'))
  534. def build_target(self, tgtype, *k, **kw):
  535. """
  536. Provide user-friendly methods to build different target types
  537. E.g. bld.framework(source='..', ...) to build a Framework target.
  538. E.g. bld.dylib(source='..', ...) to build a Dynamic library target. etc...
  539. """
  540. self.load('ccroot')
  541. kw['features'] = 'cxx cxxprogram'
  542. kw['target_type'] = tgtype
  543. return self(*k, **kw)
  544. def app(self, *k, **kw): return self.build_target('app', *k, **kw)
  545. def framework(self, *k, **kw): return self.build_target('framework', *k, **kw)
  546. def dylib(self, *k, **kw): return self.build_target('dylib', *k, **kw)
  547. def stlib(self, *k, **kw): return self.build_target('stlib', *k, **kw)
  548. def exe(self, *k, **kw): return self.build_target('exe', *k, **kw)