5 -- Execute this file to configure the build system.
8 -- Define project, version, tarname, website, and contact.
11 tarname = project:lower():gsub("%s+", "-")
12 website = "http://www.dogcows.com/yoink"
13 contact = "onefriedrice@brokenzipper.com"
16 -- This script will create three different config files from three tables
17 -- with values determined by the host and configuration options:
19 -- The table `config' contains key-value pairs to be written to the file
20 -- `config.h' which is included by sources files. Boolean values will
21 -- either #define or #undef the key with no associated values. String
22 -- values will be defined as quoted strings while any other value,
23 -- particularly numbers, will be defined unquoted.
25 -- The table `define' contains key-value pairs to be written to the file
26 -- `config.mk' which is included by the Makefile. Values are assigned to
27 -- their keys as they are, unquoted.
29 -- The table `export' contains key-value pairs to be written to the file
30 -- `config.sed' which is used by sed to perform search-and-replace on files
31 -- containing @REPLACE_ME@-style keywords. When used with sed, such
32 -- keyworded keys will be replaced by their values as they are, unquoted.
38 This script prepares ]]..project..[[ for building on your system.
39 Usage: ./configure [OPTION]... [VAR=VALUE]...
41 This is NOT an autoconf-generated configure script, though it was written
42 to be familiar by supporting many of the same options.
45 -h, --help display this help and exit
46 --host=HOST cross-compile the program to run on HOST
48 --prefix=DIR base directory to install programs to
49 --bindir=DIR directory to install executables
50 --datadir=DIR directory to install shared data files
51 --mandir=DIR directory to install manual pages
53 --disable-dependency-tracking speed up one-time builds
54 --enable-link-sh decrease the number of direct dependencies
57 --enable-debug include debugging symbols and code paths
58 --enable-double-precision use doubles instead of floats
59 --enable-profile compile in gprof profiling instructions
60 --enable-clock_gettime use clock_gettime() for timing
61 --enable-threads use threads for concurrency
62 --enable-hotloading watch assets for automatic reloading
64 --with-gtk use the gtk2 toolkit (overrides --with-qt4)
65 --with-qt4 use the qt4 gui toolkit
71 -- Setup a temporary file to collect error messages.
74 config_log = "config.log"
79 -- Define some useful functions.
82 -- Return true if a file exists, false otherwise.
83 function file_exists(file)
84 return os.execute("test -f "..file) == 0
87 -- Print an error message and exit with an error.
89 for _,value in ipairs(arg) do
90 print("fatal: "..tostring(value))
92 if file_exists(config_log) then
94 print("Look through the file `config.log' for more information:\n")
95 os.execute("tail "..config_log)
100 -- Execute a command and return its output or nil if the command failed to
102 function backtick_run(command)
103 os.execute("echo '# "..command.."' >>"..config_log)
104 local fd = io.popen(command.." 2>>"..config_log)
106 local stdout = fd:read("*l")
113 -- Try to execute a command and return true if the command finished
114 -- successfully (with an exit code of zero).
115 function try_run(command)
116 os.execute("echo '# "..command.."' >>"..config_log)
117 return os.execute(command.." >/dev/null 2>>"..config_log) == 0
120 -- Remove the whitespace surrounding a string.
122 str = str:gsub("^%s+", "")
123 return str:gsub("%s+$", "")
126 -- Trim the string and convert all sequences of whitespace to a single
128 function reduce_whitespace(str)
129 str = str:gsub("%s+", " ")
133 -- Get the CFLAGS from pkg-config for the passed libraries.
134 function pkg_config_cflags(libs)
135 local env = "PKG_CONFIG_PATH="..libdir.."/pkgconfig:$PKG_CONFIG_PATH"
136 local cmd = env.." pkg-config"
137 return backtick_run(cmd.." --cflags "..libs)
140 -- Get the LDFLAGS from pkg-config for the passed libraries.
141 function pkg_config_ldflags(libs)
142 local env = "PKG_CONFIG_PATH="..libdir.."/pkgconfig:$PKG_CONFIG_PATH"
143 local cmd = env.." pkg-config"
144 return (" "..backtick_run(cmd.." --libs "..libs)):gsub("%s%-l%S*", "")
147 -- Get the LIBS flags from pkg-config for the passed libraries.
148 function pkg_config_libs(libs)
149 local env = "PKG_CONFIG_PATH="..libdir.."/pkgconfig:$PKG_CONFIG_PATH"
150 local cmd = env.." pkg-config"
151 return backtick_run(cmd.." --libs-only-l "..libs)
154 -- Add a flag to the CFLAGS and CXXFLAGS variables.
155 function add_cflag(flag)
159 CFLAGS = string.format("%s %s", CFLAGS, flag)
161 if CXXFLAGS == "" then
164 CXXFLAGS = string.format("%s %s", CXXFLAGS, flag)
168 -- Replace a flag in the CFLAGS and CXXFLAGS variables.
169 function set_cflag(flag)
170 local cflag_set, cxxflag_set = false, false
171 CFLAGS = CFLAGS:gsub("%"..flag:sub(1, 2).."%S+", function()
175 CXXFLAGS = CXXFLAGS:gsub("%"..flag:sub(1, 2).."%S+", function()
179 if not cflag_set or not cxxflag_set then add_cflag(flag) end
182 -- Look for a command from a list that can complete successfully (with exit
183 -- code zero) given some arguments. Returns nil if none were successful.
184 function find_command(commands, args)
185 if not args then args = "" end
186 for _,command in ipairs(commands) do
187 if try_run(command.." "..args) then return command end
194 -- Perform a quick sanity check.
197 if not file_exists("configure") or not file_exists("Makefile") then
198 -- This script doesn't support out-of-tree builds.
199 die("You must `cd' to the project root where the Makefile is.")
204 -- Parse the command-line options.
207 -- This script supports many of the options provided by autoconf-generated
208 -- scripts. In particular, all the directory-related options are
209 -- supported, including --prefix, --exec-prefix, and all the --dirDIR
210 -- options for configuring installation paths. If passed, the option
211 -- parsing routine below will place these options in the global namespace
212 -- (i.e. prefix, eprefix, bindir, datadir, etc).
214 -- Feature and package options are also supported. The value of any option
215 -- of the form --enable-OPT= and --with-OPT= can be obtained with the
216 -- functions get_feature and get_package, respectively. Like
217 -- autoconf-generated scripts, passing --disable-OPT or --without-OPT is
218 -- equivalent to passing --enable-OPT=no and --without-OPT=no,
219 -- respectively. Values that are either yes or no are interpreted as
220 -- booleans. Any other values are interpreted as strings.
222 -- Definitions of the form KEY=VALUE are also supported. If passed, the
223 -- option parsing routine below will place these options in the global
226 -- Finally, the options -h, --help, and --host are also supported, the
227 -- former two calling the show_help function and the latter setting the
228 -- host global variable.
236 -- Define the option-parsing rules and handlers.
238 handlers["--help"] = function()
242 handlers["-h"] = handlers["--help"]
244 handlers["--host=(.+)"] = function(triplet)
249 local add_feature = function(feature, value)
250 if value == "yes" or value == "" then value = true end
251 if value == "no" then value = false end
252 features[feature] = value
254 handlers["--enable%-([%w_-]+)=?(.*)"] = add_feature
255 handlers["--disable%-([%w_-]+)"] = function(feature)
256 add_feature(feature, "no")
259 local add_package = function(package, value)
260 if value == "yes" or value == "" then value = true end
261 if value == "no" then value = false end
262 packages[package] = value
264 handlers["--with%-([%w_-]+)=?(.*)"] = add_package
265 handlers["--without%-([%w_-]+)"] = function(package)
266 add_package(package, "no")
269 handlers["--(%l+)dir=(.+)"] = function(dir, path)
270 _G[dir.."dir"] = path
272 handlers["--prefix=(.+)"] = function(path)
275 handlers["--exec-prefix=(.+)"] = function(path)
279 handlers["([%w_]+)=(.*)"] = function(key, value)
284 -- Define the feature and package accessors.
286 function get_feature(feature) return features[feature] end
287 function get_package(package) return packages[package] end
290 -- Now read and parse the command-line options.
292 local function parse_arg(arg)
293 for key,value in pairs(handlers) do
294 local matches = {arg:match(key)}
295 if matches[1] then value(unpack(matches)) return end
297 print("warning: unknown or incomplete argument "..arg)
300 for _,arg in ipairs(arg) do
305 -- Define default values for significant variables.
307 local function define(symbol, value)
308 if not _G[symbol] then _G[symbol] = value end
315 define("WINDRES", "")
317 define("CFLAGS", "-g -O2")
318 define("CXXFLAGS", CFLAGS)
319 define("LDFLAGS", "")
322 define("host", backtick_run("tools/config.guess"))
323 define("alt_host", backtick_run("tools/config.sub "..host))
325 define("prefix", "/usr/local")
326 define("eprefix", prefix)
327 define("bindir", eprefix.."/bin")
328 define("sbindir", eprefix.."/sbin")
329 define("libexecdir", eprefix.."/libexec")
330 define("sysconfdir", prefix.."/etc")
331 define("localstatedir", prefix.."/var")
332 define("libdir", eprefix.."/lib")
333 define("includedir", prefix.."/include")
334 define("datarootdir", prefix.."/share")
335 define("datadir", datarootdir.."/"..tarname)
336 define("infodir", datarootdir.."/info")
337 define("localedir", datarootdir.."/locale")
338 define("mandir", datarootdir.."/man")
339 define("docdir", datarootdir.."/doc/"..tarname)
341 if not features["dependency-tracking"] then
342 features["dependency-tracking"] = true
352 -- Determine special target platforms.
355 -- Win32 has some special cases related to its resource file, src/yoinkrc.
356 if host:match("mingw32") then platform = "win32" end
360 -- Look for a working toolchain.
363 print("Please wait...")
366 tmpname = os.tmpname()..".c"
367 tmpfile, err = io.open(tmpname, "w")
373 printf("Hello world!\n");
379 function extra() if not cross_compile then return "gcc", "cc" end end
382 host.."-gcc", host.."-cc",
383 alt_host.."-gcc", alt_host.."-cc",
384 extra()}, tmpname.." -o "..tmpname..".tmp")
386 os.remove(tmpname..".tmp")
387 if not CC then die("Can't find a working C compiler.") end
389 die("failed to create temporary file: "..err)
393 tmpname = os.tmpname()..".cpp"
394 tmpfile, err = io.open(tmpname, "w")
400 std::cout << "Hello world!" << std::endl;
406 function extra() if not cross_compile then return "g++", "c++" end end
409 host.."-g++", host.."-c++",
410 alt_host.."-g++", alt_host.."-c++",
411 extra()}, tmpname.." -o "..tmpname..".tmp")
413 os.remove(tmpname..".tmp")
414 if not CXX then die("Can't find a working C++ compiler.") end
416 die("failed to create temporary file: "..err)
421 function extra() if not cross_compile then return "ar" end end
426 extra()}, "--version")
427 if not AR then die("Can't find a working archiver.") end
432 function extra() if not cross_compile then return "ranlib" end end
433 RANLIB = find_command({
437 extra()}, "--version")
438 if not RANLIB then die("Can't find a working library indexer.") end
441 -- Check for WINDRES.
442 if platform == "win32" then
443 function extra() if not cross_compile then return "windres" end end
444 WINDRES = find_command({
447 alt_host.."-windres",
448 extra()}, "--version")
449 if not WINDRES then die("Can't find a working resource compiler.") end
454 -- Configure the features and packages.
457 if get_feature("debug") then
459 add_cflag("-Wall -Wno-uninitialized")
465 if get_feature("extra-warnings") then
466 add_cflag("-Wextra -Wno-unused-parameter")
469 config.USE_CLOCK_GETTIME = get_feature("clock_gettime")
470 config.USE_DOUBLE_PRECISION = get_feature("double-precision")
471 config.USE_HOTLOADING = get_feature("hotloading")
472 config.USE_THREADS = get_feature("threads")
473 config.PROFILING_ENABLED = get_feature("profile") and add_cflag("-pg")
475 if get_feature("link-sh") then
479 if get_package("gtk") then
483 if get_package("qt4") then
489 -- Check for the libraries we need.
493 local dependencies = "sdl gl glu libpng openal vorbisfile lua"
495 if get_package("gtk") then
496 dependencies = dependencies.." gtk+-2.0"
497 elseif get_package("qt4") then
498 dependencies = dependencies.." QtGui"
501 add_cflag(pkg_config_cflags(dependencies))
502 LDFLAGS = LDFLAGS .." "..pkg_config_ldflags(dependencies)
503 LIBS = LIBS .." "..pkg_config_libs(dependencies)
505 if platform == "win32" then
506 LIBS = LIBS.." -lws2_32"
507 exe_extension = ".exe"
515 -- Define the exports and definitions.
518 if platform == "win32" then
519 -- These are used in src/yoink.rc.
520 local vmajor, vminor, vrevis = version:match("^(%d*)%.?(%d*)%.?(%d*)")
521 config.VERSION_MAJOR = tonumber(vmajor) or 0
522 config.VERSION_MINOR = tonumber(vminor) or 0
523 config.VERSION_REVISION = tonumber(vrevis) or 0
526 config.PACKAGE = tarname
527 config.PACKAGE_NAME = project
528 config.PACKAGE_VERSION = version
529 config.PACKAGE_STRING = project.." "..version
530 config.PACKAGE_TARNAME = tarname
531 config.PACKAGE_URL = website
532 config.PACKAGE_BUGREPORT = contact
533 config.YOINK_GITHEAD = backtick_run("git describe master")
534 config.YOINK_DATADIR = datadir
536 define.PACKAGE = project
537 define.TARNAME = tarname.."-"..version
539 define.PLATFORM = platform
543 define.RANLIB = RANLIB
544 define.WINDRES = WINDRES
545 define.CFLAGS = reduce_whitespace(CFLAGS)
546 define.CXXFLAGS = reduce_whitespace(CXXFLAGS)
547 define.LDFLAGS = reduce_whitespace(LDFLAGS)
548 define.LIBS = reduce_whitespace(LIBS)
549 define.prefix = prefix
550 define.bindir = bindir
551 define.datadir = datadir
552 define.mandir = mandir
553 define.EXEEXT = exe_extension
554 define.DEP_TRACKING = get_feature("dependency-tracking")
556 export.datadir = datadir -- Used in doc/yoink.6.in.
557 export.PACKAGE_BUGREPORT = contact -- Used in doc/yoink.6.in.
561 -- All done; output the configuration files.
564 -- Output the program options.
565 output = io.open("config.h", "w")
566 for key,value in pairs(config) do
568 if type(value) == "boolean" then
570 output:write("#define "..key.." 1")
572 output:write("#undef "..key)
574 elseif type(value) == "string" then
575 output:write(string.format("#define %s %q", key, tostring(value)))
577 output:write(string.format("#define %s %s", key, tostring(value)))
583 -- Output the make definitions.
584 output = io.open("config.mk", "w")
585 for key,value in pairs(define) do
587 value = tostring(value)
588 output:write(string.format("%s = %s\n", key, value))
593 -- Output the exported variables.
594 output = io.open("config.sed", "w")
595 for key,value in pairs(export) do
596 key = key:gsub("/", "\\/")
597 value = value:gsub("/", "\\/")
598 output:write(string.format("s/@%s@/%s/g\n", key, value))
605 Configuration complete! You can review your configuration in `config.mk'.
607 To finish the installation, type: