Merge more file configuration functionality into new fileconfig class

This commit is contained in:
Jason Perkins 2013-07-12 11:07:26 -04:00
parent 3abbbc93f9
commit b3fdb1457d
9 changed files with 147 additions and 269 deletions

View File

@ -9,6 +9,7 @@
local cpp = premake.make.cpp
local project = premake5.project
local config = premake5.config
local fileconfig = premake5.fileconfig
--
@ -160,8 +161,8 @@
-- check to see if this file has custom rules
local rules
for cfg in project.eachconfig(prj) do
local filecfg = config.getfileconfig(cfg, node.abspath)
if config.hasCustomBuildRule(filecfg) then
local filecfg = fileconfig.getconfig(node, cfg)
if fileconfig.hasCustomBuildRule(filecfg) then
rules = true
break
end
@ -196,7 +197,7 @@
function cpp.customfilerules(prj, node)
for cfg in project.eachconfig(prj) do
local filecfg = config.getfileconfig(cfg, node.abspath)
local filecfg = fileconfig.getconfig(node, cfg)
if filecfg then
_x('ifeq ($(config),%s)', cfg.shortname)
@ -286,10 +287,10 @@
local inall = true
local custom = false
for cfg in project.eachconfig(prj) do
local filecfg = config.getfileconfig(cfg, node.abspath)
local filecfg = fileconfig.getconfig(node, cfg)
if filecfg and not filecfg.flags.ExcludeFromBuild then
incfg[cfg] = filecfg
custom = config.hasCustomBuildRule(filecfg)
custom = fileconfig.hasCustomBuildRule(filecfg)
else
inall = false
end

View File

@ -5,10 +5,12 @@
--
premake.vstudio.cs2005 = {}
local vstudio = premake.vstudio
local cs2005 = premake.vstudio.cs2005
local project = premake5.project
local config = premake5.config
local fileconfig = premake5.fileconfig
local dotnet = premake.tools.dotnet
@ -125,7 +127,7 @@
local tr = project.getsourcetree(prj)
premake.tree.traverse(tr, {
onleaf = function(node, depth)
local filecfg = config.getfileconfig(cfg, node.abspath)
local filecfg = fileconfig.getconfig(node, cfg)
local fname = path.translate(node.relpath)
-- Files that live outside of the project tree need to be "linked"

View File

@ -5,11 +5,13 @@
--
premake.vstudio.vc200x = {}
local vstudio = premake.vstudio
local vc200x = premake.vstudio.vc200x
local config = premake5.config
local context = premake.context
local project = premake5.project
local config = premake5.config
local fileconfig = premake5.fileconfig
---
@ -823,7 +825,7 @@
function vc200x.fileConfiguration(cfg, node, depth)
local filecfg = config.getfileconfig(cfg, node.abspath)
local filecfg = fileconfig.getconfig(node, cfg)
-- Generate the individual sections of the file configuration
-- element and capture the results to a buffer. I will only
@ -999,7 +1001,7 @@
function vc200x.compilerToolName(cfg, filecfg, depth)
local name
if config.hasCustomBuildRule(filecfg) then
if fileconfig.hasCustomBuildRule(filecfg) then
name = "VCCustomBuildTool"
else
name = iif(cfg.system == premake.XBOX360, "VCCLX360CompilerTool", "VCCLCompilerTool")
@ -1020,7 +1022,7 @@
function vc200x.customBuildTool(filecfg, depth)
if config.hasCustomBuildRule(filecfg) then
if fileconfig.hasCustomBuildRule(filecfg) then
_x(depth, 'CommandLine="%s"', table.concat(filecfg.buildcommands,'\r\n'))
local outputs = project.getrelative(filecfg.project, filecfg.buildoutputs)
@ -1098,7 +1100,7 @@
if #deps > 0 then
-- This is a little odd: Visual Studio wants the "relative path to project"
-- to relative to the *solution*, rather than the project doing the
-- to be relative to the *solution*, rather than the project doing the
-- referencing. Which, in theory, would break if the project is included
-- in more than one solution. But that's how they do it.

View File

@ -5,10 +5,12 @@
--
premake.vstudio.vc2010 = {}
local vc2010 = premake.vstudio.vc2010
local vstudio = premake.vstudio
local project = premake5.project
local config = premake5.config
local fileconfig = premake5.fileconfig
local tree = premake.tree
@ -444,7 +446,7 @@
for cfg in project.eachconfig(prj) do
local condition = vc2010.condition(cfg)
local filecfg = config.getfileconfig(cfg, file.abspath)
local filecfg = fileconfig.getconfig(file, cfg)
vc2010.excludedFromBuild(cfg, filecfg)
if filecfg then
vc2010.objectFileName(filecfg)
@ -471,8 +473,8 @@
for cfg in project.eachconfig(prj) do
local condition = vc2010.condition(cfg)
local filecfg = config.getfileconfig(cfg, file.abspath)
if config.hasCustomBuildRule(filecfg) then
local filecfg = fileconfig.getconfig(file, cfg)
if fileconfig.hasCustomBuildRule(filecfg) then
local commands = table.concat(filecfg.buildcommands,'\r\n')
_p(3,'<Command %s>%s</Command>', condition, premake.esc(commands))
@ -508,8 +510,8 @@
-- then they all must be marked as custom build
local hasbuildrule = false
for cfg in project.eachconfig(prj) do
local filecfg = config.getfileconfig(cfg, node.abspath)
if config.hasCustomBuildRule(filecfg) then
local filecfg = fileconfig.getconfig(node, cfg)
if fileconfig.hasCustomBuildRule(filecfg) then
hasbuildrule = true
break
end

View File

@ -213,29 +213,6 @@
end
--
-- Retrieve the configuration settings for a specific file.
--
-- @param cfg
-- The configuration object to query.
-- @param filename
-- The full, absolute path of the file to query.
-- @return
-- A configuration object for the file, or nil if the file is
-- not included in this configuration.
--
function config.getfileconfig(cfg, filename)
-- The project contains the cached list of file configurations. If I can't
-- find an entry, it means this file was excluded from that configuration.
local fcfg = cfg.project._.files[filename]
if fcfg then
return fcfg.configs[cfg]
end
return nil
end
--
-- Retrieve linking information for a specific configuration. That is,
-- the path information that is required to link against the library
@ -426,19 +403,3 @@
function config.gettargetinfo(cfg)
return buildtargetinfo(cfg, cfg.kind, "target")
end
--
-- Checks to see if the project or file configuration contains a
-- custom build rule.
--
-- @param cfg
-- A project or file configuration.
-- @return
-- True if the configuration contains settings for a custom
-- build rule.
--
function config.hasCustomBuildRule(cfg)
return cfg and (#cfg.buildcommands > 0) and (#cfg.buildoutputs > 0)
end

View File

@ -8,9 +8,27 @@
local fileconfig = premake5.fileconfig
local context = premake.context
local project = premake5.project
--
-- A little confusing: the file configuration actually contains two objects.
-- The first object, the one that is returned by fileconfig.new() and later
-- passed back in as *the* file configuration object, contains the common
-- project-wide settings for the file. This object also contains a list of
-- "sub-configurations", one for each project configuration to which the file
-- belongs.
--
-- Internally, I'm calling the first object the "file configuration" (fcfg)
-- and the children "file sub-configurations" (fsub). To distinguish them
-- from the project configurations (cfg).
--
-- Define metatables for each of types; more info below.
--
fileconfig.file_mt = {}
fileconfig.fcfg_mt = {}
fileconfig.fsub_mt = {}
--
-- Create a new file configuration object.
@ -25,27 +43,22 @@
function fileconfig.new(fname, prj)
local fcfg = {}
setmetatable(fcfg, fileconfig.file_mt)
-- Compute all the variations on path information for this file once up
-- front; will be reused by each of the configuration supported by this
-- file, and referenced by tokens in the scripts.
fcfg.abspath = fname
fcfg.relpath = premake5.project.getrelative(prj, fname)
fcfg.name = path.getname(fname)
fcfg.basename = path.getbasename(fname)
local vpath = premake5.project.getvpath(prj, fname)
if vpath ~= fname then
fcfg.vpath = vpath
else
fcfg.vpath = fcfg.relpath
end
-- Start a list of configurations supported by this file.
fcfg.project = prj
fcfg.configs = {}
fcfg.abspath = fname
-- Most of the other path properties are computed on demand
-- from the file's absolute path.
setmetatable(fcfg, fileconfig.fcfg_mt)
-- Except for the virtual path, which is expensive to compute, and
-- can be used across all the sub-configurations
local vpath = project.getvpath(prj, fname)
if vpath ~= fcfg.abspath then
fcfg.vpath = vpath
end
return fcfg
end
@ -69,10 +82,10 @@
-- specific to the file.
local environ = {}
local ctx = context.new(cfg.project.configset, environ, fcfg.abspath)
context.copyterms(ctx, cfg)
local fsub = context.new(cfg.project.configset, environ, fcfg.abspath)
context.copyterms(fsub, cfg)
fcfg.configs[cfg] = ctx
fcfg.configs[cfg] = fsub
-- set up an environment for expanding tokens contained by this file
-- configuration; based on the configuration's environment so that
@ -84,36 +97,29 @@
-- Make the context being built here accessible to tokens
environ.file = ctx
-- Merge in the file path information (virtual paths, etc.) that are
-- computed at the project level, for token expansions to use
for key, value in pairs(fcfg) do
if type(value) == "string" then
ctx[key] = value
end
end
environ.file = fsub
-- finish the setup
context.compile(ctx)
ctx.path = fcfg.relpath
ctx.config = cfg
ctx.project = cfg.project
context.compile(fsub)
fsub.abspath = fcfg.abspath
fsub.vpath = fcfg.vpath
fsub.config = cfg
fsub.project = cfg.project
-- Set the context's base directory to the project's file system
-- location. Any path tokens which are expanded in non-path fields
-- (such as the custom build commands) will be made relative to
-- this path, ensuring a portable generated project.
context.basedir(ctx, premake5.project.getlocation(cfg.project))
context.basedir(fsub, project.getlocation(cfg.project))
setmetatable(ctx, fileconfig.fcfg_mt)
setmetatable(fsub, fileconfig.fsub_mt)
end
--
-- Retrieve the configuration settings for a particular file/project
-- configuration pairing.
@ -134,28 +140,93 @@
--
-- The metatable computes most of the path related fields. I do this instead
-- of functions to make it easier to access these values from tokens, and to
-- avoid the memory overhead of all these strings for large solutions.
-- Checks to see if the project or file configuration contains a
-- custom build rule.
--
-- @param cfg
-- A project or file configuration.
-- @return
-- True if the configuration contains settings for a custom
-- build rule.
--
function fileconfig.hasCustomBuildRule(fcfg)
return fcfg and (#fcfg.buildcommands > 0) and (#fcfg.buildoutputs > 0)
end
--
-- Rather than store pre-computed strings for all of the path variations
-- (abspath, relpath, vpath, name, etc.) for each file (there can be quite
-- a lot of them) I assign a metatable to the file configuration objects
-- that will build these values on the fly.
--
-- I am using these pseudo-properties, rather than explicit functions, to make
-- it easier to fetch them script tokens (i.e. %{file.relpath} with no need
-- for knowledge of the internal Premake APIs.
--
--
-- The indexer for the file configurations. If I have a path building function
-- to fulfill the request, call it. Else this is a missing index so return nil.
--
local file_mt = fileconfig.file_mt
local fcfg_mt = fileconfig.fcfg_mt
file_mt.__index = function(file, key)
if type(file_mt[key]) == "function" then
return file_mt[key](file)
fcfg_mt.__index = function(file, key)
if type(fcfg_mt[key]) == "function" then
return fcfg_mt[key](file)
end
end
fcfg_mt.__index = function(fcfg, key)
return file_mt.__index(fcfg, key) or context.__mt.__index(fcfg, key)
--
-- The indexer for the file sub-configurations. Check for a path building
-- function first, and then fall back to the context's own value lookups.
-- TODO: Would be great if this didn't require inside knowledge of context.
--
fileconfig.fsub_mt.__index = function(fcfg, key)
return fcfg_mt.__index(fcfg, key) or context.__mt.__index(fcfg, key)
end
function file_mt.objname(fcfg)
--
-- And here are the path building functions.
--
function fcfg_mt.basename(fcfg)
return path.getbasename(fcfg.abspath)
end
function fcfg_mt.name(fcfg)
return path.getname(fcfg.abspath)
end
function fcfg_mt.objname(fcfg)
if fcfg.sequence ~= nil and fcfg.sequence > 0 then
return fcfg.basename .. fcfg.sequence
else
return fcfg.basename
end
end
function fcfg_mt.path(fcfg)
return fcfg.relpath
end
function fcfg_mt.relpath(fcfg)
return project.getrelative(fcfg.project, fcfg.abspath)
end
function fcfg_mt.vpath(fcfg)
-- This only gets called if no explicit virtual path was set
return fcfg.relpath
end

View File

@ -656,15 +656,9 @@
-- @param sorter
-- An optional comparator function for the sorting pass.
-- @return
-- A tree object containing the source file hierarchy. Leaf nodes
-- representing the individual files contain the fields:
-- abspath - the absolute path of the file
-- relpath - the relative path from the project to the file
-- vpath - the file's virtual path
-- All nodes contain the fields:
-- path - the node's path within the tree
-- realpath - the node's file system path (nil for virtual paths)
-- name - the directory or file name represented by the node
-- A tree object containing the source file hierarchy. Leaf nodes,
-- representing the individual files, are file configuration
-- objects.
--
function project.getsourcetree(prj, sorter)

View File

@ -1,154 +0,0 @@
--
-- tests/config/test_fileconfig.lua
-- Test the config object's file configuration accessor.
-- Copyright (c) 2012 Jason Perkins and the Premake project
--
T.config_fileconfig = { }
local suite = T.config_fileconfig
local project = premake5.project
local config = premake5.config
--
-- Setup and teardown
--
local sln, prj, fcfg
function suite.setup()
sln, prj = test.createsolution()
end
local function prepare(filename)
local cfg = project.getconfig(prj, "Debug")
fcfg = config.getfileconfig(cfg, path.join(os.getcwd(), filename or "hello.c"))
end
--
-- A file specified at the project level should be present in all configurations.
--
function suite.isPresent_onProjectLevel()
files "hello.c"
prepare()
test.isnotnil(fcfg)
end
--
-- A file specified only in the current configuration should return a value.
--
function suite.isPresent_onCurrentConfigOnly()
configuration "Debug"
files "hello.c"
prepare()
test.isnotnil(fcfg)
end
--
-- A file specified only in a different configuration should return nil.
--
function suite.isNotPresent_onDifferentConfigOnly()
configuration "Release"
files "hello.c"
prepare()
test.isnil(fcfg)
end
--
-- A file specified at the project, and excluded in the current configuration
-- should return nil.
--
function suite.isNotPresent_onExcludedInCurrent()
files "hello.c"
configuration "Debug"
excludes "hello.c"
prepare()
test.isnil(fcfg)
end
--
-- A file specified at the project, and excluded in a different configuration
-- should return a value.
--
function suite.isNotPresent_onExcludedInCurrent()
files "hello.c"
configuration "Release"
excludes "hello.c"
prepare()
test.isnotnil(fcfg)
end
--
-- A build option specified on a specific set of files should appear in the
-- file configuration
--
function suite.settingIsPresent_onFileSpecificFilter()
files "hello.c"
configuration "**.c"
buildoptions "-Xc"
prepare()
test.isequal({ "-Xc" }, fcfg.buildoptions)
end
--
-- A "not" filter should not provide the positive match for a
-- file configuration filename mask.
--
function suite.fileIsUnmatched_onNotFilter()
files "hello.c"
configuration "not Debug"
buildoptions "-Xc"
prepare()
test.isequal({}, fcfg.buildoptions)
end
--
-- Check case-sensitivity of file name tests.
--
function suite.fileMatches_onCaseMismatch()
files "Hello.c"
configuration "HeLLo.c"
buildoptions "-Xc"
prepare("Hello.c")
test.isequal({ "-Xc" }, fcfg.buildoptions)
end
--
-- A leading single star should match files in the same
-- folder as the project script, for consistency with files(),
-- but not files in other folders.
--
function suite.singleStarMatches_onSameFolder()
files "hello.c"
configuration "*.c"
buildoptions "-Xc"
prepare()
test.isequal({ "-Xc" }, fcfg.buildoptions)
end
function suite.singleStarNoMatch_onDifferentFolder()
files "src/hello.c"
configuration "*.c"
buildoptions "-Xc"
prepare("src/hello.c")
test.isequal({}, fcfg.buildoptions)
end

View File

@ -67,7 +67,6 @@
dofile("project/test_vpaths.lua")
-- Configuration object tests
dofile("config/test_fileconfig.lua")
dofile("config/test_linkinfo.lua")
dofile("config/test_links.lua")
dofile("config/test_targetinfo.lua")