1--
2-- api.lua
3-- Implementation of the workspace, project, and configuration APIs.
4-- Author Jason Perkins
5-- Copyright (c) 2002-2015 Jason Perkins and the Premake project
6--
7
8	local p = premake
9	p.api = {}
10
11	local api = p.api
12	local configset = p.configset
13
14
15
16---
17-- Set up a place to store the current active objects in each configuration
18-- scope (e.g. wprkspaces, projects, groups, and configurations). This likely
19-- ought to be internal scope, but it is useful for testing.
20---
21
22	api.scope = {}
23
24
25
26---
27-- Define a new class of configuration container. A container can receive and
28-- store configuration blocks, which are what hold the individial settings
29-- from the scripts. A container can also hold one or more kinds of child
30-- containers; a workspace can contain projects, for instance.
31--
32-- @param containerName
33--    The name of the new container type, e.g. "workspace". Used to define a
34--    corresponding global function, e.g. workspace() to create new instances
35--    of the container.
36-- @param parentContainer (optional)
37--    The container that can contain this one. For a project, this would be
38--    the workspace container class.
39-- @param extraScopes (optional)
40--    Each container can hold fields scoped to itself (by putting the container's
41--    class name into its scope attribute), or any of the container's children.
42--    If a container can hold scopes other than these (i.e. "config"), it can
43--    provide a list of those scopes in this argument.
44-- @returns
45--    The newly defined container class.
46---
47
48	function api.container(containerName, parentContainer, extraScopes)
49		local class, err = p.container.newClass(containerName, parentContainer, extraScopes)
50		if not class then
51			error(err, 2)
52		end
53
54		_G[containerName] = function(name)
55			local c = api._setContainer(class, name)
56			if api._isIncludingExternal then
57				c.external = true
58			end
59			return c
60		end
61
62		_G["external" .. containerName] = function(name)
63			local c = _G[containerName](name)
64			c.external = true
65			return c
66		end
67
68		-- for backward compatibility
69		p.alias(_G, "external" .. containerName, "external" .. containerName:capitalized())
70
71		return class
72	end
73
74
75
76---
77-- Register a general-purpose includeExternal() call which works just like
78-- include(), but marks any containers created while evaluating the included
79-- scripts as external. It also, loads the file regardless of how many times
80-- it has been loaded already.
81---
82
83	function includeexternal(fname)
84		local fullPath = p.findProjectScript(fname)
85		local wasIncludingExternal = api._isIncludingExternal
86		api._isIncludingExternal = true
87		fname = fullPath or fname
88		dofile(fname)
89		api._isIncludingExternal = wasIncludingExternal
90	end
91
92	p.alias(_G, "includeexternal", "includeExternal")
93
94
95
96---
97-- Return the global configuration container.
98---
99
100	function api.rootContainer()
101		return api.scope.global
102	end
103
104
105
106---
107-- Activate a new configuration container, making it the target for all
108-- subsequent configuration settings. When you call workspace() or project()
109-- to active a container, that call comes here (see api.container() for the
110-- details on how that happens).
111--
112-- @param class
113--    The container class being activated, e.g. a project or workspace.
114-- @param name
115--    The name of the container instance to be activated. If a container
116--    (e.g. project) with this name does not already exist it will be
117--    created. If name is not set, the last activated container of this
118--    class will be made current again.
119-- @return
120--    The container instance.
121---
122
123	function api._setContainer(class, name)
124		local instance
125
126		-- for backward compatibility, "*" activates the parent container
127		if name == "*" then
128			return api._setContainer(class.parent)
129		end
130
131		-- if name is not set, use whatever was last made current
132		if not name then
133			instance = api.scope[class.name]
134			if not instance then
135				error("no " .. class.name .. " in scope", 3)
136			end
137		end
138
139		-- otherwise, look up the instance by name
140		local parent
141		if not instance and class.parent then
142			parent = api.scope[class.parent.name]
143			if not parent then
144				error("no " .. class.parent.name .. " in scope", 3)
145			end
146			instance = p.container.getChild(parent, class, name)
147		end
148
149		-- if I have an existing instance, create a new configuration
150		-- block for it so I don't pick up an old filter
151		if instance then
152			configset.addFilter(instance, {}, os.getcwd())
153		end
154
155		-- otherwise, a new instance
156		if not instance then
157			instance = class.new(name, parent)
158			if parent then
159				p.container.addChild(parent, instance)
160			end
161		end
162
163		-- clear out any active child containers that might be active
164		-- (recursive call, so needs to be its own function)
165		api._clearContainerChildren(class)
166
167		-- active this container, as well as it ancestors
168		if not class.placeholder then
169			api.scope.current = instance
170		end
171
172		while instance do
173			api.scope[instance.class.name] = instance
174			if instance.class.alias then
175				api.scope[instance.class.alias] = instance
176			end
177			instance = instance.parent
178		end
179
180		return api.scope.current
181	end
182
183	function api._clearContainerChildren(class)
184		for childClass in p.container.eachChildClass(class) do
185			api.scope[childClass.name] = nil
186			if childClass.alias then
187				api.scope[childClass.alias] = nil
188			end
189			api._clearContainerChildren(childClass)
190		end
191	end
192
193
194
195---
196-- Register a new API function. See the built-in API definitions in
197-- _premake_init.lua for lots of usage examples.
198--
199-- A new global function will be created to receive values for the field.
200-- List fields will also receive a `remove...()` function to remove values.
201--
202-- @param field
203--    A table describing the new field, with these keys:
204--
205--     name     The API name of the new field. This is used to create a global
206--              function with the same name, and so should follow Lua symbol
207--              naming conventions. (required)
208--     scope    The scoping level at which this value can be used; see list
209--              below. (required)
210--     kind     The type of values that can be stored into this field; see
211--              list below. (required)
212--     allowed  An array of valid values for this field, or a function which
213--              accepts a value as input and returns the canonical value as a
214--              result, or nil if the input value is invalid. (optional)
215--     tokens   A boolean indicating whether token expansion should be
216--              performed on this field.
217--
218--   The available field scopes are:
219--
220--     project  The field applies to workspaces and projects.
221--     config   The field applies to workspaces, projects, and individual build
222--              configurations.
223--
224--   The available field kinds are:
225--
226--     string     A simple string value.
227--     path       A file system path. The value will be made into an absolute
228--                path, but no wildcard expansion will be performed.
229--     file       One or more file names. Wilcard expansion will be performed,
230--                and the results made absolute. Implies a list.
231--     directory  One of more directory names. Wildcard expansion will be
232--                performed, and the results made absolute. Implies a list.
233--     mixed      A mix of simple string values and file system paths. Values
234--                which contain a directory separator ("/") will be made
235--                absolute; other values will be left intact.
236--     table      A table of values. If the input value is not a table, it is
237--                wrapped in one.
238---
239
240	function api.register(field)
241		-- verify the name
242		local name = field.name
243		if not name then
244			error("missing name", 2)
245		end
246
247		if rawget(_G, name) then
248			error("name '" .. name .. "' in use", 2)
249		end
250
251		-- add this new field to my master list
252		field, err = p.field.new(field)
253		if not field then
254			error(err)
255		end
256
257
258		-- Flag fields which contain filesystem paths. The context object will
259		-- use this information when expanding tokens, to ensure that the paths
260		-- are still well-formed after replacements.
261
262		field.paths = p.field.property(field, "paths")
263
264		-- Add preprocessed, lowercase keys to the allowed and aliased value
265		-- lists to speed up value checking later on.
266
267		if type(field.allowed) == "table" then
268			for i, item in ipairs(field.allowed) do
269				field.allowed[item:lower()] = item
270			end
271		end
272
273		if type(field.aliases) == "table" then
274			local keys = table.keys(field.aliases)
275			for i, key in ipairs(keys) do
276				field.aliases[key:lower()] = field.aliases[key]
277			end
278		end
279
280		-- create a setter function for it
281		_G[name] = function(value)
282			return api.storeField(field, value)
283		end
284
285		if p.field.removes(field) then
286			_G["remove" .. name] = function(value)
287				return api.remove(field, value)
288			end
289		end
290
291		return field
292	end
293
294
295
296---
297-- Unregister a field definition, removing its functions and field
298-- list entries.
299---
300
301	function api.unregister(field)
302		if type(field) == "string" then
303			field = p.field.get(field)
304		end
305		p.field.unregister(field)
306		_G[field.name] = nil
307		_G["remove" .. field.name] = nil
308	end
309
310
311
312---
313-- Create an alias to one of the canonical API functions. This creates
314-- new setter and remover names pointing to the same functions.
315--
316-- @param original
317--    The name of the function to be aliased (a string value).
318-- @param alias
319--    The alias name (another string value).
320---
321
322	function api.alias(original, alias)
323		p.alias(_G, original, alias)
324		if _G["remove" .. original] then
325			p.alias(_G, "remove" .. original, "remove" .. alias)
326		end
327	end
328
329
330
331--
332-- Add a new value to a field's list of allowed values.
333--
334-- @param fieldName
335--    The name of the field to which to add the value.
336-- @param value
337--    The value to add. May be a single string value, or an array
338--    of values.
339--
340
341	function api.addAllowed(fieldName, value)
342		local field = p.field.get(fieldName)
343		if not field then
344			error("No such field: " .. fieldName, 2)
345		end
346
347		if type(value) == "table" then
348			for i, item in ipairs(value) do
349				api.addAllowed(fieldName, item)
350			end
351		else
352			field.allowed = field.allowed or {}
353			if field.allowed[value:lower()] == nil then
354				table.insert(field.allowed, value)
355				field.allowed[value:lower()] = value
356			end
357		end
358	end
359
360
361
362--
363-- Add a new value to a field's list of allowed values.
364--
365-- @param fieldName
366--    The name of the field to which to add the value.
367-- @param value
368--    The value to add. May be a single string value, or an array
369--    of values.
370--
371
372	function api.addAliases(fieldName, value)
373		local field = p.field.get(fieldName)
374		if not field then
375			error("No such field: " .. fieldName, 2)
376		end
377
378		field.aliases = field.aliases or {}
379		for k, v in pairs(value) do
380			field.aliases[k] = v
381			field.aliases[k:lower()] = v
382		end
383	end
384
385
386
387--
388-- Mark an API field as deprecated.
389--
390-- @param name
391--    The name of the field to mark as deprecated.
392-- @param message
393--    A optional message providing more information, to be shown
394--    as part of the deprecation warning message.
395-- @param handler
396--    A function to call when the field is used. Passes the value
397--    provided to the field as the only argument.
398--
399
400	function api.deprecateField(name, message, handler)
401		p.fields[name].deprecated = {
402			handler = handler,
403			message = message
404		}
405	end
406
407
408--
409-- Mark a specific value of a field as deprecated.
410--
411-- @param name
412--    The name of the field containing the value.
413-- @param value
414--    The value or values to mark as deprecated. May be a string
415--    for a single value or an array of multiple values.
416-- @param message
417--    A optional message providing more information, to be shown
418--    as part of the deprecation warning message.
419-- @param addHandler
420--    A function to call when the value is used, receiving the
421--    value as its only argument.
422-- @param removeHandler
423--    A function to call when the value is removed from a list
424--    field, receiving the value as its only argument (optional).
425--
426
427	function api.deprecateValue(name, value, message, addHandler, removeHandler)
428		if type(value) == "table" then
429			for _, v in pairs(value) do
430				api.deprecateValue(name, v, message, addHandler, removeHandler)
431			end
432		else
433			local field = p.fields[name]
434			field.deprecated = field.deprecated or {}
435			field.deprecated[value] = {
436				add = addHandler,
437				remove = removeHandler,
438				message = message
439			}
440		end
441	end
442
443
444--
445-- Control the handling of API deprecations.
446--
447-- @param value
448--    One of "on" to enable the deprecation behavior, "off" to disable it,
449--    and "error" to raise an error instead of logging a warning.
450--
451
452	function api.deprecations(value)
453		value = value:lower()
454		if not table.contains({ "on", "off", "error"}, value) then
455			error("Invalid value: " .. value, 2)
456		end
457		api._deprecations = value:lower()
458	end
459
460	api._deprecations = "on"
461
462
463
464---
465-- Return the target container instance for a field.
466--
467-- @param field
468--    The field being set or fetched.
469-- @return
470--    The currently active container instance if one is available, or nil if
471--    active container is of the wrong class.
472---
473
474	function api.target(field)
475		if p.container.classCanContain(api.scope.current.class, field.scope) then
476			return api.scope.current
477		end
478		return nil
479	end
480
481
482
483--
484-- Callback for all API functions; everything comes here first, and then
485-- gets parceled out to the individual set...() functions.
486--
487
488	function api.storeField(field, value)
489		if value == nil then
490			return
491		end
492
493		if field.deprecated and type(field.deprecated.handler) == "function" then
494			field.deprecated.handler(value)
495			if field.deprecated.message and api._deprecations ~= "off" then
496				local caller = filelineinfo(2)
497				local key = field.name .. "_" .. caller
498				p.warnOnce(key, "the field %s has been deprecated and will be removed.\n   %s\n   @%s\n", field.name, field.deprecated.message, caller)
499				if api._deprecations == "error" then
500					error("deprecation errors enabled", 3)
501				end
502			end
503		end
504
505		local target = api.target(field)
506		if not target then
507			local err = string.format("unable to set %s in %s scope, should be %s", field.name, api.scope.current.class.name, table.concat(field.scopes, ", "))
508			error(err, 3)
509		end
510
511		local status, err = configset.store(target, field, value)
512		if err then
513			error(err, 3)
514		end
515	end
516
517
518
519--
520-- The remover: adds values to be removed to the "removes" field on
521-- current configuration. Removes are keyed by the associated field,
522-- so the call `removedefines("X")` will add the entry:
523--  cfg.removes["defines"] = { "X" }
524--
525
526	function api.remove(field, value)
527		-- right now, ignore calls with no value; later might want to
528		-- return the current baked value
529		if value == nil then return end
530
531		local target = api.target(field)
532		if not target then
533			local err = string.format("unable to remove %s from %s scope, should be %s", field.name, api.scope.current.class.name, table.concat(field.scopes, ", "))
534			error(err, 3)
535		end
536
537		local hasDeprecatedValues = (type(field.deprecated) == "table")
538
539		-- Build a list of values to be removed. If this field has deprecated
540		-- values, check to see if any of those are going to be removed by this
541		-- call (which means matching against any provided wildcards) and call
542		-- the appropriate logic for removing that value.
543
544		local removes = {}
545
546		local function check(value)
547			if field.deprecated[value] then
548				local handler = field.deprecated[value]
549				if handler.remove then handler.remove(value) end
550				if handler.message and api._deprecations ~= "off" then
551					local caller = filelineinfo(8)
552					local key = field.name .. "_" .. value .. "_" .. caller
553					p.warnOnce(key, "the %s value %s has been deprecated and will be removed.\n   %s\n   @%s\n", field.name, value, handler.message, caller)
554					if api._deprecations == "error" then
555						error { msg="deprecation errors enabled" }
556					end
557				end
558			end
559		end
560
561		local function recurse(value)
562			if type(value) == "table" then
563				table.foreachi(value, recurse)
564
565			elseif hasDeprecatedValues and value:contains("*") then
566				local current = configset.fetch(target, field, {
567					matcher = function(cset, block, filter)
568						local current = cset.current
569						return criteria.matches(current._criteria, block._criteria.terms or {}) or
570							   criteria.matches(block._criteria, current._criteria.terms or {})
571					end
572				})
573
574				local mask = path.wildcards(value)
575				for _, item in ipairs(current) do
576					if item:match(mask) == item then
577						recurse(item)
578					end
579				end
580			else
581				local value, err, additional = api.checkValue(field, value)
582				if err then
583					error { msg=err }
584				end
585
586				if field.deprecated then
587					check(value)
588				end
589
590				table.insert(removes, value)
591				if additional then
592					table.insert(removes, additional)
593				end
594			end
595		end
596
597		local ok, err = pcall(function ()
598			recurse(value)
599		end)
600
601		if not ok then
602			if type(err) == "table" then
603				err = err.msg
604			end
605			error(err, 3)
606		end
607
608		configset.remove(target, field, removes)
609	end
610
611
612
613--
614-- Check to see if a value is valid for a particular field.
615--
616-- @param field
617--    The field to check against.
618-- @param value
619--    The value to check.
620-- @param kind
621--    The kind of data currently being checked, corresponding to
622--    one segment of the field's kind string (e.g. "string"). If
623--    not set, defaults to "string".
624-- @return
625--    If the value is valid for this field, the canonical version
626--    of that value is returned. If the value is not valid two
627--    values are returned: nil, and an error message.
628--
629
630	function api.checkValue(field, value, kind)
631		if not field.allowed then
632			return value
633		end
634
635		local canonical, result
636		local lowerValue = value:lower()
637
638		if field.aliases then
639			canonical = field.aliases[lowerValue]
640		end
641
642		if not canonical then
643			if type(field.allowed) == "function" then
644				canonical = field.allowed(value, kind or "string")
645			else
646				canonical = field.allowed[lowerValue]
647			end
648		end
649
650		if not canonical then
651			return nil, "invalid value '" .. value .. "' for " .. field.name
652		end
653
654		if field.deprecated and field.deprecated[canonical] then
655			local handler = field.deprecated[canonical]
656			handler.add(canonical)
657			if handler.message and api._deprecations ~= "off" then
658				local caller =  filelineinfo(9)
659				local key = field.name .. "_" .. value .. "_" .. caller
660				p.warnOnce(key, "the %s value %s has been deprecated and will be removed.\n   %s\n   @%s\n", field.name, canonical, handler.message, caller)
661				if api._deprecations == "error" then
662					return nil, "deprecation errors enabled"
663				end
664			end
665		end
666
667		return canonical
668	end
669
670
671
672---
673-- Reset the API system, clearing out any temporary or cached values.
674-- Used by the automated testing framework to clear state between
675-- individual test runs.
676---
677
678	local numBuiltInGlobalBlocks
679
680	function api.reset()
681		if numBuiltInGlobalBlocks == nil then
682			numBuiltInGlobalBlocks = #api.scope.global.blocks
683		end
684
685		for containerClass in p.container.eachChildClass(p.global) do
686			api.scope.global[containerClass.pluralName] = {}
687		end
688
689		api.scope.current = api.scope.global
690
691		local currentGlobalBlockCount = #api.scope.global.blocks
692		for i = currentGlobalBlockCount, numBuiltInGlobalBlocks, -1 do
693			table.remove(api.scope.global.blocks, i)
694		end
695
696		configset.addFilter(api.scope.current, {}, os.getcwd())
697	end
698
699
700
701--
702-- Arrays are integer indexed tables; unlike lists, a new array value
703-- will replace the old one, rather than merging both.
704--
705
706	premake.field.kind("array", {
707		store = function(field, current, value, processor)
708			if type(value) ~= "table" then
709				value = { value }
710			end
711
712			for i, item in ipairs(value) do
713				value[i] = processor(field, nil, value[i])
714			end
715
716			return value
717		end,
718		compare = function(field, a, b, processor)
719			if a == nil or b == nil or #a ~= #b then
720				return false
721			end
722			for i = 1, #a do
723				if not processor(field, a[i], b[i]) then
724					return false
725				end
726			end
727			return true
728		end
729	})
730
731
732
733---
734-- Boolean field kind; converts common yes/no strings into true/false values.
735---
736
737	premake.field.kind("boolean", {
738		store = function(field, current, value, processor)
739			local mapping = {
740				["false"] = false,
741				["no"] = false,
742				["off"] = false,
743				["on"] = true,
744				["true"] = true,
745				["yes"] = true,
746			}
747
748			if type(value) == "string" then
749				value = mapping[value:lower()]
750				if value == nil then
751					error { msg="expected boolean; got " .. value }
752				end
753				return value
754			end
755
756			if type(value) == "boolean" then
757				return value
758			end
759
760			if type(value) == "number" then
761				return (value ~= 0)
762			end
763
764			return (value ~= nil)
765		end,
766		compare = function(field, a, b, processor)
767			return (a == b)
768		end
769	})
770
771
772
773
774--
775-- Directory data kind; performs wildcard directory searches, converts
776-- results to absolute paths.
777--
778
779	premake.field.kind("directory", {
780		paths = true,
781		store = function(field, current, value, processor)
782			return path.getabsolute(value)
783		end,
784		remove = function(field, current, value, processor)
785			return path.getabsolute(value)
786		end,
787		compare = function(field, a, b, processor)
788			return (a == b)
789		end,
790
791		translate = function(field, current, _, processor)
792			if current:find("*") then
793				return os.matchdirs(current)
794			end
795			return { current }
796		end
797	})
798
799
800
801--
802-- File data kind; performs wildcard file searches, converts results
803-- to absolute paths.
804--
805
806	premake.field.kind("file", {
807		paths = true,
808		store = function(field, current, value, processor)
809			return path.getabsolute(value)
810		end,
811		remove = function(field, current, value, processor)
812			return path.getabsolute(value)
813		end,
814		compare = function(field, a, b, processor)
815			return (a == b)
816		end,
817
818		translate = function(field, current, _, processor)
819			if current:find("*") then
820				return os.matchfiles(current)
821			end
822			return { current }
823		end
824	})
825
826
827
828--
829-- Function data kind; this isn't terribly useful right now, but makes
830-- a nice extension point for modules to build on.
831--
832
833	premake.field.kind("function", {
834		store = function(field, current, value, processor)
835			local t = type(value)
836			if t ~= "function" then
837				error { msg="expected function; got " .. t }
838			end
839			return value
840		end,
841		compare = function(field, a, b, processor)
842			return (a == b)
843		end
844	})
845
846
847
848--
849-- Integer data kind; validates inputs.
850--
851
852	premake.field.kind("integer", {
853		store = function(field, current, value, processor)
854			local t = type(value)
855			if t ~= "number" then
856				error { msg="expected number; got " .. t }
857			end
858			if math.floor(value) ~= value then
859				error { msg="expected integer; got " .. tostring(value) }
860			end
861			return value
862		end,
863		compare = function(field, a, b, processor)
864			return (a == b)
865		end
866	})
867
868
869
870---
871-- Key-value data kind definition. Merges key domains; values may be any kind.
872---
873
874	local function storeKeyed(field, current, value, processor)
875		current = current or {}
876
877		for k, v in pairs(value) do
878			if processor then
879				v = processor(field, current[k], v)
880			end
881			current[k] = v
882		end
883
884		return current
885	end
886
887
888	local function mergeKeyed(field, current, value, processor)
889		value = value or {}
890		for k, v in pairs(value) do
891			current[k] = v
892		end
893		return current
894	end
895
896
897	premake.field.kind("keyed", {
898		store = storeKeyed,
899		merge = mergeKeyed,
900		compare = function(field, a, b, processor)
901			if a == nil or b == nil then
902				return false
903			end
904			for k in pairs(a) do
905				if not processor(field, a[k], b[k]) then
906					return false
907				end
908			end
909			return true
910		end,
911
912		translate = function(field, current, _, processor)
913			if not processor then
914				return { current }
915			end
916			for k, v in pairs(current) do
917				current[k] = processor(field, v, nil)[1]
918			end
919			return { current }
920		end
921	})
922
923
924---
925-- List data kind definition. Actually a misnomer, lists are more like sets in
926-- that duplicate values are weeded out; each will only appear once. Can
927-- contain any other kind of data.
928---
929
930	local function storeListItem(current, item, allowDuplicates)
931		if not allowDuplicates and current[item] then
932			table.remove(current, table.indexof(current, item))
933		end
934		table.insert(current, item)
935		current[item] = item
936	end
937
938
939	local function storeList(field, current, value, processor)
940		if type(value) == "table" then
941			-- Flatten out incoming arrays of values
942			if #value > 0 then
943				for i = 1, #value do
944					current = storeList(field, current, value[i], processor)
945				end
946				return current
947			end
948
949			-- Ignore empty lists
950			if table.isempty(value) then
951				return current
952			end
953		end
954
955		current = current or {}
956
957		if processor then
958			value = processor(field, nil, value)
959		end
960
961		if type(value) == "table" then
962			if #value > 0 then
963				for i = 1, #value do
964					storeListItem(current, value[i], field.allowDuplicates)
965				end
966			elseif not table.isempty(value) then
967				storeListItem(current, value, field.allowDuplicates)
968			end
969		elseif value then
970			storeListItem(current, value, field.allowDuplicates)
971		end
972
973		return current
974	end
975
976
977	local function mergeList(field, current, value, processor)
978		value = value or {}
979		for i = 1, #value do
980			storeListItem(current, value[i], field.allowDuplicates)
981		end
982		return current
983	end
984
985
986	premake.field.kind("list", {
987		store = storeList,
988		remove = storeList,
989		merge = mergeList,
990		compare = function(field, a, b, processor)
991			if a == nil or b == nil or #a ~= #b then
992				return false
993			end
994			for i = 1, #a do
995				if not processor(field, a[i], b[i]) then
996					return false
997				end
998			end
999			return true
1000		end,
1001
1002		translate = function(field, current, _, processor)
1003			if not processor then
1004				return { current }
1005			end
1006			local ret = {}
1007			for _, value in ipairs(current) do
1008				for _, processed in ipairs(processor(field, value, nil)) do
1009					table.insert(ret, processed)
1010				end
1011			end
1012			return { ret }
1013		end
1014	})
1015
1016
1017
1018--
1019-- Mixed data kind; values containing a directory separator "/" are converted
1020-- to absolute paths, other values left as-is. Used for links, where system
1021-- libraries and local library paths can be mixed into a single list.
1022--
1023
1024	premake.field.kind("mixed", {
1025		paths = true,
1026		store = function(field, current, value, processor)
1027			if type(value) == "string" and value:find('/', nil, true) then
1028				if string.sub(value, 1, 2) ~= "%{" then
1029					value = path.getabsolute(value)
1030				end
1031			end
1032			return value
1033		end,
1034		compare = function(field, a, b, processor)
1035			return (a == b)
1036		end
1037	})
1038
1039
1040
1041--
1042-- Number data kind; validates inputs.
1043--
1044
1045	premake.field.kind("number", {
1046		store = function(field, current, value, processor)
1047			local t = type(value)
1048			if t ~= "number" then
1049				error { msg="expected number; got " .. t }
1050			end
1051			return value
1052		end,
1053		compare = function(field, a, b, processor)
1054			return (a == b)
1055		end
1056	})
1057
1058
1059
1060--
1061-- Path data kind; converts all inputs to absolute paths.
1062--
1063
1064	premake.field.kind("path", {
1065		paths = true,
1066		store = function(field, current, value, processor)
1067			return path.deferredjoin(os.getcwd(), value)
1068		end,
1069		compare = function(field, a, b, processor)
1070			return (a == b)
1071		end
1072	})
1073
1074
1075
1076--
1077-- String data kind; performs validation against allowed fields, checks for
1078-- value deprecations.
1079--
1080
1081	premake.field.kind("string", {
1082		store = function(field, current, value, processor)
1083			if type(value) == "table" then
1084				error { msg="expected string; got table" }
1085			end
1086
1087			if value ~= nil then
1088				local err
1089				value, err = api.checkValue(field, value)
1090				if err then
1091					error { msg=err }
1092				end
1093			end
1094
1095			return value
1096		end,
1097		compare = function(field, a, b, processor)
1098			return (a == b)
1099		end
1100	})
1101
1102
1103--
1104-- Table data kind; wraps simple values into a table, returns others as-is.
1105--
1106
1107	premake.field.kind("table", {
1108		store = function(field, current, value, processor)
1109			if type(value) ~= "table" then
1110				value = { value }
1111			end
1112			return value
1113		end,
1114		compare = function(field, a, b, processor)
1115			-- TODO: is there a reliable way to check this?
1116			return true
1117		end
1118	})
1119
1120
1121
1122---
1123-- Start a new block of configuration settings, using the old, "open"
1124-- style of matching without field prefixes.
1125---
1126
1127	function configuration(terms)
1128		if terms then
1129			if (type(terms) == "table" and #terms == 1 and terms[1] == "*") or (terms == "*") then
1130				terms = nil
1131			end
1132			configset.addblock(api.scope.current, {terms}, os.getcwd())
1133		end
1134		return api.scope.current
1135	end
1136
1137
1138
1139---
1140-- Start a new block of configuration settings, using the new prefixed
1141-- style of pattern matching.
1142---
1143
1144	function filter(terms)
1145		if terms then
1146			if (type(terms) == "table" and #terms == 1 and terms[1] == "*") or (terms == "*") then
1147				terms = nil
1148			end
1149			local ok, err = configset.addFilter(api.scope.current, {terms}, os.getcwd())
1150			if not ok then
1151				error(err, 2)
1152			end
1153		end
1154	end
1155
1156
1157
1158--
1159-- Define a new action.
1160--
1161-- @param a
1162--    The new action object.
1163--
1164
1165	function newaction(a)
1166		p.action.add(a)
1167	end
1168
1169
1170--
1171-- Define a new option.
1172--
1173-- @param opt
1174--    The new option object.
1175--
1176
1177	function newoption(opt)
1178		p.option.add(opt)
1179	end
1180