]> Dogcows Code - chaz/yoink/blob - configure
8cc0b0d183071963c2c32602c3d19421f49f83fd
[chaz/yoink] / configure
1 #!/usr/bin/env lua
2
3 --
4 -- Yoink
5 -- Execute this file to configure the build system.
6 --
7
8 -- Define project, version, tarname, website, and contact.
9 project = "Yoink"
10 version = "0.1"
11 tarname = project:lower():gsub("%s+", "-")
12 website = "http://www.dogcows.com/yoink"
13 contact = "onefriedrice@brokenzipper.com"
14
15
16 -- This script will create three different config files from three tables
17 -- with values determined by the host and configuration options:
18 --
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.
24 --
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.
28 --
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.
33
34
35 function show_help()
36 print([[
37
38 This script prepares ]]..project..[[ for building on your system.
39 Usage: ./configure [OPTION]... [VAR=VALUE]...
40
41 This is NOT an autoconf-generated configure script, though it was written
42 to be familiar by supporting many of the same options.
43
44 Basic configuration:
45 -h, --help display this help and exit
46 --host=HOST cross-compile the program to run on HOST
47
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
52
53 --disable-dependency-tracking speed up one-time builds (maybe)
54 --disable-special-linking do not use direct dependency minimizer
55 --enable-profile compile in gprof profiling instructions
56
57 Program options:
58 --enable-clock_gettime use a very accurate timing function
59 --enable-debug compile in assertion checks and other debug helps
60 --enable-double-precision use larger floating-point numbers
61 --enable-hotload automatically reload modified game assets
62 --enable-threads use threads for concurrency where appropriate
63
64 --with-gtk use the gtk2 toolkit (overrides --with-qt4)
65 --with-qt4 use the qt4 gui toolkit
66 ]])
67 end
68
69
70 --
71 -- Setup a temporary file to collect error messages.
72 --
73
74 config_log = "config.log"
75 os.remove(config_log)
76
77
78 --
79 -- Define some useful functions.
80 --
81
82 -- Return true if a file exists, false otherwise.
83 function file_exists(path)
84 return os.execute(string.format("test -f %q", path)) == 0
85 end
86
87 -- Print an error message and exit with an error.
88 function die(...)
89 for i,value in ipairs(arg) do
90 print("fatal: "..tostring(value))
91 end
92 if file_exists(config_log) then
93 print()
94 print("Look through the file `config.log' for more information:\n")
95 os.execute("tail "..config_log)
96 end
97 os.exit(1)
98 end
99
100 -- Execute a command and return its output or nil if the command failed to
101 -- run.
102 function backtick_run(command)
103 os.execute("echo '# "..command.."' >>"..config_log)
104 local fd = io.popen(command.." 2>>"..config_log)
105 if fd then
106 local stdout = fd:read("*l")
107 fd:close()
108 return stdout
109 end
110 return nil
111 end
112
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
118 end
119
120 -- Remove the whitespace surrounding a string.
121 function trim(str)
122 str = str:gsub("^%s+", "")
123 return str:gsub("%s+$", "")
124 end
125
126 -- Trim the string and convert all sequences of whitespace to a single
127 -- space.
128 function reduce_whitespace(str)
129 str = str:gsub("%s+", " ")
130 return trim(str)
131 end
132
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)
138 end
139
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*", "")
145 end
146
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)
152 end
153
154 -- Add a flag to the CFLAGS and CXXFLAGS variables.
155 function add_cflag(flag)
156 if CFLAGS == "" then
157 CFLAGS = flag
158 else
159 CFLAGS = string.format("%s %s", CFLAGS, flag)
160 end
161 if CXXFLAGS == "" then
162 CXXFLAGS = flag
163 else
164 CXXFLAGS = string.format("%s %s", CXXFLAGS, flag)
165 end
166 end
167
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()
172 cflag_set = true
173 return flag
174 end)
175 CXXFLAGS = CXXFLAGS:gsub("%"..flag:sub(1, 2).."%S+", function()
176 cxxflag_set = true
177 return flag
178 end)
179 if not cflag_set or not cxxflag_set then add_cflag(flag) end
180 end
181
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 i,command in ipairs(commands) do
187 if try_run(command.." "..args) then return command end
188 end
189 return nil
190 end
191
192
193 --
194 -- Perform a quick sanity check.
195 --
196
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.")
200 end
201
202
203 --
204 -- Parse the command-line options.
205 --
206
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).
213 --
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.
221 --
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
224 -- namespace.
225 --
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.
229
230 do
231 local features = {}
232 local packages = {}
233 local handlers = {}
234
235
236 -- Define the option-parsing rules and handlers.
237
238 handlers["--help"] = function()
239 show_help()
240 os.exit(0)
241 end
242 handlers["-h"] = handlers["--help"]
243
244 handlers["--host=(.+)"] = function(triplet)
245 host = triplet
246 cross_compile = true
247 end
248
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
253 end
254 handlers["--enable%-([%w_-]+)=?(.*)"] = add_feature
255 handlers["--disable%-([%w_-]+)"] = function(feature)
256 add_feature(feature, "no")
257 end
258
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
263 end
264 handlers["--with%-([%w_-]+)=?(.*)"] = add_package
265 handlers["--without%-([%w_-]+)"] = function(package)
266 add_package(package, "no")
267 end
268
269 handlers["--(%l+)dir=(.+)"] = function(dir, path)
270 _G[dir.."dir"] = path
271 end
272 handlers["--prefix=(.+)"] = function(path)
273 prefix = path
274 end
275 handlers["--exec-prefix=(.+)"] = function(path)
276 eprefix = path
277 end
278
279 handlers["([%w_]+)=(.*)"] = function(key, value)
280 _G[key] = value
281 end
282
283
284 -- Define the feature and package accessors.
285
286 function get_feature(feature) return features[feature] end
287 function get_package(package) return packages[package] end
288
289
290 -- Now read and parse the command-line options.
291
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
296 end
297 print("warning: unknown or incomplete argument "..arg)
298 end
299
300 for i,arg in ipairs(arg) do
301 parse_arg(arg)
302 end
303
304
305 -- Define default values for significant variables.
306
307 local function define(symbol, value)
308 if not _G[symbol] then _G[symbol] = value end
309 end
310
311 define("CC", "")
312 define("CXX", "")
313 define("AR", "")
314 define("RANLIB", "")
315 define("WINDRES", "")
316
317 define("CFLAGS", "-g -O2")
318 define("CXXFLAGS", CFLAGS)
319 define("LDFLAGS", "")
320 define("LIBS", "")
321
322 define("host", backtick_run("tools/config.guess"))
323 define("alt_host", backtick_run("tools/config.sub "..host))
324
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)
340
341 if features["dependency-tracking"] == nil then
342 features["dependency-tracking"] = true
343 end
344
345 if features["special-linking"] == nil then
346 features["special-linking"] = true
347 end
348
349 define("config", {})
350 define("define", {})
351 define("export", {})
352 end
353
354
355 --
356 -- Determine special target platforms.
357 --
358
359 -- Win32 has some special cases related to its resource file, src/yoinkrc.
360 if host:match("mingw32") then platform = "win32" end
361
362
363 --
364 -- Look for a working toolchain.
365 --
366
367 print("Please wait...")
368
369 -- Check for CC.
370 tmpname = os.tmpname()..".c"
371 tmpfile, err = io.open(tmpname, "w")
372 if tmpfile then
373 tmpfile:write([[
374 #include <stdio.h>
375 int main()
376 {
377 printf("Hello world!\n");
378 return 0;
379 }
380 ]])
381 tmpfile:close()
382
383 function extra() if not cross_compile then return "gcc", "cc" end end
384 CC = find_command({
385 CC,
386 host.."-gcc", host.."-cc",
387 alt_host.."-gcc", alt_host.."-cc",
388 extra()}, tmpname.." -o "..tmpname..".tmp")
389 os.remove(tmpname)
390 os.remove(tmpname..".tmp")
391 if not CC then die("Can't find a working C compiler.") end
392 else
393 die("failed to create temporary file: "..err)
394 end
395
396 -- Check for CXX.
397 tmpname = os.tmpname()..".cpp"
398 tmpfile, err = io.open(tmpname, "w")
399 if tmpfile then
400 tmpfile:write([[
401 #include <iostream>
402 int main()
403 {
404 std::cout << "Hello world!" << std::endl;
405 return 0;
406 }
407 ]])
408 tmpfile:close()
409
410 function extra() if not cross_compile then return "g++", "c++" end end
411 CXX = find_command({
412 CXX,
413 host.."-g++", host.."-c++",
414 alt_host.."-g++", alt_host.."-c++",
415 extra()}, tmpname.." -o "..tmpname..".tmp")
416 os.remove(tmpname)
417 os.remove(tmpname..".tmp")
418 if not CXX then die("Can't find a working C++ compiler.") end
419 else
420 die("failed to create temporary file: "..err)
421 end
422
423 -- Check for AR.
424 do
425 function extra() if not cross_compile then return "ar" end end
426 AR = find_command({
427 AR,
428 host.."-ar",
429 alt_host.."-ar",
430 extra()}, "--version")
431 if not AR then die("Can't find a working archiver.") end
432 end
433
434 -- Check for RANLIB.
435 do
436 function extra() if not cross_compile then return "ranlib" end end
437 RANLIB = find_command({
438 RANLIB,
439 host.."-ranlib",
440 alt_host.."-ranlib",
441 extra()}, "--version")
442 if not RANLIB then die("Can't find a working library indexer.") end
443 end
444
445 -- Check for WINDRES.
446 if platform == "win32" then
447 function extra() if not cross_compile then return "windres" end end
448 WINDRES = find_command({
449 WINDRES,
450 host.."-windres",
451 alt_host.."-windres",
452 extra()}, "--version")
453 if not WINDRES then die("Can't find a working resource compiler.") end
454 end
455
456
457 --
458 -- Configure the features and packages.
459 --
460
461 if get_feature("debug") then
462 set_cflag("-O0")
463 add_cflag("-Wall -Wno-uninitialized")
464 config.DEBUG = true
465 else
466 config.NDEBUG = true
467 end
468
469 config.ENABLE_CLOCK_GETTIME = get_feature("clock_gettime")
470 config.ENABLE_DOUBLE_PRECISION = get_feature("double-precision")
471 config.ENABLE_HOTLOADING = get_feature("hotload")
472 config.ENABLE_THREADS = get_feature("threads")
473 if get_feature("profile") then
474 config.ENABLE_PROFILING = true
475 add_cflag("-pg")
476 LDFLAGS = LDFLAGS .. "-pg"
477 end
478
479 if get_package("gtk") then config.WITH_GTK = true end
480 if get_package("qt4") then config.WITH_QT4 = true end
481
482
483 --
484 -- Check for the libraries we need.
485 --
486
487 do
488 local dependencies = "sdl gl glu libpng openal vorbisfile lua"
489
490 if get_package("gtk") then
491 dependencies = dependencies.." gtk+-2.0"
492 elseif get_package("qt4") then
493 dependencies = dependencies.." QtGui"
494 end
495
496 add_cflag(pkg_config_cflags(dependencies))
497 LDFLAGS = LDFLAGS .." "..pkg_config_ldflags(dependencies)
498 LIBS = LIBS .." "..pkg_config_libs(dependencies)
499
500 if platform == "win32" then
501 LIBS = LIBS.." -lws2_32"
502 exe_extension = ".exe"
503 else
504 exe_extension = ""
505 end
506 end
507
508
509 --
510 -- Define the exports and definitions.
511 --
512
513 if platform == "win32" then
514 -- These are used in src/yoink.rc.
515 local vmajor, vminor, vrevis = version:match("^(%d*)%.?(%d*)%.?(%d*)")
516 config.VERSION_MAJOR = tonumber(vmajor) or 0
517 config.VERSION_MINOR = tonumber(vminor) or 0
518 config.VERSION_REVISION = tonumber(vrevis) or 0
519 end
520
521 config.PACKAGE = tarname
522 config.PACKAGE_NAME = project
523 config.PACKAGE_VERSION = version
524 config.PACKAGE_STRING = project.." "..version
525 config.PACKAGE_TARNAME = tarname
526 config.PACKAGE_URL = website
527 config.PACKAGE_BUGREPORT = contact
528 config.YOINK_GITHEAD = backtick_run("git describe master")
529 config.YOINK_DATADIR = datadir
530
531 define.PACKAGE = project
532 define.TARNAME = tarname.."-"..version
533 define.TARGET = host
534 define.PLATFORM = platform
535 define.CC = CC
536 define.CXX = CXX
537 define.AR = AR
538 define.RANLIB = RANLIB
539 define.WINDRES = WINDRES
540 define.CFLAGS = reduce_whitespace(CFLAGS)
541 define.CXXFLAGS = reduce_whitespace(CXXFLAGS)
542 define.LDFLAGS = reduce_whitespace(LDFLAGS)
543 define.LIBS = reduce_whitespace(LIBS)
544 define.prefix = prefix
545 define.bindir = bindir
546 define.datadir = datadir
547 define.mandir = mandir
548 define.EXEEXT = exe_extension
549 define.DEP_TRACKING = get_feature("dependency-tracking")
550 define.DEP_MINIMIZING = get_feature("special-linking")
551
552 export.datadir = datadir -- Used in doc/yoink.6.in.
553 export.PACKAGE_BUGREPORT = contact -- Used in doc/yoink.6.in.
554
555
556 --
557 -- All done; output the configuration files.
558 --
559
560 -- Output the program options.
561 output = io.open("config.h", "w")
562 for key,value in pairs(config) do
563 key = tostring(key)
564 if type(value) == "boolean" then
565 if value then
566 output:write("#define "..key.." 1")
567 else
568 output:write("#undef "..key)
569 end
570 elseif type(value) == "string" then
571 output:write(string.format("#define %s %q", key, tostring(value)))
572 else
573 output:write(string.format("#define %s %s", key, tostring(value)))
574 end
575 output:write("\n")
576 end
577 output:close()
578
579 -- Output the make definitions.
580 output = io.open("config.mk", "w")
581 for key,value in pairs(define) do
582 key = tostring(key)
583 value = tostring(value)
584 output:write(string.format("%s = %s\n", key, value))
585 end
586 output:close()
587
588
589 -- Output the exported variables.
590 output = io.open("config.sed", "w")
591 for key,value in pairs(export) do
592 key = key:gsub("/", "\\/")
593 value = value:gsub("/", "\\/")
594 output:write(string.format("s/@%s@/%s/g\n", key, value))
595 end
596 output:close()
597
598
599 print([[
600
601 Configuration complete! You can review your configuration in `config.mk'.
602
603 To finish the installation, type:
604 make
605 make install
606 ]])
607
This page took 0.065079 seconds and 4 git commands to generate.