ddnet/configure.lua
2010-06-03 03:26:31 +08:00

501 lines
12 KiB
Lua

--[[@GROUP Configuration@END]]--
--[[@FUNCTION
TODO
@END]]--
function NewConfig(on_configured_callback)
local config = {}
config.OnConfigured = function(self)
return true
end
if on_configured_callback then config.OnConfigured = on_configured_callback end
config.options = {}
config.settings = NewSettings()
config.NewSettings = function(self)
local s = NewSettings()
for _,v in pairs(self.options) do
v:Apply(s)
end
return s
end
config.Add = function(self, o)
table.insert(self.options, o)
self[o.name] = o
end
config.Print = function(self)
for k,v in pairs(self.options) do
print(v:FormatDisplay())
end
end
config.Save = function(self, filename)
print("saved configuration to '"..filename.."'")
local file = io.open(filename, "w")
-- Define a little helper function to save options
local saver = {}
saver.file = file
saver.line = function(self, str)
self.file:write(str .. "\n")
end
saver.option = function(self, option, name)
local valuestr = "no"
if type(option[name]) == type(0) then
valuestr = option[name]
elseif type(option[name]) == type(true) then
valuestr = "false"
if option[name] then
valuestr = "true"
end
elseif type(option[name]) == type("") then
valuestr = "'"..option[name].."'"
else
error("option "..name.." have a value of type ".. type(option[name]).." that can't be saved")
end
self.file:write(option.name.."."..name.." = ".. valuestr.."\n")
end
-- Save all the options
for k,v in pairs(self.options) do
v:Save(saver)
end
file:close()
end
config.Load = function(self, filename)
local options_func = loadfile(filename)
local options_table = {}
if not options_func then
print("auto configuration")
self:Config(filename)
options_func = loadfile(filename)
end
if options_func then
-- Setup the options tables
for k,v in pairs(self.options) do
options_table[v.name] = {}
end
setfenv(options_func, options_table)
-- this is to make sure that we get nice error messages when
-- someone sets an option that isn't valid.
local mt = {}
mt.__index = function(t, key)
local v = rawget(t, key)
if v ~= nil then return v end
error("there is no configuration option named '" .. key .. "'")
end
setmetatable(options_table, mt)
-- Process the options
options_func()
-- Copy the options
for k,v in pairs(self.options) do
if options_table[v.name] then
for k2,v2 in pairs(options_table[v.name]) do
v[k2] = v2
end
v.auto_detected = false
end
end
else
print("error: no '"..filename.."' found")
print("")
print("run 'bam config' to generate")
print("run 'bam config help' for configuration options")
print("")
os.exit(1)
end
end
config.Config = function(self, filename)
print("")
print("configuration:")
if _bam_targets[1] == "print" then
self:Load(filename)
self:Print()
print("")
print("notes:")
self:OnConfigured()
print("")
else
self:Autodetect()
print("")
print("notes:")
if self:OnConfigured() then
self:Save(filename)
end
print("")
end
end
config.Autodetect = function(self)
for k,v in pairs(self.options) do
v:Check(self.settings)
print(v:FormatDisplay())
self[v.name] = v
end
end
config.PrintHelp = function(self)
print("options:")
for k,v in pairs(self.options) do
if v.PrintHelp then
v:PrintHelp()
end
end
end
config.Finalize = function(self, filename)
if _bam_targets[0] == "config" then
if _bam_targets[1] == "help" then
self:PrintHelp()
os.exit(0)
end
self:Config(filename)
os.exit(0)
end
self:Load(filename)
bam_update_globalstamp(filename)
end
return config
end
-- Helper functions --------------------------------------
function DefaultOptionDisplay(option)
if not option.value then return "no" end
if option.value == 1 or option.value == true then return "yes" end
return option.value
end
function IsNegativeTerm(s)
if s == "no" then return true end
if s == "false" then return true end
if s == "off" then return true end
if s == "disable" then return true end
if s == "0" then return true end
return false
end
function IsPositiveTerm(s)
if s == "yes" then return true end
if s == "true" then return true end
if s == "on" then return true end
if s == "enable" then return true end
if s == "1" then return true end
return false
end
function MakeOption(name, value, check, save, display, printhelp)
local o = {}
o.name = name
o.value = value
o.Check = check
o.Save = save
o.auto_detected = true
o.FormatDisplay = function(self)
local a = "SET"
if self.auto_detected then a = "AUTO" end
return string.format("%-5s %-20s %s", a, self.name, self:Display())
end
o.Display = display
o.PrintHelp = printhelp
if o.Display == nil then o.Display = DefaultOptionDisplay end
return o
end
-- Test Compile C --------------------------------------
function OptTestCompileC(name, source, compileoptions, desc)
local check = function(option, settings)
option.value = false
if ScriptArgs[option.name] then
if IsNegativeTerm(ScriptArgs[option.name]) then
option.value = false
elseif IsPositiveTerm(ScriptArgs[option.name]) then
option.value = true
else
error(ScriptArgs[option.name].." is not a valid value for option "..option.name)
end
option.auto_detected = false
else
if CTestCompile(settings, option.source, option.compileoptions) then
option.value = true
end
end
end
local save = function(option, output)
output:option(option, "value")
end
local printhelp = function(option)
print("\t"..option.name.."=on|off")
if option.desc then print("\t\t"..option.desc) end
end
local o = MakeOption(name, false, check, save, nil, printhelp)
o.desc = desc
o.source = source
o.compileoptions = compileoptions
return o
end
-- OptToggle --------------------------------------
function OptToggle(name, default_value, desc)
local check = function(option, settings)
if ScriptArgs[option.name] then
if IsNegativeTerm(ScriptArgs[option.name]) then
option.value = false
elseif IsPositiveTerm(ScriptArgs[option.name]) then
option.value = true
else
error(ScriptArgs[option.name].." is not a valid value for option "..option.name)
end
end
end
local save = function(option, output)
output:option(option, "value")
end
local printhelp = function(option)
print("\t"..option.name.."=on|off")
if option.desc then print("\t\t"..option.desc) end
end
local o = MakeOption(name, default_value, check, save, nil, printhelp)
o.desc = desc
return o
end
-- OptInteger --------------------------------------
function OptInteger(name, default_value, desc)
local check = function(option, settings)
if ScriptArgs[option.name] then
option.value = tonumber(ScriptArgs[option.name])
end
end
local save = function(option, output)
output:option(option, "value")
end
local printhelp = function(option)
print("\t"..option.name.."=N")
if option.desc then print("\t\t"..option.desc) end
end
local o = MakeOption(name, default_value, check, save, nil, printhelp)
o.desc = desc
return o
end
-- OptString --------------------------------------
function OptString(name, default_value, desc)
local check = function(option, settings)
if ScriptArgs[option.name] then
option.value = ScriptArgs[option.name]
end
end
local save = function(option, output)
output:option(option, "value")
end
local printhelp = function(option)
print("\t"..option.name.."=STRING")
if option.desc then print("\t\t"..option.desc) end
end
local o = MakeOption(name, default_value, check, save, nil, printhelp)
o.desc = desc
return o
end
-- Find Compiler --------------------------------------
--[[@FUNCTION
TODO
@END]]--
function OptCCompiler(name, default_driver, default_c, default_cxx, desc)
local check = function(option, settings)
if ScriptArgs[option.name] then
-- set compile driver
option.driver = ScriptArgs[option.name]
-- set c compiler
if ScriptArgs[option.name..".c"] then
option.c_compiler = ScriptArgs[option.name..".c"]
end
-- set c+= compiler
if ScriptArgs[option.name..".cxx"] then
option.cxx_compiler = ScriptArgs[option.name..".cxx"]
end
option.auto_detected = false
elseif option.driver then
-- no need todo anything if we have a driver
-- TODO: test if we can find the compiler
else
if ExecuteSilent("g++ -v") == 0 then
option.driver = "gcc"
elseif ExecuteSilent("cl") == 0 then
option.driver = "cl"
else
error("no c/c++ compiler found")
end
end
--setup_compiler(option.value)
end
local apply = function(option, settings)
if option.driver == "cl" then
SetDriversCL(settings)
elseif option.driver == "gcc" then
SetDriversGCC(settings)
else
error(option.driver.." is not a known c/c++ compile driver")
end
if option.c_compiler then settings.cc.c_compiler = option.c_compiler end
if option.cxx_compiler then settings.cc.cxx_compiler = option.cxx_compiler end
end
local save = function(option, output)
output:option(option, "driver")
output:option(option, "c_compiler")
output:option(option, "cxx_compiler")
end
local printhelp = function(option)
local a = ""
if option.desc then a = "for "..option.desc end
print("\t"..option.name.."=gcc|cl")
print("\t\twhat c/c++ compile driver to use"..a)
print("\t"..option.name..".c=FILENAME")
print("\t\twhat c compiler executable to use"..a)
print("\t"..option.name..".cxx=FILENAME")
print("\t\twhat c++ compiler executable to use"..a)
end
local display = function(option)
local s = option.driver
if option.c_compiler then s = s .. " c="..option.c_compiler end
if option.cxx_compiler then s = s .. " cxx="..option.cxx_compiler end
return s
end
local o = MakeOption(name, nil, check, save, display, printhelp)
o.desc = desc
o.driver = false
o.c_compiler = false
o.cxx_compiler = false
if default_driver then o.driver = default_driver end
if default_c then o.c_compiler = default_c end
if default_cxx then o.cxx_compiler = default_cxx end
o.Apply = apply
return o
end
-- Option Library --------------------------------------
--[[@FUNCTION
TODO
@END]]--
function OptLibrary(name, header, desc)
local check = function(option, settings)
option.value = false
option.include_path = false
local function check_compile_include(filename, paths)
if CTestCompile(settings, "#include <" .. filename .. ">\nint main(){return 0;}", "") then
return ""
end
for k,v in pairs(paths) do
if CTestCompile(settings, "#include <" .. filename .. ">\nint main(){return 0;}", "-I"..v) then
return v
end
end
return false
end
if ScriptArgs[option.name] then
if IsNegativeTerm(ScriptArgs[option.name]) then
option.value = false
elseif ScriptArgs[option.name] == "system" then
option.value = true
else
option.value = true
option.include_path = ScriptArgs[option.name]
end
option.auto_detected = false
else
option.include_path = check_compile_include(option.header, {})
if option.include_path == false then
if option.required then
print(name.." library not found and is required")
error("required library not found")
end
else
option.value = true
option.include_path = false
end
end
end
local save = function(option, output)
output:option(option, "value")
output:option(option, "include_path")
end
local display = function(option)
if option.value then
if option.include_path then
return option.include_path
else
return "(in system path)"
end
else
return "not found"
end
end
local printhelp = function(option)
print("\t"..option.name.."=disable|system|PATH")
if option.desc then print("\t\t"..option.desc) end
end
local o = MakeOption(name, false, check, save, display, printhelp)
o.include_path = false
o.header = header
o.desc = desc
return o
end