xcode_common.lua 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689
  1. --
  2. -- xcode_common.lua
  3. -- Functions to generate the different sections of an Xcode project.
  4. -- Copyright (c) 2009-2015 Jason Perkins and the Premake project
  5. --
  6. local p = premake
  7. local xcode = p.modules.xcode
  8. local tree = p.tree
  9. local workspace = p.workspace
  10. local project = p.project
  11. local config = p.config
  12. local fileconfig = p.fileconfig
  13. --
  14. -- Return the Xcode build category for a given file, based on the file extension.
  15. --
  16. -- @param node
  17. -- The node to identify.
  18. -- @returns
  19. -- An Xcode build category, one of "Sources", "Resources", "Frameworks", or nil.
  20. --
  21. function xcode.getbuildcategory(node)
  22. local categories = {
  23. [".a"] = "Frameworks",
  24. [".c"] = "Sources",
  25. [".cc"] = "Sources",
  26. [".cpp"] = "Sources",
  27. [".cxx"] = "Sources",
  28. [".c++"] = "Sources",
  29. [".dylib"] = "Frameworks",
  30. [".framework"] = "Frameworks",
  31. [".m"] = "Sources",
  32. [".mm"] = "Sources",
  33. [".strings"] = "Resources",
  34. [".nib"] = "Resources",
  35. [".xib"] = "Resources",
  36. [".storyboard"] = "Resources",
  37. [".icns"] = "Resources",
  38. [".s"] = "Sources",
  39. [".S"] = "Sources",
  40. [".swift"] = "Sources",
  41. [".metal"] = "Resources",
  42. }
  43. if node.isResource then
  44. return "Resources"
  45. elseif node.cfg and (node.cfg.kind == p.SHAREDLIB or node.cfg.kind == p.STATICLIB) then
  46. return "Frameworks"
  47. end
  48. return categories[path.getextension(node.name)]
  49. end
  50. function xcode.isItemResource(project, node)
  51. local res
  52. if project and project.xcodebuildresources then
  53. if type(project.xcodebuildresources) == "table" then
  54. res = project.xcodebuildresources
  55. end
  56. end
  57. local function checkItemInList(item, list)
  58. if item then
  59. for _,v in pairs(list) do
  60. if string.find(item, path.wildcards(v)) then
  61. return true
  62. end
  63. end
  64. end
  65. return false
  66. end
  67. if (checkItemInList(node.path, res)) then
  68. return true
  69. end
  70. return false
  71. end
  72. --
  73. -- Return 'explicitFileType' if the given file is being set with 'compileas'
  74. --
  75. function xcode.getfiletypekey(node, cfg)
  76. if node.configs then
  77. local filecfg = fileconfig.getconfig(node, cfg)
  78. if filecfg and filecfg["compileas"] then
  79. return "explicitFileType"
  80. end
  81. end
  82. return "lastKnownFileType"
  83. end
  84. --
  85. -- Return the Xcode type for a given file, based on the file extension.
  86. --
  87. -- @param fname
  88. -- The file name to identify.
  89. -- @returns
  90. -- An Xcode file type, string.
  91. --
  92. function xcode.getfiletype(node, cfg)
  93. if node.configs then
  94. local filecfg = fileconfig.getconfig(node, cfg)
  95. if filecfg then
  96. if p.languages.isc(filecfg.compileas) then
  97. return "sourcecode.c.c"
  98. elseif p.languages.iscpp(filecfg.compileas) then
  99. return "sourcecode.cpp.cpp"
  100. elseif filecfg.compileas == p.OBJECTIVEC then
  101. return "sourcecode.c.objc"
  102. elseif filecfg.compileas == p.OBJECTIVECPP then
  103. return "sourcecode.cpp.objcpp"
  104. end
  105. end
  106. end
  107. local types = {
  108. [".c"] = "sourcecode.c.c",
  109. [".cc"] = "sourcecode.cpp.cpp",
  110. [".cpp"] = "sourcecode.cpp.cpp",
  111. [".css"] = "text.css",
  112. [".cxx"] = "sourcecode.cpp.cpp",
  113. [".c++"] = "sourcecode.cpp.cpp",
  114. [".S"] = "sourcecode.asm.asm",
  115. [".framework"] = "wrapper.framework",
  116. [".gif"] = "image.gif",
  117. [".h"] = "sourcecode.c.h",
  118. [".html"] = "text.html",
  119. [".lua"] = "sourcecode.lua",
  120. [".m"] = "sourcecode.c.objc",
  121. [".mm"] = "sourcecode.cpp.objc",
  122. [".nib"] = "wrapper.nib",
  123. [".storyboard"] = "file.storyboard",
  124. [".pch"] = "sourcecode.c.h",
  125. [".plist"] = "text.plist.xml",
  126. [".strings"] = "text.plist.strings",
  127. [".xib"] = "file.xib",
  128. [".icns"] = "image.icns",
  129. [".s"] = "sourcecode.asm",
  130. [".bmp"] = "image.bmp",
  131. [".wav"] = "audio.wav",
  132. [".xcassets"] = "folder.assetcatalog",
  133. [".swift"] = "sourcecode.swift",
  134. [".metal"] = "sourcecode.metal",
  135. [".dylib"] = "compiled.mach-o.dylib",
  136. }
  137. return types[path.getextension(node.path)] or "text"
  138. end
  139. xcode.escapeSpecialChars = {
  140. ['\n'] = '\\n',
  141. ['\r'] = '\\r',
  142. ['\t'] = '\\t',
  143. }
  144. function xcode.escapeChar(c)
  145. return xcode.escapeSpecialChars[c] or '\\'..c
  146. end
  147. function xcode.escapeArg(value)
  148. value = value:gsub('[\'"\\\n\r\t ]', xcode.escapeChar)
  149. return value
  150. end
  151. function xcode.escapeSetting(value)
  152. value = value:gsub('["\\\n\r\t]', xcode.escapeChar)
  153. return value
  154. end
  155. function xcode.stringifySetting(value)
  156. value = value..''
  157. if not value:match('^[%a%d_./]+$') then
  158. value = '"'..xcode.escapeSetting(value)..'"'
  159. end
  160. return value
  161. end
  162. function xcode.customStringifySetting(value)
  163. value = value..''
  164. local test = value:match('^[%a%d_./%+]+$')
  165. if test then
  166. value = '"'..xcode.escapeSetting(value)..'"'
  167. end
  168. return value
  169. end
  170. function xcode.printSetting(level, name, value)
  171. if type(value) == 'function' then
  172. value(level, name)
  173. elseif type(value) ~= 'table' then
  174. _p(level, '%s = %s;', xcode.stringifySetting(name), xcode.stringifySetting(value))
  175. --elseif #value == 1 then
  176. --_p(level, '%s = %s;', xcode.stringifySetting(name), xcode.stringifySetting(value[1]))
  177. elseif #value >= 1 then
  178. _p(level, '%s = (', xcode.stringifySetting(name))
  179. for _, item in ipairs(value) do
  180. _p(level + 1, '%s,', xcode.stringifySetting(item))
  181. end
  182. _p(level, ');')
  183. end
  184. end
  185. function xcode.printSettingsTable(level, settings)
  186. -- Maintain alphabetic order to be consistent
  187. local keys = table.keys(settings)
  188. table.sort(keys)
  189. for _, k in ipairs(keys) do
  190. xcode.printSetting(level, k, settings[k])
  191. end
  192. end
  193. function xcode.overrideSettings(settings, overrides)
  194. if type(overrides) == 'table' then
  195. for name, value in pairs(overrides) do
  196. -- Allow an override to remove a value by using false
  197. settings[name] = iif(not table.equals(value, { false }), value, nil)
  198. end
  199. end
  200. end
  201. --
  202. -- Return the Xcode product type, based target kind.
  203. --
  204. -- @param node
  205. -- The product node to identify.
  206. -- @returns
  207. -- An Xcode product type, string.
  208. --
  209. function xcode.getproducttype(node)
  210. local types = {
  211. ConsoleApp = "com.apple.product-type.tool",
  212. WindowedApp = "com.apple.product-type.application",
  213. StaticLib = "com.apple.product-type.library.static",
  214. SharedLib = "com.apple.product-type.library.dynamic",
  215. OSXBundle = "com.apple.product-type.bundle",
  216. OSXFramework = "com.apple.product-type.framework",
  217. XCTest = "com.apple.product-type.bundle.unit-test",
  218. }
  219. return types[iif(node.cfg.kind == "SharedLib" and node.cfg.sharedlibtype, node.cfg.sharedlibtype, node.cfg.kind)]
  220. end
  221. --
  222. -- Return the Xcode target type, based on the target file extension.
  223. --
  224. -- @param node
  225. -- The product node to identify.
  226. -- @returns
  227. -- An Xcode target type, string.
  228. --
  229. function xcode.gettargettype(node)
  230. local types = {
  231. ConsoleApp = "\"compiled.mach-o.executable\"",
  232. WindowedApp = "wrapper.application",
  233. StaticLib = "archive.ar",
  234. SharedLib = "\"compiled.mach-o.dylib\"",
  235. OSXBundle = "wrapper.cfbundle",
  236. OSXFramework = "wrapper.framework",
  237. XCTest = "wrapper.cfbundle",
  238. }
  239. return types[iif(node.cfg.kind == "SharedLib" and node.cfg.sharedlibtype, node.cfg.sharedlibtype, node.cfg.kind)]
  240. end
  241. --
  242. -- Return the Xcode debug information format for the current configuration
  243. --
  244. -- @param cfg
  245. -- The current configuration
  246. -- @returns
  247. -- The corresponding value of DEBUG_INFORMATION_FORMAT, or 'dwarf-with-dsym' if invalid
  248. --
  249. function xcode.getToolSet(cfg)
  250. local default = "clang"
  251. local minOSVersion = project.systemversion(cfg)
  252. if minOSVersion ~= nil
  253. and cfg.system == p.MACOSX
  254. and p.checkVersion(minOSVersion, "<10.7")
  255. then
  256. default = "gcc"
  257. end
  258. local toolset = p.tools[_OPTIONS.cc or cfg.toolset or default]
  259. if not toolset then
  260. error("Invalid toolset '" .. cfg.toolset .. "'")
  261. end
  262. return toolset
  263. end
  264. function xcode.getdebugformat(cfg)
  265. local formats = {
  266. ["Dwarf"] = "dwarf",
  267. ["Default"] = "dwarf-with-dsym",
  268. ["SplitDwarf"] = "dwarf-with-dsym",
  269. }
  270. local rval = "dwarf-with-dsym"
  271. if cfg.debugformat then
  272. rval = formats[cfg.debugformat] or rval
  273. end
  274. return rval
  275. end
  276. --
  277. -- Return a unique file name for a project. Since Xcode uses .xcodeproj's to
  278. -- represent both workspaces and projects there is a likely change of a name
  279. -- collision. Tack on a number to differentiate them.
  280. --
  281. -- @param prj
  282. -- The project being queried.
  283. -- @returns
  284. -- A uniqued file name
  285. --
  286. function xcode.getxcodeprojname(prj)
  287. -- if there is a workspace with matching name, then use "projectname1.xcodeproj"
  288. -- just get something working for now
  289. local fname = p.filename(prj, ".xcodeproj")
  290. return fname
  291. end
  292. --
  293. -- Returns true if the file name represents a framework.
  294. --
  295. -- @param fname
  296. -- The name of the file to test.
  297. --
  298. function xcode.isframework(fname)
  299. return (path.getextension(fname) == ".framework")
  300. end
  301. --
  302. -- Returns true if the file name represents a dylib.
  303. --
  304. -- @param fname
  305. -- The name of the file to test.
  306. --
  307. function xcode.isdylib(fname)
  308. return (path.getextension(fname) == ".dylib")
  309. end
  310. --
  311. -- Returns true if the file name represents a framework or dylib.
  312. --
  313. -- @param fname
  314. -- The name of the file to test.
  315. --
  316. function xcode.isframeworkordylib(fname)
  317. return xcode.isframework(fname) or xcode.isdylib(fname)
  318. end
  319. --
  320. -- Retrieves a unique 12 byte ID for an object.
  321. -- This function accepts an array of parameters that will be used to generate the id.
  322. --
  323. -- @returns
  324. -- A 24-character string representing the 12 byte ID.
  325. --
  326. function xcode.newid(...)
  327. local name = ''
  328. local arg = {...}
  329. for i, v in pairs(arg) do
  330. name = name..v..'****'
  331. end
  332. return ("%08X%08X%08X"):format(name:hash(16777619), name:hash(2166136261), name:hash(46577619))
  333. end
  334. --
  335. -- Create a product tree node and all projects in a workspace; assigning IDs
  336. -- that are needed for inter-project dependencies.
  337. --
  338. -- @param wks
  339. -- The workspace to prepare.
  340. --
  341. function xcode.prepareWorkspace(wks)
  342. -- create and cache a list of supported platforms
  343. wks.xcode = { }
  344. for prj in p.workspace.eachproject(wks) do
  345. -- need a configuration to get the target information
  346. local cfg = project.getconfig(prj, prj.configurations[1], prj.platforms[1])
  347. -- build the product tree node
  348. local bundlepath = cfg.buildtarget.bundlename ~= "" and cfg.buildtarget.bundlename or cfg.buildtarget.name;
  349. if (prj.external) then
  350. bundlepath = cfg.project.name
  351. end
  352. local node = p.tree.new(path.getname(bundlepath))
  353. node.cfg = cfg
  354. node.id = xcode.newid(node.name, "product")
  355. node.targetid = xcode.newid(node.name, "target")
  356. -- attach it to the project
  357. prj.xcode = {}
  358. prj.xcode.projectnode = node
  359. end
  360. end
  361. ---------------------------------------------------------------------------
  362. -- Section generator functions, in the same order in which they appear
  363. -- in the .pbxproj file
  364. ---------------------------------------------------------------------------
  365. function xcode.PBXBuildFile(tr)
  366. local settings = {};
  367. tree.traverse(tr, {
  368. onnode = function(node)
  369. if node.buildid then
  370. settings[node.buildid] = function(level)
  371. _p(level,'%s /* %s in %s */ = {isa = PBXBuildFile; fileRef = %s /* %s */; };',
  372. node.buildid, node.name, xcode.getbuildcategory(node), node.id, node.name)
  373. if node.embedid then
  374. local attrs = ""
  375. if xcode.shouldembedandsign(tr, node) then
  376. attrs = attrs .. "CodeSignOnCopy, "
  377. end
  378. if xcode.isframework(node.name) then
  379. attrs = attrs .. "RemoveHeadersOnCopy, "
  380. end
  381. if attrs ~= "" then
  382. attrs = " settings = {ATTRIBUTES = (" .. attrs .. "); };"
  383. end
  384. _p(level,'%s /* %s in Embed Libraries */ = {isa = PBXBuildFile; fileRef = %s /* %s */;%s };',
  385. node.embedid, node.name, node.id, node.name, attrs)
  386. end
  387. end
  388. end
  389. end
  390. })
  391. if not table.isempty(settings) then
  392. _p('/* Begin PBXBuildFile section */')
  393. xcode.printSettingsTable(2, settings);
  394. _p('/* End PBXBuildFile section */')
  395. _p('')
  396. end
  397. end
  398. function xcode.PBXContainerItemProxy(tr)
  399. local settings = {}
  400. for _, node in ipairs(tr.projects.children) do
  401. settings[node.productproxyid] = function()
  402. _p(2,'%s /* PBXContainerItemProxy */ = {', node.productproxyid)
  403. _p(3,'isa = PBXContainerItemProxy;')
  404. _p(3,'containerPortal = %s /* %s */;', node.id, path.getrelative(node.parent.parent.project.location, node.path))
  405. _p(3,'proxyType = 2;')
  406. _p(3,'remoteGlobalIDString = %s;', node.project.xcode.projectnode.id)
  407. _p(3,'remoteInfo = %s;', xcode.stringifySetting(node.project.xcode.projectnode.name))
  408. _p(2,'};')
  409. end
  410. settings[node.targetproxyid] = function()
  411. _p(2,'%s /* PBXContainerItemProxy */ = {', node.targetproxyid)
  412. _p(3,'isa = PBXContainerItemProxy;')
  413. _p(3,'containerPortal = %s /* %s */;', node.id, path.getrelative(node.parent.parent.project.location, node.path))
  414. _p(3,'proxyType = 1;')
  415. _p(3,'remoteGlobalIDString = %s;', node.project.xcode.projectnode.targetid)
  416. _p(3,'remoteInfo = %s;', xcode.stringifySetting(node.project.xcode.projectnode.name))
  417. _p(2,'};')
  418. end
  419. end
  420. if not table.isempty(settings) then
  421. _p('/* Begin PBXContainerItemProxy section */')
  422. xcode.printSettingsTable(2, settings);
  423. _p('/* End PBXContainerItemProxy section */')
  424. _p('')
  425. end
  426. end
  427. function xcode.PBXFileReference(tr)
  428. local cfg = project.getfirstconfig(tr.project)
  429. local settings = {}
  430. tree.traverse(tr, {
  431. onleaf = function(node)
  432. -- I'm only listing files here, so ignore anything without a path
  433. if not node.path then
  434. return
  435. end
  436. -- is this the product node, describing the output target?
  437. if node.kind == "product" then
  438. settings[node.id] = function(level)
  439. _p(level,'%s /* %s */ = {isa = PBXFileReference; explicitFileType = %s; includeInIndex = 0; name = %s; path = %s; sourceTree = BUILT_PRODUCTS_DIR; };',
  440. node.id, node.name, xcode.gettargettype(node), xcode.stringifySetting(node.name), xcode.stringifySetting(path.getname(node.cfg.buildtarget.bundlename ~= "" and node.cfg.buildtarget.bundlename or node.cfg.buildtarget.relpath)))
  441. end
  442. -- is this a project dependency?
  443. elseif node.parent.parent == tr.projects then
  444. settings[node.parent.id] = function(level)
  445. -- ms Is there something wrong with path is relative ?
  446. -- if we have a and b without slashes get relative should assume the same parent folder and return ../
  447. -- this works if we put it like below
  448. local relpath = path.getrelative(path.getabsolute(tr.project.location), path.getabsolute(node.parent.project.location))
  449. _p(level,'%s /* %s */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = %s; path = %s; sourceTree = SOURCE_ROOT; };',
  450. node.parent.id, node.name, xcode.customStringifySetting(node.parent.name), xcode.stringifySetting(path.join(relpath, node.parent.name)))
  451. end
  452. -- something else
  453. else
  454. settings[node.id] = function(level)
  455. local pth, src
  456. if xcode.isframework(node.path) then
  457. --respect user supplied paths
  458. -- look for special variable-starting paths for different sources
  459. local nodePath = node.path
  460. local _, matchEnd, variable = string.find(nodePath, "^%$%((.+)%)/")
  461. if variable then
  462. -- by skipping the last '/' we support the same absolute/relative
  463. -- paths as before
  464. nodePath = string.sub(nodePath, matchEnd + 1)
  465. end
  466. if string.find(nodePath,'/') then
  467. if string.find(nodePath,'^%.')then
  468. --error('relative paths are not currently supported for frameworks')
  469. pth = path.getrelative(tr.project.location, node.path)
  470. --print(tr.project.location, node.path , pth)
  471. src = "SOURCE_ROOT"
  472. variable = src
  473. else
  474. pth = nodePath
  475. src = "<absolute>"
  476. end
  477. end
  478. -- if it starts with a variable, use that as the src instead
  479. if variable then
  480. src = variable
  481. -- if we are using a different source tree, it has to be relative
  482. -- to that source tree, so get rid of any leading '/'
  483. if string.find(pth, '^/') then
  484. pth = string.sub(pth, 2)
  485. end
  486. else
  487. pth = "System/Library/Frameworks/" .. node.path
  488. src = "SDKROOT"
  489. end
  490. elseif xcode.isdylib(node.path) then
  491. --respect user supplied paths
  492. -- look for special variable-starting paths for different sources
  493. local nodePath = node.path
  494. local _, matchEnd, variable = string.find(nodePath, "^%$%((.+)%)/")
  495. if variable then
  496. -- by skipping the last '/' we support the same absolute/relative
  497. -- paths as before
  498. nodePath = string.sub(nodePath, matchEnd + 1)
  499. end
  500. if string.find(nodePath,'^%/') then
  501. pth = nodePath
  502. src = "<absolute>"
  503. else
  504. pth = path.getrelative(tr.project.location, node.path)
  505. src = "SOURCE_ROOT"
  506. end
  507. else
  508. -- something else; probably a source code file
  509. src = "<group>"
  510. if node.abspath then
  511. pth = path.getrelative(tr.project.location, node.abspath)
  512. else
  513. pth = node.path
  514. end
  515. --end
  516. end
  517. _p(level,'%s /* %s */ = {isa = PBXFileReference; %s = %s; name = %s; path = %s; sourceTree = %s; };',
  518. node.id, node.name, xcode.getfiletypekey(node, cfg), xcode.getfiletype(node, cfg), xcode.stringifySetting(node.name), xcode.stringifySetting(pth), xcode.stringifySetting(src))
  519. end
  520. end
  521. end
  522. })
  523. if not table.isempty(settings) then
  524. _p('/* Begin PBXFileReference section */')
  525. xcode.printSettingsTable(2, settings)
  526. _p('/* End PBXFileReference section */')
  527. _p('')
  528. end
  529. end
  530. function xcode.PBXFrameworksBuildPhase(tr)
  531. _p('/* Begin PBXFrameworksBuildPhase section */')
  532. _p(2,'%s /* Frameworks */ = {', tr.products.children[1].fxstageid)
  533. _p(3,'isa = PBXFrameworksBuildPhase;')
  534. _p(3,'buildActionMask = 2147483647;')
  535. _p(3,'files = (')
  536. -- write out library dependencies
  537. tree.traverse(tr.frameworks, {
  538. onleaf = function(node)
  539. if node.buildid then
  540. _p(4,'%s /* %s in Frameworks */,', node.buildid, node.name)
  541. end
  542. end
  543. })
  544. -- write out project dependencies
  545. tree.traverse(tr.projects, {
  546. onleaf = function(node)
  547. if node.buildid then
  548. _p(4,'%s /* %s in Frameworks */,', node.buildid, node.name)
  549. end
  550. end
  551. })
  552. _p(3,');')
  553. _p(3,'runOnlyForDeploymentPostprocessing = 0;')
  554. _p(2,'};')
  555. _p('/* End PBXFrameworksBuildPhase section */')
  556. _p('')
  557. end
  558. function xcode.embedListContains(list, node)
  559. -- Frameworks and dylibs referenced by path are expected
  560. -- to provide the file name (but not path). Project
  561. -- references are expected to provide the project name.
  562. local entryname = node.name
  563. if node.parent.project then
  564. entryname = node.parent.project.name
  565. end
  566. return table.contains(list, entryname)
  567. end
  568. function xcode.shouldembed(tr, node)
  569. if not xcode.isframeworkordylib(node.name) then
  570. return false
  571. end
  572. for _, cfg in ipairs(tr.configs) do
  573. if xcode.embedListContains(cfg.embed, node) or xcode.embedListContains(cfg.embedAndSign, node) then
  574. return true
  575. end
  576. end
  577. end
  578. function xcode.shouldembedandsign(tr, node)
  579. if not xcode.isframeworkordylib(node.name) then
  580. return false
  581. end
  582. for _, cfg in ipairs(tr.configs) do
  583. if xcode.embedListContains(cfg.embedAndSign, node) then
  584. return true
  585. end
  586. end
  587. end
  588. function xcode.PBXCopyFilesBuildPhaseForEmbedFrameworks(tr)
  589. _p('/* Begin PBXCopyFilesBuildPhase section */')
  590. _p(2,'%s /* Embed Libraries */ = {', tr.products.children[1].embedstageid)
  591. _p(3,'isa = PBXCopyFilesBuildPhase;')
  592. _p(3,'buildActionMask = 2147483647;')
  593. _p(3,'dstPath = "";')
  594. _p(3,'dstSubfolderSpec = 10;')
  595. _p(3,'files = (')
  596. -- write out library dependencies
  597. tree.traverse(tr.frameworks, {
  598. onleaf = function(node)
  599. if node.embedid then
  600. _p(4,'%s /* %s in Frameworks */,', node.embedid, node.name)
  601. end
  602. end
  603. })
  604. -- write out project dependencies
  605. tree.traverse(tr.projects, {
  606. onleaf = function(node)
  607. if node.embedid then
  608. _p(4,'%s /* %s in Projects */,', node.embedid, node.parent.project.name)
  609. end
  610. end
  611. })
  612. _p(3,');')
  613. _p(3,'name = "Embed Libraries";')
  614. _p(3,'runOnlyForDeploymentPostprocessing = 0;')
  615. _p(2,'};')
  616. _p('/* End PBXCopyFilesBuildPhase section */')
  617. _p('')
  618. end
  619. function xcode.PBXGroup(tr)
  620. local settings = {}
  621. tree.traverse(tr, {
  622. onnode = function(node)
  623. -- Skip over anything that isn't a proper group
  624. if (node.path and #node.children == 0) or node.kind == "vgroup" then
  625. return
  626. end
  627. local function isAggregateTarget(node)
  628. local productsId = xcode.newid("Products")
  629. return node.id == productsId and node.parent.project and node.parent.project.kind == "Utility"
  630. end
  631. if isAggregateTarget(node) then
  632. return
  633. end
  634. settings[node.productgroupid or node.id] = function()
  635. -- project references get special treatment
  636. if node.parent == tr.projects then
  637. _p(2,'%s /* Products */ = {', node.productgroupid)
  638. else
  639. _p(2,'%s /* %s */ = {', node.id, node.name)
  640. end
  641. _p(3,'isa = PBXGroup;')
  642. _p(3,'children = (')
  643. for _, childnode in ipairs(node.children) do
  644. if not isAggregateTarget(childnode) then
  645. _p(4,'%s /* %s */,', childnode.id, childnode.name)
  646. end
  647. end
  648. _p(3,');')
  649. if node.parent == tr.projects then
  650. _p(3,'name = Products;')
  651. else
  652. _p(3,'name = %s;', xcode.stringifySetting(node.name))
  653. local vpath = project.getvpath(tr.project, node.name)
  654. if node.path and node.name ~= vpath then
  655. local p = node.path
  656. if node.parent.path then
  657. p = path.getrelative(node.parent.path, node.path)
  658. end
  659. _p(3,'path = %s;', xcode.stringifySetting(p))
  660. end
  661. end
  662. _p(3,'sourceTree = "<group>";')
  663. _p(2,'};')
  664. end
  665. end
  666. }, true)
  667. if not table.isempty(settings) then
  668. _p('/* Begin PBXGroup section */')
  669. xcode.printSettingsTable(2, settings)
  670. _p('/* End PBXGroup section */')
  671. _p('')
  672. end
  673. end
  674. function xcode.GetBuildCommands(tr)
  675. local buildCommandInfos = {}
  676. tree.traverse(tr, {
  677. onnode = function(node)
  678. if node.buildcommandid then
  679. local info = {
  680. node = node,
  681. inputs = { node.relpath },
  682. outputs = {},
  683. depends = {},
  684. transact = false,
  685. }
  686. for cfg in project.eachconfig(tr.project) do
  687. local filecfg = fileconfig.getconfig(node, cfg)
  688. if filecfg then
  689. for _, v in ipairs(filecfg.buildinputs) do
  690. table.insert(info.inputs, project.getrelative(tr.project, v))
  691. end
  692. for _, v in ipairs(filecfg.buildoutputs) do
  693. table.insert(info.outputs, project.getrelative(tr.project, v))
  694. end
  695. end
  696. end
  697. table.insert(buildCommandInfos, info)
  698. end
  699. end
  700. })
  701. for _, mine in ipairs(buildCommandInfos) do
  702. for _, their in ipairs(buildCommandInfos) do
  703. if mine ~= their then
  704. for _, input in ipairs(mine.inputs) do
  705. if table.contains(their.outputs, input) then
  706. table.insert(mine.depends, their)
  707. break
  708. end
  709. end
  710. end
  711. end
  712. end
  713. local buildCommands = {}
  714. local leftover = #buildCommandInfos
  715. while leftover > 0 do
  716. local prev = leftover
  717. for _, info in ipairs(buildCommandInfos) do
  718. if not info.transact then
  719. local transact = true
  720. for _, depend in ipairs(info.depends) do
  721. transact = depend.transact
  722. if not transact then
  723. break
  724. end
  725. end
  726. if transact then
  727. table.insert(buildCommands, info.node)
  728. info.transact = true
  729. leftover = leftover - 1
  730. end
  731. end
  732. end
  733. if prev == leftover then
  734. error('detect circular reference.')
  735. end
  736. end
  737. return buildCommands
  738. end
  739. function xcode.PBXAggregateOrNativeTarget(tr, pbxTargetName)
  740. local kinds = {
  741. Aggregate = {
  742. "Utility",
  743. },
  744. Native = {
  745. "ConsoleApp",
  746. "WindowedApp",
  747. "SharedLib",
  748. "StaticLib",
  749. },
  750. }
  751. local hasTarget = false
  752. for _, node in ipairs(tr.products.children) do
  753. hasTarget = table.contains(kinds[pbxTargetName], node.cfg.kind)
  754. if hasTarget then
  755. break
  756. end
  757. end
  758. if not hasTarget then
  759. return
  760. end
  761. _p('/* Begin PBX%sTarget section */', pbxTargetName)
  762. local buildCommands = xcode.GetBuildCommands(tr)
  763. for _, node in ipairs(tr.products.children) do
  764. local name = tr.project.name
  765. local function hasBuildCommands(which)
  766. for _, cfg in ipairs(tr.configs) do
  767. if #cfg[which] > 0 then
  768. return true
  769. end
  770. end
  771. end
  772. _p(2,'%s /* %s */ = {', node.targetid, name)
  773. _p(3,'isa = PBX%sTarget;', pbxTargetName)
  774. _p(3,'buildConfigurationList = %s /* Build configuration list for PBX%sTarget "%s" */;', node.cfgsection, pbxTargetName, xcode.escapeSetting(name))
  775. _p(3,'buildPhases = (')
  776. if hasBuildCommands('prebuildcommands') then
  777. _p(4,'9607AE1010C857E500CD1376 /* Prebuild */,')
  778. end
  779. for _, v in ipairs(buildCommands) do
  780. _p(4,'%s /* Build "%s" */,', v.buildcommandid, v.name)
  781. end
  782. if pbxTargetName == "Native" then
  783. _p(4,'%s /* Resources */,', node.resstageid)
  784. _p(4,'%s /* Sources */,', node.sourcesid)
  785. end
  786. if hasBuildCommands('prelinkcommands') then
  787. _p(4,'9607AE3510C85E7E00CD1376 /* Prelink */,')
  788. end
  789. if pbxTargetName == "Native" then
  790. _p(4,'%s /* Frameworks */,', node.fxstageid)
  791. _p(4,'%s /* Embed Libraries */,', node.embedstageid)
  792. end
  793. if hasBuildCommands('postbuildcommands') then
  794. _p(4,'9607AE3710C85E8F00CD1376 /* Postbuild */,')
  795. end
  796. _p(3,');')
  797. _p(3,'buildRules = (')
  798. _p(3,');')
  799. _p(3,'dependencies = (')
  800. for _, node in ipairs(tr.projects.children) do
  801. _p(4,'%s /* PBXTargetDependency */,', node.targetdependid)
  802. end
  803. _p(3,');')
  804. _p(3,'name = %s;', xcode.stringifySetting(name))
  805. if pbxTargetName == "Native" then
  806. local p
  807. if node.cfg.kind == "ConsoleApp" then
  808. p = "$(HOME)/bin"
  809. elseif node.cfg.kind == "WindowedApp" then
  810. p = "$(HOME)/Applications"
  811. end
  812. if p then
  813. _p(3,'productInstallPath = %s;', xcode.stringifySetting(p))
  814. end
  815. end
  816. _p(3,'productName = %s;', xcode.stringifySetting(name))
  817. if pbxTargetName == "Native" then
  818. _p(3,'productReference = %s /* %s */;', node.id, node.name)
  819. _p(3,'productType = %s;', xcode.stringifySetting(xcode.getproducttype(node)))
  820. end
  821. _p(2,'};')
  822. end
  823. _p('/* End PBX%sTarget section */', pbxTargetName)
  824. _p('')
  825. end
  826. function xcode.PBXAggregateTarget(tr)
  827. xcode.PBXAggregateOrNativeTarget(tr, "Aggregate")
  828. end
  829. function xcode.PBXNativeTarget(tr)
  830. xcode.PBXAggregateOrNativeTarget(tr, "Native")
  831. end
  832. function xcode.PBXProject(tr)
  833. _p('/* Begin PBXProject section */')
  834. _p(2,'08FB7793FE84155DC02AAC07 /* Project object */ = {')
  835. _p(3,'isa = PBXProject;')
  836. local capabilities = tr.project.xcodesystemcapabilities
  837. if not table.isempty(capabilities) then
  838. local keys = table.keys(capabilities)
  839. table.sort(keys)
  840. _p(3, 'attributes = {')
  841. _p(4, 'TargetAttributes = {')
  842. _p(5, '%s = {', tr.project.xcode.projectnode.targetid)
  843. _p(6, 'SystemCapabilities = {')
  844. for _, key in pairs(keys) do
  845. _p(7, '%s = {', key)
  846. _p(8, 'enabled = %d;', iif(capabilities[key], 1, 0))
  847. _p(7, '};')
  848. end
  849. _p(6, '};')
  850. _p(5, '};')
  851. _p(4, '};')
  852. _p(3, '};')
  853. end
  854. _p(3,'buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "%s" */;', tr.name)
  855. _p(3,'compatibilityVersion = "Xcode 3.2";')
  856. _p(3,'hasScannedForEncodings = 1;')
  857. _p(3,'mainGroup = %s /* %s */;', tr.id, tr.name)
  858. _p(3,'projectDirPath = "";')
  859. if #tr.projects.children > 0 then
  860. _p(3,'projectReferences = (')
  861. for _, node in ipairs(tr.projects.children) do
  862. _p(4,'{')
  863. _p(5,'ProductGroup = %s /* Products */;', node.productgroupid)
  864. _p(5,'ProjectRef = %s /* %s */;', node.id, path.getname(node.path))
  865. _p(4,'},')
  866. end
  867. _p(3,');')
  868. end
  869. _p(3,'projectRoot = "";')
  870. _p(3,'targets = (')
  871. for _, node in ipairs(tr.products.children) do
  872. _p(4,'%s /* %s */,', node.targetid, node.name)
  873. end
  874. _p(3,');')
  875. _p(2,'};')
  876. _p('/* End PBXProject section */')
  877. _p('')
  878. end
  879. function xcode.PBXReferenceProxy(tr)
  880. local settings = {}
  881. tree.traverse(tr.projects, {
  882. onleaf = function(node)
  883. settings[node.id] = function()
  884. _p(2,'%s /* %s */ = {', node.id, node.name)
  885. _p(3,'isa = PBXReferenceProxy;')
  886. _p(3,'fileType = %s;', xcode.gettargettype(node))
  887. _p(3,'path = %s;', xcode.stringifySetting(node.name))
  888. _p(3,'remoteRef = %s /* PBXContainerItemProxy */;', node.parent.productproxyid)
  889. _p(3,'sourceTree = BUILT_PRODUCTS_DIR;')
  890. _p(2,'};')
  891. end
  892. end
  893. })
  894. if not table.isempty(settings) then
  895. _p('/* Begin PBXReferenceProxy section */')
  896. xcode.printSettingsTable(2, settings)
  897. _p('/* End PBXReferenceProxy section */')
  898. _p('')
  899. end
  900. end
  901. function xcode.PBXResourcesBuildPhase(tr)
  902. _p('/* Begin PBXResourcesBuildPhase section */')
  903. for _, target in ipairs(tr.products.children) do
  904. _p(2,'%s /* Resources */ = {', target.resstageid)
  905. _p(3,'isa = PBXResourcesBuildPhase;')
  906. _p(3,'buildActionMask = 2147483647;')
  907. _p(3,'files = (')
  908. tree.traverse(tr, {
  909. onnode = function(node)
  910. if xcode.getbuildcategory(node) == "Resources" then
  911. _p(4,'%s /* %s in Resources */,', node.buildid, node.name)
  912. end
  913. end
  914. })
  915. _p(3,');')
  916. _p(3,'runOnlyForDeploymentPostprocessing = 0;')
  917. _p(2,'};')
  918. end
  919. _p('/* End PBXResourcesBuildPhase section */')
  920. _p('')
  921. end
  922. function xcode.PBXShellScriptBuildPhase(tr)
  923. local wrapperWritten = false
  924. local function doblock(id, name, which)
  925. -- see if there are any config-specific commands to add
  926. local commands = {}
  927. for _, cfg in ipairs(tr.configs) do
  928. local cfgcmds = cfg[which]
  929. if #cfgcmds > 0 then
  930. table.insert(commands, 'if [ "${CONFIGURATION}" = "' .. cfg.buildcfg .. '" ]; then')
  931. for i = 1, #cfgcmds do
  932. table.insert(commands, cfgcmds[i])
  933. end
  934. table.insert(commands, 'fi')
  935. end
  936. end
  937. if #commands > 0 then
  938. table.insert(commands, 1, 'set -e') -- Tells the shell to exit when any command fails
  939. commands = os.translateCommands(commands, p.MACOSX)
  940. if not wrapperWritten then
  941. _p('/* Begin PBXShellScriptBuildPhase section */')
  942. wrapperWritten = true
  943. end
  944. _p(2,'%s /* %s */ = {', id, name)
  945. _p(3,'isa = PBXShellScriptBuildPhase;')
  946. _p(3,'buildActionMask = 2147483647;')
  947. _p(3,'files = (')
  948. _p(3,');')
  949. _p(3,'inputPaths = (');
  950. _p(3,');');
  951. _p(3,'name = %s;', name);
  952. _p(3,'outputPaths = (');
  953. _p(3,');');
  954. _p(3,'runOnlyForDeploymentPostprocessing = 0;');
  955. _p(3,'shellPath = /bin/sh;');
  956. _p(3,'shellScript = %s;', xcode.stringifySetting(table.concat(commands, '\n')))
  957. _p(2,'};')
  958. end
  959. end
  960. doblock("9607AE1010C857E500CD1376", "Prebuild", "prebuildcommands")
  961. local settings = {}
  962. tree.traverse(tr, {
  963. onnode = function(node)
  964. if node.buildcommandid then
  965. settings[node.buildcommandid] = function(level)
  966. local commands = {}
  967. local inputs = {}
  968. local outputs = {}
  969. for cfg in project.eachconfig(tr.project) do
  970. local filecfg = fileconfig.getconfig(node, cfg)
  971. if filecfg then
  972. table.insert(commands, 'if [ "${CONFIGURATION}" = "' .. cfg.buildcfg .. '" ]; then')
  973. if filecfg.buildmessage then
  974. table.insert(commands, '\techo "' .. filecfg.buildmessage .. '"')
  975. end
  976. local cmds = os.translateCommandsAndPaths(filecfg.buildcommands, filecfg.project.basedir, filecfg.project.location)
  977. for _, cmd in ipairs(cmds) do
  978. table.insert(commands, '\t' .. cmd)
  979. end
  980. table.insert(commands, 'fi')
  981. for _, v in ipairs(filecfg.buildinputs) do
  982. if not table.indexof(inputs, v) then
  983. table.insert(inputs, v)
  984. end
  985. end
  986. for _, v in ipairs(filecfg.buildoutputs) do
  987. if not table.indexof(outputs, v) then
  988. table.insert(outputs, v)
  989. end
  990. end
  991. end
  992. end
  993. if #commands > 0 then
  994. table.insert(commands, 1, 'set -e') -- Tells the shell to exit when any command fails
  995. end
  996. _p(level,'%s /* Build "%s" */ = {', node.buildcommandid, node.name)
  997. _p(level+1,'isa = PBXShellScriptBuildPhase;')
  998. _p(level+1,'buildActionMask = 2147483647;')
  999. _p(level+1,'files = (')
  1000. _p(level+1,');')
  1001. _p(level+1,'inputPaths = (');
  1002. _p(level+2,'"%s",', xcode.escapeSetting(node.relpath))
  1003. for _, v in ipairs(inputs) do
  1004. _p(level+2,'"%s",', xcode.escapeSetting(project.getrelative(tr.project, v)))
  1005. end
  1006. _p(level+1,');')
  1007. _p(level+1,'name = %s;', xcode.stringifySetting('Build "' .. node.name .. '"'))
  1008. _p(level+1,'outputPaths = (')
  1009. for _, v in ipairs(outputs) do
  1010. _p(level+2,'"%s",', xcode.escapeSetting(project.getrelative (tr.project, v)))
  1011. end
  1012. _p(level+1,');')
  1013. _p(level+1,'runOnlyForDeploymentPostprocessing = 0;');
  1014. _p(level+1,'shellPath = /bin/sh;');
  1015. _p(level+1,'shellScript = %s;', xcode.stringifySetting(table.concat(commands, '\n')))
  1016. _p(level,'};')
  1017. end
  1018. end
  1019. end
  1020. })
  1021. if not table.isempty(settings) then
  1022. if not wrapperWritten then
  1023. _p('/* Begin PBXShellScriptBuildPhase section */')
  1024. wrapperWritten = true
  1025. end
  1026. xcode.printSettingsTable(2, settings)
  1027. end
  1028. doblock("9607AE3510C85E7E00CD1376", "Prelink", "prelinkcommands")
  1029. doblock("9607AE3710C85E8F00CD1376", "Postbuild", "postbuildcommands")
  1030. if wrapperWritten then
  1031. _p('/* End PBXShellScriptBuildPhase section */')
  1032. _p('')
  1033. end
  1034. end
  1035. function xcode.PBXSourcesBuildPhase(tr)
  1036. _p('/* Begin PBXSourcesBuildPhase section */')
  1037. for _, target in ipairs(tr.products.children) do
  1038. _p(2,'%s /* Sources */ = {', target.sourcesid)
  1039. _p(3,'isa = PBXSourcesBuildPhase;')
  1040. _p(3,'buildActionMask = 2147483647;')
  1041. _p(3,'files = (')
  1042. tree.traverse(tr, {
  1043. onleaf = function(node)
  1044. if xcode.getbuildcategory(node) == "Sources" and node.buildid then
  1045. _p(4,'%s /* %s in Sources */,', node.buildid, node.name)
  1046. end
  1047. end
  1048. })
  1049. _p(3,');')
  1050. _p(3,'runOnlyForDeploymentPostprocessing = 0;')
  1051. _p(2,'};')
  1052. end
  1053. _p('/* End PBXSourcesBuildPhase section */')
  1054. _p('')
  1055. end
  1056. function xcode.PBXVariantGroup(tr)
  1057. local settings = {}
  1058. tree.traverse(tr, {
  1059. onbranch = function(node)
  1060. settings[node.id] = function()
  1061. if node.kind == "vgroup" then
  1062. _p(2,'%s /* %s */ = {', node.id, node.name)
  1063. _p(3,'isa = PBXVariantGroup;')
  1064. _p(3,'children = (')
  1065. for _, lang in ipairs(node.children) do
  1066. _p(4,'%s /* %s */,', lang.id, lang.name)
  1067. end
  1068. _p(3,');')
  1069. _p(3,'name = %s;', node.name)
  1070. _p(3,'sourceTree = "<group>";')
  1071. _p(2,'};')
  1072. end
  1073. end
  1074. end
  1075. })
  1076. if not table.isempty(settings) then
  1077. _p('/* Begin PBXVariantGroup section */')
  1078. xcode.printSettingsTable(2, settings)
  1079. _p('/* End PBXVariantGroup section */')
  1080. _p('')
  1081. end
  1082. end
  1083. function xcode.PBXTargetDependency(tr)
  1084. local settings = {}
  1085. tree.traverse(tr.projects, {
  1086. onleaf = function(node)
  1087. settings[node.parent.targetdependid] = function()
  1088. _p(2,'%s /* PBXTargetDependency */ = {', node.parent.targetdependid)
  1089. _p(3,'isa = PBXTargetDependency;')
  1090. _p(3,'name = %s;', xcode.stringifySetting(node.name))
  1091. _p(3,'targetProxy = %s /* PBXContainerItemProxy */;', node.parent.targetproxyid)
  1092. _p(2,'};')
  1093. end
  1094. end
  1095. })
  1096. if not table.isempty(settings) then
  1097. _p('/* Begin PBXTargetDependency section */')
  1098. xcode.printSettingsTable(2, settings)
  1099. _p('/* End PBXTargetDependency section */')
  1100. _p('')
  1101. end
  1102. end
  1103. function xcode.XCBuildConfiguration_Target(tr, target, cfg)
  1104. local settings = {}
  1105. settings['ALWAYS_SEARCH_USER_PATHS'] = 'NO'
  1106. if cfg.symbols ~= p.OFF then
  1107. settings['DEBUG_INFORMATION_FORMAT'] = xcode.getdebugformat(cfg)
  1108. end
  1109. if cfg.kind ~= "StaticLib" and cfg.buildtarget.prefix ~= '' then
  1110. settings['EXECUTABLE_PREFIX'] = cfg.buildtarget.prefix
  1111. end
  1112. if cfg.buildtarget.extension then
  1113. local exts = {
  1114. WindowedApp = "app",
  1115. SharedLib = "dylib",
  1116. StaticLib = "a",
  1117. OSXBundle = "bundle",
  1118. OSXFramework = "framework",
  1119. XCTest = "xctest",
  1120. }
  1121. local ext = cfg.buildtarget.extension:sub(2)
  1122. if ext ~= exts[iif(cfg.kind == "SharedLib" and cfg.sharedlibtype, cfg.sharedlibtype, cfg.kind)] then
  1123. if cfg.kind == "WindowedApp" or (cfg.kind == "SharedLib" and cfg.sharedlibtype) then
  1124. settings['WRAPPER_EXTENSION'] = ext
  1125. elseif cfg.kind == "SharedLib" or cfg.kind == "StaticLib" then
  1126. settings['EXECUTABLE_EXTENSION'] = ext
  1127. end
  1128. end
  1129. end
  1130. local outdir = path.getrelative(tr.project.location, path.getdirectory(cfg.buildtarget.relpath))
  1131. if outdir ~= "." then
  1132. settings['CONFIGURATION_BUILD_DIR'] = outdir
  1133. end
  1134. settings['GCC_DYNAMIC_NO_PIC'] = 'NO'
  1135. if tr.infoplist then
  1136. settings['INFOPLIST_FILE'] = config.findfile(cfg, path.getextension(tr.infoplist.name))
  1137. end
  1138. local installpaths = {
  1139. ConsoleApp = '/usr/local/bin',
  1140. WindowedApp = '"$(HOME)/Applications"',
  1141. SharedLib = '/usr/local/lib',
  1142. StaticLib = '/usr/local/lib',
  1143. OSXBundle = '$(LOCAL_LIBRARY_DIR)/Bundles',
  1144. OSXFramework = '$(LOCAL_LIBRARY_DIR)/Frameworks',
  1145. }
  1146. settings['INSTALL_PATH'] = installpaths[iif(cfg.kind == "SharedLib" and cfg.sharedlibtype, cfg.sharedlibtype, cfg.kind)]
  1147. local fileNameList = {}
  1148. local file_tree = project.getsourcetree(tr.project)
  1149. tree.traverse(tr, {
  1150. onnode = function(node)
  1151. if node.buildid and not node.isResource and node.abspath then
  1152. -- ms this seems to work on visual studio !!!
  1153. -- why not in xcode ??
  1154. local filecfg = fileconfig.getconfig(node, cfg)
  1155. if filecfg and filecfg.flags.ExcludeFromBuild then
  1156. --fileNameList = fileNameList .. " " ..filecfg.name
  1157. table.insert(fileNameList, xcode.escapeArg(node.name))
  1158. end
  1159. --ms new way
  1160. -- if the file is not in this config file list excluded it from build !!!
  1161. --if not cfg.files[node.abspath] then
  1162. -- table.insert(fileNameList, xcode.escapeArg(node.name))
  1163. --end
  1164. end
  1165. end
  1166. })
  1167. if not table.isempty(fileNameList) then
  1168. settings['EXCLUDED_SOURCE_FILE_NAMES'] = fileNameList
  1169. end
  1170. settings['PRODUCT_NAME'] = iif(cfg.kind == "ConsoleApp" and cfg.buildtarget.extension, cfg.buildtarget.basename .. cfg.buildtarget.extension, cfg.buildtarget.basename)
  1171. if os.istarget(p.IOS) then
  1172. settings['SDKROOT'] = 'iphoneos'
  1173. settings['CODE_SIGN_IDENTITY[sdk=iphoneos*]'] = cfg.xcodecodesigningidentity or 'iPhone Developer'
  1174. local minOSVersion = project.systemversion(cfg)
  1175. if minOSVersion ~= nil then
  1176. settings['IPHONEOS_DEPLOYMENT_TARGET'] = minOSVersion
  1177. end
  1178. local families = {
  1179. ['iPhone/iPod touch'] = '1',
  1180. ['iPad'] = '2',
  1181. ['Universal'] = '1,2',
  1182. }
  1183. local family = families[cfg.iosfamily]
  1184. if family then
  1185. settings['TARGETED_DEVICE_FAMILY'] = family
  1186. end
  1187. else
  1188. local minOSVersion = project.systemversion(cfg)
  1189. if minOSVersion ~= nil then
  1190. settings['MACOSX_DEPLOYMENT_TARGET'] = minOSVersion
  1191. end
  1192. end
  1193. --ms not by default ...add it manually if you need it
  1194. --settings['COMBINE_HIDPI_IMAGES'] = 'YES'
  1195. xcode.overrideSettings(settings, cfg.xcodebuildsettings)
  1196. _p(2,'%s /* %s */ = {', cfg.xcode.targetid, cfg.buildcfg)
  1197. _p(3,'isa = XCBuildConfiguration;')
  1198. _p(3,'buildSettings = {')
  1199. xcode.printSettingsTable(4, settings)
  1200. _p(3,'};')
  1201. xcode.printSetting(3, 'name', cfg.buildcfg);
  1202. _p(2,'};')
  1203. end
  1204. xcode.cLanguageStandards = {
  1205. ["Default"] = "compiler-default", -- explicit compiler default
  1206. ["C89"] = "c89",
  1207. ["C90"] = "c90",
  1208. ["C99"] = "c99",
  1209. ["C11"] = "c11",
  1210. ["gnu89"] = "gnu89",
  1211. ["gnu90"] = "gnu90",
  1212. ["gnu99"] = "gnu99",
  1213. ["gnu11"] = "gnu11"
  1214. }
  1215. function xcode.XCBuildConfiguration_CLanguageStandard(settings, cfg)
  1216. -- if no cppdialect is provided, don't set C dialect
  1217. -- projects without C dialect set will use compiler default
  1218. if not cfg.cdialect then
  1219. return
  1220. end
  1221. local cLanguageStandard = xcode.cLanguageStandards[cfg.cdialect]
  1222. if cLanguageStandard then
  1223. settings['GCC_C_LANGUAGE_STANDARD'] = cLanguageStandard
  1224. end
  1225. end
  1226. xcode.cppLanguageStandards = {
  1227. ["Default"] = "compiler-default", -- explicit compiler default
  1228. ["C++98"] = "c++98",
  1229. ["C++0x"] = "c++11",
  1230. ["C++11"] = "c++11",
  1231. ["C++1y"] = "c++14",
  1232. ["C++14"] = "c++14",
  1233. ["C++1z"] = "c++1z",
  1234. ["C++17"] = "c++1z",
  1235. ["C++2a"] = "c++2a",
  1236. ["C++20"] = "c++2a",
  1237. ["gnu++98"] = "gnu++98",
  1238. ["gnu++0x"] = "gnu++0x",
  1239. ["gnu++11"] = "gnu++0x", -- Xcode project GUI uses gnu++0x, but gnu++11 also works
  1240. ["gnu++1y"] = "gnu++14",
  1241. ["gnu++14"] = "gnu++14",
  1242. ["gnu++1z"] = "gnu++1z",
  1243. ["gnu++17"] = "gnu++1z",
  1244. ["gnu++2a"] = "gnu++2a",
  1245. ["gnu++20"] = "gnu++2a",
  1246. }
  1247. function xcode.XCBuildConfiguration_CppLanguageStandard(settings, cfg)
  1248. -- if no cppdialect is provided, don't set C++ dialect
  1249. -- projects without C++ dialect set will use compiler default
  1250. if not cfg.cppdialect then
  1251. return
  1252. end
  1253. local cppLanguageStandard = xcode.cppLanguageStandards[cfg.cppdialect]
  1254. if cppLanguageStandard then
  1255. settings['CLANG_CXX_LANGUAGE_STANDARD'] = cppLanguageStandard
  1256. end
  1257. end
  1258. function xcode.XCBuildConfiguration_SwiftLanguageVersion(settings, cfg)
  1259. -- if no swiftversion is provided, don't set swift version
  1260. -- Projects with swift files but without swift version will refuse
  1261. -- to build on Xcode but setting a default SWIFT_VERSION may have
  1262. -- unexpected interactions with other systems like cocoapods
  1263. if cfg.swiftversion then
  1264. settings['SWIFT_VERSION'] = cfg.swiftversion
  1265. end
  1266. end
  1267. function xcode.XCBuildConfiguration_Project(tr, cfg)
  1268. local settings = {}
  1269. local toolset = xcode.getToolSet(cfg)
  1270. local archs = {
  1271. Native = "$(NATIVE_ARCH_ACTUAL)",
  1272. x86 = "i386",
  1273. x86_64 = "x86_64",
  1274. Universal32 = "$(ARCHS_STANDARD_32_BIT)",
  1275. Universal64 = "$(ARCHS_STANDARD_64_BIT)",
  1276. Universal = "$(ARCHS_STANDARD_32_64_BIT)",
  1277. }
  1278. settings['ARCHS'] = archs[cfg.platform or "Native"]
  1279. --ms This is the default so don;t write it
  1280. --settings['SDKROOT'] = 'macosx'
  1281. local targetdir = path.getdirectory(cfg.buildtarget.relpath)
  1282. if targetdir ~= "." then
  1283. settings['CONFIGURATION_BUILD_DIR'] = '$(SYMROOT)'
  1284. end
  1285. settings['CONFIGURATION_TEMP_DIR'] = '$(OBJROOT)'
  1286. if config.isDebugBuild(cfg) then
  1287. settings['COPY_PHASE_STRIP'] = 'NO'
  1288. end
  1289. xcode.XCBuildConfiguration_CLanguageStandard(settings, cfg)
  1290. xcode.XCBuildConfiguration_CppLanguageStandard(settings, cfg)
  1291. if cfg.exceptionhandling == p.OFF then
  1292. settings['GCC_ENABLE_CPP_EXCEPTIONS'] = 'NO'
  1293. end
  1294. if cfg.rtti == p.OFF then
  1295. settings['GCC_ENABLE_CPP_RTTI'] = 'NO'
  1296. end
  1297. if cfg.symbols == p.ON and cfg.editandcontinue ~= "Off" then
  1298. settings['GCC_ENABLE_FIX_AND_CONTINUE'] = 'YES'
  1299. end
  1300. if cfg.exceptionhandling == p.OFF then
  1301. settings['GCC_ENABLE_OBJC_EXCEPTIONS'] = 'NO'
  1302. end
  1303. local optimizeMap = { On = 3, Size = 's', Speed = 3, Full = 'fast', Debug = 1 }
  1304. settings['GCC_OPTIMIZATION_LEVEL'] = optimizeMap[cfg.optimize] or 0
  1305. if cfg.pchheader and not cfg.flags.NoPCH then
  1306. settings['GCC_PRECOMPILE_PREFIX_HEADER'] = 'YES'
  1307. settings['GCC_PREFIX_HEADER'] = cfg.pchheader
  1308. end
  1309. local escapedDefines = { }
  1310. for i,v in ipairs(cfg.defines) do
  1311. escapedDefines[i] = xcode.escapeArg(v)
  1312. end
  1313. settings['GCC_PREPROCESSOR_DEFINITIONS'] = escapedDefines
  1314. settings["GCC_SYMBOLS_PRIVATE_EXTERN"] = 'NO'
  1315. if cfg.unsignedchar ~= nil then
  1316. settings['GCC_CHAR_IS_UNSIGNED_CHAR'] = iif(cfg.unsignedchar, "YES", "NO")
  1317. end
  1318. if cfg.flags.FatalWarnings then
  1319. settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES'
  1320. end
  1321. settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'YES'
  1322. settings['GCC_WARN_UNUSED_VARIABLE'] = 'YES'
  1323. local includedirs = project.getrelative(cfg.project, cfg.includedirs)
  1324. for i,v in ipairs(includedirs) do
  1325. cfg.includedirs[i] = p.quoted(v)
  1326. end
  1327. settings['USER_HEADER_SEARCH_PATHS'] = cfg.includedirs
  1328. local sysincludedirs = project.getrelative(cfg.project, cfg.sysincludedirs)
  1329. for i,v in ipairs(sysincludedirs) do
  1330. cfg.sysincludedirs[i] = p.quoted(v)
  1331. end
  1332. if not table.isempty(cfg.sysincludedirs) then
  1333. table.insert(cfg.sysincludedirs, "$(inherited)")
  1334. end
  1335. settings['SYSTEM_HEADER_SEARCH_PATHS'] = cfg.sysincludedirs
  1336. for i,v in ipairs(cfg.libdirs) do
  1337. cfg.libdirs[i] = p.project.getrelative(cfg.project, cfg.libdirs[i])
  1338. end
  1339. for i,v in ipairs(cfg.syslibdirs) do
  1340. cfg.syslibdirs[i] = p.project.getrelative(cfg.project, cfg.syslibdirs[i])
  1341. end
  1342. settings['LIBRARY_SEARCH_PATHS'] = table.join (cfg.libdirs, cfg.syslibdirs)
  1343. for i,v in ipairs(cfg.frameworkdirs) do
  1344. cfg.frameworkdirs[i] = p.project.getrelative(cfg.project, cfg.frameworkdirs[i])
  1345. end
  1346. settings['FRAMEWORK_SEARCH_PATHS'] = cfg.frameworkdirs
  1347. local runpathdirs = table.join (cfg.runpathdirs, config.getsiblingtargetdirs (cfg))
  1348. settings['LD_RUNPATH_SEARCH_PATHS'] = toolset.getrunpathdirs(cfg, runpathdirs, "path")
  1349. local objDir = path.getrelative(tr.project.location, cfg.objdir)
  1350. settings['OBJROOT'] = objDir
  1351. settings['ONLY_ACTIVE_ARCH'] = iif(p.config.isDebugBuild(cfg), 'YES', 'NO')
  1352. -- build list of "other" C/C++ flags
  1353. local checks = {
  1354. ["-ffast-math"] = cfg.floatingpoint == "Fast",
  1355. ["-fomit-frame-pointer"] = cfg.omitframepointer == "On",
  1356. ["-fno-omit-frame-pointer"] = cfg.omitframepointer == "Off",
  1357. }
  1358. local flags = { }
  1359. for flag, check in pairs(checks) do
  1360. if check then
  1361. table.insert(flags, flag)
  1362. end
  1363. end
  1364. --[[if (type(cfg.buildoptions) == "table") then
  1365. for k,v in pairs(cfg.buildoptions) do
  1366. table.insertflat(flags, string.explode(v," -"))
  1367. end
  1368. end
  1369. ]]
  1370. settings['OTHER_CFLAGS'] = table.join(flags, cfg.buildoptions)
  1371. -- build list of "other" linked flags.
  1372. flags = { }
  1373. for _, lib in ipairs(config.getlinks(cfg, "system")) do
  1374. if not xcode.isframeworkordylib(lib) then
  1375. table.insert(flags, "-l" .. lib)
  1376. end
  1377. end
  1378. --ms this step is for reference projects only
  1379. for _, lib in ipairs(config.getlinks(cfg, "dependencies", "object")) do
  1380. if (lib.external) then
  1381. if not xcode.isframeworkordylib(lib.linktarget.basename) then
  1382. table.insert(flags, "-l" .. xcode.escapeArg(lib.linktarget.basename))
  1383. end
  1384. end
  1385. end
  1386. settings['OTHER_LDFLAGS'] = table.join(flags, cfg.linkoptions)
  1387. if cfg.flags.StaticRuntime then
  1388. settings['STANDARD_C_PLUS_PLUS_LIBRARY_TYPE'] = 'static'
  1389. end
  1390. if targetdir ~= "." then
  1391. settings['SYMROOT'] = path.getrelative(tr.project.location, targetdir)
  1392. end
  1393. if cfg.warnings == "Off" then
  1394. settings['WARNING_CFLAGS'] = '-w'
  1395. elseif cfg.warnings == "High" then
  1396. settings['WARNING_CFLAGS'] = '-Wall'
  1397. elseif cfg.warnings == "Extra" then
  1398. settings['WARNING_CFLAGS'] = '-Wall -Wextra'
  1399. elseif cfg.warnings == "Everything" then
  1400. settings['WARNING_CFLAGS'] = '-Weverything'
  1401. end
  1402. xcode.XCBuildConfiguration_SwiftLanguageVersion(settings, cfg)
  1403. xcode.overrideSettings(settings, cfg.xcodebuildsettings)
  1404. _p(2,'%s /* %s */ = {', cfg.xcode.projectid, cfg.buildcfg)
  1405. _p(3,'isa = XCBuildConfiguration;')
  1406. _p(3,'buildSettings = {')
  1407. xcode.printSettingsTable(4, settings)
  1408. _p(3,'};')
  1409. xcode.printSetting(3, 'name', cfg.buildcfg);
  1410. _p(2,'};')
  1411. end
  1412. function xcode.XCBuildConfiguration(tr)
  1413. local settings = {}
  1414. for _, target in ipairs(tr.products.children) do
  1415. for _, cfg in ipairs(tr.configs) do
  1416. settings[cfg.xcode.targetid] = function()
  1417. xcode.XCBuildConfiguration_Target(tr, target, cfg)
  1418. end
  1419. end
  1420. end
  1421. for _, cfg in ipairs(tr.configs) do
  1422. settings[cfg.xcode.projectid] = function()
  1423. xcode.XCBuildConfiguration_Project(tr, cfg)
  1424. end
  1425. end
  1426. if not table.isempty(settings) then
  1427. _p('/* Begin XCBuildConfiguration section */')
  1428. xcode.printSettingsTable(0, settings)
  1429. _p('/* End XCBuildConfiguration section */')
  1430. _p('')
  1431. end
  1432. end
  1433. function xcode.XCBuildConfigurationList(tr)
  1434. local wks = tr.project.workspace
  1435. local defaultCfgName = xcode.stringifySetting(tr.configs[1].buildcfg)
  1436. local settings = {}
  1437. for _, target in ipairs(tr.products.children) do
  1438. settings[target.cfgsection] = function()
  1439. _p(2,'%s /* Build configuration list for PBXNativeTarget "%s" */ = {', target.cfgsection, target.name)
  1440. _p(3,'isa = XCConfigurationList;')
  1441. _p(3,'buildConfigurations = (')
  1442. for _, cfg in ipairs(tr.configs) do
  1443. _p(4,'%s /* %s */,', cfg.xcode.targetid, cfg.buildcfg)
  1444. end
  1445. _p(3,');')
  1446. _p(3,'defaultConfigurationIsVisible = 0;')
  1447. _p(3,'defaultConfigurationName = %s;', defaultCfgName)
  1448. _p(2,'};')
  1449. end
  1450. end
  1451. settings['1DEB928908733DD80010E9CD'] = function()
  1452. _p(2,'1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "%s" */ = {', tr.name)
  1453. _p(3,'isa = XCConfigurationList;')
  1454. _p(3,'buildConfigurations = (')
  1455. for _, cfg in ipairs(tr.configs) do
  1456. _p(4,'%s /* %s */,', cfg.xcode.projectid, cfg.buildcfg)
  1457. end
  1458. _p(3,');')
  1459. _p(3,'defaultConfigurationIsVisible = 0;')
  1460. _p(3,'defaultConfigurationName = %s;', defaultCfgName)
  1461. _p(2,'};')
  1462. end
  1463. _p('/* Begin XCConfigurationList section */')
  1464. xcode.printSettingsTable(2, settings)
  1465. _p('/* End XCConfigurationList section */')
  1466. end