Port unique objects directory logic to the new platforms API

This commit is contained in:
Jason Perkins 2012-01-11 16:30:59 -05:00
parent 9749188cc4
commit fa4813cf3b
11 changed files with 310 additions and 151 deletions

View File

@ -82,8 +82,11 @@
local architecture = vstudio.architecture(cfg)
_p(3,'Name="%s|%s"', premake.esc(platform), premake.esc(architecture))
_p(3,'OutputDirectory="%s"', premake.esc(config.gettargetinfo(cfg).directory))
_p(3,'IntermediateDirectory="%s"', premake.esc("obj\\" .. cfg.buildcfg .. "\\" .. cfg.project.name))
local outdir = path.translate(config.gettargetinfo(cfg).directory)
_p(3,'OutputDirectory="%s"', premake.esc(outdir))
local objdir = path.translate(config.getuniqueobjdir(cfg))
_p(3,'IntermediateDirectory="%s"', premake.esc(objdir))
local cfgtype
if (cfg.kind == "SharedLib") then

View File

@ -73,7 +73,7 @@
debugformat =
{
kind = "string",
scope = "container",
scope = "config",
allowed = {
"c7",
}
@ -596,20 +596,15 @@
error(err, 4)
end
if not container[fieldname] then
container[fieldname] = {}
end
if type(values) ~= "table" then
error("invalid value; table expected", 4)
end
local field = container[fieldname]
container[fieldname] = container[fieldname] or {}
local field = container[fieldname] or {}
for key,value in pairs(values) do
if not field[key] then
field[key] = {}
end
field[key] = field[key] or {}
table.insertflat(field[key], value)
end

View File

@ -45,6 +45,11 @@
--
function config.gettargetinfo(cfg)
-- have I cached results from a previous call?
if cfg.targetinfo then
return cfg.targetinfo
end
local basedir = project.getlocation(prj)
local directory = cfg.targetdir or basedir
@ -108,10 +113,62 @@
info.prefix = prefix
info.suffix = suffix
if config.getpathstyle(cfg) == premake.WINDOWS then
info.directory = path.translate(info.directory, "\\")
info.fullpath = path.translate(info.fullpath, "\\")
end
-- cache the results for future calls
cfg.targetinfo = info
return info
end
--
-- Retrieve the objects (or intermediates) directory for this configuration
-- that is unique for this entire solution. This ensures that builds of
-- different configurations will not step on each others' object files.
-- The path is built from these choices, in order:
--
-- [1] -> the objects directory as set in the config
-- [2] -> [1] + the platform name
-- [3] -> [2] + the build configuration name
-- [4] -> [3] + the project name
--
--
-- @param cfg
-- The configuration object to query.
-- @return
-- A objects directory that is unique for the solution.
--
function config.getuniqueobjdir(cfg)
-- compute the four options for a specific configuration
local function getobjdirs(cfg)
local dirs = { }
dirs[1] = path.getabsolute(path.join(project.getlocation(cfg.project), cfg.objdir or "obj"))
dirs[2] = path.join(dirs[1], cfg.platform or "")
dirs[3] = path.join(dirs[2], cfg.buildcfg)
dirs[4] = path.join(dirs[3], cfg.project.name)
return dirs
end
-- walk all of the configs in the solution, and count the number of
-- times each obj dir gets used
local counts = {}
for sln in premake.solution.each() do
for _, prj in ipairs(sln.projects) do
for testcfg in project.eachconfig(prj, "objdir") do
local dirs = getobjdirs(testcfg)
for _, dir in ipairs(dirs) do
counts[dir] = (counts[dir] or 0) + 1
end
end
end
end
-- now test for dirs for the request configuration, and use the
-- first one that isn't in conflict
local dirs = getobjdirs(cfg)
for _, dir in ipairs(dirs) do
if counts[dir] == 1 then
local prjlocation = project.getlocation(cfg.project)
return path.getrelative(prjlocation, dir)
end
end
end

View File

@ -29,11 +29,14 @@
-- @param filterterms
-- An optional list of filter terms. Only configuration blocks which
-- match all of the terms in the list will be included in the result.
-- @returns
-- @param filterfield
-- An optional configuration field name. If set, that specific field,
-- and only that field, will be baked and returned.
-- @return
-- A configuration object.
--
function oven.bake(container, filterTerms)
function oven.bake(container, filterTerms, filterField)
filterTerms = filterTerms or {}
-- keyword/term tests are case-insensitive; convert all terms to lowercase
@ -45,7 +48,7 @@
-- If I'm baking a project, start with the values from the solution level
local cfg
if container.solution then
cfg = oven.bake(container.solution, filterTerms)
cfg = oven.bake(container.solution, filterTerms, filterField)
else
cfg = {}
end
@ -57,7 +60,7 @@
-- into my configuration-in-progress, if they pass the keyword filter
for _, block in ipairs(container.blocks) do
if oven.filter(block, filterTerms) then
oven.merge(cfg, block)
oven.merge(cfg, block, filterField)
end
end
@ -130,21 +133,59 @@
-- Merge from an individual block into the configuration object.
--
-- @param cfg
-- The configuration currently being built.
-- The configuration currently being built; will contain the new values.
-- @param block
-- The block containing the values to merge.
-- @param filterField
-- An optional configuration field name. If present, only this specific
-- field will be merged.
-- @return
-- The configuration object, which is also modified in place.
--
function oven.merge(cfg, block)
for key, value in pairs(block) do
if not nomerge[key] then
if type(value) == "table" then
cfg[key] = oven.mergetables(cfg[key] or {}, value)
else
cfg[key] = value
function oven.merge(cfg, block, filterField)
if filterField then
oven.mergefield(cfg, filterField, block[filterField])
else
for key, value in pairs(block) do
if not nomerge[key] then
oven.mergefield(cfg, key, value)
end
end
end
return cfg
end
--
-- Merges a single field from a configuration block into a baked
-- configuration object.
-- @param cfg
-- The configuration currently being built; will contain the new values.
-- @param name
-- The name of the field being merged.
-- @param value
-- The value of the field being merged.
--
function oven.mergefield(cfg, name, value)
-- is this field part of the Premake API? If no, just copy and done
local field = premake.fields[name]
if not field then
cfg[name] = value
return
end
if field.kind == "keyvalue" or field.kind == "keypath" then
cfg[name] = cfg[name] or {}
for key, keyvalue in pairs(value) do
cfg[name][key] = oven.mergetables(cfg[name][key] or {}, keyvalue)
end
elseif type(value) == "table" then
cfg[name] = oven.mergetables(cfg[name] or {}, value)
else
cfg[name] = value
end
end
@ -161,7 +202,11 @@
function oven.mergetables(original, additions)
for _, item in ipairs(additions) do
table.insert(original, item)
-- prevent duplicates
if not original[item] then
original[item] = item
table.insert(original, item)
end
end
return original
end

View File

@ -15,11 +15,14 @@
--
-- @param prj
-- The project object to query.
-- @param field
-- An optional field name. If specified, only that field will be
-- included in the resulting configuration object.
-- @return
-- An iterator function returning configuration objects.
--
function project.eachconfig(prj)
function project.eachconfig(prj, field)
local buildconfigs = prj.solution.configurations or {}
local platforms = prj.solution.platforms or {}
@ -37,7 +40,7 @@
return nil
end
return project.getconfig(prj, buildconfigs[i], platforms[j])
return project.getconfig(prj, buildconfigs[i], platforms[j], field)
end
end
@ -52,12 +55,15 @@
-- The name of the build configuration on which to filter.
-- @param platform
-- Optional; the name of the platform on which to filter.
-- @param field
-- An optional field name. If specified, only that field will be
-- included in the resulting configuration object.
-- @return
-- A configuration object.
--
function project.getconfig(prj, buildcfg, platform)
local cfg = premake5.oven.bake(prj, { buildcfg, platform })
function project.getconfig(prj, buildcfg, platform, field)
local cfg = premake5.oven.bake(prj, { buildcfg, platform }, field)
cfg.buildcfg = buildcfg
cfg.platform = platform

View File

@ -38,7 +38,7 @@
<Configuration
Name="Debug|Win32"
OutputDirectory="."
IntermediateDirectory="obj\Debug\MyProject"
IntermediateDirectory="obj\Debug"
ConfigurationType="1"
CharacterSet="2"
>
@ -72,3 +72,18 @@
Name="Debug x64|x64"
]]
end
--
-- The output directory should use backslashes
--
function suite.usesX64Architecture_onX64Platform()
targetdir("../bin")
prepare()
test.capture [[
<Configuration
Name="Debug|Win32"
OutputDirectory="..\bin"
]]
end

View File

@ -1,99 +0,0 @@
--
-- tests/baking/test_merging.lua
-- Verifies different field types are merged properly during baking.
-- Copyright (c) 2011 Jason Perkins and the Premake project
--
T.baking_merging = { }
local suite = T.baking_merging
--
-- Setup code
--
local sln, prj, cfg
function suite.setup()
sln = solution "MySolution"
configurations { "Debug", "Release" }
end
local function prepare()
premake.bake.buildconfigs()
prj = premake.solution.getproject(sln, 1)
end
--
-- String value tests
--
function suite.Strings_AreReplaced()
kind "SharedLib"
project "MyProject"
kind "StaticLib"
prepare()
test.isequal("StaticLib", prj.kind)
end
function suite.Strings_KeepPreviousValue()
kind "SharedLib"
project "MyProject"
prepare()
test.isequal("SharedLib", prj.kind)
end
--
-- List tests
--
function suite.Lists_KeepPreviousValue()
project "MyProject"
prepare()
test.isequal("Debug:Release", table.concat(prj.configurations, ":"))
end
function suite.Lists_AreJoined()
defines { "SOLUTION" }
project "MyProject"
defines { "PROJECT" }
prepare()
test.isequal("SOLUTION:PROJECT", table.concat(prj.defines, ":"))
end
function suite.Lists_RemoveDuplicates()
defines { "SOLUTION", "DUPLICATE" }
project "MyProject"
defines { "PROJECT", "DUPLICATE" }
prepare()
test.isequal("SOLUTION:DUPLICATE:PROJECT", table.concat(prj.defines, ":"))
end
function suite.Lists_FlattensNestedTables()
defines { "ROOT", { "NESTED" } }
project "MyProject"
prepare()
test.isequal("ROOT:NESTED", table.concat(prj.defines, ":"))
end
--
-- Key/value tests
--
function suite.KeyValue_AreMerged()
vpaths { ["Solution"] = "*.sln" }
project "MyProject"
vpaths { ["Project"] = "*.prj" }
prepare()
test.isequal({"*.sln"}, prj.vpaths["Solution"])
test.isequal({"*.prj"}, prj.vpaths["Project"])
end
function suite.KeyValue_MergesValues()
vpaths { ["Solution"] = "*.sln", ["Project"] = "*.prj" }
project "MyProject"
vpaths { ["Project"] = "*.prjx" }
prepare()
test.isequal({"*.prj","*.prjx"}, prj.vpaths["Project"])
end

92
tests/config/test_objdir.lua Executable file
View File

@ -0,0 +1,92 @@
--
-- tests/config/test_objdir.lua
-- Test the config object's build target accessor.
-- Copyright (c) 2012 Jason Perkins and the Premake project
--
T.config_objdir = { }
local suite = T.config_objdir
local config = premake5.config
--
-- Setup and teardown
--
local sln, prj, cfg
function suite.setup()
_ACTION = "test"
sln = solution("MySolution")
prj = project("MyProject")
system "macosx"
end
local function prepare()
local platforms = sln.platforms or {}
cfg = premake5.project.getconfig(prj, "Debug", platforms[1])
return config.getuniqueobjdir(cfg)
end
--
-- Objects directory should "obj" by default.
--
function suite.directoryIsObj_onNoValueSet()
configurations { "Debug" }
local dir = prepare()
test.isequal("obj", dir)
end
--
-- If a conflict occurs between platforms, the platform names should
-- be used to make unique.
--
function suite.directoryIncludesPlatform_onConflictAndPlatform()
configurations { "Debug" }
platforms { "x32", "x64" }
local dir = prepare()
test.isequal("obj/x32", dir)
end
--
-- If a conflict occurs between build configurations, the build
-- configuration names should be used to make unique.
--
function suite.directoryIncludesBuildCfg_onConflictAndNoPlatforms()
configurations { "Debug", "Release" }
local dir = prepare()
test.isequal("obj/Debug", dir)
end
--
-- If a conflict occurs between both build configurations and platforms,
-- both should be used to make unique.
--
function suite.directoryIncludesBuildCfg_onConflictAndNoPlatforms()
configurations { "Debug", "Release" }
platforms { "x32", "x64" }
local dir = prepare()
test.isequal("obj/x32/Debug", dir)
end
--
-- If a conflict occurs between projects, the project name should be
-- used to make unique.
--
function suite.directoryIncludesBuildCfg_onConflictAndNoPlatforms()
configurations { "Debug", "Release" }
project "MyProject2"
local dir = prepare()
test.isequal("obj/Debug/MyProject", dir)
end

View File

@ -49,19 +49,6 @@
end
--
-- A backslash path separator should be used when the target
-- action requires one.
--
function suite.directoryUsesBackslash_onWindows()
_ACTION = "vs2008"
targetdir "../bin"
i = prepare()
test.isequal("..\\bin", i.directory)
end
--
-- Base name should use the project name by default.
--

View File

@ -59,6 +59,7 @@
-- Project API tests
dofile("test_project.lua")
dofile("config/test_objdir.lua")
dofile("config/test_targetinfo.lua")
dofile("project/test_baking.lua")
dofile("project/test_eachconfig.lua")
@ -67,7 +68,6 @@
-- Baking tests
dofile("base/test_baking.lua")
dofile("baking/test_merging.lua")
-- Clean tests
dofile("actions/test_clean.lua")

View File

@ -1,7 +1,7 @@
--
-- tests/project/test_baking.lua
-- Test the Premake 5.0 oven.
-- Copyright (c) 2011 Jason Perkins and the Premake project
-- Test the Premake oven, which handles flattening of configurations.
-- Copyright (c) 2011-2012 Jason Perkins and the Premake project
--
T.project_baking = { }
@ -53,6 +53,7 @@
test.isequal(0, #cfg.defines)
end
--
-- Values defined at the solution level should be included in configurations
-- built from the solution.
@ -139,9 +140,9 @@
function suite.configValuePresent_ifMatchingFilterTerm()
configuration("Debug")
defines("DEBUG")
kind "SharedLib"
cfg = oven.bake(sln, {"Debug"})
test.isequal("DEBUG", cfg.defines[1])
test.isequal("SharedLib", cfg.kind)
end
@ -174,3 +175,60 @@
cfg = oven.bake(sln)
test.isnil(cfg.keywords)
end
--
-- Requests for a single field should return just that value.
--
function suite.fieldValueReturned_onFilterFieldPresent()
configuration("Debug")
kind "SharedLib"
cfg = oven.bake(sln, {"Debug"}, "kind")
test.isequal("SharedLib", cfg.kind)
end
function suite.otherFieldsNotReturned_onFilterFieldPresent()
configuration("Debug")
kind("SharedLib")
defines("DEBUG")
cfg = oven.bake(sln, {"Debug"}, "kind")
test.isnil(cfg.defines)
end
--
-- Duplicate values should be removed from list values.
--
function suite.removesDuplicateValues()
defines { "SOLUTION", "DUPLICATE" }
prj = project("MyProject")
defines { "PROJECT", "DUPLICATE" }
cfg = oven.bake(prj, {"Debug"})
test.isequal("SOLUTION|DUPLICATE|PROJECT", table.concat(cfg.defines, "|"))
end
--
-- Multiple calls to key-value functions should be merged into a single key-value table.
-- I don't have any config-level key-value fields yet, so have to bake project instead.
--
function suite.keyValuesAreMerged_onMultipleKeys()
vpaths { ["Solution"] = "*.sln" }
prj = project("MyProject")
vpaths { ["Project"] = "*.prj" }
cfg = oven.merge(oven.merge({}, sln), prj)
test.isequal({"*.sln"}, cfg.vpaths["Solution"])
test.isequal({"*.prj"}, cfg.vpaths["Project"])
end
function suite.keyValuesAreMerged_onMultipleValues()
vpaths { ["Solution"] = "*.sln", ["Project"] = "*.prj" }
prj = project "MyProject"
vpaths { ["Project"] = "*.prjx" }
cfg = oven.merge(oven.merge({}, sln), prj)
test.isequal({"*.prj","*.prjx"}, cfg.vpaths["Project"])
end