Patch 2794681: Add usage() to copy settings from dependent projects (Korval)
With help from Conrad Mercer. Still needs unit tests (and testing in general)
This commit is contained in:
parent
ec0c649d62
commit
a92ab43bde
@ -1,3 +1,10 @@
|
||||
-------
|
||||
4.3 (in progress)
|
||||
-------
|
||||
|
||||
* Patch 2794681: Add usage() to copy settings from dependent projects (Korval)
|
||||
|
||||
|
||||
-------
|
||||
4.2.2 (in progress)
|
||||
-------
|
||||
@ -97,7 +104,6 @@
|
||||
- Bug 2790865: SharedLib on OSX fixes (Ash Berlin)
|
||||
- Bug 2790882: Trailing slash in path.getabsolute (Ash Berlin)
|
||||
|
||||
|
||||
RC2 -> RC3
|
||||
|
||||
- Bug 2805763: GCC PCH breaks on path
|
||||
|
118
src/base/api.lua
118
src/base/api.lua
@ -52,6 +52,7 @@
|
||||
{
|
||||
kind = "list",
|
||||
scope = "config",
|
||||
usagecopy = true,
|
||||
},
|
||||
|
||||
excludes =
|
||||
@ -71,6 +72,7 @@
|
||||
kind = "list",
|
||||
scope = "config",
|
||||
isflags = true,
|
||||
usagecopy = true,
|
||||
allowed = {
|
||||
"EnableSSE",
|
||||
"EnableSSE2",
|
||||
@ -99,7 +101,7 @@
|
||||
"Unicode",
|
||||
"Unsafe",
|
||||
"WinMain"
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
framework =
|
||||
@ -156,6 +158,7 @@
|
||||
{
|
||||
kind = "dirlist",
|
||||
scope = "config",
|
||||
usagecopy = true,
|
||||
},
|
||||
|
||||
kind =
|
||||
@ -185,6 +188,7 @@
|
||||
{
|
||||
kind = "dirlist",
|
||||
scope = "config",
|
||||
linkagecopy = true,
|
||||
},
|
||||
|
||||
linkoptions =
|
||||
@ -203,8 +207,8 @@
|
||||
value = path.getabsolute(value)
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
end,
|
||||
linkagecopy = true,
|
||||
},
|
||||
|
||||
location =
|
||||
@ -331,6 +335,12 @@
|
||||
return value:upper()
|
||||
end
|
||||
},
|
||||
|
||||
uses =
|
||||
{
|
||||
kind = "list",
|
||||
scope = "config",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -592,10 +602,53 @@
|
||||
return cfg
|
||||
end
|
||||
|
||||
local function createproject(name, sln, isUsage)
|
||||
local prj = {}
|
||||
|
||||
function project(name)
|
||||
if not name then
|
||||
return iif(type(premake.CurrentContainer) == "project", premake.CurrentContainer, nil)
|
||||
-- attach a type
|
||||
setmetatable(prj, {
|
||||
__type = "project",
|
||||
})
|
||||
|
||||
-- add to master list keyed by both name and index
|
||||
table.insert(sln.projects, prj)
|
||||
if(isUsage) then
|
||||
--If we're creating a new usage project, and there's already a project
|
||||
--with our name, then set us as the usage project for that project.
|
||||
--Otherwise, set us as the project in that slot.
|
||||
if(sln.projects[name]) then
|
||||
sln.projects[name].usageProj = prj;
|
||||
else
|
||||
sln.projects[name] = prj
|
||||
end
|
||||
else
|
||||
--If we're creating a regular project, and there's already a project
|
||||
--with our name, then it must be a usage project. Set it as our usage project
|
||||
--and set us as the project in that slot.
|
||||
if(sln.projects[name]) then
|
||||
prj.usageProj = sln.projects[name];
|
||||
end
|
||||
|
||||
sln.projects[name] = prj
|
||||
end
|
||||
|
||||
prj.solution = sln
|
||||
prj.name = name
|
||||
prj.basedir = os.getcwd()
|
||||
prj.location = prj.basedir
|
||||
prj.uuid = os.uuid()
|
||||
prj.blocks = { }
|
||||
prj.usage = isUsage;
|
||||
|
||||
return prj;
|
||||
end
|
||||
|
||||
function usage(name)
|
||||
if (not name) then
|
||||
--Only return usage projects.
|
||||
if(type(premake.CurrentContainer) ~= "project") then return nil end
|
||||
if(not premake.CurrentContainer.usage) then return nil end
|
||||
return premake.CurrentContainer
|
||||
end
|
||||
|
||||
-- identify the parent solution
|
||||
@ -609,26 +662,45 @@
|
||||
error("no active solution", 2)
|
||||
end
|
||||
|
||||
-- if this is a new project, create it
|
||||
premake.CurrentContainer = sln.projects[name]
|
||||
if (not premake.CurrentContainer) then
|
||||
local prj = { }
|
||||
premake.CurrentContainer = prj
|
||||
-- if this is a new project, or the project in that slot doesn't have a usage, create it
|
||||
if((not sln.projects[name]) or
|
||||
((not sln.projects[name].usage) and (not sln.projects[name].usageProj))) then
|
||||
premake.CurrentContainer = createproject(name, sln, true)
|
||||
else
|
||||
premake.CurrentContainer = iff(sln.projects[name].usage,
|
||||
sln.projects[name], sln.projects[name].usageProj)
|
||||
end
|
||||
|
||||
-- add to master list keyed by both name and index
|
||||
table.insert(sln.projects, prj)
|
||||
sln.projects[name] = prj
|
||||
-- add an empty, global configuration to the project
|
||||
configuration { }
|
||||
|
||||
-- attach a type
|
||||
setmetatable(prj, {
|
||||
__type = "project",
|
||||
})
|
||||
return premake.CurrentContainer
|
||||
end
|
||||
|
||||
prj.solution = sln
|
||||
prj.name = name
|
||||
prj.basedir = os.getcwd()
|
||||
prj.uuid = os.uuid()
|
||||
prj.blocks = { }
|
||||
function project(name)
|
||||
if (not name) then
|
||||
--Only return non-usage projects
|
||||
if(type(premake.CurrentContainer) ~= "project") then return nil end
|
||||
if(premake.CurrentContainer.usage) then return nil end
|
||||
return premake.CurrentContainer
|
||||
end
|
||||
|
||||
-- identify the parent solution
|
||||
local sln
|
||||
if (type(premake.CurrentContainer) == "project") then
|
||||
sln = premake.CurrentContainer.solution
|
||||
else
|
||||
sln = premake.CurrentContainer
|
||||
end
|
||||
if (type(sln) ~= "solution") then
|
||||
error("no active solution", 2)
|
||||
end
|
||||
|
||||
-- if this is a new project, or the old project is a usage project, create it
|
||||
if((not sln.projects[name]) or sln.projects[name].usage) then
|
||||
premake.CurrentContainer = createproject(name, sln)
|
||||
else
|
||||
premake.CurrentContainer = sln.projects[name];
|
||||
end
|
||||
|
||||
-- add an empty, global configuration to the project
|
||||
|
@ -418,7 +418,192 @@
|
||||
end
|
||||
end
|
||||
|
||||
local function getCfgKind(cfg)
|
||||
if(cfg.kind) then
|
||||
return cfg.kind;
|
||||
end
|
||||
|
||||
if(cfg.project.__configs[""] and cfg.project.__configs[""].kind) then
|
||||
return cfg.project.__configs[""].kind;
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function getprojrec(dstArray, foundList, cfg, cfgname, searchField, bLinkage)
|
||||
if(not cfg) then return end
|
||||
|
||||
local foundUsePrjs = {};
|
||||
for _, useName in ipairs(cfg[searchField]) do
|
||||
local testName = useName:lower();
|
||||
if((not foundList[testName])) then
|
||||
local theProj = nil;
|
||||
local theUseProj = nil;
|
||||
for _, prj in ipairs(cfg.project.solution.projects) do
|
||||
if (prj.name:lower() == testName) then
|
||||
if(prj.usage) then
|
||||
theUseProj = prj;
|
||||
else
|
||||
theProj = prj;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--Must connect to a usage project.
|
||||
if(theUseProj) then
|
||||
foundList[testName] = true;
|
||||
local prjEntry = {
|
||||
name = testName,
|
||||
proj = theProj,
|
||||
usageProj = theUseProj,
|
||||
bLinkageOnly = bLinkage,
|
||||
};
|
||||
dstArray[testName] = prjEntry;
|
||||
table.insert(foundUsePrjs, theUseProj);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, usePrj in ipairs(foundUsePrjs) do
|
||||
--Links can only recurse through static libraries.
|
||||
if((searchField ~= "links") or
|
||||
(getCfgKind(usePrj.__configs[cfgname]) == "StaticLib")) then
|
||||
getprojrec(dstArray, foundList, usePrj.__configs[cfgname],
|
||||
cfgname, searchField, bLinkage);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- This function will recursively get all projects that the given configuration has in its "uses"
|
||||
-- field. The return values are a list of tables. Each table in that list contains the following:
|
||||
-- name = The lowercase name of the project.
|
||||
-- proj = The project. Can be nil if it is usage-only.
|
||||
-- usageProj = The usage project. Can't be nil, as using a project that has no
|
||||
-- usage project is not put into the list.
|
||||
-- bLinkageOnly = If this is true, then only the linkage information should be copied.
|
||||
-- The recursion will only look at the "uses" field on *usage* projects.
|
||||
-- This function will also add projects to the list that are mentioned in the "links"
|
||||
-- field of usage projects. These will only copy linker information, but they will recurse.
|
||||
-- through other "links" fields.
|
||||
--
|
||||
local function getprojectsconnections(cfg, cfgname)
|
||||
local dstArray = {};
|
||||
local foundList = {};
|
||||
foundList[cfg.project.name:lower()] = true;
|
||||
|
||||
--First, follow the uses recursively.
|
||||
getprojrec(dstArray, foundList, cfg, cfgname, "uses", false);
|
||||
|
||||
--Next, go through all of the usage projects and recursively get their links.
|
||||
--But only if they're not already there. Get the links as linkage-only.
|
||||
local linkArray = {};
|
||||
for prjName, prjEntry in pairs(dstArray) do
|
||||
getprojrec(linkArray, foundList, prjEntry.usageProj.__configs[cfgname], cfgname,
|
||||
"links", true);
|
||||
end
|
||||
|
||||
--Copy from linkArray into dstArray.
|
||||
for prjName, prjEntry in pairs(linkArray) do
|
||||
dstArray[prjName] = prjEntry;
|
||||
end
|
||||
|
||||
return dstArray;
|
||||
end
|
||||
|
||||
|
||||
local function isnameofproj(cfg, strName)
|
||||
local sln = cfg.project.solution;
|
||||
local strTest = strName:lower();
|
||||
for prjIx, prj in ipairs(sln.projects) do
|
||||
if (prj.name:lower() == strTest) then
|
||||
return true;
|
||||
end
|
||||
end
|
||||
|
||||
return false;
|
||||
end
|
||||
--
|
||||
-- Copies the field from dstCfg to srcCfg.
|
||||
--
|
||||
local function copydependentfield(srcCfg, dstCfg, strSrcField)
|
||||
local srcField = premake.fields[strSrcField];
|
||||
local strDstField = strSrcField;
|
||||
|
||||
if type(srcCfg[strSrcField]) == "table" then
|
||||
--handle paths.
|
||||
if (srcField.kind == "dirlist" or srcField.kind == "filelist") and
|
||||
(not nofixup[strSrcField]) then
|
||||
for i,p in ipairs(srcCfg[strSrcField]) do
|
||||
table.insert(dstCfg[strDstField],
|
||||
path.rebase(p, srcCfg.project.location, dstCfg.project.location))
|
||||
end
|
||||
else
|
||||
if(strSrcField == "links") then
|
||||
for i,p in ipairs(srcCfg[strSrcField]) do
|
||||
if(not isnameofproj(dstCfg, p)) then
|
||||
table.insert(dstCfg[strDstField], p)
|
||||
else
|
||||
printf("Failed to copy '%s' from proj '%s'.",
|
||||
p, srcCfg.project.name);
|
||||
end
|
||||
end
|
||||
else
|
||||
for i,p in ipairs(srcCfg[strSrcField]) do
|
||||
table.insert(dstCfg[strDstField], p)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if(srcField.kind == "path" and (not nofixup[strSrcField])) then
|
||||
dstCfg[strDstField] = path.rebase(srcCfg[strSrcField],
|
||||
prj.location, dstCfg.project.location);
|
||||
else
|
||||
dstCfg[strDstField] = srcCfg[strSrcField];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- This function will take the list of project entries and apply their usage project data
|
||||
-- to the given configuration. It will copy compiling information for the projects that are
|
||||
-- not listed as linkage-only. It will copy the linking information for projects only if
|
||||
-- the source project is not a static library. It won't copy linking information
|
||||
-- if the project is in this solution; instead it will add that project to the configuration's
|
||||
-- links field, expecting that Premake will handle the rest.
|
||||
--
|
||||
local function copyusagedata(cfg, cfgname, linkToProjs)
|
||||
local myPrj = cfg.project;
|
||||
local bIsStaticLib = (getCfgKind(cfg) == "StaticLib");
|
||||
|
||||
for prjName, prjEntry in pairs(linkToProjs) do
|
||||
local srcPrj = prjEntry.usageProj;
|
||||
local srcCfg = srcPrj.__configs[cfgname];
|
||||
|
||||
for name, field in pairs(premake.fields) do
|
||||
if(srcCfg[name]) then
|
||||
if(field.usagecopy) then
|
||||
if(not prjEntry.bLinkageOnly) then
|
||||
copydependentfield(srcCfg, cfg, name)
|
||||
end
|
||||
elseif(field.linkagecopy) then
|
||||
--Copy the linkage data if we're building a non-static thing
|
||||
--and this is a pure usage project. If it's not pure-usage, then
|
||||
--we will simply put the project's name in the links field later.
|
||||
if((not bIsStaticLib) and (not prjEntry.proj)) then
|
||||
copydependentfield(srcCfg, cfg, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if((not bIsStaticLib) and prjEntry.proj) then
|
||||
printf("Adding direct link to project '%s' from project '%s'",
|
||||
prjEntry.proj.name, getCfgKind(cfg));
|
||||
table.insert(cfg.links, prjEntry.proj.name);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Takes the configuration information stored in solution->project->block
|
||||
@ -453,6 +638,44 @@
|
||||
end
|
||||
end
|
||||
|
||||
--This loop finds the projects that a configuration is connected to
|
||||
--via its "uses" field. It will then copy any usage project information from that
|
||||
--usage project to the configuration in question.
|
||||
for sln in premake.solution.each() do
|
||||
for prjIx, prj in ipairs(sln.projects) do
|
||||
if(not prj.usage) then
|
||||
for cfgname, cfg in pairs(prj.__configs) do
|
||||
printf("prj '%s', cfg '%s'", prj.name, cfgname);
|
||||
printf("before:");
|
||||
for _, linkName in ipairs(cfg.links) do
|
||||
printf("\t link to '%s'.", linkName)
|
||||
end
|
||||
|
||||
local usesPrjs = getprojectsconnections(cfg, cfgname);
|
||||
copyusagedata(cfg, cfgname, usesPrjs)
|
||||
printf("after:");
|
||||
for _, linkName in ipairs(cfg.links) do
|
||||
printf("link to '%s'", linkName)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove all usage projects.
|
||||
for sln in premake.solution.each() do
|
||||
local removeList = {};
|
||||
for index, prj in ipairs(sln.projects) do
|
||||
if(prj.usage) then
|
||||
table.insert(removeList, 1, index); --Add in reverse order.
|
||||
end
|
||||
end
|
||||
|
||||
for _, index in ipairs(removeList) do
|
||||
table.remove(sln.projects, index);
|
||||
end
|
||||
end
|
||||
|
||||
-- assign unique object directories to each configuration
|
||||
builduniquedirs()
|
||||
|
||||
|
Reference in New Issue
Block a user