xcode_project.lua 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. ---
  2. -- xcode/xcode4_project.lua
  3. -- Generate an Xcode project file.
  4. -- Author Jason Perkins
  5. -- Modified by Mihai Sebea
  6. -- Copyright (c) 2009-2015 Jason Perkins and the Premake project
  7. ---
  8. local p = premake
  9. local m = p.modules.xcode
  10. local xcode = p.modules.xcode
  11. local project = p.project
  12. local config = p.config
  13. local fileconfig = p.fileconfig
  14. local tree = p.tree
  15. --
  16. -- Checks if a node must be excluded completely from a target or not. It will
  17. -- return true only if the node has the "ExcludeFromBuild" flag in all the
  18. -- configurations.
  19. --
  20. -- @param node
  21. -- The node to check.
  22. -- @param prj
  23. -- The project being generated.
  24. -- @returns
  25. -- A boolean, telling whether the node must be excluded from its target or not.
  26. --
  27. function xcode.mustExcludeFromTarget(node, prj)
  28. if not node.configs then
  29. return false
  30. end
  31. local value
  32. for cfg in premake.project.eachconfig(prj) do
  33. local filecfg = premake.fileconfig.getconfig(node, cfg)
  34. if filecfg then
  35. local newValue = not not filecfg.flags.ExcludeFromBuild
  36. if value == nil then
  37. value = newValue
  38. elseif value ~= newValue then
  39. p.warn(node.name .. " is excluded in just some configurations. Autocompletion will not work correctly on this file in Xcode.")
  40. return false
  41. end
  42. end
  43. end
  44. return value
  45. end
  46. --
  47. -- Create a tree corresponding to what is shown in the Xcode project browser
  48. -- pane, with nodes for files and folders, resources, frameworks, and products.
  49. --
  50. -- @param prj
  51. -- The project being generated.
  52. -- @returns
  53. -- A tree, loaded with metadata, which mirrors Xcode's view of the project.
  54. --
  55. function xcode.buildprjtree(prj)
  56. local tr = project.getsourcetree(prj, nil , false)
  57. tr.project = prj
  58. -- create a list of build configurations and assign IDs
  59. tr.configs = {}
  60. for cfg in project.eachconfig(prj) do
  61. cfg.xcode = {}
  62. cfg.xcode.targetid = xcode.newid(prj.xcode.projectnode.name, cfg.buildcfg, "target")
  63. cfg.xcode.projectid = xcode.newid(tr.name, cfg.buildcfg)
  64. table.insert(tr.configs, cfg)
  65. end
  66. -- convert localized resources from their filesystem layout (English.lproj/MainMenu.xib)
  67. -- to Xcode's display layout (MainMenu.xib/English).
  68. tree.traverse(tr, {
  69. onbranch = function(node)
  70. if path.getextension(node.name) == ".lproj" then
  71. local lang = path.getbasename(node.name) -- "English", "French", etc.
  72. -- create a new language group for each file it contains
  73. for _, filenode in ipairs(node.children) do
  74. local grpnode = node.parent.children[filenode.name]
  75. if not grpnode then
  76. grpnode = tree.insert(node.parent, tree.new(filenode.name))
  77. grpnode.kind = "vgroup"
  78. end
  79. -- convert the file node to a language node and add to the group
  80. filenode.name = path.getbasename(lang)
  81. tree.insert(grpnode, filenode)
  82. end
  83. -- remove this directory from the tree
  84. tree.remove(node)
  85. end
  86. end
  87. })
  88. -- the special folder "Frameworks" lists all linked frameworks
  89. tr.frameworks = tree.new("Frameworks")
  90. for cfg in project.eachconfig(prj) do
  91. for _, link in ipairs(config.getlinks(cfg, "system", "fullpath")) do
  92. local name = path.getname(link)
  93. if xcode.isframeworkordylib(name) and not tr.frameworks.children[name] then
  94. node = tree.insert(tr.frameworks, tree.new(name))
  95. node.path = link
  96. end
  97. end
  98. end
  99. -- only add it to the tree if there are frameworks to link
  100. if #tr.frameworks.children > 0 then
  101. tree.insert(tr, tr.frameworks)
  102. end
  103. -- the special folder "Products" holds the target produced by the project; this
  104. -- is populated below
  105. tr.products = tree.insert(tr, tree.new("Products"))
  106. -- the special folder "Projects" lists sibling project dependencies
  107. tr.projects = tree.new("Projects")
  108. for _, dep in ipairs(project.getdependencies(prj, "linkOnly")) do
  109. xcode.addDependency(prj, tr, dep, true)
  110. end
  111. for _, dep in ipairs(project.getdependencies(prj, "dependOnly")) do
  112. xcode.addDependency(prj, tr, dep, false)
  113. end
  114. if #tr.projects.children > 0 then
  115. tree.insert(tr, tr.projects)
  116. end
  117. -- Final setup
  118. tree.traverse(tr, {
  119. onnode = function(node)
  120. local nodePath
  121. if node.path then
  122. nodePath = path.getrelative(tr.project.location, node.path)
  123. end
  124. -- assign IDs to every node in the tree
  125. node.id = xcode.newid(node.name, nil, nodePath)
  126. node.isResource = xcode.isItemResource(prj, node)
  127. -- check to see if this file has custom build
  128. if node.configs then
  129. for cfg in project.eachconfig(prj) do
  130. local filecfg = fileconfig.getconfig(node, cfg)
  131. if fileconfig.hasCustomBuildRule(filecfg) then
  132. if not node.buildcommandid then
  133. node.buildcommandid = xcode.newid(node.name, "buildcommand", nodePath)
  134. end
  135. end
  136. end
  137. end
  138. -- assign build IDs to buildable files
  139. if xcode.getbuildcategory(node) and not node.excludefrombuild and not xcode.mustExcludeFromTarget(node, tr.project) then
  140. node.buildid = xcode.newid(node.name, "build", nodePath)
  141. if xcode.shouldembed(tr, node) then
  142. node.embedid = xcode.newid(node.name, "embed", nodepath)
  143. end
  144. end
  145. -- remember key files that are needed elsewhere
  146. if string.endswith(node.name, "Info.plist") then
  147. tr.infoplist = node
  148. end
  149. end
  150. }, true)
  151. -- Plug in the product node into the Products folder in the tree. The node
  152. -- was built in xcode.prepareWorkspace() in xcode_common.lua; it contains IDs
  153. -- that are necessary for inter-project dependencies
  154. node = tree.insert(tr.products, prj.xcode.projectnode)
  155. node.kind = "product"
  156. node.path = node.cfg.buildtarget.fullpath
  157. node.cfgsection = xcode.newid(node.name, "cfg")
  158. node.resstageid = xcode.newid(node.name, "rez")
  159. node.sourcesid = xcode.newid(node.name, "src")
  160. node.fxstageid = xcode.newid(node.name, "fxs")
  161. node.embedstageid = xcode.newid(node.name, "embed")
  162. return tr
  163. end
  164. function xcode.addDependency(prj, tr, dep, build)
  165. -- create a child node for the dependency's xcodeproj
  166. local xcpath = xcode.getxcodeprojname(dep)
  167. local xcnode = tree.insert(tr.projects, tree.new(path.getname(xcpath)))
  168. xcnode.path = xcpath
  169. xcnode.project = dep
  170. xcnode.productgroupid = xcode.newid(xcnode.name, "prodgrp")
  171. xcnode.productproxyid = xcode.newid(xcnode.name, "prodprox")
  172. xcnode.targetproxyid = xcode.newid(xcnode.name, "targprox")
  173. xcnode.targetdependid = xcode.newid(xcnode.name, "targdep")
  174. -- create a grandchild node for the dependency's link target
  175. local lprj = p.workspace.findproject(prj.workspace, dep.name)
  176. local cfg = project.findClosestMatch(lprj, prj.configurations[1])
  177. node = tree.insert(xcnode, tree.new(cfg.linktarget.name))
  178. node.path = cfg.linktarget.fullpath
  179. node.cfg = cfg
  180. -- don't link the dependency if it's a dependency only
  181. if build == false then
  182. node.excludefrombuild = true
  183. end
  184. end
  185. ---
  186. -- Generate an Xcode .xcodeproj for a Premake project.
  187. ---
  188. m.elements.project = function(prj)
  189. return {
  190. m.header,
  191. }
  192. end
  193. function m.generateProject(prj)
  194. local tr = xcode.buildprjtree(prj)
  195. p.callArray(m.elements.project, prj)
  196. xcode.PBXBuildFile(tr)
  197. xcode.PBXContainerItemProxy(tr)
  198. xcode.PBXFileReference(tr)
  199. xcode.PBXFrameworksBuildPhase(tr)
  200. xcode.PBXCopyFilesBuildPhaseForEmbedFrameworks(tr)
  201. xcode.PBXGroup(tr)
  202. xcode.PBXNativeTarget(tr)
  203. xcode.PBXAggregateTarget(tr)
  204. xcode.PBXProject(tr)
  205. xcode.PBXReferenceProxy(tr)
  206. xcode.PBXResourcesBuildPhase(tr)
  207. xcode.PBXShellScriptBuildPhase(tr)
  208. xcode.PBXSourcesBuildPhase(tr)
  209. xcode.PBXTargetDependency(tr)
  210. xcode.PBXVariantGroup(tr)
  211. xcode.XCBuildConfiguration(tr)
  212. xcode.XCBuildConfigurationList(tr)
  213. xcode.footer(prj)
  214. end
  215. function m.header(prj)
  216. p.w('// !$*UTF8*$!')
  217. p.push('{')
  218. p.w('archiveVersion = 1;')
  219. p.w('classes = {')
  220. p.w('};')
  221. p.w('objectVersion = 46;')
  222. p.push('objects = {')
  223. p.w()
  224. end
  225. function xcode.footer(prj)
  226. p.pop('};')
  227. p.w('rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;')
  228. p.pop('}')
  229. end