]> Dogcows Code - chaz/yoink/blob - configure
repository cleanup
[chaz/yoink] / configure
1 #!/usr/bin/env lua
2
3 --
4 -- Yoink
5 -- Execute this file to configure the build system.
6 --
7
8 project = "Yoink"
9 package = project:lower()
10 version = "0.1"
11 bugreport = "chaz@dogcows.com"
12
13
14 function ShowHelp()
15 print([[
16
17 This script prepares ]]..project..[[ for building on your system.
18 Usage: ./configure [OPTION]... [VAR=VALUE]...
19
20 This is NOT an autoconf-generated configure script, though it was written
21 to be familiar by supporting many of the same options.
22
23 Basic configuration:
24 -h, --help display this help and exit
25 --host=HOST cross-compile the program to run on HOST
26
27 --prefix=DIR base directory to install programs to
28 --bindir=DIR directory to install executables
29 --datadir=DIR directory to install shared data files
30 --mandir=DIR directory to install manual pages
31
32 --disable-dependency-tracking speed up one-time builds
33 --enable-link-sh decrease the number of direct dependencies
34
35 Program options:
36 --enable-debug include debugging symbols and code paths
37 --enable-double-precision use doubles instead of floats
38 --enable-profile compile in gprof profiling instructions
39 --enable-clock_gettime use clock_gettime() for timing
40 --enable-threads use threads for concurrency
41 --enable-hotloading watch assets for automatic reloading
42
43 --with-gtk use the gtk2 toolkit (overrides --with-qt4)
44 --with-qt4 use the qt4 gui toolkit
45 ]])
46 end
47
48
49 --
50 -- Define some useful functions.
51 --
52
53 function Die(...)
54 for _,value in ipairs(arg) do
55 print("fatal: "..tostring(value))
56 end
57 os.exit(1)
58 end
59
60 function FileExists(file)
61 return os.execute("test -f "..file) == 0
62 end
63
64 function ReadCommand(command)
65 local fd = io.popen(command)
66 if fd then
67 local stdout = fd:read("*l")
68 fd:close()
69 return stdout
70 end
71 return nil
72 end
73
74 function TryCommand(command)
75 return os.execute(command.." 2>&1 >/dev/null") == 0
76 end
77
78 function Trim(str)
79 str = str:gsub("^%s+", "")
80 return str:gsub("%s+$", "")
81 end
82
83 function Reduce(str)
84 str = str:gsub("%s+", " ")
85 return Trim(str)
86 end
87
88
89 --
90 -- Perform a quick sanity check.
91 --
92
93 if not FileExists("configure") or not FileExists("Makefile") then
94 Die("You must `cd' to the project root where the Makefile is.")
95 end
96
97
98 --
99 -- Parse the command-line options.
100 --
101
102 do
103 local features = {}
104 local packages = {}
105 local directories = {}
106 local definitions = {}
107
108 local function AddFeature(feature, value)
109 if value == "yes" or value == "" then value = true end
110 if value == "no" then value = false end
111 features[feature] = value
112 end
113
114 local function AddPackage(package, value)
115 if value == "yes" or value == "" then value = true end
116 if value == "no" then value = false end
117 packages[package] = value
118 end
119
120 local function AddDirectory(directory, path)
121 directories[directory] = path
122 end
123
124 local function AddDefinition(key, value)
125 definitions[key] = value
126 end
127
128 local handlers = {
129 ["--help"] = function() ShowHelp() os.exit(0) end,
130 ["--host=(.+)"] = function(arg) host = arg cross_compile = true end,
131 ["--enable%-([%w_-]+)=?(.*)"] = AddFeature,
132 ["--disable%-([%w_-]+)"] = function(arg) AddFeature(arg, "no") end,
133 ["--with%-([%w_-]+)=?(.*)"] = AddPackage,
134 ["--without%-([%w_-]+)"] = function(arg) AddPackage(arg, "no") end,
135 ["--(%l+)dir=(.+)"] = AddDirectory,
136 ["--prefix=(.+)"] = function(arg) prefix = arg end,
137 ["--exec-prefix=(.+)"] = function(arg) eprefix = arg end,
138 ["([%w_]+)=(.*)"] = AddDefinition,
139 }
140 handlers["-h"] = handlers["--help"]
141
142 local function ParseArg(arg)
143 for key,value in pairs(handlers) do
144 local matches = {arg:match(key)}
145 if matches[1] then
146 value(unpack(matches))
147 return
148 end
149 end
150 print("warning: unknown or incomplete argument "..arg)
151 end
152
153
154 -- Define some default values.
155
156 prefix = "/usr/local"
157
158 CC = ""
159 CXX = ""
160 AR = ""
161 RANLIB = ""
162 WINDRES = ""
163
164 CFLAGS = "-g -O2"
165 CXXFLAGS = CFLAGS
166 LDFLAGS = ""
167 LIBS = ""
168
169 features["dependency-tracking"] = true
170
171
172 -- Read the arguments from the command-line.
173 for _,arg in ipairs(arg) do
174 ParseArg(arg)
175 end
176
177 function GetFeature(feature) return features[feature] end
178 function GetPackage(package) return packages[package] end
179
180 for key,value in pairs(directories) do
181 _G[key.."dir"] = value
182 end
183 for key,value in pairs(definitions) do
184 _G[key] = value
185 end
186
187
188 -- Define the dependent values.
189
190 if not host then host = ReadCommand("tools/config.guess") end
191 alt_host = ReadCommand("tools/config.sub "..host)
192
193 if not eprefix then eprefix = prefix end
194 if not bindir then bindir = eprefix.."/bin" end
195 if not sbindir then sbindir = eprefix.."/sbin" end
196 if not libexecdir then libexecdir = eprefix.."/libexec" end
197 if not sysconfdir then sysconfdir = prefix.."/etc" end
198 if not localstatedir then localstatedir = prefix.."/var" end
199 if not libdir then libdir = eprefix.."/lib" end
200 if not includedir then includedir = prefix.."/include" end
201 if not datarootdir then datarootdir = prefix.."/share" end
202 if not datadir then datadir = datarootdir.."/"..package end
203 if not infodir then infodir = datarootdir.."/info" end
204 if not localedir then localedir = datarootdir.."/locale" end
205 if not mandir then mandir = datarootdir.."/man" end
206 if not docdir then docdir = datarootdir.."/doc/"..package end
207
208 cflags = ""
209 config = {}
210 define = {}
211 export = {}
212
213 define.DEP_TRACKING = GetFeature("dependency-tracking")
214 end
215
216
217 --
218 -- Determine the target platform.
219 --
220
221 if host:match("mingw32") then platform = "win32"
222 elseif host:match("netbsd") then platform = "netbsd" end
223
224
225 --
226 -- Define the check function.
227 --
228
229 do
230 local path = os.getenv("PATH")
231
232 -- 1. List of possible command names.
233 -- 2. Command arguments.
234 function FindCommand(commands, args)
235 if not args then args = "" end
236 for _,command in ipairs(commands) do
237 if command then
238 for dir in path:gmatch("[^:]+") do
239 if FileExists(dir.."/"..command) and TryCommand(command.." "..args) then
240 return command
241 end
242 end
243 end
244 end
245 return nil
246 end
247 end
248
249
250 --
251 -- Look for a working toolchain.
252 --
253
254 print("Please wait...")
255
256 -- Check for CC.
257 tmpname = os.tmpname()..".c"
258 tmpfile, err = io.open(tmpname, "w")
259 if tmpfile then
260 tmpfile:write([[
261 #include <stdio.h>
262 int main()
263 {
264 printf("Hello world!\n");
265 return 0;
266 }
267 ]])
268 tmpfile:close()
269
270 function extra() if not cross_compile then return "gcc", "cc" end end
271 CC = FindCommand({
272 CC,
273 host.."-gcc",
274 host.."-cc",
275 alt_host.."-gcc",
276 alt_host.."-cc",
277 extra()}, tmpname.." -o "..tmpname..".tmp")
278 os.remove(tmpname)
279 os.remove(tmpname..".tmp")
280 if not CC then Die("Can't find a working C compiler.") end
281 else
282 Die("failed to create temporary file: "..err)
283 end
284
285 -- Check for CXX.
286 tmpname = os.tmpname()..".c"
287 tmpfile, err = io.open(tmpname, "w")
288 if tmpfile then
289 tmpfile:write([[
290 #include <iostream>
291 int main()
292 {
293 std::cout << "Hello world!" << std::endl;
294 return 0;
295 }
296 ]])
297 tmpfile:close()
298
299 function extra() if not cross_compile then return "g++", "c++" end end
300 CXX = FindCommand({
301 CXX,
302 host.."-g++",
303 host.."-c++",
304 alt_host.."-g++",
305 alt_host.."-c++",
306 extra()}, tmpname.." -o "..tmpname..".tmp")
307 os.remove(tmpname)
308 os.remove(tmpname..".tmp")
309 if not CXX then Die("Can't find a working C++ compiler.") end
310 else
311 Die("failed to create temporary file: "..err)
312 end
313
314 -- Check for AR.
315 do
316 function extra() if not cross_compile then return "ar" end end
317 AR = FindCommand({
318 AR,
319 host.."-ar",
320 alt_host.."-ar",
321 extra()}, "--version")
322 if not AR then Die("Can't find a working archiver.") end
323 end
324
325 -- Check for RANLIB.
326 do
327 function extra() if not cross_compile then return "ranlib" end end
328 RANLIB = FindCommand({
329 RANLIB,
330 host.."-ranlib",
331 alt_host.."-ranlib",
332 extra()}, "--version")
333 if not RANLIB then Die("Can't find a working library indexer.") end
334 end
335
336 -- Check for WINDRES.
337 if platform == "win32" then
338 function extra() if not cross_compile then return "windres" end end
339 WINDRES = FindCommand({
340 WINDRES,
341 host.."-windres",
342 alt_host.."-windres",
343 extra()}, "--version")
344 if not WINDRES then Die("Can't find a working win32 resource compiler.") end
345 end
346
347
348 --
349 -- Configure the features and packages.
350 --
351
352 if GetFeature("debug") then
353 cflags = cflags.." -O0 -Wall -Wno-uninitialized"
354 config.DEBUG = true
355 else
356 config.NDEBUG = true
357 end
358
359 if GetFeature("double-precision") then
360 config.USE_DOUBLE_PRECISION = true
361 end
362
363 if GetFeature("profile") then
364 cflags = cflags.." -pg"
365 config.PROFILING_ENABLED = true
366 end
367
368 if GetFeature("extra-warnings") then
369 cflags = cflags.." -Wextra -Wno-unused-parameter"
370 end
371
372 if GetFeature("link-sh") then
373 -- TODO
374 end
375
376 if GetFeature("clock_gettime") then
377 -- TODO
378 end
379
380 if GetFeature("threads") then
381 config.USE_THREADS = true
382 end
383
384 if GetFeature("hotloading") then
385 print("FYI: Hotloading is very experimental and only works on Linux.");
386 config.USE_HOTLOADING = true
387 end
388
389 if GetPackage("gtk") then
390 -- TODO
391 end
392
393 if GetPackage("qt4") then
394 -- TODO
395 end
396
397
398 --
399 -- Check for the libraries we need.
400 --
401
402 do
403 local command = "PKG_CONFIG_PATH="..libdir.."/pkgconfig:$PKG_CONFIG_PATH pkg-config"
404 local deps = "sdl gl glu libpng openal vorbisfile lua"
405
406 if GetPackage("gtk") then
407 deps = deps.." gtk+-2.0"
408 elseif GetPackage("qt4") then
409 deps = deps.." QtGui"
410 end
411
412 local pc_cflags = ReadCommand(command.." --cflags "..deps)
413 if not pc_cflags then Die("Couldn't determine CFLAGS.") end
414 CFLAGS = CFLAGS.." "..pc_cflags
415 CXXFLAGS = CXXFLAGS.." "..pc_cflags
416
417 local pc_libs = ReadCommand(command.." --libs "..deps)
418 if not pc_libs then Die("Couldn't determine LDFLAGS or LIBS.") end
419 for lib in pc_libs:gmatch("%-l%S+") do
420 LIBS = LIBS.." "..lib
421 end
422 for ldflag in pc_libs:gmatch("%-[^l]%S*") do
423 LDFLAGS = LDFLAGS.." "..ldflag
424 end
425
426 if platform == "win32" then
427 LIBS = LIBS.." -lws2_32"
428 exe_extension = ".exe"
429 else
430 exe_extension = ""
431 end
432 end
433
434 CFLAGS = Reduce(CFLAGS.." "..cflags)
435 CXXFLAGS = Reduce(CXXFLAGS.." "..cflags)
436 LDFLAGS = Reduce(LDFLAGS)
437 LIBS = Reduce(LIBS)
438
439
440 --
441 -- Define the exports and definitions.
442 --
443
444 config.YOINK_DATADIR = datadir
445 export.YOINK_DATADIR = datadir
446
447 do
448 local vmajor = ReadCommand("v=$(echo "..version.." | cut -d. -f1); echo ${v:-0}")
449 local vminor = ReadCommand("v=$(echo "..version.." | cut -d. -f2); echo ${v:-0}")
450 local vrevis = ReadCommand("v=$(echo "..version.." | cut -d. -f3); echo ${v:-0}")
451
452 config.VERSION_MAJOR = tonumber(vmajor)
453 config.VERSION_MINOR = tonumber(vminor)
454 config.VERSION_REVISION = tonumber(vrevis)
455 end
456
457 do
458 -- Determine and define the git revision.
459 local head = ReadCommand("git log -n1 --date=short --pretty=format:\"%h (%ad)\"")
460 config.YOINK_GITHEAD = head
461 end
462
463 config.PACKAGE = project
464 config.PACKAGE_NAME = project
465 config.VERSION = version
466 config.PACKAGE_VERSION = version
467 config.PACKAGE_STRING = project.." "..version
468 config.PACKAGE_BUGREPORT = bugreport
469
470 define.TARGET = host
471 define.PLATFORM = platform
472 define.CC = CC
473 define.CXX = CXX
474 define.AR = AR
475 define.RANLIB = RANLIB
476 define.WINDRES = WINDRES
477 define.CFLAGS = CFLAGS
478 define.CXXFLAGS = CXXFLAGS
479 define.LDFLAGS = LDFLAGS
480 define.LIBS = LIBS
481 define.prefix = prefix
482 define.bindir = bindir
483 define.datadir = datadir
484 define.mandir = mandir
485 define.EXEEXT = exe_extension
486
487 export.PACKAGE_BUGREPORT = bugreport
488
489
490 --
491 -- All done; output the configuration files.
492 --
493
494 -- Print the program options.
495 output = io.open("config.h", "w")
496 for key,value in pairs(config) do
497 key = tostring(key)
498 if type(value) == "boolean" then
499 if value then
500 output:write("#define "..key)
501 else
502 output:write("#undef "..key)
503 end
504 elseif type(value) == "string" then
505 output:write(string.format("#define %s %q", key, tostring(value)))
506 else
507 output:write(string.format("#define %s %s", key, tostring(value)))
508 end
509 output:write("\n")
510 end
511 output:close()
512
513 -- Print the make definitions.
514 output = io.open("config.mk", "w")
515 for key,value in pairs(define) do
516 key = tostring(key)
517 value = tostring(value)
518 output:write(string.format("%s = %s\n", key, value))
519 end
520 output:close()
521
522
523 -- Print the exported variables.
524 output = io.open("config.sed", "w")
525 for key,value in pairs(export) do
526 key = key:gsub("/", "\\/")
527 value = value:gsub("/", "\\/")
528 output:write(string.format("s/@%s@/%s/g\n", key, value))
529 end
530 output:close()
531
532
533 print([[
534
535 Configuration complete! You can review your configuration in `config.mk'.
536
537 To finish the installation, type:
538 make
539 make install
540 ]])
541
This page took 0.054774 seconds and 4 git commands to generate.