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