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