premake/modules/xcode/xcode_project.lua
2021-06-23 20:03:18 -04:00

269 lines
7.8 KiB
Lua

---
-- xcode/xcode4_project.lua
-- Generate an Xcode project file.
-- Author Jason Perkins
-- Modified by Mihai Sebea
-- Copyright (c) 2009-2015 Jason Perkins and the Premake project
---
local p = premake
local m = p.modules.xcode
local xcode = p.modules.xcode
local project = p.project
local config = p.config
local fileconfig = p.fileconfig
local tree = p.tree
--
-- Checks if a node must be excluded completely from a target or not. It will
-- return true only if the node has the "ExcludeFromBuild" flag in all the
-- configurations.
--
-- @param node
-- The node to check.
-- @param prj
-- The project being generated.
-- @returns
-- A boolean, telling whether the node must be excluded from its target or not.
--
function xcode.mustExcludeFromTarget(node, prj)
if not node.configs then
return false
end
local value
for cfg in premake.project.eachconfig(prj) do
local filecfg = premake.fileconfig.getconfig(node, cfg)
if filecfg then
local newValue = not not filecfg.flags.ExcludeFromBuild
if value == nil then
value = newValue
elseif value ~= newValue then
p.warn(node.name .. " is excluded in just some configurations. Autocompletion will not work correctly on this file in Xcode.")
return false
end
end
end
return value
end
--
-- Create a tree corresponding to what is shown in the Xcode project browser
-- pane, with nodes for files and folders, resources, frameworks, and products.
--
-- @param prj
-- The project being generated.
-- @returns
-- A tree, loaded with metadata, which mirrors Xcode's view of the project.
--
function xcode.buildprjtree(prj)
local tr = project.getsourcetree(prj, nil , false)
tr.project = prj
-- create a list of build configurations and assign IDs
tr.configs = {}
for cfg in project.eachconfig(prj) do
cfg.xcode = {}
cfg.xcode.targetid = xcode.newid(prj.xcode.projectnode.name, cfg.buildcfg, "target")
cfg.xcode.projectid = xcode.newid(tr.name, cfg.buildcfg)
table.insert(tr.configs, cfg)
end
-- convert localized resources from their filesystem layout (English.lproj/MainMenu.xib)
-- to Xcode's display layout (MainMenu.xib/English).
tree.traverse(tr, {
onbranch = function(node)
if path.getextension(node.name) == ".lproj" then
local lang = path.getbasename(node.name) -- "English", "French", etc.
-- create a new language group for each file it contains
for _, filenode in ipairs(node.children) do
local grpnode = node.parent.children[filenode.name]
if not grpnode then
grpnode = tree.insert(node.parent, tree.new(filenode.name))
grpnode.kind = "vgroup"
end
-- convert the file node to a language node and add to the group
filenode.name = path.getbasename(lang)
tree.insert(grpnode, filenode)
end
-- remove this directory from the tree
tree.remove(node)
end
end
})
-- the special folder "Frameworks" lists all linked frameworks
tr.frameworks = tree.new("Frameworks")
for cfg in project.eachconfig(prj) do
for _, link in ipairs(config.getlinks(cfg, "system", "fullpath")) do
local name = path.getname(link)
if xcode.isframeworkordylib(name) and not tr.frameworks.children[name] then
node = tree.insert(tr.frameworks, tree.new(name))
node.path = link
end
end
end
-- only add it to the tree if there are frameworks to link
if #tr.frameworks.children > 0 then
tree.insert(tr, tr.frameworks)
end
-- the special folder "Products" holds the target produced by the project; this
-- is populated below
tr.products = tree.insert(tr, tree.new("Products"))
-- the special folder "Projects" lists sibling project dependencies
tr.projects = tree.new("Projects")
for _, dep in ipairs(project.getdependencies(prj, "linkOnly")) do
xcode.addDependency(prj, tr, dep, true)
end
for _, dep in ipairs(project.getdependencies(prj, "dependOnly")) do
xcode.addDependency(prj, tr, dep, false)
end
if #tr.projects.children > 0 then
tree.insert(tr, tr.projects)
end
-- Final setup
tree.traverse(tr, {
onnode = function(node)
local nodePath
if node.path then
nodePath = path.getrelative(tr.project.location, node.path)
end
-- assign IDs to every node in the tree
node.id = xcode.newid(node.name, nil, nodePath)
node.isResource = xcode.isItemResource(prj, node)
-- check to see if this file has custom build
if node.configs then
for cfg in project.eachconfig(prj) do
local filecfg = fileconfig.getconfig(node, cfg)
if fileconfig.hasCustomBuildRule(filecfg) then
if not node.buildcommandid then
node.buildcommandid = xcode.newid(node.name, "buildcommand", nodePath)
end
end
end
end
-- assign build IDs to buildable files
if xcode.getbuildcategory(node) and not node.excludefrombuild and not xcode.mustExcludeFromTarget(node, tr.project) then
node.buildid = xcode.newid(node.name, "build", nodePath)
if xcode.shouldembed(tr, node) then
node.embedid = xcode.newid(node.name, "embed", nodepath)
end
end
-- remember key files that are needed elsewhere
if string.endswith(node.name, "Info.plist") then
tr.infoplist = node
end
end
}, true)
-- Plug in the product node into the Products folder in the tree. The node
-- was built in xcode.prepareWorkspace() in xcode_common.lua; it contains IDs
-- that are necessary for inter-project dependencies
node = tree.insert(tr.products, prj.xcode.projectnode)
node.kind = "product"
node.path = node.cfg.buildtarget.fullpath
node.cfgsection = xcode.newid(node.name, "cfg")
node.resstageid = xcode.newid(node.name, "rez")
node.sourcesid = xcode.newid(node.name, "src")
node.fxstageid = xcode.newid(node.name, "fxs")
node.embedstageid = xcode.newid(node.name, "embed")
return tr
end
function xcode.addDependency(prj, tr, dep, build)
-- create a child node for the dependency's xcodeproj
local xcpath = xcode.getxcodeprojname(dep)
local xcnode = tree.insert(tr.projects, tree.new(path.getname(xcpath)))
xcnode.path = xcpath
xcnode.project = dep
xcnode.productgroupid = xcode.newid(xcnode.name, "prodgrp")
xcnode.productproxyid = xcode.newid(xcnode.name, "prodprox")
xcnode.targetproxyid = xcode.newid(xcnode.name, "targprox")
xcnode.targetdependid = xcode.newid(xcnode.name, "targdep")
-- create a grandchild node for the dependency's link target
local lprj = p.workspace.findproject(prj.workspace, dep.name)
local cfg = project.findClosestMatch(lprj, prj.configurations[1])
node = tree.insert(xcnode, tree.new(cfg.linktarget.name))
node.path = cfg.linktarget.fullpath
node.cfg = cfg
-- don't link the dependency if it's a dependency only
if build == false then
node.excludefrombuild = true
end
end
---
-- Generate an Xcode .xcodeproj for a Premake project.
---
m.elements.project = function(prj)
return {
m.header,
}
end
function m.generateProject(prj)
local tr = xcode.buildprjtree(prj)
p.callArray(m.elements.project, prj)
xcode.PBXBuildFile(tr)
xcode.PBXContainerItemProxy(tr)
xcode.PBXFileReference(tr)
xcode.PBXFrameworksBuildPhase(tr)
xcode.PBXCopyFilesBuildPhaseForEmbedFrameworks(tr)
xcode.PBXGroup(tr)
xcode.PBXNativeTarget(tr)
xcode.PBXAggregateTarget(tr)
xcode.PBXProject(tr)
xcode.PBXReferenceProxy(tr)
xcode.PBXResourcesBuildPhase(tr)
xcode.PBXShellScriptBuildPhase(tr)
xcode.PBXSourcesBuildPhase(tr)
xcode.PBXTargetDependency(tr)
xcode.PBXVariantGroup(tr)
xcode.XCBuildConfiguration(tr)
xcode.XCBuildConfigurationList(tr)
xcode.footer(prj)
end
function m.header(prj)
p.w('// !$*UTF8*$!')
p.push('{')
p.w('archiveVersion = 1;')
p.w('classes = {')
p.w('};')
p.w('objectVersion = 46;')
p.push('objects = {')
p.w()
end
function xcode.footer(prj)
p.pop('};')
p.w('rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;')
p.pop('}')
end