X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=configure;h=d4435162569e366a992f552e31ac3cdaff3added;hp=5e691c171eacb6b4b11a9f02aba5e4285de63638;hb=51069fee9139ab8d14ecc80dffbe5deecb73d9e0;hpb=bd62b2e6a6e5f1af5a635df3ff1a07f363d9ffe0 diff --git a/configure b/configure index 5e691c1..d443516 100755 --- a/configure +++ b/configure @@ -5,13 +5,34 @@ -- Execute this file to configure the build system. -- +-- Define project, version, tarname, website, and contact. project = "Yoink" -package = project:lower() version = "0.1" -bugreport = "chaz@dogcows.com" +tarname = project:lower():gsub("%s+", "-") +website = "http://www.dogcows.com/yoink" +contact = "onefriedrice@brokenzipper.com" -function ShowHelp() +-- This script will create three different config files from three tables +-- with values determined by the host and configuration options: +-- +-- The table `config' contains key-value pairs to be written to the file +-- `config.h' which is included by sources files. Boolean values will +-- either #define or #undef the key with no associated values. String +-- values will be defined as quoted strings while any other value, +-- particularly numbers, will be defined unquoted. +-- +-- The table `define' contains key-value pairs to be written to the file +-- `config.mk' which is included by the Makefile. Values are assigned to +-- their keys as they are, unquoted. +-- +-- The table `export' contains key-value pairs to be written to the file +-- `config.sed' which is used by sed to perform search-and-replace on files +-- containing @REPLACE_ME@-style keywords. When used with sed, such +-- keyworded keys will be replaced by their values as they are, unquoted. + + +function show_help() print([[ This script prepares ]]..project..[[ for building on your system. @@ -40,29 +61,46 @@ Program options: --enable-threads use threads for concurrency --enable-hotloading watch assets for automatic reloading - --with-gtk use gtk2 toolkit (overrides --with-qt4) + --with-gtk use the gtk2 toolkit (overrides --with-qt4) --with-qt4 use the qt4 gui toolkit ]]) end +-- +-- Setup a temporary file to collect error messages. +-- + +error_log = "config.log" +os.remove(error_log) + + -- -- Define some useful functions. -- -function Die(...) +-- Return true if a file exists, false otherwise. +function file_exists(file) + return os.execute("test -f "..file) == 0 +end + +-- Print an error message and exit with an error. +function die(...) for _,value in ipairs(arg) do print("fatal: "..tostring(value)) end + if file_exists(error_log) then + print() + print("Look through the file `config.log' for more information:\n") + os.execute("tail "..error_log) + end os.exit(1) end -function FileExists(file) - return os.execute("test -f "..file) == 0 -end - -function ReadCommand(command) - local fd = io.popen(command) +-- Execute a command and return its output or nil if the command failed to +-- run. +function backtick_run(command) + local fd = io.popen(command.." 2>>"..error_log) if fd then local stdout = fd:read("*l") fd:close() @@ -71,18 +109,82 @@ function ReadCommand(command) return nil end -function TryCommand(command) - return os.execute(command.." 2>&1 >/dev/null") == 0 +-- Try to execute a command and return true if the command finished +-- successfully (with an exit code of zero). +function try_run(command) + return os.execute(command.." >/dev/null 2>>"..error_log) == 0 end -function Trim(str) +-- Remove the whitespace surrounding a string. +function trim(str) str = str:gsub("^%s+", "") return str:gsub("%s+$", "") end -function Reduce(str) +-- Trim the string and convert all sequences of whitespace to a single +-- space. +function reduce_whitespace(str) str = str:gsub("%s+", " ") - return Trim(str) + return trim(str) +end + +-- Get the CFLAGS from pkg-config for the passed libraries. +function pkg_config_cflags(libs) + local env = "PKG_CONFIG_PATH="..libdir.."/pkgconfig:$PKG_CONFIG_PATH" + local cmd = env.." pkg-config" + return backtick_run(cmd.." --cflags "..libs) +end + +-- Get the LDFLAGS from pkg-config for the passed libraries. +function pkg_config_ldflags(libs) + local env = "PKG_CONFIG_PATH="..libdir.."/pkgconfig:$PKG_CONFIG_PATH" + local cmd = env.." pkg-config" + return (" "..backtick_run(cmd.." --libs "..libs)):gsub("%s%-l%S*", "") +end + +-- Get the LIBS flags from pkg-config for the passed libraries. +function pkg_config_libs(libs) + local env = "PKG_CONFIG_PATH="..libdir.."/pkgconfig:$PKG_CONFIG_PATH" + local cmd = env.." pkg-config" + return backtick_run(cmd.." --libs-only-l "..libs) +end + +-- Add a flag to the CFLAGS and CXXFLAGS variables. +function add_cflag(flag) + if CFLAGS == "" then + CFLAGS = flag + else + CFLAGS = string.format("%s %s", CFLAGS, flag) + end + if CXXFLAGS == "" then + CXXFLAGS = flag + else + CXXFLAGS = string.format("%s %s", CXXFLAGS, flag) + end +end + +-- Replace a flag in the CFLAGS and CXXFLAGS variables. +function set_cflag(flag) + local cflag_set, cxxflag_set = false, false + CFLAGS = CFLAGS:gsub("%"..flag:sub(1, 2).."%S+", function() + cflag_set = true + return flag + end) + CXXFLAGS = CXXFLAGS:gsub("%"..flag:sub(1, 2).."%S+", function() + cxxflag_set = true + return flag + end) + if not cflag_set or not cxxflag_set then add_cflag(flag) end +end + +-- Look for a command from a list that can complete successfully (with exit +-- code zero) given some arguments. Returns nil if none were successful. +function find_command(commands, args) + if not args then args = "" end + for _,command in ipairs(commands) do + if try_run(command.." "..args) then return command end + end + return nil end @@ -90,8 +192,9 @@ end -- Perform a quick sanity check. -- -if not FileExists("configure") or not FileExists("Makefile") then - Die("You must `cd' to the project root where the Makefile is.") +if not file_exists("configure") or not file_exists("Makefile") then + -- This script doesn't support out-of-tree builds. + die("You must `cd' to the project root where the Makefile is.") end @@ -99,152 +202,156 @@ end -- Parse the command-line options. -- +-- This script supports many of the options provided by autoconf-generated +-- scripts. In particular, all the directory-related options are +-- supported, including --prefix, --exec-prefix, and all the --dirDIR +-- options for configuring installation paths. If passed, the option +-- parsing routine below will place these options in the global namespace +-- (i.e. prefix, eprefix, bindir, datadir, etc). +-- +-- Feature and package options are also supported. The value of any option +-- of the form --enable-OPT= and --with-OPT= can be obtained with the +-- functions get_feature and get_package, respectively. Like +-- autoconf-generated scripts, passing --disable-OPT or --without-OPT is +-- equivalent to passing --enable-OPT=no and --without-OPT=no, +-- respectively. Values that are either yes or no are interpreted as +-- booleans. Any other values are interpreted as strings. +-- +-- Definitions of the form KEY=VALUE are also supported. If passed, the +-- option parsing routine below will place these options in the global +-- namespace. +-- +-- Finally, the options -h, --help, and --host are also supported, the +-- former two calling the show_help function and the latter setting the +-- host global variable. + do local features = {} local packages = {} - local directories = {} - local definitions = {} + local handlers = {} + + + -- Define the option-parsing rules and handlers. + + handlers["--help"] = function() + show_help() + os.exit(0) + end + handlers["-h"] = handlers["--help"] + + handlers["--host=(.+)"] = function(triplet) + host = triplet + cross_compile = true + end - local function AddFeature(feature, value) + local add_feature = function(feature, value) if value == "yes" or value == "" then value = true end if value == "no" then value = false end features[feature] = value end + handlers["--enable%-([%w_-]+)=?(.*)"] = add_feature + handlers["--disable%-([%w_-]+)"] = function(feature) + add_feature(feature, "no") + end - local function AddPackage(package, value) + local add_package = function(package, value) if value == "yes" or value == "" then value = true end if value == "no" then value = false end packages[package] = value end + handlers["--with%-([%w_-]+)=?(.*)"] = add_package + handlers["--without%-([%w_-]+)"] = function(package) + add_package(package, "no") + end - local function AddDirectory(directory, path) - directories[directory] = path + handlers["--(%l+)dir=(.+)"] = function(dir, path) + _G[dir.."dir"] = path + end + handlers["--prefix=(.+)"] = function(path) + prefix = path + end + handlers["--exec-prefix=(.+)"] = function(path) + eprefix = path end - local function AddDefinition(key, value) - definitions[key] = value + handlers["([%w_]+)=(.*)"] = function(key, value) + _G[key] = value end - local handlers = { - ["--help"] = function() ShowHelp() os.exit(0) end, - ["--host=(.+)"] = function(arg) host = arg cross_compile = true end, - ["--enable%-([%w_-]+)=?(.*)"] = AddFeature, - ["--disable%-([%w_-]+)"] = function(arg) AddFeature(arg, "no") end, - ["--with%-([%w_-]+)=?(.*)"] = AddPackage, - ["--without%-([%w_-]+)"] = function(arg) AddPackage(arg, "no") end, - ["--(%l+)dir=(.+)"] = AddDirectory, - ["--prefix=(.+)"] = function(arg) prefix = arg end, - ["--exec-prefix=(.+)"] = function(arg) eprefix = arg end, - ["([%w_]+)=(.*)"] = AddDefinition, - } - handlers["-h"] = handlers["--help"] - - local function ParseArg(arg) + + -- Define the feature and package accessors. + + function get_feature(feature) return features[feature] end + function get_package(package) return packages[package] end + + + -- Now read and parse the command-line options. + + local function parse_arg(arg) for key,value in pairs(handlers) do local matches = {arg:match(key)} - if matches[1] then - value(unpack(matches)) - return - end + if matches[1] then value(unpack(matches)) return end end print("warning: unknown or incomplete argument "..arg) end - - -- Define some default values. - - prefix = "/usr/local" - - CC = "" - CXX = "" - AR = "" - RANLIB = "" - WINDRES = "" - - CFLAGS = "-g -O2" - CXXFLAGS = CFLAGS - LDFLAGS = "" - LIBS = "" - - features["dependency-tracking"] = true - - - -- Read the arguments from the command-line. for _,arg in ipairs(arg) do - ParseArg(arg) + parse_arg(arg) end - function GetFeature(feature) return features[feature] end - function GetPackage(package) return packages[package] end + + -- Define default values for significant variables. - for key,value in pairs(directories) do - _G[key.."dir"] = value + local function define(symbol, value) + if not _G[symbol] then _G[symbol] = value end end - for key,value in pairs(definitions) do - _G[key] = value - end - - -- Define the dependent values. + define("CC", "") + define("CXX", "") + define("AR", "") + define("RANLIB", "") + define("WINDRES", "") + + define("CFLAGS", "-g -O2") + define("CXXFLAGS", CFLAGS) + define("LDFLAGS", "") + define("LIBS", "") + + define("host", backtick_run("tools/config.guess")) + define("alt_host", backtick_run("tools/config.sub "..host)) + + define("prefix", "/usr/local") + define("eprefix", prefix) + define("bindir", eprefix.."/bin") + define("sbindir", eprefix.."/sbin") + define("libexecdir", eprefix.."/libexec") + define("sysconfdir", prefix.."/etc") + define("localstatedir", prefix.."/var") + define("libdir", eprefix.."/lib") + define("includedir", prefix.."/include") + define("datarootdir", prefix.."/share") + define("datadir", datarootdir.."/"..tarname) + define("infodir", datarootdir.."/info") + define("localedir", datarootdir.."/locale") + define("mandir", datarootdir.."/man") + define("docdir", datarootdir.."/doc/"..tarname) + + if not features["dependency-tracking"] then + features["dependency-tracking"] = true + end - if not host then host = ReadCommand("build/config.guess") end - alt_host = ReadCommand("build/config.sub "..host) - - if not eprefix then eprefix = prefix end - if not bindir then bindir = eprefix.."/bin" end - if not sbindir then sbindir = eprefix.."/sbin" end - if not libexecdir then libexecdir = eprefix.."/libexec" end - if not sysconfdir then sysconfdir = prefix.."/etc" end - if not localstatedir then localstatedir = prefix.."/var" end - if not libdir then libdir = eprefix.."/lib" end - if not includedir then includedir = prefix.."/include" end - if not datarootdir then datarootdir = prefix.."/share" end - if not datadir then datadir = datarootdir.."/"..package end - if not infodir then infodir = datarootdir.."/info" end - if not localedir then localedir = datarootdir.."/locale" end - if not mandir then mandir = datarootdir.."/man" end - if not docdir then docdir = datarootdir.."/doc/"..package end - - cflags = "" - config = {} - define = {} - export = {} - - define.DEP_TRACKING = GetFeature("dependency-tracking") + define("config", {}) + define("define", {}) + define("export", {}) end -- --- Determine the target platform. +-- Determine special target platforms. -- -if host:match("mingw32") then platform = "win32" -elseif host:match("netbsd") then platform = "netbsd" end - - --- --- Define the check function. --- - -do - local path = os.getenv("PATH") - - -- 1. List of possible command names. - -- 2. Command arguments. - function FindCommand(commands, args) - if not args then args = "" end - for _,command in ipairs(commands) do - if command then - for dir in path:gmatch("[^:]+") do - if FileExists(dir.."/"..command) and TryCommand(command.." "..args) then - return command - end - end - end - end - return nil - end -end +-- Win32 has some special cases related to its resource file, src/yoinkrc. +if host:match("mingw32") then platform = "win32" end -- @@ -263,22 +370,21 @@ int main() { printf("Hello world!\n"); return 0; -}]]) +} +]]) tmpfile:close() function extra() if not cross_compile then return "gcc", "cc" end end - CC = FindCommand({ + CC = find_command({ CC, - host.."-gcc", - host.."-cc", - alt_host.."-gcc", - alt_host.."-cc", + host.."-gcc", host.."-cc", + alt_host.."-gcc", alt_host.."-cc", extra()}, tmpname.." -o "..tmpname..".tmp") os.remove(tmpname) os.remove(tmpname..".tmp") - if not CC then Die("Can't find a working C compiler.") end + if not CC then die("Can't find a working C compiler.") end else - Die("failed to create temporary file: "..err) + die("failed to create temporary file: "..err) end -- Check for CXX. @@ -291,55 +397,54 @@ int main() { std::cout << "Hello world!" << std::endl; return 0; -}]]) +} +]]) tmpfile:close() function extra() if not cross_compile then return "g++", "c++" end end - CXX = FindCommand({ + CXX = find_command({ CXX, - host.."-g++", - host.."-c++", - alt_host.."-g++", - alt_host.."-c++", + host.."-g++", host.."-c++", + alt_host.."-g++", alt_host.."-c++", extra()}, tmpname.." -o "..tmpname..".tmp") os.remove(tmpname) os.remove(tmpname..".tmp") - if not CXX then Die("Can't find a working C++ compiler.") end + if not CXX then die("Can't find a working C++ compiler.") end else - Die("failed to create temporary file: "..err) + die("failed to create temporary file: "..err) end -- Check for AR. do function extra() if not cross_compile then return "ar" end end - AR = FindCommand({ + AR = find_command({ AR, host.."-ar", alt_host.."-ar", extra()}, "--version") - if not AR then Die("Can't find a working archiver.") end + if not AR then die("Can't find a working archiver.") end end -- Check for RANLIB. do function extra() if not cross_compile then return "ranlib" end end - RANLIB = FindCommand({ + RANLIB = find_command({ RANLIB, host.."-ranlib", alt_host.."-ranlib", extra()}, "--version") - if not RANLIB then Die("Can't find a working library indexer.") end + if not RANLIB then die("Can't find a working library indexer.") end end -- Check for WINDRES. if platform == "win32" then function extra() if not cross_compile then return "windres" end end - WINDRES = FindCommand({ + WINDRES = find_command({ WINDRES, host.."-windres", alt_host.."-windres", extra()}, "--version") - if not WINDRES then Die("Can't find a working win32 resource compiler.") end + if not WINDRES then die("Can't find a working resource compiler.") end end @@ -347,48 +452,33 @@ end -- Configure the features and packages. -- -if GetFeature("debug") then - cflags = cflags.." -O0 -Wall -Wno-uninitialized" +if get_feature("debug") then + set_cflag("-O0") + add_cflag("-Wall -Wno-uninitialized") config.DEBUG = true else config.NDEBUG = true end -if GetFeature("double-precision") then - config.USE_DOUBLE_PRECISION = true -end - -if GetFeature("profile") then - cflags = cflags.." -pg" - config.PROFILING_ENABLED = true -end - -if GetFeature("extra-warnings") then - cflags = cflags.." -Wextra -Wno-unused-parameter" +if get_feature("extra-warnings") then + add_cflag("-Wextra -Wno-unused-parameter") end -if GetFeature("link-sh") then - -- TODO -end +config.USE_CLOCK_GETTIME = get_feature("clock_gettime") +config.USE_DOUBLE_PRECISION = get_feature("double-precision") +config.USE_HOTLOADING = get_feature("hotloading") +config.USE_THREADS = get_feature("threads") +config.PROFILING_ENABLED = get_feature("profile") and add_cflag("-pg") -if GetFeature("clock_gettime") then +if get_feature("link-sh") then -- TODO end -if GetFeature("threads") then - config.USE_THREADS = true -end - -if GetFeature("hotloading") then - print("FYI: Hotloading is very experimental and only works on Linux."); - config.USE_HOTLOADING = true -end - -if GetPackage("gtk") then +if get_package("gtk") then -- TODO end -if GetPackage("qt4") then +if get_package("qt4") then -- TODO end @@ -398,28 +488,17 @@ end -- do - local command = "PKG_CONFIG_PATH="..libdir.."/pkgconfig:$PKG_CONFIG_PATH pkg-config" - local deps = "sdl gl glu libpng openal vorbisfile lua" + local dependencies = "sdl gl glu libpng openal vorbisfile lua" - if GetPackage("gtk") then - deps = deps.." gtk+-2.0" - elseif GetPackage("qt4") then - deps = deps.." QtGui" + if get_package("gtk") then + dependencies = dependencies.." gtk+-2.0" + elseif get_package("qt4") then + dependencies = dependencies.." QtGui" end - local pc_cflags = ReadCommand(command.." --cflags "..deps) - if not pc_cflags then Die("Couldn't determine CFLAGS.") end - CFLAGS = CFLAGS.." "..pc_cflags - CXXFLAGS = CXXFLAGS.." "..pc_cflags - - local pc_libs = ReadCommand(command.." --libs "..deps) - if not pc_libs then Die("Couldn't determine LDFLAGS or LIBS.") end - for lib in pc_libs:gmatch("%-l%S+") do - LIBS = LIBS.." "..lib - end - for ldflag in pc_libs:gmatch("%-[^l]%S*") do - LDFLAGS = LDFLAGS.." "..ldflag - end + add_cflag(pkg_config_cflags(dependencies)) + LDFLAGS = LDFLAGS .." "..pkg_config_ldflags(dependencies) + LIBS = LIBS .." "..pkg_config_libs(dependencies) if platform == "win32" then LIBS = LIBS.." -lws2_32" @@ -429,71 +508,64 @@ do end end -CFLAGS = Reduce(CFLAGS.." "..cflags) -CXXFLAGS = Reduce(CXXFLAGS.." "..cflags) -LDFLAGS = Reduce(LDFLAGS) -LIBS = Reduce(LIBS) - -- -- Define the exports and definitions. -- -config.YOINK_DATADIR = datadir -export.YOINK_DATADIR = datadir - -do - local vmajor = ReadCommand("v=$(echo "..version.." | cut -d. -f1); echo ${v:-0}") - local vminor = ReadCommand("v=$(echo "..version.." | cut -d. -f2); echo ${v:-0}") - local vrevis = ReadCommand("v=$(echo "..version.." | cut -d. -f3); echo ${v:-0}") - - config.VERSION_MAJOR = tonumber(vmajor) - config.VERSION_MINOR = tonumber(vminor) - config.VERSION_REVISION = tonumber(vrevis) -end - -do - -- Determine and define the git revision. - local head = ReadCommand("git log -n1 --date=short --pretty=format:\"%h (%ad)\"") - config.YOINK_GITHEAD = head +if platform == "win32" then + -- These are used in src/yoink.rc. + local vmajor, vminor, vrevis = version:match("^(%d*)%.?(%d*)%.?(%d*)") + config.VERSION_MAJOR = tonumber(vmajor) or 0 + config.VERSION_MINOR = tonumber(vminor) or 0 + config.VERSION_REVISION = tonumber(vrevis) or 0 end -config.PACKAGE = project +config.PACKAGE = tarname config.PACKAGE_NAME = project -config.VERSION = version config.PACKAGE_VERSION = version config.PACKAGE_STRING = project.." "..version -config.PACKAGE_BUGREPORT = bugreport - -define.TARGET = host -define.PLATFORM = platform -define.CC = CC -define.CXX = CXX -define.AR = AR -define.RANLIB = RANLIB -define.WINDRES = WINDRES -define.CFLAGS = CFLAGS -define.CXXFLAGS = CXXFLAGS -define.LDFLAGS = LDFLAGS -define.LIBS = LIBS -define.prefix = prefix -define.bindir = bindir -define.datadir = datadir -define.mandir = mandir -define.EXEEXT = exe_extension +config.PACKAGE_TARNAME = tarname +config.PACKAGE_URL = website +config.PACKAGE_BUGREPORT = contact +config.YOINK_GITHEAD = backtick_run("git describe master") +config.YOINK_DATADIR = datadir + +define.PACKAGE = project +define.TARNAME = tarname.."-"..version +define.TARGET = host +define.PLATFORM = platform +define.CC = CC +define.CXX = CXX +define.AR = AR +define.RANLIB = RANLIB +define.WINDRES = WINDRES +define.CFLAGS = reduce_whitespace(CFLAGS) +define.CXXFLAGS = reduce_whitespace(CXXFLAGS) +define.LDFLAGS = reduce_whitespace(LDFLAGS) +define.LIBS = reduce_whitespace(LIBS) +define.prefix = prefix +define.bindir = bindir +define.datadir = datadir +define.mandir = mandir +define.EXEEXT = exe_extension +define.DEP_TRACKING = get_feature("dependency-tracking") + +export.datadir = datadir -- Used in doc/yoink.6.in. +export.PACKAGE_BUGREPORT = contact -- Used in doc/yoink.6.in. -- -- All done; output the configuration files. -- --- Print the program options. +-- Output the program options. output = io.open("config.h", "w") for key,value in pairs(config) do key = tostring(key) if type(value) == "boolean" then if value then - output:write("#define "..key) + output:write("#define "..key.." 1") else output:write("#undef "..key) end @@ -506,7 +578,7 @@ for key,value in pairs(config) do end output:close() --- Print the make definitions. +-- Output the make definitions. output = io.open("config.mk", "w") for key,value in pairs(define) do key = tostring(key) @@ -516,7 +588,7 @@ end output:close() --- Print the exported variables. +-- Output the exported variables. output = io.open("config.sed", "w") for key,value in pairs(export) do key = key:gsub("/", "\\/")