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