123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- --
- -- fileconfig.lua
- -- The set of configuration information for a specific file.
- -- Copyright (c) 2011-2014 Jason Perkins and the Premake project
- --
- local p = premake
- p.fileconfig = {}
- local fileconfig = p.fileconfig
- local context = p.context
- local project = p.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.fcfg_mt = {}
- fileconfig.fsub_mt = {}
- --
- -- Create a new file configuration object.
- --
- -- @param fname
- -- The absolute path to the file.
- -- @param prj
- -- The project which contains the file.
- -- @return
- -- A new file configuration object.
- --
- function fileconfig.new(fname, prj)
- local environ = { }
- local fcfg = context.new(prj, environ)
- context.copyFilters(fcfg, prj)
- context.addFilter(fcfg, "files", fname:lower())
- for key, value in pairs(prj.environ) do
- environ[key] = value
- end
- environ.file = fcfg
- context.compile(fcfg)
- fcfg.project = prj
- fcfg.workspace = prj.workspace
- fcfg.configs = {}
- fcfg.abspath = fname
- context.basedir(fcfg, prj.location)
- -- 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
- --
- -- Associate a new project configuration with a file. It is possible for a
- -- file to only appear in a subset of a project's configurations.
- --
- -- @param fcfg
- -- The file configuration to which the project configuration should be
- -- associated.
- -- @param cfg
- -- The project configuration to associate.
- --
- function fileconfig.addconfig(fcfg, cfg)
- local prj = cfg.project
- local wks = cfg.workspace
- -- Create a new context object for this configuration-file pairing.
- -- The context has the ability to pull out configuration settings
- -- specific to the file.
- local environ = {}
- local fsub = context.new(prj, environ)
- context.copyFilters(fsub, fcfg)
- context.mergeFilters(fsub, cfg)
- fcfg.configs[cfg] = fsub
- -- set up an environment for expanding tokens contained by this file
- -- configuration; based on the configuration's environment so that
- -- any magic set up there gets maintained
- for key, value in pairs(cfg.environ) do
- environ[key] = value
- end
- for key, value in pairs(fcfg.environ) do
- environ[key] = value
- end
- -- finish the setup
- context.compile(fsub)
- fsub.abspath = fcfg.abspath
- fsub.vpath = fcfg.vpath
- fsub.config = cfg
- fsub.project = prj
- fsub.workspace = wks
- -- 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(fsub, prj.location)
- return setmetatable(fsub, fileconfig.fsub_mt)
- end
- --
- -- Retrieve the configuration settings for a particular file/project
- -- configuration pairing.
- --
- -- @param fcfg
- -- The file configuration to query.
- -- @param cfg
- -- The project configuration to query.
- -- @return
- -- The configuration context for the pairing, or nil if this project
- -- configuration is not associated with this file.
- --
- function fileconfig.getconfig(fcfg, cfg)
- return fcfg.configs[cfg]
- 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 fileconfig.hasCustomBuildRule(fcfg)
- return fcfg and (#fcfg.buildcommands > 0) and (#fcfg.buildoutputs > 0)
- end
- --
- -- Checks to see if the file configuration contains any unique information,
- -- or if it is the same as its parent configuration.
- --
- -- @param fcfg
- -- A file configuration.
- -- @return
- -- True if the file configuration contains values which differ from the
- -- parent project configuration, false otherwise.
- --
- function fileconfig.hasFileSettings(fcfg)
- if not fcfg then
- return false
- end
- for key, field in pairs(p.fields) do
- if field.scopes[1] == "config" then
- local value = fcfg[field.name]
- if value then
- if type(value) == "table" then
- if #value > 0 then
- return true
- end
- else
- return true
- end
- end
- end
- end
- return false
- 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 fall back to the context's own value lookups.
- --
- local fcfg_mt = fileconfig.fcfg_mt
- fcfg_mt.__index = function(fcfg, key)
- if type(fcfg_mt[key]) == "function" then
- return fcfg_mt[key](fcfg)
- end
- return context.__mt.__index(fcfg, key)
- end
- --
- -- 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(fsub, key)
- if type(fcfg_mt[key]) == "function" then
- return fcfg_mt[key](fsub)
- end
- return context.__mt.__index(fsub, key)
- end
- --
- -- And here are the path building functions.
- --
- function fcfg_mt.basename(fcfg)
- return path.getbasename(fcfg.abspath)
- end
- function fcfg_mt.directory(fcfg)
- return path.getdirectory(fcfg.abspath)
- end
- function fcfg_mt.reldirectory(fcfg)
- return path.getdirectory(fcfg.relpath)
- 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
- function fcfg_mt.extension(fcfg)
- return path.getextension(fcfg.abspath)
- end
|