ieee1275: split up grub_machine_get_bootlocation
[grub.git] / gentpl.py
1 #! /usr/bin/python
2 #  GRUB  --  GRand Unified Bootloader
3 #  Copyright (C) 2010,2011,2012,2013  Free Software Foundation, Inc.
4 #
5 #  GRUB is free software: you can redistribute it and/or modify
6 #  it under the terms of the GNU General Public License as published by
7 #  the Free Software Foundation, either version 3 of the License, or
8 #  (at your option) any later version.
9 #
10 #  GRUB is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #  GNU General Public License for more details.
14 #
15 #  You should have received a copy of the GNU General Public License
16 #  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
17
18 from __future__ import print_function
19
20 __metaclass__ = type
21
22 from optparse import OptionParser
23 import re
24
25 #
26 # This is the python script used to generate Makefile.*.am
27 #
28
29 GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot",
30                    "i386_multiboot", "i386_ieee1275", "x86_64_efi",
31                    "i386_xen", "x86_64_xen",
32                    "mips_loongson", "sparc64_ieee1275",
33                    "powerpc_ieee1275", "mips_arc", "ia64_efi",
34                    "mips_qemu_mips", "arm_uboot", "arm_efi", "arm64_efi",
35                    "arm_coreboot"]
36
37 GROUPS = {}
38
39 GROUPS["common"]   = GRUB_PLATFORMS[:]
40
41 # Groups based on CPU
42 GROUPS["i386"]     = [ "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", "i386_multiboot", "i386_ieee1275" ]
43 GROUPS["x86_64"]   = [ "x86_64_efi" ]
44 GROUPS["x86"]      = GROUPS["i386"] + GROUPS["x86_64"]
45 GROUPS["mips"]     = [ "mips_loongson", "mips_qemu_mips", "mips_arc" ]
46 GROUPS["sparc64"]  = [ "sparc64_ieee1275" ]
47 GROUPS["powerpc"]  = [ "powerpc_ieee1275" ]
48 GROUPS["arm"]      = [ "arm_uboot", "arm_efi", "arm_coreboot" ]
49 GROUPS["arm64"]    = [ "arm64_efi" ]
50
51 # Groups based on firmware
52 GROUPS["efi"]  = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi" ]
53 GROUPS["ieee1275"]   = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ]
54 GROUPS["uboot"] = [ "arm_uboot" ]
55 GROUPS["xen"]  = [ "i386_xen", "x86_64_xen" ]
56 GROUPS["coreboot"]  = [ "i386_coreboot", "arm_coreboot" ]
57
58 # emu is a special case so many core functionality isn't needed on this platform
59 GROUPS["noemu"]   = GRUB_PLATFORMS[:]; GROUPS["noemu"].remove("emu")
60
61 # Groups based on hardware features
62 GROUPS["cmos"] = GROUPS["x86"][:] + ["mips_loongson", "mips_qemu_mips",
63                                      "sparc64_ieee1275", "powerpc_ieee1275"]
64 GROUPS["cmos"].remove("i386_efi"); GROUPS["cmos"].remove("x86_64_efi");
65 GROUPS["pci"]      = GROUPS["x86"] + ["mips_loongson"]
66 GROUPS["usb"]      = GROUPS["pci"] + ["arm_coreboot"]
67
68 # If gfxterm is main output console integrate it into kernel
69 GROUPS["videoinkernel"] = ["mips_loongson", "i386_coreboot", "arm_coreboot" ]
70 GROUPS["videomodules"]   = GRUB_PLATFORMS[:];
71 for i in GROUPS["videoinkernel"]: GROUPS["videomodules"].remove(i)
72
73 # Similar for terminfo
74 GROUPS["terminfoinkernel"] = [ "emu", "mips_loongson", "mips_arc", "mips_qemu_mips" ] + GROUPS["xen"] + GROUPS["ieee1275"] + GROUPS["uboot"];
75 GROUPS["terminfomodule"]   = GRUB_PLATFORMS[:];
76 for i in GROUPS["terminfoinkernel"]: GROUPS["terminfomodule"].remove(i)
77
78 # Flattened Device Trees (FDT)
79 GROUPS["fdt"] = [ "arm64_efi", "arm_uboot", "arm_efi" ]
80
81 # Needs software helpers for division
82 # Must match GRUB_DIVISION_IN_SOFTWARE in misc.h
83 GROUPS["softdiv"] = GROUPS["arm"] + ["ia64_efi"]
84 GROUPS["no_softdiv"]   = GRUB_PLATFORMS[:]
85 for i in GROUPS["softdiv"]: GROUPS["no_softdiv"].remove(i)
86
87 # Miscellaneous groups scheduled to disappear in future
88 GROUPS["i386_coreboot_multiboot_qemu"] = ["i386_coreboot", "i386_multiboot", "i386_qemu"]
89 GROUPS["nopc"] = GRUB_PLATFORMS[:]; GROUPS["nopc"].remove("i386_pc")
90
91 #
92 # Create platform => groups reverse map, where groups covering that
93 # platform are ordered by their sizes
94 #
95 RMAP = {}
96 for platform in GRUB_PLATFORMS:
97     # initialize with platform itself as a group
98     RMAP[platform] = [ platform ]
99
100     for k in GROUPS.keys():
101         v = GROUPS[k]
102         # skip groups that don't cover this platform
103         if platform not in v: continue
104
105         bigger = []
106         smaller = []
107         # partition currently known groups based on their size
108         for group in RMAP[platform]:
109             if group in GRUB_PLATFORMS: smaller.append(group)
110             elif len(GROUPS[group]) < len(v): smaller.append(group)
111             else: bigger.append(group)
112         # insert in the middle
113         RMAP[platform] = smaller + [ k ] + bigger
114
115 #
116 # Input
117 #
118
119 # We support a subset of the AutoGen definitions file syntax.  Specifically,
120 # compound names are disallowed; some preprocessing directives are
121 # disallowed (though #if/#endif are allowed; note that, like AutoGen, #if
122 # skips everything to the next #endif regardless of the value of the
123 # conditional); and shell-generated strings, Scheme-generated strings, and
124 # here strings are disallowed.
125
126 class AutogenToken:
127     (autogen, definitions, eof, var_name, other_name, string, number,
128      semicolon, equals, comma, lbrace, rbrace, lbracket, rbracket) = range(14)
129
130 class AutogenState:
131     (init, need_def, need_tpl, need_semi, need_name, have_name, need_value,
132      need_idx, need_rbracket, indx_name, have_value, done) = range(12)
133
134 class AutogenParseError(Exception):
135     def __init__(self, message, path, line):
136         super(AutogenParseError, self).__init__(message)
137         self.path = path
138         self.line = line
139
140     def __str__(self):
141         return (
142             super(AutogenParseError, self).__str__() +
143             " at file %s line %d" % (self.path, self.line))
144
145 class AutogenDefinition(list):
146     def __getitem__(self, key):
147         try:
148             return super(AutogenDefinition, self).__getitem__(key)
149         except TypeError:
150             for name, value in self:
151                 if name == key:
152                     return value
153
154     def __contains__(self, key):
155         for name, value in self:
156             if name == key:
157                 return True
158         return False
159
160     def get(self, key, default):
161         for name, value in self:
162             if name == key:
163                 return value
164         else:
165             return default
166
167     def find_all(self, key):
168         for name, value in self:
169             if name == key:
170                 yield value
171
172 class AutogenParser:
173     def __init__(self):
174         self.definitions = AutogenDefinition()
175         self.def_stack = [("", self.definitions)]
176         self.curdef = None
177         self.new_name = None
178         self.cur_path = None
179         self.cur_line = 0
180
181     @staticmethod
182     def is_unquotable_char(c):
183         return (ord(c) in range(ord("!"), ord("~") + 1) and
184                 c not in "#,;<=>[\\]`{}?*'\"()")
185
186     @staticmethod
187     def is_value_name_char(c):
188         return c in ":^-_" or c.isalnum()
189
190     def error(self, message):
191         raise AutogenParseError(message, self.cur_file, self.cur_line)
192
193     def read_tokens(self, f):
194         data = f.read()
195         end = len(data)
196         offset = 0
197         while offset < end:
198             while offset < end and data[offset].isspace():
199                 if data[offset] == "\n":
200                     self.cur_line += 1
201                 offset += 1
202             if offset >= end:
203                 break
204             c = data[offset]
205             if c == "#":
206                 offset += 1
207                 try:
208                     end_directive = data.index("\n", offset)
209                     directive = data[offset:end_directive]
210                     offset = end_directive
211                 except ValueError:
212                     directive = data[offset:]
213                     offset = end
214                 name, value = directive.split(None, 1)
215                 if name == "if":
216                     try:
217                         end_if = data.index("\n#endif", offset)
218                         new_offset = end_if + len("\n#endif")
219                         self.cur_line += data[offset:new_offset].count("\n")
220                         offset = new_offset
221                     except ValueError:
222                         self.error("#if without matching #endif")
223                 else:
224                     self.error("Unhandled directive '#%s'" % name)
225             elif c == "{":
226                 yield AutogenToken.lbrace, c
227                 offset += 1
228             elif c == "=":
229                 yield AutogenToken.equals, c
230                 offset += 1
231             elif c == "}":
232                 yield AutogenToken.rbrace, c
233                 offset += 1
234             elif c == "[":
235                 yield AutogenToken.lbracket, c
236                 offset += 1
237             elif c == "]":
238                 yield AutogenToken.rbracket, c
239                 offset += 1
240             elif c == ";":
241                 yield AutogenToken.semicolon, c
242                 offset += 1
243             elif c == ",":
244                 yield AutogenToken.comma, c
245                 offset += 1
246             elif c in ("'", '"'):
247                 s = []
248                 while True:
249                     offset += 1
250                     if offset >= end:
251                         self.error("EOF in quoted string")
252                     if data[offset] == "\n":
253                         self.cur_line += 1
254                     if data[offset] == "\\":
255                         offset += 1
256                         if offset >= end:
257                             self.error("EOF in quoted string")
258                         if data[offset] == "\n":
259                             self.cur_line += 1
260                         # Proper escaping unimplemented; this can be filled
261                         # out if needed.
262                         s.append("\\")
263                         s.append(data[offset])
264                     elif data[offset] == c:
265                         offset += 1
266                         break
267                     else:
268                         s.append(data[offset])
269                 yield AutogenToken.string, "".join(s)
270             elif c == "/":
271                 offset += 1
272                 if data[offset] == "*":
273                     offset += 1
274                     try:
275                         end_comment = data.index("*/", offset)
276                         new_offset = end_comment + len("*/")
277                         self.cur_line += data[offset:new_offset].count("\n")
278                         offset = new_offset
279                     except ValueError:
280                         self.error("/* without matching */")
281                 elif data[offset] == "/":
282                     try:
283                         offset = data.index("\n", offset)
284                     except ValueError:
285                         pass
286             elif (c.isdigit() or
287                   (c == "-" and offset < end - 1 and
288                    data[offset + 1].isdigit())):
289                 end_number = offset + 1
290                 while end_number < end and data[end_number].isdigit():
291                     end_number += 1
292                 yield AutogenToken.number, data[offset:end_number]
293                 offset = end_number
294             elif self.is_unquotable_char(c):
295                 end_name = offset
296                 while (end_name < end and
297                        self.is_value_name_char(data[end_name])):
298                     end_name += 1
299                 if end_name < end and self.is_unquotable_char(data[end_name]):
300                     while (end_name < end and
301                            self.is_unquotable_char(data[end_name])):
302                         end_name += 1
303                     yield AutogenToken.other_name, data[offset:end_name]
304                     offset = end_name
305                 else:
306                     s = data[offset:end_name]
307                     if s.lower() == "autogen":
308                         yield AutogenToken.autogen, s
309                     elif s.lower() == "definitions":
310                         yield AutogenToken.definitions, s
311                     else:
312                         yield AutogenToken.var_name, s
313                     offset = end_name
314             else:
315                 self.error("Invalid input character '%s'" % c)
316         yield AutogenToken.eof, None
317
318     def do_need_name_end(self, token):
319         if len(self.def_stack) > 1:
320             self.error("Definition blocks were left open")
321
322     def do_need_name_var_name(self, token):
323         self.new_name = token
324
325     def do_end_block(self, token):
326         if len(self.def_stack) <= 1:
327             self.error("Too many close braces")
328         new_name, parent_def = self.def_stack.pop()
329         parent_def.append((new_name, self.curdef))
330         self.curdef = parent_def
331
332     def do_empty_val(self, token):
333         self.curdef.append((self.new_name, ""))
334
335     def do_str_value(self, token):
336         self.curdef.append((self.new_name, token))
337
338     def do_start_block(self, token):
339         self.def_stack.append((self.new_name, self.curdef))
340         self.curdef = AutogenDefinition()
341
342     def do_indexed_name(self, token):
343         self.new_name = token
344
345     def read_definitions_file(self, f):
346         self.curdef = self.definitions
347         self.cur_line = 0
348         state = AutogenState.init
349
350         # The following transition table was reduced from the Autogen
351         # documentation:
352         #   info -f autogen -n 'Full Syntax'
353         transitions = {
354             AutogenState.init: {
355                 AutogenToken.autogen: (AutogenState.need_def, None),
356             },
357             AutogenState.need_def: {
358                 AutogenToken.definitions: (AutogenState.need_tpl, None),
359             },
360             AutogenState.need_tpl: {
361                 AutogenToken.var_name: (AutogenState.need_semi, None),
362                 AutogenToken.other_name: (AutogenState.need_semi, None),
363                 AutogenToken.string: (AutogenState.need_semi, None),
364             },
365             AutogenState.need_semi: {
366                 AutogenToken.semicolon: (AutogenState.need_name, None),
367             },
368             AutogenState.need_name: {
369                 AutogenToken.autogen: (AutogenState.need_def, None),
370                 AutogenToken.eof: (AutogenState.done, self.do_need_name_end),
371                 AutogenToken.var_name: (
372                     AutogenState.have_name, self.do_need_name_var_name),
373                 AutogenToken.rbrace: (
374                     AutogenState.have_value, self.do_end_block),
375             },
376             AutogenState.have_name: {
377                 AutogenToken.semicolon: (
378                     AutogenState.need_name, self.do_empty_val),
379                 AutogenToken.equals: (AutogenState.need_value, None),
380                 AutogenToken.lbracket: (AutogenState.need_idx, None),
381             },
382             AutogenState.need_value: {
383                 AutogenToken.var_name: (
384                     AutogenState.have_value, self.do_str_value),
385                 AutogenToken.other_name: (
386                     AutogenState.have_value, self.do_str_value),
387                 AutogenToken.string: (
388                     AutogenState.have_value, self.do_str_value),
389                 AutogenToken.number: (
390                     AutogenState.have_value, self.do_str_value),
391                 AutogenToken.lbrace: (
392                     AutogenState.need_name, self.do_start_block),
393             },
394             AutogenState.need_idx: {
395                 AutogenToken.var_name: (
396                     AutogenState.need_rbracket, self.do_indexed_name),
397                 AutogenToken.number: (
398                     AutogenState.need_rbracket, self.do_indexed_name),
399             },
400             AutogenState.need_rbracket: {
401                 AutogenToken.rbracket: (AutogenState.indx_name, None),
402             },
403             AutogenState.indx_name: {
404                 AutogenToken.semicolon: (
405                     AutogenState.need_name, self.do_empty_val),
406                 AutogenToken.equals: (AutogenState.need_value, None),
407             },
408             AutogenState.have_value: {
409                 AutogenToken.semicolon: (AutogenState.need_name, None),
410                 AutogenToken.comma: (AutogenState.need_value, None),
411             },
412         }
413
414         for code, token in self.read_tokens(f):
415             if code in transitions[state]:
416                 state, handler = transitions[state][code]
417                 if handler is not None:
418                     handler(token)
419             else:
420                 self.error(
421                     "Parse error in state %s: unexpected token '%s'" % (
422                         state, token))
423             if state == AutogenState.done:
424                 break
425
426     def read_definitions(self, path):
427         self.cur_file = path
428         with open(path) as f:
429             self.read_definitions_file(f)
430
431 defparser = AutogenParser()
432
433 #
434 # Output
435 #
436
437 outputs = {}
438
439 def output(s, section=''):
440     if s == "":
441         return
442     outputs.setdefault(section, [])
443     outputs[section].append(s)
444
445 def write_output(section=''):
446     for s in outputs.get(section, []):
447         print(s, end='')
448
449 #
450 # Global variables
451 #
452
453 def gvar_add(var, value):
454     output(var + " += " + value + "\n")
455
456 #
457 # Per PROGRAM/SCRIPT variables 
458 #
459
460 seen_vars = set()
461
462 def vars_init(defn, *var_list):
463     name = defn['name']
464
465     if name not in seen_target and name not in seen_vars:
466         for var in var_list:
467             output(var + "  = \n", section='decl')
468         seen_vars.add(name)
469
470 def var_set(var, value):
471     output(var + "  = " + value + "\n")
472
473 def var_add(var, value):
474     output(var + " += " + value + "\n")
475
476 #
477 # Variable names and rules
478 #
479
480 canonical_name_re = re.compile(r'[^0-9A-Za-z@_]')
481 canonical_name_suffix = ""
482
483 def set_canonical_name_suffix(suffix):
484     global canonical_name_suffix
485     canonical_name_suffix = suffix
486
487 def cname(defn):
488     return canonical_name_re.sub('_', defn['name'] + canonical_name_suffix)
489
490 def rule(target, source, cmd):
491     if cmd[0] == "\n":
492         output("\n" + target + ": " + source + cmd.replace("\n", "\n\t") + "\n")
493     else:
494         output("\n" + target + ": " + source + "\n\t" + cmd.replace("\n", "\n\t") + "\n")
495
496 #
497 # Handle keys with platform names as values, for example:
498 #
499 # kernel = {
500 #   nostrip = emu;
501 #   ...
502 # }
503 #
504 def platform_tagged(defn, platform, tag):
505     for value in defn.find_all(tag):
506         for group in RMAP[platform]:
507             if value == group:
508                 return True
509     return False
510
511 def if_platform_tagged(defn, platform, tag, snippet_if, snippet_else=None):
512     if platform_tagged(defn, platform, tag):
513         return snippet_if
514     elif snippet_else is not None:
515         return snippet_else
516
517 #
518 # Handle tagged values
519 #
520 # module = {
521 #   extra_dist = ...
522 #   extra_dist = ...
523 #   ...
524 # };
525 #
526 def foreach_value(defn, tag, closure):
527     r = []
528     for value in defn.find_all(tag):
529         r.append(closure(value))
530     return ''.join(r)
531
532 #
533 # Handle best matched values for a platform, for example:
534 #
535 # module = {
536 #   cflags = '-Wall';
537 #   emu_cflags = '-Wall -DGRUB_EMU=1';
538 #   ...
539 # }
540 #
541 def foreach_platform_specific_value(defn, platform, suffix, nonetag, closure):
542     r = []
543     for group in RMAP[platform]:
544         values = list(defn.find_all(group + suffix))
545         if values:
546             for value in values:
547                 r.append(closure(value))
548             break
549     else:
550         for value in defn.find_all(nonetag):
551             r.append(closure(value))
552     return ''.join(r)
553
554 #
555 # Handle values from sum of all groups for a platform, for example:
556 #
557 # module = {
558 #   common = kern/misc.c;
559 #   emu = kern/emu/misc.c;
560 #   ...
561 # }
562 #
563 def foreach_platform_value(defn, platform, suffix, closure):
564     r = []
565     for group in RMAP[platform]:
566         for value in defn.find_all(group + suffix):
567             r.append(closure(value))
568     return ''.join(r)
569
570 def platform_conditional(platform, closure):
571     output("\nif COND_" + platform + "\n")
572     closure(platform)
573     output("endif\n")
574
575 #
576 # Handle guarding with platform-specific "enable" keys, for example:
577 #
578 #  module = {
579 #    name = pci;
580 #    noemu = bus/pci.c;
581 #    emu = bus/emu/pci.c;
582 #    emu = commands/lspci.c;
583 #
584 #    enable = emu;
585 #    enable = i386_pc;
586 #    enable = x86_efi;
587 #    enable = i386_ieee1275;
588 #    enable = i386_coreboot;
589 #  };
590 #
591 def foreach_enabled_platform(defn, closure):
592     if 'enable' in defn:
593         for platform in GRUB_PLATFORMS:
594             if platform_tagged(defn, platform, "enable"):
595                platform_conditional(platform, closure)
596     else:
597         for platform in GRUB_PLATFORMS:
598             platform_conditional(platform, closure)
599
600 #
601 # Handle guarding with platform-specific automake conditionals, for example:
602 #
603 #  module = {
604 #    name = usb;
605 #    common = bus/usb/usb.c;
606 #    noemu = bus/usb/usbtrans.c;
607 #    noemu = bus/usb/usbhub.c;
608 #    enable = emu;
609 #    enable = i386;
610 #    enable = mips_loongson;
611 #    emu_condition = COND_GRUB_EMU_SDL;
612 #  };
613 #
614 def under_platform_specific_conditionals(defn, platform, closure):
615     output(foreach_platform_specific_value(defn, platform, "_condition", "condition", lambda cond: "if " + cond + "\n"))
616     closure(defn, platform)
617     output(foreach_platform_specific_value(defn, platform, "_condition", "condition", lambda cond: "endif " + cond + "\n"))
618
619 def platform_specific_values(defn, platform, suffix, nonetag):
620     return foreach_platform_specific_value(defn, platform, suffix, nonetag,
621                                            lambda value: value + " ")
622
623 def platform_values(defn, platform, suffix):
624     return foreach_platform_value(defn, platform, suffix, lambda value: value + " ")
625
626 def extra_dist(defn):
627     return foreach_value(defn, "extra_dist", lambda value: value + " ")
628
629 def platform_sources(defn, p): return platform_values(defn, p, "")
630 def platform_nodist_sources(defn, p): return platform_values(defn, p, "_nodist")
631
632 def platform_startup(defn, p): return platform_specific_values(defn, p, "_startup", "startup")
633 def platform_ldadd(defn, p): return platform_specific_values(defn, p, "_ldadd", "ldadd")
634 def platform_dependencies(defn, p): return platform_specific_values(defn, p, "_dependencies", "dependencies")
635 def platform_cflags(defn, p): return platform_specific_values(defn, p, "_cflags", "cflags")
636 def platform_ldflags(defn, p): return platform_specific_values(defn, p, "_ldflags", "ldflags")
637 def platform_cppflags(defn, p): return platform_specific_values(defn, p, "_cppflags", "cppflags")
638 def platform_ccasflags(defn, p): return platform_specific_values(defn, p, "_ccasflags", "ccasflags")
639 def platform_stripflags(defn, p): return platform_specific_values(defn, p, "_stripflags", "stripflags")
640 def platform_objcopyflags(defn, p): return platform_specific_values(defn, p, "_objcopyflags", "objcopyflags")
641
642 #
643 # Emit snippet only the first time through for the current name.
644 #
645 seen_target = set()
646
647 def first_time(defn, snippet):
648     if defn['name'] not in seen_target:
649         return snippet
650     return ''
651
652 def is_platform_independent(defn):
653     if 'enable' in defn:
654         return False
655     for suffix in [ "", "_nodist" ]:
656         template = platform_values(defn, GRUB_PLATFORMS[0], suffix)
657         for platform in GRUB_PLATFORMS[1:]:
658             if template != platform_values(defn, platform, suffix):
659                 return False
660
661     for suffix in [ "startup", "ldadd", "dependencies", "cflags", "ldflags", "cppflags", "ccasflags", "stripflags", "objcopyflags", "condition" ]:
662         template = platform_specific_values(defn, GRUB_PLATFORMS[0], "_" + suffix, suffix)
663         for platform in GRUB_PLATFORMS[1:]:
664             if template != platform_specific_values(defn, platform, "_" + suffix, suffix):
665                 return False
666     for tag in [ "nostrip" ]:
667         template = platform_tagged(defn, GRUB_PLATFORMS[0], tag)
668         for platform in GRUB_PLATFORMS[1:]:
669             if template != platform_tagged(defn, platform, tag):
670                 return False
671
672     return True
673
674 def module(defn, platform):
675     name = defn['name']
676     set_canonical_name_suffix(".module")
677
678     gvar_add("platform_PROGRAMS", name + ".module")
679     gvar_add("MODULE_FILES", name + ".module$(EXEEXT)")
680
681     var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform) + " ## platform sources")
682     var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources")
683     var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
684     var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_MODULE) " + platform_cflags(defn, platform))
685     var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_MODULE) " + platform_ldflags(defn, platform))
686     var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform))
687     var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform))
688     var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF) " + platform_dependencies(defn, platform))
689
690     gvar_add("dist_noinst_DATA", extra_dist(defn))
691     gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
692     gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
693
694     gvar_add("MOD_FILES", name + ".mod")
695     gvar_add("MARKER_FILES", name + ".marker")
696     gvar_add("CLEANFILES", name + ".marker")
697     output("""
698 """ + name + """.marker: $(""" + cname(defn) + """_SOURCES) $(nodist_""" + cname(defn) + """_SOURCES)
699         $(TARGET_CPP) -DGRUB_LST_GENERATOR $(CPPFLAGS_MARKER) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(""" + cname(defn) + """_CPPFLAGS) $(CPPFLAGS) $^ > $@.new || (rm -f $@; exit 1)
700         grep 'MARKER' $@.new > $@; rm -f $@.new
701 """)
702
703 def kernel(defn, platform):
704     name = defn['name']
705     set_canonical_name_suffix(".exec")
706     gvar_add("platform_PROGRAMS", name + ".exec")
707     var_set(cname(defn) + "_SOURCES", platform_startup(defn, platform))
708     var_add(cname(defn) + "_SOURCES", platform_sources(defn, platform))
709     var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources")
710     var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
711     var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_KERNEL) " + platform_cflags(defn, platform))
712     var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_KERNEL) " + platform_ldflags(defn, platform))
713     var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_KERNEL) " + platform_cppflags(defn, platform))
714     var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_KERNEL) " + platform_ccasflags(defn, platform))
715     var_set(cname(defn) + "_STRIPFLAGS", "$(AM_STRIPFLAGS) $(STRIPFLAGS_KERNEL) " + platform_stripflags(defn, platform))
716     var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF)")
717
718     gvar_add("dist_noinst_DATA", extra_dist(defn))
719     gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
720     gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
721
722     gvar_add("platform_DATA", name + ".img")
723     gvar_add("CLEANFILES", name + ".img")
724     rule(name + ".img", name + ".exec$(EXEEXT)",
725          if_platform_tagged(defn, platform, "nostrip",
726 """if test x$(TARGET_APPLE_LINKER) = x1; then \
727      $(TARGET_OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -ed2022 -wd1106 -nu -nd $< $@; \
728    elif test ! -z '$(TARGET_OBJ2ELF)'; then \
729      $(TARGET_OBJ2ELF) $< $@ || (rm -f $@; exit 1); \
730    else cp $< $@; fi""",
731 """if test x$(TARGET_APPLE_LINKER) = x1; then \
732   $(TARGET_STRIP) -S -x $(""" + cname(defn) + """) -o $@.bin $<; \
733   $(TARGET_OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -ed2022 -ed2016 -wd1106 -nu -nd $@.bin $@; \
734   rm -f $@.bin; \
735    elif test ! -z '$(TARGET_OBJ2ELF)'; then \
736      """  + "$(TARGET_STRIP) $(" + cname(defn) + "_STRIPFLAGS) -o $@.bin $< && \
737      $(TARGET_OBJ2ELF) $@.bin $@ || (rm -f $@; rm -f $@.bin; exit 1); \
738      rm -f $@.bin; \
739 else """  + "$(TARGET_STRIP) $(" + cname(defn) + "_STRIPFLAGS) -o $@ $<; \
740 fi"""))
741
742 def image(defn, platform):
743     name = defn['name']
744     set_canonical_name_suffix(".image")
745     gvar_add("platform_PROGRAMS", name + ".image")
746     var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform))
747     var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + "## platform nodist sources")
748     var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
749     var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_IMAGE) " + platform_cflags(defn, platform))
750     var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_IMAGE) " + platform_ldflags(defn, platform))
751     var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_IMAGE) " + platform_cppflags(defn, platform))
752     var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_IMAGE) " + platform_ccasflags(defn, platform))
753     var_set(cname(defn) + "_OBJCOPYFLAGS", "$(OBJCOPYFLAGS_IMAGE) " + platform_objcopyflags(defn, platform))
754     # var_set(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform))
755
756     gvar_add("dist_noinst_DATA", extra_dist(defn))
757     gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
758     gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
759
760     gvar_add("platform_DATA", name + ".img")
761     gvar_add("CLEANFILES", name + ".img")
762     rule(name + ".img", name + ".image$(EXEEXT)", """
763 if test x$(TARGET_APPLE_LINKER) = x1; then \
764   $(MACHO2IMG) $< $@; \
765 else \
766   $(TARGET_OBJCOPY) $(""" + cname(defn) + """_OBJCOPYFLAGS) --strip-unneeded -R .note -R .comment -R .note.gnu.build-id -R .MIPS.abiflags -R .reginfo -R .rel.dyn -R .note.gnu.gold-version -R .ARM.exidx $< $@; \
767 fi
768 """)
769
770 def library(defn, platform):
771     name = defn['name']
772     set_canonical_name_suffix("")
773
774     vars_init(defn,
775               cname(defn) + "_SOURCES",
776               "nodist_" + cname(defn) + "_SOURCES",
777               cname(defn) + "_CFLAGS",
778               cname(defn) + "_CPPFLAGS",
779               cname(defn) + "_CCASFLAGS")
780     #         cname(defn) + "_DEPENDENCIES")
781
782     if name not in seen_target:
783         gvar_add("noinst_LIBRARIES", name)
784     var_add(cname(defn) + "_SOURCES", platform_sources(defn, platform))
785     var_add("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform))
786     var_add(cname(defn) + "_CFLAGS", first_time(defn, "$(AM_CFLAGS) $(CFLAGS_LIBRARY) ") + platform_cflags(defn, platform))
787     var_add(cname(defn) + "_CPPFLAGS", first_time(defn, "$(AM_CPPFLAGS) $(CPPFLAGS_LIBRARY) ") + platform_cppflags(defn, platform))
788     var_add(cname(defn) + "_CCASFLAGS", first_time(defn, "$(AM_CCASFLAGS) $(CCASFLAGS_LIBRARY) ") + platform_ccasflags(defn, platform))
789     # var_add(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform))
790
791     gvar_add("dist_noinst_DATA", extra_dist(defn))
792     if name not in seen_target:
793         gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
794         gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
795
796 def installdir(defn, default="bin"):
797     return defn.get('installdir', default)
798
799 def manpage(defn, adddeps):
800     name = defn['name']
801     mansection = defn['mansection']
802
803     output("if COND_MAN_PAGES\n")
804     gvar_add("man_MANS", name + "." + mansection)
805     rule(name + "." + mansection, name + " " + adddeps, """
806 chmod a+x """ + name + """
807 PATH=$(builddir):$$PATH pkgdatadir=$(builddir) $(HELP2MAN) --section=""" + mansection + """ -i $(top_srcdir)/docs/man/""" + name + """.h2m -o $@ """ + name + """
808 """)
809     gvar_add("CLEANFILES", name + "." + mansection)
810     output("endif\n")
811
812 def program(defn, platform, test=False):
813     name = defn['name']
814     set_canonical_name_suffix("")
815
816     if 'testcase' in defn:
817         gvar_add("check_PROGRAMS", name)
818         gvar_add("TESTS", name)
819     else:
820         var_add(installdir(defn) + "_PROGRAMS", name)
821         if 'mansection' in defn:
822             manpage(defn, "")
823
824     var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform))
825     var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform))
826     var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
827     var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_PROGRAM) " + platform_cflags(defn, platform))
828     var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_PROGRAM) " + platform_ldflags(defn, platform))
829     var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_PROGRAM) " + platform_cppflags(defn, platform))
830     var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_PROGRAM) " + platform_ccasflags(defn, platform))
831     # var_set(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform))
832
833     gvar_add("dist_noinst_DATA", extra_dist(defn))
834     gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
835     gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
836
837 def data(defn, platform):
838     var_add("dist_" + installdir(defn) + "_DATA", platform_sources(defn, platform))
839     gvar_add("dist_noinst_DATA", extra_dist(defn))
840
841 def transform_data(defn, platform):
842     name = defn['name']
843
844     var_add(installdir(defn) + "_DATA", name)
845
846     rule(name, "$(top_builddir)/config.status " + platform_sources(defn, platform) + platform_dependencies(defn, platform), """
847 (for x in """ + platform_sources(defn, platform) + """; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:-
848 chmod a+x """ + name + """
849 """)
850
851     gvar_add("CLEANFILES", name)
852     gvar_add("EXTRA_DIST", extra_dist(defn))
853     gvar_add("dist_noinst_DATA", platform_sources(defn, platform))
854
855 def script(defn, platform):
856     name = defn['name']
857
858     if 'testcase' in defn:
859         gvar_add("check_SCRIPTS", name)
860         gvar_add ("TESTS", name)
861     else:
862         var_add(installdir(defn) + "_SCRIPTS", name)
863         if 'mansection' in defn:
864             manpage(defn, "grub-mkconfig_lib")
865
866     rule(name, "$(top_builddir)/config.status " + platform_sources(defn, platform) + platform_dependencies(defn, platform), """
867 (for x in """ + platform_sources(defn, platform) + """; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:-
868 chmod a+x """ + name + """
869 """)
870
871     gvar_add("CLEANFILES", name)
872     gvar_add("EXTRA_DIST", extra_dist(defn))
873     gvar_add("dist_noinst_DATA", platform_sources(defn, platform))
874
875 def rules(target, closure):
876     seen_target.clear()
877     seen_vars.clear()
878
879     for defn in defparser.definitions.find_all(target):
880         if is_platform_independent(defn):
881             under_platform_specific_conditionals(defn, GRUB_PLATFORMS[0], closure)
882         else:
883             foreach_enabled_platform(
884                 defn,
885                 lambda p: under_platform_specific_conditionals(defn, p, closure))
886         # Remember that we've seen this target.
887         seen_target.add(defn['name'])
888
889 parser = OptionParser(usage="%prog DEFINITION-FILES")
890 _, args = parser.parse_args()
891
892 for arg in args:
893     defparser.read_definitions(arg)
894
895 rules("module", module)
896 rules("kernel", kernel)
897 rules("image", image)
898 rules("library", library)
899 rules("program", program)
900 rules("script", script)
901 rules("data", data)
902 rules("transform_data", transform_data)
903
904 write_output(section='decl')
905 write_output()