From b3fdb1457d0c972db91bc82a2820b7af4c5d5f4d Mon Sep 17 00:00:00 2001 From: Jason Perkins Date: Fri, 12 Jul 2013 11:07:26 -0400 Subject: [PATCH] Merge more file configuration functionality into new fileconfig class --- src/actions/make/make_cpp.lua | 11 +- src/actions/vstudio/vs2005_csproj.lua | 4 +- src/actions/vstudio/vs200x_vcproj.lua | 12 +- src/actions/vstudio/vs2010_vcxproj.lua | 12 +- src/project/config.lua | 39 ------ src/project/fileconfig.lua | 171 +++++++++++++++++-------- src/project/project.lua | 12 +- tests/config/test_fileconfig.lua | 154 ---------------------- tests/premake4.lua | 1 - 9 files changed, 147 insertions(+), 269 deletions(-) delete mode 100644 tests/config/test_fileconfig.lua diff --git a/src/actions/make/make_cpp.lua b/src/actions/make/make_cpp.lua index ce2df42c..be936fcb 100644 --- a/src/actions/make/make_cpp.lua +++ b/src/actions/make/make_cpp.lua @@ -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 diff --git a/src/actions/vstudio/vs2005_csproj.lua b/src/actions/vstudio/vs2005_csproj.lua index fe705c86..e316e94e 100644 --- a/src/actions/vstudio/vs2005_csproj.lua +++ b/src/actions/vstudio/vs2005_csproj.lua @@ -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" diff --git a/src/actions/vstudio/vs200x_vcproj.lua b/src/actions/vstudio/vs200x_vcproj.lua index d97dd5ba..ae175b53 100644 --- a/src/actions/vstudio/vs200x_vcproj.lua +++ b/src/actions/vstudio/vs200x_vcproj.lua @@ -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. diff --git a/src/actions/vstudio/vs2010_vcxproj.lua b/src/actions/vstudio/vs2010_vcxproj.lua index c268651d..8057302c 100644 --- a/src/actions/vstudio/vs2010_vcxproj.lua +++ b/src/actions/vstudio/vs2010_vcxproj.lua @@ -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,'%s', 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 diff --git a/src/project/config.lua b/src/project/config.lua index 43294c94..0b322852 100755 --- a/src/project/config.lua +++ b/src/project/config.lua @@ -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 diff --git a/src/project/fileconfig.lua b/src/project/fileconfig.lua index b8f114fe..35d759ef 100644 --- a/src/project/fileconfig.lua +++ b/src/project/fileconfig.lua @@ -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 diff --git a/src/project/project.lua b/src/project/project.lua index 42ddd9eb..679d5f51 100755 --- a/src/project/project.lua +++ b/src/project/project.lua @@ -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) diff --git a/tests/config/test_fileconfig.lua b/tests/config/test_fileconfig.lua deleted file mode 100644 index 76ec8141..00000000 --- a/tests/config/test_fileconfig.lua +++ /dev/null @@ -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 - diff --git a/tests/premake4.lua b/tests/premake4.lua index 42e45750..98c0b511 100644 --- a/tests/premake4.lua +++ b/tests/premake4.lua @@ -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")