]> Dogcows Code - chaz/yoink/blob - build/config.lua
remove some unused stlplus modules
[chaz/yoink] / build / config.lua
1 --
2 -- Yoink Build System
3 -- Execute this script with Lua to prepare the project for compiling.
4 --
5 -- Copyright (c) 2011, Charles McGarvey
6 -- Distributable under the terms and conditions of the 2-clause BSD
7 -- license; see the file COPYING for a complete text of the license.
8 --
9
10
11 --local function trace(event, line)
12 --local s = debug.getinfo(2).short_src
13 --print(s .. ":" .. line)
14 --end
15 --debug.sethook(trace, "l")
16
17
18 -- Convert the arguments into standard form. Each argument should be a
19 -- string. A table is returned with the arguments' standard form as keys.
20 -- If an argument has a value, it will correspond to the appropriate key in
21 -- the table. This function supports autoconf-style arguments, including:
22 -- 1. -h, --help produce a --help key with a value of true.
23 -- 2. --enable-feature=[yes|no|whatever], --disable-feature produce a
24 -- --enable-feature key with a value of true, false, or a string.
25 -- 3. --with-package=[yes|no|whatever], --without-feature produce a
26 -- --with-package key with a value of true, false, or a string.
27 -- 4. SYMBOL=VALUE produce a SYMBOL key with VALUE as the string value.
28 -- 5. Anything else will appear as a key in the table as-is with a value of
29 -- true.
30 -- If multiple arguments mapping to the same key are present in the
31 -- argument list, the last one is used.
32 local function parseArguments(...)
33 local result = {}
34 local filters = {}
35
36 local function setPair(key, value) result[key] = value end
37 local function setTrue(key) result[key] = true end
38 local function addLibrarySearchPath(path)
39 package.path = path .. "/?.lua;" .. package.path
40 package.cpath = path .. "/?.so;" .. package.cpath
41 end
42
43 table.insert(filters, {"^-h$", function() result["--help"] = true end})
44 table.insert(filters, {"^-L(.*)$", addLibrarySearchPath})
45
46 table.insert(filters, {"^(--enable%-[%w_-]+)=?(.*)$", setPair})
47 table.insert(filters, {"^--disable%-([%w_-]+)$", function(feature)
48 setPair("--enable-" .. feature, "no")
49 end})
50
51 table.insert(filters, {"^(--with%-[%w_-]+)=?(.*)$", setPair})
52 table.insert(filters, {"^--without%-([%w_-]+)$", function(package)
53 setPair("--with-" .. package, "no")
54 end})
55
56 table.insert(filters, {"^([%w_-]+)=(.*)$", setPair})
57 table.insert(filters, {"^([%w_-]+)$", setTrue})
58
59 for _,a in ipairs(arg)
60 do
61 for _,filter in pairs(filters)
62 do
63 local matches = {a:match(filter[1])}
64 if matches[1] then filter[2](unpack(matches)) break end
65 end
66 end
67
68 return result
69 end
70
71
72 local arguments = parseArguments(unpack(arg))
73 local interactive = arguments["--interactive"]
74 local rules = arguments["--rules"] or "options.lua"
75 local configfile = arguments["--configfile"] or "config.mk"
76 local printendmsg = arguments["--print-instructions"]
77 local exportHeader = arguments["--export-header"]
78 local exportTerms = arguments["--export-terms"]
79
80
81 local util = require "utility"
82 util.logfile = "config.log"
83
84
85 local printInfo = util.printer("%s - %s\n")
86 local printWarning = util.printer("\a%s - %s\n", "33")
87 local printError = util.printer("\a%s - %s\n", "31")
88 local function beginProcess(title, caption)
89 print(string.format("%s:\n", title))
90 local updater = util.printer(" [%3d%%] %s\n")
91 updater(0, caption)
92 return function(progress, caption)
93 if progress ~= nil
94 then
95 if 0.0 <= progress and progress <= 1.0 then
96 progress = progress * 100
97 end
98 return updater(progress, caption)
99 end
100 print()
101 return 0
102 end
103 end
104
105 local function loadDialog(name, configfile)
106 local dialog
107 local result, err = pcall(function() dialog = require "dialog" end)
108 if not result then printWarning(err) return nil end
109 dialog.title = string.format("%s - %s Configuration", configfile, name)
110 printInfo = dialog.msgbox
111 printWarning = dialog.msgbox
112 printError = dialog.msgbox
113 beginProcess = dialog.gauge
114 return dialog
115 end
116
117
118 local function topologicalSort(lookup)
119 local dependants = util.copy(lookup, "v", {v = function() return 0 end})
120 for _,option in pairs(lookup)
121 do
122 for _,dep in ipairs(option:getDirectDependants())
123 do
124 dependants[dep] = dependants[dep] + 1
125 end
126 end
127
128 local sorted = {}
129 local queued = {}
130 for symbol,count in pairs(dependants)
131 do
132 if count == 0 then table.insert(queued, symbol) end
133 end
134 while 0 < #queued
135 do
136 local symbol = table.remove(queued)
137 table.insert(sorted, symbol)
138 for _,dep in ipairs(lookup[symbol]:getDirectDependants())
139 do
140 dependants[dep] = dependants[dep] - 1
141 if dependants[dep] == 0 then table.insert(queued, dep) end
142 end
143 end
144
145 local remaining = {}
146 for symbol,count in pairs(dependants)
147 do
148 if 0 < count then table.insert(remaining, symbol) end
149 end
150 if 0 < #remaining
151 then
152 printWarning("Q.A. Notice", "One or more circular dependencies were detected involving these symbols: "
153 .. table.concat(remaining, ", "))
154 for _,symbol in ipairs(remaining) do table.insert(sorted, symbol) end
155 end
156
157 return sorted
158 end
159
160 local function checkSymbols(symbols)
161 local check = topologicalSort(symbols)
162 local isErr = false
163 local updateTask = function() end
164 if 1 < #check
165 then
166 updateTask = beginProcess("Checking Symbols", "Resolving dependencies...", 6, 65)
167 end
168 for i = 1, #check
169 do
170 local option = symbols[check[i]]
171 if option:validate() == nil then isErr = true end
172 updateTask(i / #check,
173 (option:getSymbol() .. ": " .. option:toExpandedString()):truncate(60))
174 end
175 updateTask()
176 if isErr then return nil end
177 return true
178 end
179
180
181 ---------------------------------------------------------------------------
182 local Option = util.class()
183 ---------------------------------------------------------------------------
184
185 function Option:__init(rule, lookup, objects)
186 self.name = rule.name
187 self.caption = rule.caption
188 self.help = rule.help
189 self.value = rule.value
190 self.symbol = rule.symbol
191 self.cmdline = rule.cmdline
192 self.config = rule.config
193 self.export = rule.export
194 self.visible = rule.visible
195 self.check = rule.check
196 self.temp = rule.temp
197 self.before = rule.before
198 self.after = rule.after
199 self.lookup = lookup or {}
200 self.objects = objects or {}
201
202 if type(self.check) == "function" then setfenv(self.check, self.lookup) end
203 if type(self.visible) == "function" then setfenv(self.visible, self.lookup) end
204
205 if self.symbol ~= nil
206 then
207 if self.lookup[self.symbol] ~= nil
208 then
209 printWarning("duplicate symbol defined:", self.symbol)
210 end
211 self.lookup[self.symbol] = self.value
212 self.objects[self.symbol] = self
213 end
214 end
215
216 -- Get the symbol of the option. The symbol is used as a reference in the
217 -- string values of other options. It must be unique.
218 function Option:getSymbol()
219 return self.symbol
220 end
221
222 -- Get the argument of the option.
223 function Option:getArg()
224 return self.cmdline
225 end
226
227 -- Get the value of the option as a string.
228 function Option:toString()
229 return tostring(self.lookup[self.symbol])
230 end
231
232 -- Get the value of the option as an expanded string. For most types of
233 -- options, this is the same as Option:toString().
234 function Option:toExpandedString()
235 return self:toString()
236 end
237
238 function Option:getDirectDependants()
239 return self.before or {}
240 end
241
242 function Option:getUnsortedDependants(dependants)
243 dependants = dependants or {}
244 if dependants[self.symbol] then return dependants end
245 dependants[self.symbol] = self
246 if self.before
247 then
248 for _,dep in ipairs(self.before)
249 do
250 local option = self.objects[dep]
251 if option
252 then
253 dependants = option:getUnsortedDependants(dependants)
254 else
255 printWarning("invalid dependency: " .. dep)
256 end
257 end
258 end
259 return dependants
260 end
261
262 function Option:addDependant(dependency)
263 self.before = self.before or {}
264 table.insert(self.before, dependency)
265 end
266
267 function Option:convertDependenciesToDependants()
268 if self.after
269 then
270 local dep = table.remove(self.after)
271 while dep ~= nil
272 do
273 local option = self.objects[dep]
274 if option
275 then
276 option:addDependant(self.symbol)
277 else
278 printWarning("invalid dependency: " .. dep)
279 end
280 dep = table.remove(self.after)
281 end
282 end
283 end
284
285 function Option:getObjects()
286 return self.objects
287 end
288
289 -- Check the value of the option for validity.
290 -- Returns a valid value based on the given value, or nil (with an error
291 -- message) if the value is invalid and could not be converted to a valid
292 -- value.
293 function Option:validate(value)
294 local err
295 if value == nil then value = self.lookup[self.symbol] end
296 self.problem = nil
297 if type(self.check) == "function"
298 then
299 value, err = self.check(value)
300 if value == nil then self.problem = err return nil, err end
301 end
302 self.lookup[self.symbol] = value
303 return value, err
304 end
305
306 -- Set the value of the option to the value in a new lookup table.
307 function Option:applyTable(lookup)
308 end
309
310 -- Set the value of the option based on a table of argument flags.
311 function Option:applyArguments()
312 end
313
314 -- Set the value of the option.
315 function Option:set(value)
316 self.lookup[self.symbol] = value
317 if checkSymbols(self:getUnsortedDependants())
318 then
319 return self.lookup[self.symbol]
320 end
321 end
322
323 -- Write the symbol and current value to the config file.
324 function Option:writeSymbol(fd)
325 if self.symbol ~= nil and not self.temp
326 then
327 fd:write(util.align(self.symbol, "= " .. tostring(self.lookup[self.symbol]), 16) .. "\n")
328 end
329 end
330
331 -- Write the option value to the config header file.
332 function Option:writeCppLine(fd)
333 end
334
335 -- Write the option value as a string replacement to the sed file.
336 function Option:writeSedLine(fd)
337 if self.export
338 then
339 local value = self:toExpandedString():gsub("/", "\\/")
340 local function writeLine(key)
341 key = tostring(key):gsub("/", "\\/")
342 fd:write(string.format("s/@%s@/%s/g\n", key, value))
343 end
344 if type(self.export) == "table"
345 then
346 for _,key in ipairs(self.export) do writeLine(key) end
347 else
348 writeLine(self.export)
349 end
350 end
351 end
352
353 -- Run an interactive menu with the given dialog context. The type of menu
354 -- run will depend on the type of option.
355 function Option:showMenu(dialog)
356 end
357
358 -- Get whether or not there is a problem with the current value of the
359 -- option.
360 function Option:isValid()
361 return type(self.problem) ~= "string"
362 end
363
364 -- Get a human-readable description of the problem(s) this option faces
365 -- before it can be considered valid.
366 function Option:getProblem()
367 return self.problem
368 end
369
370 -- Get the name to be used for this option in the interactive menu.
371 function Option:getMenuItem()
372 if not self:isValid() then return self.name .. " <!>" end
373 return self.name
374 end
375
376 -- Get the label used to represent this option in a menu.
377 function Option:getLabel()
378 return ""
379 end
380
381 -- Get whether or not this option should be visible in a menu of options.
382 -- Returns true if option is visible, false otherwise.
383 function Option:isVisible()
384 if type(self.visible) == "function" then return self.visible() end
385 return true
386 end
387
388 -- If the option has an argument flag, print out a line with information
389 -- about the purpose of this option.
390 function Option:printHelpLine()
391 if self.cmdline ~= nil
392 then
393 print(util.align(" " .. self:getArg(), self.caption, 32))
394 end
395 end
396
397 function Option:getHelpText()
398 local value = self:toString()
399 local help = self.help or "No help available.\n"
400 local name = self.name or "Unnamed"
401 local symbol = self:getSymbol() or "None"
402 local arg = self:getArg() or "None"
403
404 local problem = self:getProblem()
405 if problem then problem = "\nProblem(s):\n" .. problem
406 else problem = "" end
407
408 return [[
409
410 ]] .. help .. [[
411
412 Option Name: ]] .. name .. [[
413
414 Symbol: ]] .. symbol .. [[
415
416 Current Value: ]] .. value .. [[
417
418 Argument: ]] .. arg .. [[
419
420 ]] .. problem
421 end
422
423
424 ---------------------------------------------------------------------------
425 local StringOption = util.class(Option)
426 ---------------------------------------------------------------------------
427
428 -- Get the expanded option value. Symbols which begin with dollar signs
429 -- will be substituted for their values.
430 function StringOption:toExpandedString()
431 local function expand(value)
432 _, value, count = pcall(string.gsub, value, "%$%(([%w_]+)%)", self.lookup)
433 if not count then count = 0 end
434 return value, count
435 end
436 local value = self.lookup[self.symbol]
437 local iterations = 0
438 while iterations < 8
439 do
440 local count = 0
441 value, count = expand(value)
442 if count == 0 then return value end
443 iterations = iterations + 1
444 end
445 return value
446 end
447
448 function StringOption:applyTable(lookup)
449 local value = lookup[self.symbol]
450 if type(value) == "string" then self.lookup[self.symbol] = tostring(value) end
451 end
452
453 function StringOption:applyArguments(args)
454 local value = args[self.cmdline]
455 if value ~= nil then self:validate(tostring(value)) end
456 end
457
458 function StringOption:writeCppLine(fd)
459 if self.config
460 then
461 local value = self:toExpandedString()
462 local function writeLine(key)
463 if type(self.value) == "string"
464 then
465 fd:write(string.format("#define %s %q\n", key, value))
466 else
467 fd:write(string.format("#define %s %s\n", key, value))
468 end
469 end
470 if type(self.config) == "table"
471 then
472 for _,key in ipairs(self.config) do writeLine(key) end
473 else
474 writeLine(self.config)
475 end
476 end
477 end
478
479 function StringOption:showMenu(dialog)
480 local code, item = dialog.inputbox(self.name, self.caption, self.lookup[self.symbol])
481 if code == 0 and item ~= self.lookup[self.symbol]
482 then
483 return self:set(item)
484 end
485 end
486
487 function StringOption:getLabel()
488 return self:toExpandedString()
489 end
490
491
492 ---------------------------------------------------------------------------
493 local EnumOption = util.class(StringOption)
494 ---------------------------------------------------------------------------
495
496 -- An enumeration option is like a string, but it can only hold certain
497 -- pre-determined values. In a rule, it is distinguished by its value key
498 -- which refers to an array of the possible (string) values.
499 function EnumOption:__init(rule, lookup, objects)
500 Option.__init(self, rule, lookup, objects)
501 self.lookup[self.symbol] = self.value[1]
502 end
503
504 function EnumOption:getArg()
505 if self.cmdline == nil then return nil end
506 return self.cmdline .. "=[" .. table.concat(self.value, "|") .. "]"
507 end
508
509 function EnumOption:validate(str)
510 str = str or self.lookup[self.symbol]
511 for _,value in ipairs(self.value)
512 do
513 if value == str then return Option.validate(self, str) end
514 end
515 self.problem = "The assigned value (" .. str .. ") is not a valid option."
516 return nil, self.problem
517 end
518
519 function EnumOption:applyArguments(args)
520 -- Just like applying arguments for strings, but if the argument is
521 -- false, assume the first value should be assigned.
522 local value = args[self.cmdline]
523 if value == false then self:validate(self.value[1]) return end
524 StringOption.applyArguments(self, args)
525 end
526
527 function EnumOption:writeCppLine(fd)
528 if self.config
529 then
530 local value = self:toString():upper()
531 local function writeLine(key)
532 key = tostring(key) .. "_" .. value
533 fd:write(string.format("#define %s 1\n", key))
534 end
535 if type(self.config) == "table"
536 then
537 for _,key in ipairs(self.config) do writeLine(key) end
538 else
539 writeLine(self.config)
540 end
541 end
542 end
543
544 function EnumOption:showMenu(dialog)
545 local menuitems = {}
546 for _,value in ipairs(self.value)
547 do
548 table.insert(menuitems, {value, ""})
549 end
550 local code, item = dialog.menu(
551 self.name,
552 self.caption,
553 menuitems,
554 self.lookup[self.symbol],
555 {["--ok-label"] = "Select"}
556 )
557 if code == 0 and item ~= self.lookup[self.symbol]
558 then
559 return self:set(item)
560 end
561 end
562
563 function EnumOption:getLabel()
564 return "[" .. StringOption.getLabel(self) .. "]"
565 end
566
567
568 ---------------------------------------------------------------------------
569 local BooleanOption = util.class(Option)
570 ---------------------------------------------------------------------------
571
572 function BooleanOption:toString()
573 if self.lookup[self.symbol] then return "Yes" else return "No" end
574 end
575
576 function BooleanOption:applyTable(lookup)
577 local value = lookup[self.symbol]
578 if type(value) == "boolean" then self.lookup[self.symbol] = value end
579 end
580
581 function BooleanOption:applyArguments(args)
582 local value = args[self.cmdline]
583 if value == "yes" or value == "" then value = true end
584 if value == "no" then value = false end
585 if type(value) == "boolean" then self:validate(value) end
586 end
587
588 function BooleanOption:writeCppLine(fd)
589 if self.config
590 then
591 local value = self.lookup[self.symbol]
592 local function writeLine(key)
593 -- Reverse the value if key starts with a bang.
594 local value = value
595 if key:byte(1) == 33 then key = key:sub(2) value = not value end
596 if value
597 then
598 fd:write("#define " .. key .. " 1\n")
599 else
600 fd:write("/* #undef " .. key .. " */\n")
601 end
602 end
603 if type(self.config) == "table"
604 then
605 for _,key in ipairs(self.config) do writeLine(key) end
606 else
607 writeLine(self.config)
608 end
609 end
610 end
611
612 function BooleanOption:showMenu(dialog)
613 local item = not self.lookup[self.symbol]
614 return self:set(item)
615 end
616
617 function BooleanOption:getLabel()
618 if self.lookup[self.symbol] then return "[X]" else return "[ ]" end
619 end
620
621
622 ---------------------------------------------------------------------------
623 local NullOption = util.class(Option)
624 ---------------------------------------------------------------------------
625
626 -- Null options don't really hold any useful information; they just
627 -- translate to a blank line in the menuconfig. Any non-table object in a
628 -- rule will become this type of option.
629 function NullOption:__init()
630 self.name = ""
631 self.lookup = {}
632 end
633
634 function NullOption:validate()
635 return true
636 end
637 function NullOption:isVisible()
638 return true
639 end
640
641
642 ---------------------------------------------------------------------------
643 local GroupOption = util.class(Option)
644 ---------------------------------------------------------------------------
645
646 local function optionFactory(rule, lookup, objects)
647 local jump = setmetatable({
648 string = StringOption,
649 number = StringOption,
650 table = EnumOption,
651 boolean = BooleanOption
652 }, {
653 __index = function() return GroupOption end
654 })
655 if type(rule) == "table"
656 then
657 return jump[type(rule.value)](rule, lookup, objects)
658 else
659 -- If the rule is not a table, just insert a placeholder option for
660 -- a blank line.
661 return NullOption()
662 end
663 end
664
665 -- A GroupOption is not really an option itself, but a group of options.
666 function GroupOption:__init(rule, lookup, objects)
667 Option.__init(self, rule, lookup, objects)
668 self.children = {}
669
670 for _,child in ipairs(rule)
671 do
672 table.insert(self.children, optionFactory(child, self.lookup, self.objects))
673 end
674 end
675
676 function GroupOption:toString()
677 return "n/a"
678 end
679
680 -- Call the method with an arbitrary number of arguments to each
681 -- sub-option.
682 function GroupOption:recurse(method, ...)
683 for _,child in ipairs(self.children)
684 do
685 child[method](child, unpack(arg))
686 end
687 end
688
689 function GroupOption:convertDependenciesToDependants()
690 self:recurse("convertDependenciesToDependants")
691 end
692
693 -- Validate each sub-option in order. The validation will short-circuit
694 -- and return nil (with an error message) upon the first failed validation.
695 function GroupOption:validate()
696 for _,child in ipairs(self.children)
697 do
698 local result, err = child:validate()
699 if result == nil then return result, err end
700 end
701 return true
702 end
703
704 function GroupOption:isValid()
705 for _,child in ipairs(self.children)
706 do
707 if not child:isValid() then return false end
708 end
709 return true
710 end
711
712 function GroupOption:getProblem()
713 local problems = ""
714 for _,child in ipairs(self.children)
715 do
716 local problem = child:getProblem()
717 if problem
718 then
719 local symbol = child:getSymbol()
720 if symbol
721 then
722 problems = problems .. util.align(symbol .. ":", problem, 16) .. "\n"
723 else
724 problems = problems .. problem .. "\n"
725 end
726 util.align(symbol, problem, 16)
727 end
728 end
729 if problems == "" then return nil end
730 return util.trim(problems)
731 end
732
733 function GroupOption:applyTable(lookup)
734 self:recurse("applyTable", lookup)
735 end
736
737 function GroupOption:applyArguments(args)
738 self:recurse("applyArguments", args)
739 end
740
741 function GroupOption:writeSymbol(fd)
742 if self.name ~= nil then
743 fd:write(string.format("\n# %s\n", self.name))
744 self:recurse("writeSymbol", fd)
745 end
746 end
747
748 function GroupOption:writeCppLine(fd)
749 self:recurse("writeCppLine", fd)
750 end
751
752 function GroupOption:writeSedLine(fd)
753 self:recurse("writeSedLine", fd)
754 end
755
756
757 function GroupOption:showMenu(dialog)
758 local running, dirty, selected = true
759 while running
760 do
761 local menuitems = {}
762 for _,value in ipairs(self.children)
763 do
764 if type(value) ~= "table"
765 then
766 table.insert(menuitems, value)
767 elseif type(value.name) == "string" and value:isVisible()
768 then
769 local name = value:getMenuItem()
770 local label = value:getLabel()
771 local caption = ""
772 if type(value.caption) == "string"
773 then
774 caption = value.caption
775 menuitems["HELP " .. value.caption] = value
776 end
777 menuitems[name] = value
778 table.insert(menuitems, {name, label, caption})
779 end
780 end
781
782 local code, item = dialog.menu(
783 self.name,
784 self.caption,
785 menuitems,
786 selected,
787 {
788 ["--ok-label"] = "Select",
789 ["--cancel-label"] = "Exit",
790 ["--item-help"] = true,
791 ["--help-button"] = true
792 }
793 )
794
795 if code == 0
796 then
797 local value = menuitems[item]
798 if type(value) == "table"
799 then
800 if value:showMenu(dialog) ~= nil then dirty = "changed" end
801 selected = value:getMenuItem()
802 else
803 selected = value
804 end
805 elseif code == 2
806 then
807 local value = menuitems[item]
808 if value
809 then
810 dialog.msgbox(value.name, value:getHelpText(), {["--no-collapse"] = true})
811 selected = value:getMenuItem()
812 else
813 dialog.msgbox("No Help", "Sorry, no help is available for this option.")
814 end
815 else
816 running = false
817 end
818 end
819 return dirty
820 end
821
822 function GroupOption:getLabel()
823 return "-->"
824 end
825
826 function GroupOption:printHelpLine()
827 if self:isVisible() and self.name ~= nil then
828 print(string.format("\n%s:", self.name))
829 self:recurse("printHelpLine")
830 end
831 end
832
833
834 -- Print arguments and usage information for all of the sub-options.
835 function GroupOption:printHelp(name)
836 print([[
837
838 This script prepares ]] .. name .. [[ for building on your system.
839 Usage: ./configure [OPTION]... [VAR=VALUE]...]])
840
841 self:printHelpLine()
842 print()
843 end
844
845 -- Set values of the sub-options based on a table that is loaded from a
846 -- file. The table is formatted as one or more lines with keys and values
847 -- separated by an equal sign. The pound sign (#) serves to start a
848 -- comment on the line. The first equal sign on a line is used as the
849 -- separator, so keys cannot have an equal sign.
850 function GroupOption:loadFromFile(filepath)
851 local lookup = {}
852 local fd = io.open(filepath)
853 if fd
854 then
855 for line in fd:lines()
856 do
857 if not line:find("^%s*#")
858 then
859 key, value = line:match("^%s*(%S.-)%s*=%s*(.*)%s*")
860 if value
861 then
862 if value:upper() == "TRUE" then value = true
863 elseif value:upper() == "FALSE" then value = false end
864 end
865 if key then lookup[key] = value end
866 end
867 end
868 fd:close()
869 end
870 self:applyTable(lookup)
871 end
872
873 -- Save a table with the current values of the sub-options to a file. The
874 -- file can be reloaded with GroupOption:loadFromFile.
875 function GroupOption:saveToFile(filepath)
876 local fd = io.open(filepath, "w")
877 if fd
878 then
879 fd:write(string.format("\n# Auto-generated: %s", os.date()))
880 self:writeSymbol(fd)
881 fd:write("\n")
882 else
883 printError("couldn't save config file to:", filepath)
884 end
885 end
886
887 -- Exports the config header file. The key-value pairs of sub-options with
888 -- "config" defined will be written to the file.
889 function GroupOption:exportHeader(filepath)
890 local fd = io.open(filepath, "w")
891 self:writeCppLine(fd)
892 fd:close()
893 end
894
895 -- Exports the search 'n replace file. The key-value pairs of sub-options
896 -- with "export" defined will be written to the file.
897 function GroupOption:exportTerms(filepath)
898 local fd = io.open(filepath, "w")
899 self:writeSedLine(fd)
900 fd:close()
901 end
902
903 -- Run menuconfig. This is different from showMenu in that this method
904 -- tracks changes and returns an action: save, cancel, nochange or error.
905 function GroupOption:runMenu()
906 local result, dirty = nil, false
907 while result == nil
908 do
909 if self:showMenu(dialog) ~= nil then dirty = true end
910 if not self:isValid()
911 then
912 local code = dialog.yesno("Oh drat!",
913 "There is at least one problem with the configuration, marked with <!>, and the configuration will not be saved. Do you really want to exit without saving?",
914 {
915 ["--extra-button"] = false,
916 ["--ok-label"] = "Exit",
917 ["--cancel-label"] = "Back"
918 })
919 if code == 0 or code == 255 then result = "error" end
920 elseif dirty
921 then
922 local code = dialog.yesno("Save Changes?",
923 "Your configuration has been altered. Do you want to save the changes?",
924 {
925 ["--ok-label"] = "Save",
926 ["--cancel-label"] = "Back",
927 ["--extra-button"] = true,
928 ["--extra-label"] = "Exit"
929 })
930 if code == 0 then result = "save"
931 elseif code == 3 or code == 255 then result = "cancel" end
932 else
933 result = "nochange"
934 end
935 end
936 return result
937 end
938
939
940 local loadFn = assert(loadfile(rules))
941 local name, rules = loadFn()
942
943 local options = optionFactory(rules)
944 if arguments["--help"] then options:printHelp(name) os.exit() end
945
946 options:loadFromFile(configfile)
947 if exportHeader or exportTerms
948 then
949 if exportHeader then options:exportHeader(exportHeader) end
950 if exportTerms then options:exportTerms(exportTerms) end
951 os.exit()
952 end
953
954 print()
955 printInfo(name, "Preparing for building and installation on your system.\n")
956 options:applyArguments(arguments)
957
958 if interactive then loadDialog(name, configfile) end
959
960 options:convertDependenciesToDependants()
961 checkSymbols(options:getObjects())
962
963 if dialog then
964 local action = options:runMenu()
965 if action == "exit" or action == "error"
966 then
967 print("configuration aborted by user request")
968 os.exit(1)
969 else
970 options:saveToFile(configfile)
971 print("configuration saved to " .. configfile)
972 end
973 elseif options:isValid() then
974 options:saveToFile(configfile)
975 else
976 printError("Uh oh!", [[
977 There is at least one unresolved problem with the configuration.
978 ]] .. options:getProblem() .. "\n")
979 os.exit(2)
980 end
981
982 if printendmsg ~= "no"
983 then
984 printInfo("Configuration complete!", [[
985 To finish the installation, type:
986 make
987 make install
988 ]])
989 end
990
991 -- vi:ts=4 sw=4 tw=75
992
This page took 0.075012 seconds and 4 git commands to generate.