1. Premake
  2. Untitled project
  3. premake-dev

Commits

Jason Perkins  committed adc26d2

Ported list handling to new configuration objects

  • Participants
  • Parent commits 31302d5
  • Branches default

Comments (0)

Files changed (20)

File src/base/api.lua

View file
 	local configset = premake.configset
 
 
-	premake.fields = 
-	{
-	}
+	premake.fields = {}
 		
 
 --
 				return api.remove(field, value)
 			end
 		end
+		
+		-- if the field needs special handling, tell the config
+		-- set system about it
+		local merge = field.kind:endswith("-list")
+		configset.registerfield(field.name, { merge = merge })
 	end
 
 
 
 --
 -- Callback for all API functions; everything comes here first, and then
--- parceled out to the individual set...() functions.
+-- gets parceled out to the individual set...() functions.
 --
 
 	function api.callback(field, value)
 		end)
 		
 		if not status then
-			error(result.msg, 3)
+			if type(result) == "table" then
+				result = result.msg
+			end
+			error(result, 3)
 		end
 	end
 
 		-- right now, ignore calls with no value; later might want to
 		-- return the current baked value
 		if not value then return end
-		
-		-- hack: start a new configuration block if I can, so that the
-		-- remove will be processed in the same context as it appears in
-		-- the script. Can be removed when I rewrite the internals to
-		-- be truly declarative
-		if field.scope == "config" then
-			configuration(api.scope.configuration.terms)
-		end
-		
-		local target = api.gettarget(field.scope)
-		
-		-- start a removal list, and make it the target for my values
-		target.removes = {}
-		target.removes[field.name] = {}
-		target = target.removes[field.name]
 
-		-- some field kinds have a removal function to process the value
+		-- process the values list
 		local kind = api.getbasekind(field)
 		local remover = api["remove" .. kind] or table.insert
 
-		-- iterate the list of values and add them all to the list
-		local function addvalue(value)
-			-- recurse into tables
+		local removes = {}
+		
+		function recurse(value)
 			if type(value) == "table" then
 				for _, v in ipairs(value) do
-					addvalue(v)
+					recurse(v)
 				end
-			
-			-- insert simple values
 			else
-				remover(target, value)
+				remover(removes, value)
 			end
 		end
+
+		recurse(value)
 		
-		addvalue(value)
+		local target = api.gettarget(field.scope)
+		configset.removevalues(target.configset, field.name, removes)
 	end
 
 
 --
 
 	function api.setarray(target, name, field, value)
+		-- if the target is the project, configset will be set and I can push
+		-- the value there. Otherwise I was called to store into some other kind
+		-- of object (i.e. an array or list)		
+		target = target.configset or target
+		
 		-- put simple values in an array
 		if type(value) ~= "table" then
 			value = { value }
 --
 
 	function api.setlist(target, name, field, value)
-		-- start with the existing list, or an empty one
-		target[name] = target[name] or {}
-		target = target[name]
-		
 		-- find the contained data type
 		local kind = api.getbasekind(field)
 		local setter = api["set" .. kind]
 
-		-- function to add values
-		local function addvalue(value)
-			-- recurse into tables
+		-- am I setting a configurable object, or some kind of subfield?
+		local result
+		if name == field.name then
+			target = target.configset
+			result = {}
+		else
+			result = target[name]
+		end
+
+		-- process all of the values, according to the data type
+		local result = {}
+		function recurse(value)
 			if type(value) == "table" then
 				for _, v in ipairs(value) do
-					addvalue(v)
+					recurse(v)
 				end
-			
-			-- insert simple values
 			else
-				setter(target, #target + 1, field, value)
+				setter(result, #result + 1, field, value)
 			end
 		end
-		
-		addvalue(value)
+		recurse(value)
+
+		target[name] = result
 	end
 
 
 --
 
 	function api.setobject(target, name, field, value)
+		target = target.configset or target
 		target[name] = value
 	end
 
 			table.insert(cfg.keywords, path.wildcards(word):lower())
 		end
 
+		--[[
 		-- initialize list-type fields to empty tables
 		for name, field in pairs(premake.fields) do
 			if field.kind:endswith("-list") then
 				cfg[name] = { }
 			end
 		end
-		
+		--]]
+
 		-- this is the new place for storing scoped objects
 		api.scope.configuration = cfg
 

File src/base/configset.lua

View file
 	local configset = premake.configset
 	local criteria = premake.criteria
 
-	
+	configset._fields = {}
+
+
 --
 -- Create a new configuration set.
 --
 
 
 --
+-- Register a field that requires special handling.
+--
+-- @param name
+--    The name of the field to register.
+-- @param behavior
+--    A table containing the flags:
+--
+--     merge - if set, the field will be treated as a list, and multiple
+--             values will be merged together when fetched.
+--     keys  - if set, the field will be treated an associative array (sets
+--             of key-value pairs) instead of an indexed array.
+--
+
+	function configset.registerfield(name, behavior)
+		configset._fields[name] = behavior
+	end
+
+
+--
 -- Create a new block of configuration field-value pairs, with the provided
 -- set of context terms to control their application.
 --
 --
 
 	function configset.addvalue(cset, fieldname, value)
-		cset._current[fieldname] = value
+		local current = cset._current
+		local field = configset._fields[fieldname]
+		if field and field.merge then
+			current[fieldname] = current[fieldname] or {}
+			table.insert(current[fieldname], value)
+		else
+			current[fieldname] = value
+		end
+	end
+
+
+--
+-- Remove values from a configuration set.
+--
+-- @param cset
+--    The configuration set from which to remove.
+-- @param fieldname
+--    The name of the field holding the values to be removed.
+-- @param values
+--    A list of values to be removed.
+--
+
+	function configset.removevalues(cset, fieldname, values)
+		-- removes are always processed first; starting a new block here
+		-- ensures that they will be processed in the proper order
+		local current = cset._current
+		configset.addblock(cset, current._criteria.terms, current._basedir)
+
+		values = table.flatten(values)
+		for i, value in ipairs(values) do
+			values[i] = path.wildcards(value):lower()
+		end
+
+		-- add a list of removed values to the block
+		current = cset._current
+		current._removes = {}
+		current._removes[fieldname] = values
+	end
+
+
+--
+-- Check to see if an individual configuration block applies to the
+-- given context and filename.
+--
+
+	local function testblock(block, context, filename)
+		-- Make file tests relative to the blocks base directory,
+		-- so path relative pattern matches will work.
+		if block._basedir and filename then
+			filename = path.getrelative(block._basedir, filename)
+		end
+		return criteria.matches(block._criteria, context, filename)
+	end
+
+
+--
+-- Retrieve a directly assigned value from the configuration set. No merging
+-- takes place; the last value set is the one returned.
+--
+
+	local function fetchassign(cset, fieldname, context, filename)
+		local n = #cset._blocks
+		for i = n, 1, -1 do
+			local block = cset._blocks[i]
+			if block[fieldname] and testblock(block, context, filename) then
+				return block[fieldname]
+			end
+		end
+		
+		if cset._parent ~= cset then
+			return fetchassign(cset._parent, fieldname, context, filename)
+		end
+	end
+
+
+--
+-- Retrieve a merged value from the configuration set; all values are
+-- assembled together into a single result.
+--
+
+	local function fetchmerge(cset, fieldname, context, filename)
+		local result = {}
+
+		-- grab values from the parent set first
+		if cset._parent ~= cset then
+			result = fetchmerge(cset._parent, fieldname, context, filename)
+		end
+
+		function add(value)
+			-- recurse into tables to flatten out the list as I go
+			if type(value) == "table" then
+				for _, v in ipairs(value) do
+					add(v)
+				end
+			else
+				-- if the value is already in the result, remove it; enables later
+				-- blocks to adjust the order of earlier values
+				if result[value] then
+					table.remove(result, table.indexof(result, value))
+				end
+
+				-- add the value with both an index and a key (for fast existence checks)
+				table.insert(result, value)
+				result[value] = value
+			end
+		end
+		
+		function remove(patterns)
+			for _, pattern in ipairs(patterns) do
+				local i = 1
+				while i <= #result do
+					local value = result[i]:lower()
+					if value:match(pattern) == value then
+						result[result[i]] = nil
+						table.remove(result, i)
+					else
+						i = i + 1
+					end
+				end
+			end
+		end
+
+		for _, block in ipairs(cset._blocks) do
+			if testblock(block, context, filename) then
+				if block._removes and block._removes[fieldname] then
+					remove(block._removes[fieldname])
+				end
+				
+				local value = block[fieldname]
+				if value then
+					add(value)
+				end
+			end
+		end
+		
+		return result
 	end
 
 
 --
 
 	function configset.fetchvalue(cset, fieldname, context, filename)
-		local value = nil
+		local value
 
-		if cset._parent ~= cset then
-			value = configset.fetchvalue(cset._parent, fieldname, context, filename)
-		end
-
-		for _, block in ipairs(cset._blocks) do
-			-- Make file tests relative to the blocks base directory,
-			-- so path relative pattern matches will work.
-			local fn = filename
-			if block._basedir and filename then
-				fn = path.getrelative(block._basedir, filename)
-			end
-
-			if criteria.matches(block._criteria, context, fn) then
-				value = block[fieldname] or value
+		-- should this field be merged or assigned?
+		local field = configset._fields[fieldname]
+		local merge = field and field.merge
+		
+		if merge then
+			value = fetchmerge(cset, fieldname, context, filename)
+		else
+			value = fetchassign(cset, fieldname, context, filename)			
+			-- if value is an object, return a copy of it, so that they can
+			-- modified by the caller without altering the source data
+			if type(value) == "table" then
+				value = table.deepcopy(value)
 			end
 		end
 		
 		return value
 	end
-
-
+	
+	
 --
 -- Metatable allows configuration sets to be used like objects in the API.
 -- Setting a value adds it to the currently active block. Getting a value

File src/base/context.lua

View file
 	function context.new(cfgset, environ, filename)
 		local ctx = {}
 		ctx._cfgset = cfgset
-		ctx._environ = environ or {}
+		ctx.environ = environ or {}
 		ctx._filename = { filename } or {}
 		ctx._terms = {}
 		
 			local field = premake.fields[key]
 			if field and field.tokens then
 				local ispath = field.kind:startswith("path")
-				value = premake.detoken.expand(value, ctx._environ, ispath)
+				value = premake.detoken.expand(value, ctx.environ, ispath)
 			end
 			
 			-- store the result for later lookups

File src/base/detoken.lua

View file
 			return result
 		end
 
-		local count
-		repeat
-			value, count = value:gsub("%%{(.-)}", function(token)
-				local result, err = expandtoken(token, environ)
-				if not result then
-					error(err, 0)
-				end
-				return result
-			end)
-		until count == 0
-
-		-- if a path, look for a split out embedded absolute paths
-		if ispath then
-			local i, j
+		function expandvalue(value)
+			local count
 			repeat
-				i, j = value:find("\0")
-				if i then
-					value = value:sub(i + 1)
-				end
-			until not i
+				value, count = value:gsub("%%{(.-)}", function(token)
+					local result, err = expandtoken(token, environ)
+					if not result then
+						error(err, 0)
+					end
+					return result
+				end)
+			until count == 0
+	
+			-- if a path, look for a split out embedded absolute paths
+			if ispath 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
 
-		return value
+		function recurse(value)
+			if type(value) == "table" then
+				for k, v in pairs(value) do
+					value[k] = recurse(v)
+				end
+				return value
+			else
+				return expandvalue(value)
+			end
+		end
+				
+		return recurse(value)
 	end
 

File src/base/solution.lua

View file
 		-- add to master list keyed by both name and index
 		table.insert(premake.solution.list, sln)
 		premake.solution.list[name] = sln
-			
-		-- attach a type descriptor
-		setmetatable(sln, { __type="solution" })
 
 		sln.name = name
 		sln.blocks = {}
 		cset.filename = name
 		sln.configset = cset
 
+		-- attach a type descriptor
+		setmetatable(sln, {
+			__type = "solution",
+			__index = function(sln, key)
+				return sln.configset[key]
+			end,
+		})
+
 		return sln
 	end
 
 		local result = oven.merge({}, sln)
 		result.baked = true
 		result.blocks = sln.blocks
+
+
+		-- TODO: HACK, TRANSITIONAL, REMOVE: pass requests for missing values 
+		-- through to the config context. Eventually all values will be in the 
+		-- context and the cfg wrapper can be done away with
+		result.context = ctx
+		sln.context = ctx
+
+		setmetatable(result, {
+			__index = function(sln, key)
+				return sln.context[key]
+			end,
+		})
+		setmetatable(sln, getmetatable(result))
+		
 		
 		-- bake all of the projects in the list, and store that result
 		local projects = {}
 		-- build a master list of solution-level configuration/platform pairs
 		result.configs = solution.bakeconfigs(result)
 
-		-- TODO: HACK, TRANSITIONAL, REMOVE: pass requests for missing values 
-		-- through to the config context. Eventually all values will be in the 
-		-- context and the cfg wrapper can be done away with
-		result.context = ctx
-		sln.context = ctx
-
-		setmetatable(result, {
-			__index = function(sln, key)
-				return sln.context[key]
-			end,
-		})
-		setmetatable(sln, getmetatable(result))
-		
 		return result
 	end
 

File src/project/config.lua

View file
 			-- and cache the result
 			cfg.files[filename] = filecfg
 
-			-- NEW:
-			-- set up an environment for expanding tokens contained 
-			-- by this file configuration
+			-- 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
 			local environ = {
-				sln = cfg.solution,
-				prj = cfg.project,
-				cfg = cfg,
 				file = filecfg
 			}
+			for envkey, envval in pairs(cfg.context.environ) do
+				environ[envkey] = envval
+			end
 			
 			-- create a context to provide access to this file's information
 			local ctx = context.new(cfg.project.configset, environ, filename)

File src/project/project.lua

View file
 		cset.filename = name
 		cset.uuid = os.uuid()
 		prj.configset = cset
-			
+
 		-- attach a type descriptor
-		prj.storage = {}
 		setmetatable(prj, {
-			__type="project",
+			__type = "project",
 			__index = function(prj, key)
 				return prj.configset[key]
 			end,
 		-- TODO: OLD, REMOVE: build an old-style configuration to wrap context, for now 
 		local result = oven.merge(oven.merge({}, sln), prj)
 		result.solution = sln
-		result.platforms = result.platforms or {}
 		result.blocks = prj.blocks
 		result.baked = true
 
 		cfg.project = prj
 		cfg.context = ctx
 
-		environ.cfg = cfg
+		-- File this under "too clever by half": I want path tokens (targetdir, etc.)
+		-- to expand to relative paths, so they can be used in custom build rules and
+		-- other places where it would be impractical to detect and convert them. So
+		-- create a proxy object with an attached metatable that converts path fields
+		-- on the fly as they are requested.
+		local proxy = {}
+		setmetatable(proxy, {
+			__index = function(proxy, key)
+				local field = premake.fields[key]
+				if field and field.kind == "path" then
+					return premake5.project.getrelative(cfg.project, cfg[key])
+				end
+				return cfg[key]
+			end
+		})
+
+		environ.cfg = proxy
+
 
 		-- TODO: HACK, TRANSITIONAL, REMOVE: pass requests for missing values 
 		-- through to the config context. Eventually all values will be in the 
 --
 
 	function project.bakeconfigmap(prj)
-		-- Apply any mapping tables to the project's initial configuration set,
-		-- which includes configurations inherited from the solution. These rules
-		-- may cause configurations to be added ore removed from the project.
 		local configs = table.fold(prj.configurations or {}, prj.platforms or {})
 		for i, cfg in ipairs(configs) do
 			configs[i] = project.mapconfig(prj, cfg[1], cfg[2])

File tests/actions/vstudio/vc2010/test_filters.lua

View file
 
 	function suite.filter_onVpath()
 		files { "src/hello.c", "hello.h" }
-		vpaths { ["Source Files"] = "**.c" }		
+		vpaths { ["Source Files"] = "**.c" }
 		prepare("ClCompile")
 		test.capture [[
 	<ItemGroup>

File tests/base/test_baking.lua

---
--- tests/test_baking.lua
--- Automated test suite for the configuration baking functions.
--- Copyright (c) 2009, 2010 Jason Perkins and the Premake project
---
-
-	T.baking = { }
-	local suite = T.baking
-
---
--- Setup code
---
-
-	local prj, cfg
-	function suite.setup()
-		_ACTION = "gmake"
-		
-		solution "MySolution"
-		configurations { "Debug", "Release" }
-		platforms { "x32", "PS3" }
-		
-		defines "SOLUTION"
-		
-		configuration "Debug"
-		defines "SOLUTION_DEBUG"
-		
-		prj = project "MyProject"
-		language "C"
-		kind "SharedLib"
-		targetdir "../bin"
-		defines "PROJECT"
-		
-		configuration "Debug"
-		defines "DEBUG"
-		  
-		configuration "Release"
-		defines "RELEASE"
-
-		configuration "native"
-		defines "NATIVE"
-		
-		configuration "x32"
-		defines "X86_32"
-		
-		configuration "x64"
-		defines "X86_64"
-	end
-
-	local function prepare()
-		premake.bake.buildconfigs()
-		prj = premake.getconfig(prj)
-		cfg = premake.getconfig(prj, "Debug")
-	end
-	
-
---
--- Tests
---
-
-	function suite.ProjectWideSettings()
-		prepare()
-		test.isequal("SOLUTION:PROJECT:NATIVE", table.concat(prj.defines,":"))
-	end
-
-	
-	function suite.BuildCfgSettings()
-		prepare()
-		test.isequal("SOLUTION:SOLUTION_DEBUG:PROJECT:DEBUG:NATIVE", table.concat(cfg.defines,":"))
-	end
-
-
-	function suite.PlatformSettings()
-		prepare()
-		local cfg = premake.getconfig(prj, "Debug", "x32")
-		test.isequal("SOLUTION:SOLUTION_DEBUG:PROJECT:DEBUG:X86_32", table.concat(cfg.defines,":"))
-	end
-
-			
-	function suite.SetsConfigName()
-		prepare()
-		local cfg = premake.getconfig(prj, "Debug", "x32")
-		test.isequal("Debug", cfg.name)
-	end
-
-	
-	function suite.SetsPlatformName()
-		prepare()
-		local cfg = premake.getconfig(prj, "Debug", "x32")
-		test.isequal("x32", cfg.platform)
-	end
-
-	
-	function suite.SetsPlatformNativeName()
-		test.isequal("Native", cfg.platform)
-	end
-
-	
-	function suite.SetsShortName()
-		prepare()
-		local cfg = premake.getconfig(prj, "Debug", "x32")
-		test.isequal("debug32", cfg.shortname)
-	end
-
-	
-	function suite.SetsNativeShortName()
-		prepare()
-		test.isequal("debug", cfg.shortname)
-	end
-
-	
-	function suite.SetsLongName()
-		prepare()
-		local cfg = premake.getconfig(prj, "Debug", "x32")
-		test.isequal("Debug|x32", cfg.longname)
-	end
-
-	
-	function suite.SetsNativeLongName()
-		prepare()
-		test.isequal("Debug", cfg.longname)
-	end
-
-	
-	function suite.SetsProject()
-		prepare()
-		local cfg = premake.getconfig(prj, "Debug", "x32")
-		test.istrue(prj.project == cfg.project)
-	end
-
-
-
---
--- Target system testing
---
-
-	function suite.SetsTargetSystem_OnNative()
-		prepare()
-		test.isequal(os.get(), cfg.system)
-	end
-
-	function suite.SetTargetSystem_OnCrossCompiler()
-		prepare()
-		local cfg = premake.getconfig(prj, "Debug", "PS3")
-		test.isequal("PS3", cfg.system)
-	end
-
-
-	
---
--- Configuration-specific kinds
---
-
-	function suite.SetsConfigSpecificKind()
-		configuration "Debug"
-		kind "ConsoleApp"
-		prepare()
-		test.isequal("ConsoleApp", cfg.kind)
-	end
-
-
---
--- Platform kind translation
---
-
-	function suite.SetsTargetKind_OnSupportedKind()
-		prepare()
-		test.isequal("SharedLib", cfg.kind)
-	end
-
-	function suite.SetsTargetKind_OnUnsupportedKind()
-		prepare()
-		local cfg = premake.getconfig(prj, "Debug", "PS3")
-		test.isequal("StaticLib", cfg.kind)
-	end

File tests/base/test_configset.lua

View file
 		configset.addvalue(cset, "buildaction", "copy")
 		test.isequal("copy", configset.fetchvalue(cset, "buildaction", {}, path.join(os.getcwd(), "hello.c")))
 	end
+
+
+--
+-- List fields should return an empty list of not set.
+--
+
+     function suite.lists_returnsEmptyTable_onNotSet()
+          test.isequal({}, configset.fetchvalue(cset, "buildoptions", {}))
+     end
+
+--
+-- List fields should merge values fetched from different blocks.
+--
+
+	function suite.lists_mergeValues_onFetch()
+		configset.addvalue(cset, "buildoptions", "v1")
+		configset.addblock(cset, { "windows" })
+		configset.addvalue(cset, "buildoptions", "v2")
+		test.isequal({"v1", "v2"}, configset.fetchvalue(cset, "buildoptions", {"windows"}))
+	end
+
+
+--
+-- Multiple adds to a list field in the same block should be merged together.
+--
+
+	function suite.lists_mergeValues_onAdd()
+		configset.addvalue(cset, "buildoptions", "v1")
+		configset.addvalue(cset, "buildoptions", "v2")
+		test.isequal({"v1", "v2"}, configset.fetchvalue(cset, "buildoptions", {"windows"}))
+	end
+
+
+--
+-- Fetched lists should be both keyed and indexed.
+--
+
+	function suite.lists_includeValueKeys()
+		configset.addvalue(cset, "buildoptions", { "v1", "v2" })
+		local x = configset.fetchvalue(cset, "buildoptions", {})
+		test.isequal("v2", x.v2)
+	end
+
+
+--
+-- Check removing a value with an exact match.
+--
+
+	function suite.remove_onExactValueMatch()
+		configset.addvalue(cset, "flags", { "Symbols", "Optimize", "NoRTTI" })
+		configset.removevalues(cset, "flags", { "Optimize" })
+		test.isequal({ "Symbols", "NoRTTI" }, configset.fetchvalue(cset, "flags", {}))
+	end
+
+	function suite.remove_onMultipleValues()
+		configset.addvalue(cset, "flags", { "Symbols", "NoExceptions", "Optimize", "NoRTTI" })
+		configset.removevalues(cset, "flags", { "NoExceptions", "NoRTTI" })
+		test.isequal({ "Symbols", "Optimize" }, configset.fetchvalue(cset, "flags", {}))
+	end
+
+
+--
+-- Remove should also accept wildcards.
+--
+
+	function suite.remove_onWildcard()
+		configset.addvalue(cset, "defines", { "WIN32", "WIN64", "LINUX", "MACOSX" })
+		configset.removevalues(cset, "defines", { "WIN*" })
+		test.isequal({ "LINUX", "MACOSX" }, configset.fetchvalue(cset, "defines", {}))
+	end

File tests/base/test_detoken.lua

View file
 		x = detoken.expand("bin/debug/%{cfg.basedir}", environ, true)
 		test.isequal(os.getcwd(), x)
 	end
+
+
+--
+-- If the value being expanded is a table, iterate over all of its values.
+--
+
+	function suite.expandsAllItemsInList()
+		x = detoken.expand({ "A%{1}", "B%{2}", "C%{3}" }, environ)
+		test.isequal({ "A1", "B2", "C3" }, x)
+	end

File tests/config/test_fileconfig.lua

View file
 		configuration "not Debug"
 		buildoptions "-Xc"
 		prepare()
-		test.isnil(fcfg.buildoptions)
+		test.isequal({}, fcfg.buildoptions)
 	end
 
 
 		configuration "*.c"
 		buildoptions "-Xc"
 		prepare("src/hello.c")
-		test.isnil(fcfg.buildoptions)
+		test.isequal({}, fcfg.buildoptions)
 	end
 

File tests/oven/test_basics.lua

---
--- tests/oven/test_basics.lua
--- Test the Premake oven, which handles flattening of configurations.
--- Copyright (c) 2011-2012 Jason Perkins and the Premake project
---
-
-	T.oven_basics = { }
-	local suite = T.oven_basics
-	local oven = premake5.oven
-
-
---
--- Setup and teardown
---
-
-	local sln, prj
-
-	function suite.setup()
-		sln = solution("MySolution")
-	end
-
-
---
--- Test pulling "project global" values, which are associated with
--- all configurations in the project.
---
-
-	function suite.callPullProjectLevelConfig()
-		prj = project("MyProject")
-		files { "hello.cpp" }
-		cfg = oven.bake(prj, sln, {}, "files")
-		test.isequal("hello.cpp", cfg.files[1]:sub(-9))
-	end
-
-
---
--- The keywords field should NOT be included in the configuration objects
--- returned by the backing process.
---
-
-	function suite.noKeywordsInBakingResults()
-		configuration("Debug")
-		defines("DEBUG")
-		cfg = oven.bake(sln)
-		test.isnil(cfg.keywords)
-	end
-
-
---
--- Requests for a single field should return just that value.
---
-
-	function suite.otherFieldsNotReturned_onFilterFieldPresent()
-		configuration("Debug")
-		kind("SharedLib")
-		defines("DEBUG")
-		cfg = oven.bake(sln, nil, {"Debug"}, "kind")
-		test.isnil(cfg.defines)
-	end

File tests/oven/test_keyvalues.lua

---
--- tests/oven/test_keyvalues.lua
--- Test the handling of key-value data types in the oven.
--- Copyright (c) 2011-2012 Jason Perkins and the Premake project
---
-
-	T.oven_keyvalues = { }
-	local suite = T.oven_keyvalues
-	local oven = premake5.oven
-
-
---
--- Setup and teardown
---
-
-	local sln, prj
-
-	function suite.setup()
-		sln = solution("MySolution")
-	end
-
-
---
--- Make sure that key-value types show up in the baked result.
---
-
-	function suite.valuePresentInResult()
-		configmap { ["key"] = "value" }		
-		local cfg = oven.merge({}, sln)
-		test.isequal("value", cfg.configmap["key"][1])
-	end
-
-	
---
--- When multiple key-value blocks are present, the resulting keys
--- should be merged into a single result.
---
-
-	function suite.keysMerged_onMultipleValues()
-		configmap { ["sln"] = "slnvalue" }
-		prj = project("MyProject")
-		configmap { ["prj"] = "prjvalue" }
-		local cfg = oven.merge(prj, sln)
-		test.istrue(cfg.configmap.sln ~= nil and cfg.configmap.prj ~= nil)
-	end

File tests/oven/test_lists.lua

---
--- tests/oven/test_lists.lua
--- Test the Premake oven list handling.
--- Copyright (c) 2011-2012 Jason Perkins and the Premake project
---
-
-	T.oven_lists = { }
-	local suite = T.oven_lists
-	local oven = premake5.oven
-
-
---
--- Setup and teardown
---
-
-	local sln, prj
-
-	function suite.setup()
-		sln = solution("MySolution")
-	end
-
-
---
--- API values that are not set in any configuration should be initialized
--- with empty defaults (makes downstream usage easier).
---
-
-	function suite.emptyDefaultsSet_forMissingApiValues()
-		local cfg = oven.bake(sln)
-		test.isequal(0, #cfg.defines)
-	end
-
-
---
--- Values defined at the solution level should be included in configurations
--- built from the solution.
---
-
-	function suite.solutionValuePresent_onSolutionConfig()
-		defines "SOLUTION"
-		local cfg = oven.bake(sln)
-		test.isequal("SOLUTION", table.concat(cfg.defines))
-	end
-
-
---
--- Values defined at the project level should be included in configurations
--- built from the project.
---
-
-	function suite.projectValuePreset_onProjectConfig()
-		prj = project "MyProject"
-		defines "PROJECT"
-		local cfg = oven.bake(prj, sln)
-		test.isequal("PROJECT", table.concat(cfg.defines))
-	end
-
-
---
--- Values defined at the solution level should also be present in 
--- configurations built from projects within that solution.
---
-
-	function suite.solutionValuePresent_onProjectConfig()
-		defines("SOLUTION")
-		prj = project("MyProject")
-		local cfg = oven.bake(prj, sln)
-		test.isequal("SOLUTION", table.concat(cfg.defines))
-	end
-
-
---
--- When a list value is present at both the solution and project
--- level, the values should be merged, with the solution values
--- coming first.
---
-
-	function suite.solutionAndProjectValuesMerged_onProjectConfig()
-		defines("SOLUTION")
-		prj = project("MyProject")
-		defines("PROJECT")
-		local cfg = oven.bake(prj, sln)
-		test.isequal("SOLUTION|PROJECT", table.concat(cfg.defines, "|"))
-	end
-
-
---
--- A value specified in a block with more general terms should appear
--- in more specific configurations.
---
-
-	function suite.valueFromGeneralConfigPreset_onMoreSpecificConfig()
-		defines("SOLUTION")
-		local cfg = oven.bake(sln, nil, {"Debug"})
-		test.isequal("SOLUTION", table.concat(cfg.defines))
-	end
-
-	function suite.valueFromGeneralConfigPreset_onMoreSpecificConfig()
-		configuration("Debug")
-		defines("DEBUG")
-		local cfg = oven.bake(sln, nil, {"Debug","Windows"})
-		test.isequal("DEBUG", table.concat(cfg.defines))
-	end
-
-
---
--- Values present in a specific configuration should only be included
--- if a matching filter term is present.
---
-
-	function suite.configValueNotPresent_ifNoMatchingFilterTerm()
-		configuration("Debug")
-		defines("DEBUG")
-		cfg = oven.bake(sln)
-		test.isequal(0, #cfg.defines)
-	end
-
-
---
--- When values for a field are present in solution and project configurations,
--- all should be copied, with the solution values first.
---
-
-	function suite.solutionAndProjectAndConfigValuesMerged()
-		defines("SOLUTION")
-		configuration("Debug")
-		defines("SLN_DEBUG")
-		prj = project("MyProject")
-		defines("PROJECT")
-		configuration("Debug")
-		defines("PRJ_DEBUG")
-		cfg = oven.bake(prj	, sln, {"Debug"})
-		test.isequal("SOLUTION|SLN_DEBUG|PROJECT|PRJ_DEBUG", table.concat(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, sln, {"Debug"})
-		test.isequal("SOLUTION|PROJECT|DUPLICATE", table.concat(cfg.defines, "|"))
-	end

File tests/oven/test_objects.lua

---
--- tests/oven/test_objects.lua
--- Test Premake oven handling of objects.
--- Copyright (c) 2011-2012 Jason Perkins and the Premake project
---
-
-	T.oven_objects = { }
-	local suite = T.oven_objects
-	local oven = premake5.oven
-
-
---
--- Setup and teardown
---
-
-	local sln, prj
-
-	function suite.setup()
-		sln = solution("MySolution")
-	end
-
-
---
--- Object values should be merged into baked results.
---
-
-	function suite.objectValuesAreMerged()
-		buildrule { description="test" }
-		cfg = oven.bake(sln)
-		test.isequal("test", cfg.buildrule.description)
-	end
-
-	function suite.objectValueOverwritten_onMultipleValues()
-		buildrule { description="sln" }
-		prj = project("MyProject")
-		buildrule { description="prj" }
-		cfg = oven.bake(prj, sln, {"Debug"})
-		test.isequal("prj", cfg.buildrule.description)
-	end

File tests/oven/test_removes.lua

---
--- tests/oven/test_removes.lua
--- Test the Premake oven ability to remove values from lists.
--- Copyright (c) 2011-2012 Jason Perkins and the Premake project
---
-
-	T.oven_removes = { }
-	local suite = T.oven_removes
-	local project = premake5.project
-
-
---
--- Setup and teardown
---
-
-	local sln, prj, cfg
-
-	function suite.setup()
-		sln, prj = test.createsolution()
-	end
-	
-	local function prepare()
-		cfg = premake5.project.getconfig(prj, "Debug")
-	end
-
-
-
---
--- Check removing a value with an exact match.
---
-
-	function suite.remove_onExactValueMatch()
-		flags { "Symbols", "Optimize", "NoRTTI" }
-		removeflags "Optimize"
-		prepare()
-		test.isequal({ "Symbols", "NoRTTI" }, cfg.flags)
-	end
-
-	function suite.remove_onMultipleValues()
-		flags { "Symbols", "NoExceptions", "Optimize", "NoRTTI" }
-		removeflags { "NoExceptions", "NoRTTI" }
-		prepare()
-		test.isequal({ "Symbols", "Optimize" }, cfg.flags)
-	end
-
-
---
--- Remove should also accept wildcards.
---
-
-	function suite.remove_onWildcard()
-		defines { "WIN32", "WIN64", "LINUX", "MACOSX" }
-		removedefines { "WIN*" }
-		prepare()
-		test.isequal({ "LINUX", "MACOSX" }, cfg.defines)
-	end
-
---
--- Remove should removed both indexed and keyed values.
---
-
-	function suite.remove_onExactValueMatch()
-		flags { "Symbols", "Optimize", "NoRTTI" }
-		removeflags "Optimize"
-		prepare()
-		test.isnil(cfg.flags.Optimize)
-	end
-
---
--- Remove should also work with file paths.
---
-
-	function suite.remove_onFileField()
-		files { "hello.c", "goodbye.c" }
-		removefiles { "goodbye.c" }
-		prepare()
-		test.isequal({ path.join(os.getcwd(), "hello.c") }, cfg.files)
-	end
-
-	function suite.remove_onExcludesWildcard()
-		files { "hello.c", "goodbye.c" }
-		excludes { "goodbye.*" }
-		prepare()
-		test.isequal({ path.join(os.getcwd(), "hello.c") }, cfg.files)
-	end
-
-
---
--- Remove should work on container-level fields too.
---
-
-	function suite.remove_onContainerField()
-		removeconfigurations { "Release" }
-		prepare()
-		test.isequal({ "Debug" }, cfg.project.configurations)
-	end

File tests/oven/test_tokens.lua

---
--- tests/oven/test_tokens.lua
--- Test the Premake oven's handling of tokens.
--- Copyright (c) 2012 Jason Perkins and the Premake project
---
-
-	T.oven_tokens = { }
-	local suite = T.oven_tokens
-	local oven = premake5.oven
-	local project = premake5.project
-	local config = premake5.config
-
-
---
--- Setup and teardown
---
-
-	local sln, prj, cfg
-
-	function suite.setup()
-		premake.api.register { 
-			name = "testapi", 
-			kind = "string", 
-			scope = "config",
-			tokens = true
-		}
-
-		sln = test.createsolution()
-	end
-
-	function suite.teardown()
-		testapi = nil
-	end
-	
-	local function prepare()
-		-- some values are only accessible after a full bake
-		sln = premake.solution.bake(sln)
-		prj = premake.solution.getproject_ng(sln, 1)
-		cfg = project.getconfig(prj, "Debug")
-	end
-
-
---
--- Verify that multiple values can be expanded.
---
-
-	function suite.doesExpandMultipleValues()
-		testapi "bin/%{prj.name}/%{cfg.buildcfg}"
-		prepare()
-		test.isequal("bin/MyProject/Debug", cfg.testapi)
-	end
-
-
---
--- Verify that file-specific values are expanded.
---
-
-	function suite.doesExpandTokens_onFileCfg()
-		files { "hello.c" }
-		configuration "hello.c"
-			testapi "%{cfg.buildcfg}"
-		prepare()
-		local fcfg = config.getfileconfig(cfg, os.getcwd().."/hello.c")
-		test.isequal("Debug", fcfg.testapi)
-	end
-
-
---
--- Verify handling of tokens in a build rule.
---
-
-	function suite.doesExpandFileTokens_inBuildRules()
-		files { "shaders/hello.cg" }
-		configuration { "**.cg" }
-			buildrule {
-				commands = {
-					"cgc --profile gp5vp %{file.path} -o %{cfg.objdir}/%{file.basename}.gxp",
-				},
-				outputs = {
-					"%{cfg.objdir}/%{file.basename}.o"
-				}
-			}
-		prepare()
-		local fcfg = config.getfileconfig(cfg, os.getcwd().."/shaders/hello.cg")
-		test.isequal("cgc --profile gp5vp shaders/hello.cg -o obj/Debug/hello.gxp", fcfg.buildrule.commands[1])
-	end
-
-
---
--- Make sure that the same token source can be applied to multiple targets.
---
-
-	function suite.canReuseTokenSources()
-		files { "shaders/hello.cg", "shaders/goodbye.cg" }
-		configuration { "**.cg" }
-			buildrule {
-				commands = {
-					"cgc --profile gp5vp %{file.path} -o %{cfg.objdir}/%{file.basename}.gxp",
-				},
-				outputs = {
-					"%{cfg.objdir}/%{file.basename}.o"
-				}
-			}
-		prepare()
-		local fcfg = config.getfileconfig(cfg, os.getcwd().."/shaders/hello.cg")
-		test.isequal("cgc --profile gp5vp shaders/hello.cg -o obj/Debug/hello.gxp", fcfg.buildrule.commands[1])
-		fcfg = config.getfileconfig(cfg, os.getcwd().."/shaders/goodbye.cg")
-		test.isequal("cgc --profile gp5vp shaders/goodbye.cg -o obj/Debug/goodbye.gxp", fcfg.buildrule.commands[1])
-	end
-
-
---
--- Verify the global namespace is still accessible.
---
-
-	function suite.canUseGlobalFunctions()
-		testapi "%{iif(true, 'a', 'b')}"
-		prepare()
-		test.isequal("a", cfg.testapi)
-	end
-
-
---
--- Make sure I can use tokens in the objects directory and targets,
--- which can also be a tokens themselves.
---
-
-	function suite.canUseTokensInObjDir()
-		objdir "tmp/%{prj.name}_%{cfg.buildcfg}"
-		testapi "%{cfg.objdir}"
-		prepare()
-		test.isequal(path.join(os.getcwd(),"tmp/MyProject_Debug"), cfg.testapi)
-	end
-
-	function suite.canUseTokensInBuildTarget()
-		targetdir "bin/%{prj.name}_%{cfg.buildcfg}"
-		testapi "%{cfg.targetdir}"
-		prepare()
-		test.isequal(path.join(os.getcwd(),"bin/MyProject_Debug"), cfg.testapi)
-	end
-
-
---
--- Verify that solution-level values are expanded too.
---
-
-	function suite.canUseTokens_onSolution()
-		solution "MySolution"
-		location "build/%{sln.name}"
-		prepare()
-		test.isequal(os.getcwd() .. "/build/MySolution", sln.location)
-	end
-
-
---
--- Verify that target information is available.
---
-
-	function suite.canAccessBuildTarget()
-		_OS = "windows"
-		targetdir "%{cfg.buildcfg}"
-		testapi "%{cfg.buildtarget.relpath}"
-		prepare()
-		test.isequal("Debug/MyProject.exe", cfg.testapi)
-	end
-
-	function suite.canAccessLinkTarget()
-		_OS = "windows"
-		kind "SharedLib"
-		testapi "%{cfg.linktarget.relpath}"
-		prepare()
-		test.isequal("MyProject.lib", cfg.testapi)
-	end
-
-
---
--- Verify that tokens can expand to absolute paths.
---
-
-	function suite.canExpandToAbsPath()
-		targetdir "%{os.getcwd()}/%{cfg.buildcfg}"
-		prepare()
-		test.isequal(path.join(os.getcwd(), "Debug"), cfg.targetdir)
-	end
-

File tests/premake4.lua

View file
 	dofile("api/test_register.lua")
 	dofile("api/test_string_kind.lua")
 
-	-- Baking tests
-	-- dofile("base/test_baking.lua")
-	dofile("oven/test_basics.lua")
-	dofile("oven/test_keyvalues.lua")
-	dofile("oven/test_lists.lua")
-	dofile("oven/test_objects.lua")
-	dofile("oven/test_removes.lua")
-	dofile("oven/test_tokens.lua")
-
 	-- Toolset tests
 	dofile("tools/test_dotnet.lua")
 	dofile("tools/test_gcc.lua")

File tests/project/test_eachconfig.lua

View file
 			configurations ( buildcfgs )
 		end
 		prj = premake.solution.getproject_ng(sln, 1)
-		for cfg in premake5.project.eachconfig(prj, field) do
+		for cfg in premake5.project.eachconfig(prj) do
 			_p(2,'%s:%s', cfg.buildcfg or "", cfg.platform or "")
 		end
 	end
 	
 --
 -- Test mapping a build configuration to a build config/platform pair.
+-- This will cause a second platform to appear in the project, alongside
+-- the one defined by the solution.
 --
 
 	function suite.mapsBuildCfg_toBuildCfgAndPlatform()