203 lines
5.3 KiB
Lua
203 lines
5.3 KiB
Lua
--
|
|
-- detoken.lua
|
|
--
|
|
-- Expands tokens.
|
|
--
|
|
-- Copyright (c) 2011-2014 Jason Perkins and the Premake project
|
|
--
|
|
|
|
local p = premake
|
|
p.detoken = {}
|
|
|
|
local detoken = p.detoken
|
|
|
|
|
|
--
|
|
-- Expand tokens in a value.
|
|
--
|
|
-- @param value
|
|
-- The value containing the tokens to be expanded.
|
|
-- @param environ
|
|
-- An execution environment for any token expansion. This is a list of
|
|
-- key-value pairs that will be inserted as global variables into the
|
|
-- token expansion runtime environment.
|
|
-- @param field
|
|
-- The definition of the field which stores the value.
|
|
-- @param basedir
|
|
-- If provided, path tokens encountered in non-path fields (where
|
|
-- field.paths is set to false) will be made relative to this location.
|
|
-- @return
|
|
-- The value with any contained tokens expanded.
|
|
--
|
|
|
|
function detoken.expand(value, environ, field, basedir)
|
|
function expandtoken(token, e, f)
|
|
-- fetch the path variable from the action, if needed
|
|
local varMap = {}
|
|
if f.pathVars or e.overridePathVars then
|
|
local action = p.action.current()
|
|
if action then
|
|
varMap = action.pathVars or {}
|
|
end
|
|
end
|
|
|
|
-- fetch the pathVars from the enviroment.
|
|
local envMap = e.pathVars or {}
|
|
|
|
-- enable access to the global environment
|
|
setmetatable(e, {__index = _G})
|
|
|
|
local isAbs = false
|
|
local err
|
|
local result
|
|
local success
|
|
|
|
-- if the token starts with a !, don't try making it relative.
|
|
local dontMakeRelative = token:startswith('!')
|
|
if dontMakeRelative then
|
|
token = token:sub(2, -1)
|
|
end
|
|
|
|
-- If this token is in my path variable mapping tables, replace the
|
|
-- value with the one from the map. This needs to go here because
|
|
-- I don't want to make the result relative, but I don't want the
|
|
-- absolute path handling below.
|
|
local mapped = envMap[token] or varMap[token]
|
|
if mapped then
|
|
err = nil
|
|
result = mapped
|
|
if type(result) == "function" then
|
|
success, result = pcall(result, e)
|
|
if not success then
|
|
return nil, result
|
|
end
|
|
end
|
|
|
|
if (type(result) == "table") then
|
|
isAbs = result.absolute
|
|
result = result.token
|
|
else
|
|
isAbs = path.isabsolute(result)
|
|
end
|
|
else
|
|
-- convert the token into a function to execute
|
|
local func
|
|
func, err = load("return " .. token, nil, 't', e)
|
|
if not func then
|
|
return nil, "load error: " .. err
|
|
end
|
|
|
|
-- run it and get the result
|
|
success, result = pcall(func)
|
|
if not success then
|
|
err = result
|
|
result = nil
|
|
else
|
|
err = nil
|
|
result = result or ""
|
|
end
|
|
|
|
if result ~= nil then
|
|
-- ensure we got a string.
|
|
result = tostring(result)
|
|
|
|
-- If the result is an absolute path, and it is being inserted into
|
|
-- a NON-path value, I need to make it relative to the project that
|
|
-- will contain it. Otherwise I ended up with an absolute path in
|
|
-- the generated project, and it can no longer be moved around.
|
|
if path.hasdeferredjoin(result) then
|
|
result = path.resolvedeferredjoin(result)
|
|
end
|
|
isAbs = path.isabsolute(result)
|
|
if isAbs and not f.paths and basedir and not dontMakeRelative then
|
|
result = path.getrelative(basedir, result)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- If the result is an absolute path, and it is being inserted into
|
|
-- a path value, place a special marker at the start of it. After
|
|
-- all results have been processed, I can look for these markers to
|
|
-- find the last absolute path expanded.
|
|
--
|
|
-- Example: the value "/home/user/myprj/%{cfg.objdir}" expands to:
|
|
-- "/home/user/myprj//home/user/myprj/obj/Debug".
|
|
--
|
|
-- By inserting a marker this becomes:
|
|
-- "/home/user/myprj/[\0]/home/user/myprj/obj/Debug".
|
|
--
|
|
-- I can now trim everything before the marker to get the right
|
|
-- result, which should always be the last absolute path specified:
|
|
-- "/home/user/myprj/obj/Debug"
|
|
|
|
if result ~= nil and isAbs and f.paths then
|
|
result = "\0" .. result
|
|
end
|
|
return result, err
|
|
end
|
|
|
|
function expandvalue(value, e, f)
|
|
if type(value) ~= "string" then
|
|
return value
|
|
end
|
|
|
|
local count
|
|
repeat
|
|
value, count = value:gsub("%%{(.-)}", function(token)
|
|
local result, err = expandtoken(token:gsub("\\", "\\\\"), e, f)
|
|
if err then
|
|
error(err .. " in token: " .. token, 0)
|
|
end
|
|
if not result then
|
|
error("Token returned nil, it may not exist: " .. token, 0)
|
|
end
|
|
return result
|
|
end)
|
|
until count == 0
|
|
|
|
-- if a path, look for a split out embedded absolute paths
|
|
if f.paths then
|
|
local i, j
|
|
repeat
|
|
i, j = value:find("\0")
|
|
if i then
|
|
value = value:sub(i + 1)
|
|
end
|
|
until not i
|
|
end
|
|
return value
|
|
end
|
|
|
|
local expand_cache = {}
|
|
|
|
function recurse(value, e, f)
|
|
if type(value) == "table" then
|
|
local res_table = {}
|
|
|
|
for k, v in pairs(value) do
|
|
if tonumber(k) ~= nil then
|
|
res_table[k] = recurse(v, e, f)
|
|
else
|
|
local nk = recurse(k, e, f)
|
|
res_table[nk] = recurse(v, e, f)
|
|
end
|
|
end
|
|
|
|
return res_table
|
|
else
|
|
local res = expand_cache[value]
|
|
if res == nil then
|
|
if type(value) == "string" and path.hasdeferredjoin(value) then
|
|
value = path.resolvedeferredjoin(value)
|
|
end
|
|
res = expandvalue(value, e, f)
|
|
expand_cache[value] = res
|
|
end
|
|
return res
|
|
end
|
|
end
|
|
|
|
return recurse(value, environ, field or {})
|
|
end
|
|
|