diff options
author | Tom Zanussi <tom.zanussi@intel.com> | 2012-01-24 00:20:15 -0600 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2012-03-22 19:21:15 +0000 |
commit | cd8182e6892986d73a1f0252d38682b9d5c07b22 (patch) | |
tree | efcf627128c9139075d88de3fff42193f5240db3 /scripts/lib/bsp/engine.py | |
parent | cf80db1c85ebdf2c74bf71c41c5b211e8a395f06 (diff) | |
download | poky-cd8182e6892986d73a1f0252d38682b9d5c07b22.tar.gz |
yocto-bsp: add templating engine
The main implementation of the Yocto BSP templating engine,
essentially containing the internal implementation of the 'yocto-bsp
create' and yocto-bsp list' commands.
Signed-off-by: Tom Zanussi <tom.zanussi@intel.com>
Diffstat (limited to 'scripts/lib/bsp/engine.py')
-rw-r--r-- | scripts/lib/bsp/engine.py | 1436 |
1 files changed, 1436 insertions, 0 deletions
diff --git a/scripts/lib/bsp/engine.py b/scripts/lib/bsp/engine.py new file mode 100644 index 0000000000..d2f0735a65 --- /dev/null +++ b/scripts/lib/bsp/engine.py | |||
@@ -0,0 +1,1436 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2012, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module implements the templating engine used by 'yocto-bsp' to | ||
22 | # create BSPs. The BSP templates are simply the set of files expected | ||
23 | # to appear in a generated BSP, marked up with a small set of tags | ||
24 | # used to customize the output. The engine parses through the | ||
25 | # templates and generates a Python program containing all the logic | ||
26 | # and input elements needed to display and retrieve BSP-specific | ||
27 | # information from the user. The resulting program uses those results | ||
28 | # to generate the final BSP files. | ||
29 | # | ||
30 | # AUTHORS | ||
31 | # Tom Zanussi <tom.zanussi (at] intel.com> | ||
32 | # | ||
33 | |||
34 | import os | ||
35 | import sys | ||
36 | from abc import ABCMeta, abstractmethod | ||
37 | from tags import * | ||
38 | import shlex | ||
39 | import json | ||
40 | |||
41 | class Line(): | ||
42 | """ | ||
43 | Generic (abstract) container representing a line that will appear | ||
44 | in the BSP-generating program. | ||
45 | """ | ||
46 | __metaclass__ = ABCMeta | ||
47 | |||
48 | def __init__(self, line): | ||
49 | self.line = line | ||
50 | self.generated_line = "" | ||
51 | self.prio = sys.maxint | ||
52 | self.discard = False | ||
53 | |||
54 | @abstractmethod | ||
55 | def gen(self, context = None): | ||
56 | """ | ||
57 | Generate the final executable line that will appear in the | ||
58 | BSP-generation program. | ||
59 | """ | ||
60 | pass | ||
61 | |||
62 | def escape(self, line): | ||
63 | """ | ||
64 | Escape single and double quotes and backslashes until I find | ||
65 | something better (re.escape() escapes way too much). | ||
66 | """ | ||
67 | return line.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") | ||
68 | |||
69 | def parse_error(self, msg, lineno, line): | ||
70 | raise SyntaxError("%s: %s" % (msg, line)) | ||
71 | |||
72 | |||
73 | class NormalLine(Line): | ||
74 | """ | ||
75 | Container for normal (non-tag) lines. | ||
76 | """ | ||
77 | def __init__(self, line): | ||
78 | Line.__init__(self, line) | ||
79 | self.is_filename = False | ||
80 | self.is_dirname = False | ||
81 | self.out_filebase = None | ||
82 | |||
83 | def gen(self, context = None): | ||
84 | if self.is_filename: | ||
85 | line = "of = open(\"" + os.path.join(self.out_filebase, self.escape(self.line)) + "\", \"w\")" | ||
86 | elif self.is_dirname: | ||
87 | dirname = os.path.join(self.out_filebase, self.escape(self.line)) | ||
88 | line = "if not os.path.exists(\"" + dirname + "\"): os.mkdir(\"" + dirname + "\")" | ||
89 | else: | ||
90 | line = "of.write(\"" + self.escape(self.line) + "\\n\")" | ||
91 | return line | ||
92 | |||
93 | |||
94 | class CodeLine(Line): | ||
95 | """ | ||
96 | Container for Python code tag lines. | ||
97 | """ | ||
98 | def __init__(self, line): | ||
99 | Line.__init__(self, line) | ||
100 | |||
101 | def gen(self, context = None): | ||
102 | return self.line | ||
103 | |||
104 | |||
105 | class Assignment: | ||
106 | """ | ||
107 | Representation of everything we know about {{=name }} tags. | ||
108 | Instances of these are used by Assignment lines. | ||
109 | """ | ||
110 | def __init__(self, start, end, name): | ||
111 | self.start = start | ||
112 | self.end = end | ||
113 | self.name = name | ||
114 | |||
115 | |||
116 | class AssignmentLine(NormalLine): | ||
117 | """ | ||
118 | Container for normal lines containing assignment tags. Assignment | ||
119 | tags must be in ascending order of 'start' value. | ||
120 | """ | ||
121 | def __init__(self, line): | ||
122 | NormalLine.__init__(self, line) | ||
123 | self.assignments = [] | ||
124 | |||
125 | def add_assignment(self, start, end, name): | ||
126 | self.assignments.append(Assignment(start, end, name)) | ||
127 | |||
128 | def gen(self, context = None): | ||
129 | line = self.escape(self.line) | ||
130 | |||
131 | for assignment in self.assignments: | ||
132 | replacement = "\" + " + assignment.name + " + \"" | ||
133 | idx = line.find(ASSIGN_TAG) | ||
134 | line = line[:idx] + replacement + line[idx + assignment.end - assignment.start:] | ||
135 | if self.is_filename: | ||
136 | return "of = open(\"" + os.path.join(self.out_filebase, line) + "\", \"w\")" | ||
137 | elif self.is_dirname: | ||
138 | dirname = os.path.join(self.out_filebase, line) | ||
139 | return "if not os.path.exists(\"" + dirname + "\"): os.mkdir(\"" + dirname + "\")" | ||
140 | else: | ||
141 | return "of.write(\"" + line + "\\n\")" | ||
142 | |||
143 | |||
144 | class InputLine(Line): | ||
145 | """ | ||
146 | Base class for Input lines. | ||
147 | """ | ||
148 | def __init__(self, props, tag, lineno): | ||
149 | Line.__init__(self, tag) | ||
150 | self.props = props | ||
151 | self.lineno = lineno | ||
152 | |||
153 | try: | ||
154 | self.prio = int(props["prio"]) | ||
155 | except KeyError: | ||
156 | self.prio = sys.maxint | ||
157 | |||
158 | def gen(self, context = None): | ||
159 | try: | ||
160 | depends_on = self.props["depends-on"] | ||
161 | try: | ||
162 | depends_on_val = self.props["depends-on-val"] | ||
163 | except KeyError: | ||
164 | self.parse_error("No 'depends-on-val' for 'depends-on' property", | ||
165 | self.lineno, self.line) | ||
166 | except KeyError: | ||
167 | pass | ||
168 | |||
169 | |||
170 | class EditBoxInputLine(InputLine): | ||
171 | """ | ||
172 | Base class for 'editbox' Input lines. | ||
173 | |||
174 | props: | ||
175 | name: example - "Load address" | ||
176 | msg: example - "Please enter the load address" | ||
177 | result: | ||
178 | Sets the value of the variable specified by 'name' to | ||
179 | whatever the user typed. | ||
180 | """ | ||
181 | def __init__(self, props, tag, lineno): | ||
182 | InputLine.__init__(self, props, tag, lineno) | ||
183 | |||
184 | def gen(self, context = None): | ||
185 | InputLine.gen(self, context) | ||
186 | name = self.props["name"] | ||
187 | if not name: | ||
188 | self.parse_error("No input 'name' property found", | ||
189 | self.lineno, self.line) | ||
190 | msg = self.props["msg"] | ||
191 | if not msg: | ||
192 | self.parse_error("No input 'msg' property found", | ||
193 | self.lineno, self.line) | ||
194 | |||
195 | try: | ||
196 | default_choice = self.props["default"] | ||
197 | except KeyError: | ||
198 | default_choice = "" | ||
199 | |||
200 | msg += " [default: " + default_choice + "]" | ||
201 | |||
202 | line = name + " = default(raw_input(\"" + msg + " \"), " + name + ")" | ||
203 | |||
204 | return line | ||
205 | |||
206 | |||
207 | class BooleanInputLine(InputLine): | ||
208 | """ | ||
209 | Base class for boolean Input lines. | ||
210 | props: | ||
211 | name: example - "keyboard" | ||
212 | msg: example - "Got keyboard?" | ||
213 | result: | ||
214 | Sets the value of the variable specified by 'name' to "yes" or "no" | ||
215 | example - keyboard = "yes" | ||
216 | """ | ||
217 | def __init__(self, props, tag, lineno): | ||
218 | InputLine.__init__(self, props, tag, lineno) | ||
219 | |||
220 | def gen(self, context = None): | ||
221 | InputLine.gen(self, context) | ||
222 | name = self.props["name"] | ||
223 | if not name: | ||
224 | self.parse_error("No input 'name' property found", | ||
225 | self.lineno, self.line) | ||
226 | msg = self.props["msg"] | ||
227 | if not msg: | ||
228 | self.parse_error("No input 'msg' property found", | ||
229 | self.lineno, self.line) | ||
230 | |||
231 | try: | ||
232 | default_choice = self.props["default"] | ||
233 | except KeyError: | ||
234 | default_choice = "" | ||
235 | |||
236 | msg += " [default: " + default_choice + "]" | ||
237 | |||
238 | line = name + " = boolean(raw_input(\"" + msg + " \"), " + name + ")" | ||
239 | |||
240 | return line | ||
241 | |||
242 | |||
243 | class ListInputLine(InputLine): | ||
244 | """ | ||
245 | Base class for List-based Input lines. e.g. Choicelist, Checklist. | ||
246 | """ | ||
247 | __metaclass__ = ABCMeta | ||
248 | |||
249 | def __init__(self, props, tag, lineno): | ||
250 | InputLine.__init__(self, props, tag, lineno) | ||
251 | self.choices = [] | ||
252 | |||
253 | def gen_choicepair_list(self): | ||
254 | """Generate a list of 2-item val:desc lists from self.choices.""" | ||
255 | if not self.choices: | ||
256 | return None | ||
257 | |||
258 | choicepair_list = list() | ||
259 | |||
260 | for choice in self.choices: | ||
261 | choicepair = [] | ||
262 | choicepair.append(choice.val) | ||
263 | choicepair.append(choice.desc) | ||
264 | choicepair_list.append(choicepair) | ||
265 | |||
266 | return choicepair_list | ||
267 | |||
268 | def gen_degenerate_choicepair_list(self, choices): | ||
269 | """Generate a list of 2-item val:desc with val=desc from passed-in choices.""" | ||
270 | choicepair_list = list() | ||
271 | |||
272 | for choice in choices: | ||
273 | choicepair = [] | ||
274 | choicepair.append(choice) | ||
275 | choicepair.append(choice) | ||
276 | choicepair_list.append(choicepair) | ||
277 | |||
278 | return choicepair_list | ||
279 | |||
280 | def exec_listgen_fn(self, context = None): | ||
281 | """ | ||
282 | Execute the list-generating function contained as a string in | ||
283 | the "gen" property. | ||
284 | """ | ||
285 | retval = None | ||
286 | try: | ||
287 | fname = self.props["gen"] | ||
288 | modsplit = fname.split('.') | ||
289 | mod_fn = modsplit.pop() | ||
290 | mod = '.'.join(modsplit) | ||
291 | |||
292 | __import__(mod) | ||
293 | # python 2.7 has a better way to do this using importlib.import_module | ||
294 | m = sys.modules[mod] | ||
295 | |||
296 | fn = getattr(m, mod_fn) | ||
297 | if not fn: | ||
298 | self.parse_error("couldn't load function specified for 'gen' property ", | ||
299 | self.lineno, self.line) | ||
300 | retval = fn(context) | ||
301 | if not retval: | ||
302 | self.parse_error("function specified for 'gen' property returned nothing ", | ||
303 | self.lineno, self.line) | ||
304 | except KeyError: | ||
305 | pass | ||
306 | |||
307 | return retval | ||
308 | |||
309 | def gen_choices_str(self, choicepairs): | ||
310 | """ | ||
311 | Generate a numbered list of choices from a list of choicepairs | ||
312 | for display to the user. | ||
313 | """ | ||
314 | choices_str = "" | ||
315 | |||
316 | for i, choicepair in enumerate(choicepairs): | ||
317 | choices_str += "\t" + str(i + 1) + ") " + choicepair[1] + "\n" | ||
318 | |||
319 | return choices_str | ||
320 | |||
321 | def gen_choices_val_str(self, choicepairs): | ||
322 | """ | ||
323 | Generate an array of choice values corresponding to the | ||
324 | numbered list generated by gen_choices_str(). | ||
325 | """ | ||
326 | choices_val_list = "[" | ||
327 | |||
328 | for i, choicepair in enumerate(choicepairs): | ||
329 | choices_val_list += "\"" + choicepair[0] + "\"," | ||
330 | choices_val_list += "]" | ||
331 | |||
332 | return choices_val_list | ||
333 | |||
334 | def gen_choices_val_list(self, choicepairs): | ||
335 | """ | ||
336 | Generate an array of choice values corresponding to the | ||
337 | numbered list generated by gen_choices_str(). | ||
338 | """ | ||
339 | choices_val_list = [] | ||
340 | |||
341 | for i, choicepair in enumerate(choicepairs): | ||
342 | choices_val_list.append(choicepair[0]) | ||
343 | |||
344 | return choices_val_list | ||
345 | |||
346 | def gen_choices_list(self, context = None, checklist = False): | ||
347 | """ | ||
348 | Generate an array of choice values corresponding to the | ||
349 | numbered list generated by gen_choices_str(). | ||
350 | """ | ||
351 | choices = self.exec_listgen_fn(context) | ||
352 | if choices: | ||
353 | if len(choices) == 0: | ||
354 | self.parse_error("No entries available for input list", | ||
355 | self.lineno, self.line) | ||
356 | choicepairs = self.gen_degenerate_choicepair_list(choices) | ||
357 | else: | ||
358 | if len(self.choices) == 0: | ||
359 | self.parse_error("No entries available for input list", | ||
360 | self.lineno, self.line) | ||
361 | choicepairs = self.gen_choicepair_list() | ||
362 | |||
363 | return choicepairs | ||
364 | |||
365 | def gen_choices(self, context = None, checklist = False): | ||
366 | """ | ||
367 | Generate an array of choice values corresponding to the | ||
368 | numbered list generated by gen_choices_str(), display it to | ||
369 | the user, and process the result. | ||
370 | """ | ||
371 | msg = self.props["msg"] | ||
372 | if not msg: | ||
373 | self.parse_error("No input 'msg' property found", | ||
374 | self.lineno, self.line) | ||
375 | |||
376 | try: | ||
377 | default_choice = self.props["default"] | ||
378 | except KeyError: | ||
379 | default_choice = "" | ||
380 | |||
381 | msg += " [default: " + default_choice + "]" | ||
382 | |||
383 | choicepairs = self.gen_choices_list(context, checklist) | ||
384 | |||
385 | choices_str = self.gen_choices_str(choicepairs) | ||
386 | choices_val_list = self.gen_choices_val_list(choicepairs) | ||
387 | if checklist: | ||
388 | choiceval = default(find_choicevals(raw_input(msg + "\n" + choices_str), choices_val_list), default_choice) | ||
389 | else: | ||
390 | choiceval = default(find_choiceval(raw_input(msg + "\n" + choices_str), choices_val_list), default_choice) | ||
391 | |||
392 | return choiceval | ||
393 | |||
394 | |||
395 | def find_choiceval(choice_str, choice_list): | ||
396 | """ | ||
397 | Take number as string and return val string from choice_list, | ||
398 | empty string if oob. choice_list is a simple python list. | ||
399 | """ | ||
400 | choice_val = "" | ||
401 | |||
402 | try: | ||
403 | choice_idx = int(choice_str) | ||
404 | if choice_idx <= len(choice_list): | ||
405 | choice_idx -= 1 | ||
406 | choice_val = choice_list[choice_idx] | ||
407 | except ValueError: | ||
408 | pass | ||
409 | |||
410 | return choice_val | ||
411 | |||
412 | |||
413 | def find_choicevals(choice_str, choice_list): | ||
414 | """ | ||
415 | Take numbers as space-separated string and return vals list from | ||
416 | choice_list, empty list if oob. choice_list is a simple python | ||
417 | list. | ||
418 | """ | ||
419 | choice_vals = [] | ||
420 | |||
421 | choices = choice_str.split() | ||
422 | for choice in choices: | ||
423 | choice_vals.append(find_choiceval(choice, choice_list)) | ||
424 | |||
425 | return choice_vals | ||
426 | |||
427 | |||
428 | def default(input_str, name): | ||
429 | """ | ||
430 | Return default if no input_str, otherwise stripped input_str. | ||
431 | """ | ||
432 | if not input_str: | ||
433 | return name | ||
434 | |||
435 | return input_str.strip() | ||
436 | |||
437 | |||
438 | def boolean(input_str, name): | ||
439 | """ | ||
440 | Return lowercase version of first char in string, or value in name. | ||
441 | """ | ||
442 | if not input_str: | ||
443 | return name | ||
444 | |||
445 | str = input_str.lower().strip() | ||
446 | if str and str[0] == "y" or str[0] == "n": | ||
447 | return str[0] | ||
448 | else: | ||
449 | return name | ||
450 | |||
451 | |||
452 | deferred_choices = {} | ||
453 | |||
454 | def gen_choices_defer(input_line, context, checklist = False): | ||
455 | """ | ||
456 | Save the context hashed the name of the input item, which will be | ||
457 | passed to the gen function later. | ||
458 | """ | ||
459 | name = input_line.props["name"] | ||
460 | |||
461 | try: | ||
462 | nameappend = input_line.props["nameappend"] | ||
463 | except KeyError: | ||
464 | nameappend = "" | ||
465 | |||
466 | filename = input_line.props["filename"] | ||
467 | |||
468 | closetag_start = filename.find(CLOSE_TAG) | ||
469 | |||
470 | if closetag_start != -1: | ||
471 | filename = filename[closetag_start + len(CLOSE_TAG):] | ||
472 | |||
473 | filename = filename.strip() | ||
474 | filename = os.path.splitext(filename)[0] | ||
475 | |||
476 | captured_context = capture_context(context) | ||
477 | context["filename"] = filename | ||
478 | captured_context["filename"] = filename | ||
479 | context["nameappend"] = nameappend | ||
480 | captured_context["nameappend"] = nameappend | ||
481 | |||
482 | deferred_choice = (input_line, captured_context, checklist) | ||
483 | key = name + "_" + filename + "_" + nameappend | ||
484 | deferred_choices[key] = deferred_choice | ||
485 | |||
486 | |||
487 | def invoke_deferred_choices(name): | ||
488 | """ | ||
489 | Invoke the choice generation function using the context hashed by | ||
490 | 'name'. | ||
491 | """ | ||
492 | deferred_choice = deferred_choices[name] | ||
493 | input_line = deferred_choice[0] | ||
494 | context = deferred_choice[1] | ||
495 | checklist = deferred_choice[2] | ||
496 | |||
497 | context["name"] = name | ||
498 | |||
499 | choices = input_line.gen_choices(context, checklist) | ||
500 | |||
501 | return choices | ||
502 | |||
503 | |||
504 | class ChoicelistInputLine(ListInputLine): | ||
505 | """ | ||
506 | Base class for choicelist Input lines. | ||
507 | props: | ||
508 | name: example - "xserver_choice" | ||
509 | msg: example - "Please select an xserver for this machine" | ||
510 | result: | ||
511 | Sets the value of the variable specified by 'name' to whichever Choice was chosen | ||
512 | example - xserver_choice = "xserver_vesa" | ||
513 | """ | ||
514 | def __init__(self, props, tag, lineno): | ||
515 | ListInputLine.__init__(self, props, tag, lineno) | ||
516 | |||
517 | def gen(self, context = None): | ||
518 | InputLine.gen(self, context) | ||
519 | |||
520 | gen_choices_defer(self, context) | ||
521 | name = self.props["name"] | ||
522 | nameappend = context["nameappend"] | ||
523 | filename = context["filename"] | ||
524 | |||
525 | try: | ||
526 | default_choice = self.props["default"] | ||
527 | except KeyError: | ||
528 | default_choice = "" | ||
529 | |||
530 | line = name + " = default(invoke_deferred_choices(\"" + name + "_" + filename + "_" + nameappend + "\"), \"" + default_choice + "\")" | ||
531 | |||
532 | return line | ||
533 | |||
534 | |||
535 | class ListValInputLine(InputLine): | ||
536 | """ | ||
537 | Abstract base class for choice and checkbox Input lines. | ||
538 | """ | ||
539 | def __init__(self, props, tag, lineno): | ||
540 | InputLine.__init__(self, props, tag, lineno) | ||
541 | |||
542 | try: | ||
543 | self.val = self.props["val"] | ||
544 | except KeyError: | ||
545 | self.parse_error("No input 'val' property found", self.lineno, self.line) | ||
546 | |||
547 | try: | ||
548 | self.desc = self.props["msg"] | ||
549 | except KeyError: | ||
550 | self.parse_error("No input 'msg' property found", self.lineno, self.line) | ||
551 | |||
552 | |||
553 | class ChoiceInputLine(ListValInputLine): | ||
554 | """ | ||
555 | Base class for choicelist item Input lines. | ||
556 | """ | ||
557 | def __init__(self, props, tag, lineno): | ||
558 | ListValInputLine.__init__(self, props, tag, lineno) | ||
559 | |||
560 | def gen(self, context = None): | ||
561 | return None | ||
562 | |||
563 | |||
564 | class ChecklistInputLine(ListInputLine): | ||
565 | """ | ||
566 | Base class for checklist Input lines. | ||
567 | """ | ||
568 | def __init__(self, props, tag, lineno): | ||
569 | ListInputLine.__init__(self, props, tag, lineno) | ||
570 | |||
571 | def gen(self, context = None): | ||
572 | InputLine.gen(self, context) | ||
573 | |||
574 | gen_choices_defer(self, context, True) | ||
575 | name = self.props["name"] | ||
576 | nameappend = context["nameappend"] | ||
577 | filename = context["filename"] | ||
578 | |||
579 | try: | ||
580 | default_choice = self.props["default"] | ||
581 | except KeyError: | ||
582 | default_choice = "" | ||
583 | |||
584 | line = name + " = default(invoke_deferred_choices(\"" + name + "_" + filename + "_" + nameappend + "\"), \"" + default_choice + "\")" | ||
585 | |||
586 | return line | ||
587 | |||
588 | |||
589 | class CheckInputLine(ListValInputLine): | ||
590 | """ | ||
591 | Base class for checklist item Input lines. | ||
592 | """ | ||
593 | def __init__(self, props, tag, lineno): | ||
594 | ListValInputLine.__init__(self, props, tag, lineno) | ||
595 | |||
596 | def gen(self, context = None): | ||
597 | return None | ||
598 | |||
599 | |||
600 | class SubstrateBase(object): | ||
601 | """ | ||
602 | Base class for both expanded and unexpanded file and dir container | ||
603 | objects. | ||
604 | """ | ||
605 | def __init__(self, filename, filebase, out_filebase): | ||
606 | self.filename = filename | ||
607 | self.filebase = filebase | ||
608 | self.out_filebase = out_filebase | ||
609 | self.raw_lines = [] | ||
610 | self.expanded_lines = [] | ||
611 | self.prev_choicelist = None | ||
612 | |||
613 | def parse_error(self, msg, lineno, line): | ||
614 | raise SyntaxError("%s: [%s: %d]: %s" % (msg, self.filename, lineno, line)) | ||
615 | |||
616 | def expand_input_tag(self, tag, lineno): | ||
617 | """ | ||
618 | Input tags consist of the word 'input' at the beginning, | ||
619 | followed by name:value property pairs which are converted into | ||
620 | a dictionary. | ||
621 | """ | ||
622 | propstr = tag[len(INPUT_TAG):] | ||
623 | |||
624 | props = dict(prop.split(":", 1) for prop in shlex.split(propstr)) | ||
625 | props["filename"] = self.filename | ||
626 | |||
627 | input_type = props[INPUT_TYPE_PROPERTY] | ||
628 | if not props[INPUT_TYPE_PROPERTY]: | ||
629 | self.parse_error("No input 'type' property found", lineno, tag) | ||
630 | |||
631 | if input_type == "boolean": | ||
632 | return BooleanInputLine(props, tag, lineno) | ||
633 | if input_type == "edit": | ||
634 | return EditBoxInputLine(props, tag, lineno) | ||
635 | elif input_type == "choicelist": | ||
636 | self.prev_choicelist = ChoicelistInputLine(props, tag, lineno) | ||
637 | return self.prev_choicelist | ||
638 | elif input_type == "choice": | ||
639 | if not self.prev_choicelist: | ||
640 | self.parse_error("Found 'choice' input tag but no previous choicelist", | ||
641 | lineno, tag) | ||
642 | choice = ChoiceInputLine(props, tag, lineno) | ||
643 | self.prev_choicelist.choices.append(choice) | ||
644 | return choice | ||
645 | elif input_type == "checklist": | ||
646 | return ChecklistInputLine(props, tag, lineno) | ||
647 | elif input_type == "check": | ||
648 | return CheckInputLine(props, tag, lineno) | ||
649 | |||
650 | def expand_assignment_tag(self, start, line, lineno): | ||
651 | """ | ||
652 | Expand all tags in a line. | ||
653 | """ | ||
654 | expanded_line = AssignmentLine(line.strip()) | ||
655 | |||
656 | while start != -1: | ||
657 | end = line.find(CLOSE_TAG, start) | ||
658 | if end == -1: | ||
659 | self.parse_error("No close tag found for assignment tag", lineno, line) | ||
660 | else: | ||
661 | name = line[start + len(ASSIGN_TAG):end].strip() | ||
662 | expanded_line.add_assignment(start, end + len(CLOSE_TAG), name) | ||
663 | start = line.find(ASSIGN_TAG, end) | ||
664 | |||
665 | return expanded_line | ||
666 | |||
667 | def expand_tag(self, line, lineno): | ||
668 | """ | ||
669 | Returns a processed tag line, or None if there was no tag | ||
670 | |||
671 | The rules for tags are very simple: | ||
672 | - No nested tags | ||
673 | - Tags start with {{ and end with }} | ||
674 | - An assign tag, {{=, can appear anywhere and will | ||
675 | be replaced with what the assignment evaluates to | ||
676 | - Any other tag occupies the whole line it is on | ||
677 | - if there's anything else on the tag line, it's an error | ||
678 | - if it starts with 'input', it's an input tag and | ||
679 | will only be used for prompting and setting variables | ||
680 | - anything else is straight Python | ||
681 | - tags are in effect only until the next blank line or tag or 'pass' tag | ||
682 | - we don't have indentation in tags, but we need some way to end a block | ||
683 | forcefully without blank lines or other tags - that's the 'pass' tag | ||
684 | - todo: implement pass tag | ||
685 | - directories and filenames can have tags as well, but only assignment | ||
686 | and 'if' code lines | ||
687 | - directories and filenames are the only case where normal tags can | ||
688 | coexist with normal text on the same 'line' | ||
689 | """ | ||
690 | start = line.find(ASSIGN_TAG) | ||
691 | if start != -1: | ||
692 | return self.expand_assignment_tag(start, line, lineno) | ||
693 | |||
694 | start = line.find(OPEN_TAG) | ||
695 | if start == -1: | ||
696 | return None | ||
697 | |||
698 | end = line.find(CLOSE_TAG, 0) | ||
699 | if end == -1: | ||
700 | self.parse_error("No close tag found for open tag", lineno, line) | ||
701 | |||
702 | tag = line[start + len(OPEN_TAG):end].strip() | ||
703 | |||
704 | if not tag.lstrip().startswith(INPUT_TAG): | ||
705 | return CodeLine(tag) | ||
706 | |||
707 | return self.expand_input_tag(tag, lineno) | ||
708 | |||
709 | def expand_file_or_dir_name(self): | ||
710 | """ | ||
711 | Expand file or dir names into codeline. Dirnames and | ||
712 | filenames can only have assignments or if statements. First | ||
713 | translate if statements into CodeLine + (dirname or filename | ||
714 | creation). | ||
715 | """ | ||
716 | lineno = 0 | ||
717 | |||
718 | line = self.filename[len(self.filebase):] | ||
719 | if line.startswith("/"): | ||
720 | line = line[1:] | ||
721 | opentag_start = -1 | ||
722 | |||
723 | start = line.find(OPEN_TAG) | ||
724 | while start != -1: | ||
725 | if not line[start:].startswith(ASSIGN_TAG): | ||
726 | opentag_start = start | ||
727 | break | ||
728 | start += len(ASSIGN_TAG) | ||
729 | start = line.find(OPEN_TAG, start) | ||
730 | |||
731 | if opentag_start != -1: | ||
732 | end = line.find(CLOSE_TAG, opentag_start) | ||
733 | if end == -1: | ||
734 | self.parse_error("No close tag found for open tag", lineno, line) | ||
735 | # we have a {{ tag i.e. code | ||
736 | tag = line[opentag_start + len(OPEN_TAG):end].strip() | ||
737 | |||
738 | if not tag.lstrip().startswith(IF_TAG): | ||
739 | self.parse_error("Only 'if' tags are allowed in file or directory names", | ||
740 | lineno, line) | ||
741 | self.expanded_lines.append(CodeLine(tag)) | ||
742 | |||
743 | # everything after }} is the actual filename (possibly with assignments) | ||
744 | # everything before is the pathname | ||
745 | line = line[:opentag_start] + line[end + len(CLOSE_TAG):].strip() | ||
746 | |||
747 | assign_start = line.find(ASSIGN_TAG) | ||
748 | if assign_start != -1: | ||
749 | assignment_tag = self.expand_assignment_tag(assign_start, line, lineno) | ||
750 | if isinstance(self, SubstrateFile): | ||
751 | assignment_tag.is_filename = True | ||
752 | assignment_tag.out_filebase = self.out_filebase | ||
753 | elif isinstance(self, SubstrateDir): | ||
754 | assignment_tag.is_dirname = True | ||
755 | assignment_tag.out_filebase = self.out_filebase | ||
756 | self.expanded_lines.append(assignment_tag) | ||
757 | return | ||
758 | |||
759 | normal_line = NormalLine(line) | ||
760 | if isinstance(self, SubstrateFile): | ||
761 | normal_line.is_filename = True | ||
762 | normal_line.out_filebase = self.out_filebase | ||
763 | elif isinstance(self, SubstrateDir): | ||
764 | normal_line.is_dirname = True | ||
765 | normal_line.out_filebase = self.out_filebase | ||
766 | self.expanded_lines.append(normal_line) | ||
767 | |||
768 | def expand(self): | ||
769 | """ | ||
770 | Expand the file or dir name first, eventually this ends up | ||
771 | creating the file or dir. | ||
772 | """ | ||
773 | self.expand_file_or_dir_name() | ||
774 | |||
775 | |||
776 | class SubstrateFile(SubstrateBase): | ||
777 | """ | ||
778 | Container for both expanded and unexpanded substrate files. | ||
779 | """ | ||
780 | def __init__(self, filename, filebase, out_filebase): | ||
781 | SubstrateBase.__init__(self, filename, filebase, out_filebase) | ||
782 | |||
783 | def read(self): | ||
784 | if self.raw_lines: | ||
785 | return | ||
786 | f = open(self.filename) | ||
787 | self.raw_lines = f.readlines() | ||
788 | |||
789 | def expand(self): | ||
790 | """Expand the contents of all template tags in the file.""" | ||
791 | SubstrateBase.expand(self) | ||
792 | self.read() | ||
793 | |||
794 | for lineno, line in enumerate(self.raw_lines): | ||
795 | expanded_line = self.expand_tag(line, lineno + 1) # humans not 0-based | ||
796 | if not expanded_line: | ||
797 | expanded_line = NormalLine(line.rstrip()) | ||
798 | self.expanded_lines.append(expanded_line) | ||
799 | |||
800 | def gen(self, context = None): | ||
801 | """Generate the code that generates the BSP.""" | ||
802 | base_indent = 0 | ||
803 | |||
804 | indent = new_indent = base_indent | ||
805 | |||
806 | for line in self.expanded_lines: | ||
807 | genline = line.gen(context) | ||
808 | if not genline: | ||
809 | continue | ||
810 | if isinstance(line, InputLine): | ||
811 | line.generated_line = genline | ||
812 | continue | ||
813 | if genline.startswith(OPEN_START): | ||
814 | if indent == 1: | ||
815 | base_indent = 1 | ||
816 | if indent: | ||
817 | if genline == BLANKLINE_STR or (not genline.startswith(NORMAL_START) | ||
818 | and not genline.startswith(OPEN_START)): | ||
819 | indent = new_indent = base_indent | ||
820 | if genline.endswith(":"): | ||
821 | new_indent = base_indent + 1 | ||
822 | line.generated_line = (indent * INDENT_STR) + genline | ||
823 | indent = new_indent | ||
824 | |||
825 | |||
826 | class SubstrateDir(SubstrateBase): | ||
827 | """ | ||
828 | Container for both expanded and unexpanded substrate dirs. | ||
829 | """ | ||
830 | def __init__(self, filename, filebase, out_filebase): | ||
831 | SubstrateBase.__init__(self, filename, filebase, out_filebase) | ||
832 | |||
833 | def expand(self): | ||
834 | SubstrateBase.expand(self) | ||
835 | |||
836 | def gen(self, context = None): | ||
837 | """Generate the code that generates the BSP.""" | ||
838 | indent = new_indent = 0 | ||
839 | for line in self.expanded_lines: | ||
840 | genline = line.gen(context) | ||
841 | if not genline: | ||
842 | continue | ||
843 | if genline.endswith(":"): | ||
844 | new_indent = 1 | ||
845 | else: | ||
846 | new_indent = 0 | ||
847 | line.generated_line = (indent * INDENT_STR) + genline | ||
848 | indent = new_indent | ||
849 | |||
850 | |||
851 | def expand_target(target, all_files, out_filebase): | ||
852 | """ | ||
853 | Expand the contents of all template tags in the target. This | ||
854 | means removing tags and categorizing or creating lines so that | ||
855 | future passes can process and present input lines and generate the | ||
856 | corresponding lines of the Python program that will be exec'ed to | ||
857 | actually produce the final BSP. 'all_files' includes directories. | ||
858 | """ | ||
859 | for root, dirs, files in os.walk(target): | ||
860 | for file in files: | ||
861 | if file.endswith("~") or file.endswith("#"): | ||
862 | continue | ||
863 | f = os.path.join(root, file) | ||
864 | sfile = SubstrateFile(f, target, out_filebase) | ||
865 | sfile.expand() | ||
866 | all_files.append(sfile) | ||
867 | |||
868 | for dir in dirs: | ||
869 | d = os.path.join(root, dir) | ||
870 | sdir = SubstrateDir(d, target, out_filebase) | ||
871 | sdir.expand() | ||
872 | all_files.append(sdir) | ||
873 | |||
874 | |||
875 | def gen_program_machine_lines(machine, program_lines): | ||
876 | """ | ||
877 | Use the input values we got from the command line. | ||
878 | """ | ||
879 | line = "machine = \"" + machine + "\"" | ||
880 | |||
881 | program_lines.append(line) | ||
882 | |||
883 | |||
884 | def sort_inputlines(input_lines): | ||
885 | """Sort input lines according to priority (position).""" | ||
886 | input_lines.sort(key = lambda l: l.prio) | ||
887 | |||
888 | |||
889 | def find_parent_dependency(lines, depends_on): | ||
890 | for i, line in lines: | ||
891 | if isinstance(line, CodeLine): | ||
892 | continue | ||
893 | if line.props["name"] == depends_on: | ||
894 | return i | ||
895 | |||
896 | return -1 | ||
897 | |||
898 | |||
899 | def process_inputline_dependencies(input_lines, all_inputlines): | ||
900 | """If any input lines depend on others, put the others first.""" | ||
901 | for line in input_lines: | ||
902 | if isinstance(line, InputLineGroup): | ||
903 | group_inputlines = [] | ||
904 | process_inputline_dependencies(line.group, group_inputlines) | ||
905 | line.group = group_inputlines | ||
906 | all_inputlines.append(line) | ||
907 | continue | ||
908 | |||
909 | if isinstance(line, CodeLine) or isinstance(line, NormalLine): | ||
910 | all_inputlines.append(line) | ||
911 | continue | ||
912 | |||
913 | try: | ||
914 | depends_on = line.props["depends-on"] | ||
915 | depends_codeline = "if " + line.props["depends-on"] + " == \"" + line.props["depends-on-val"] + "\":" | ||
916 | all_inputlines.append(CodeLine(depends_codeline)) | ||
917 | all_inputlines.append(line) | ||
918 | except KeyError: | ||
919 | all_inputlines.append(line) | ||
920 | |||
921 | |||
922 | def conditional_filename(filename): | ||
923 | """ | ||
924 | Check if the filename itself contains a conditional statement. If | ||
925 | so, return a codeline for it. | ||
926 | """ | ||
927 | opentag_start = filename.find(OPEN_TAG) | ||
928 | |||
929 | if opentag_start != -1: | ||
930 | if filename[opentag_start:].startswith(ASSIGN_TAG): | ||
931 | return None | ||
932 | end = filename.find(CLOSE_TAG, opentag_start) | ||
933 | if end == -1: | ||
934 | print "No close tag found for open tag in filename %s" % filename | ||
935 | sys.exit(1) | ||
936 | |||
937 | # we have a {{ tag i.e. code | ||
938 | tag = filename[opentag_start + len(OPEN_TAG):end].strip() | ||
939 | if not tag.lstrip().startswith(IF_TAG): | ||
940 | print "Only 'if' tags are allowed in file or directory names, filename: %s" % filename | ||
941 | sys.exit(1) | ||
942 | |||
943 | return CodeLine(tag) | ||
944 | |||
945 | return None | ||
946 | |||
947 | |||
948 | class InputLineGroup(InputLine): | ||
949 | """ | ||
950 | InputLine that does nothing but group other input lines | ||
951 | corresponding to all the input lines in a SubstrateFile so they | ||
952 | can be generated as a group. prio is the only property used. | ||
953 | """ | ||
954 | def __init__(self, codeline): | ||
955 | InputLine.__init__(self, {}, "", 0) | ||
956 | self.group = [] | ||
957 | self.prio = sys.maxint | ||
958 | self.group.append(codeline) | ||
959 | |||
960 | def append(self, line): | ||
961 | self.group.append(line) | ||
962 | if line.prio < self.prio: | ||
963 | self.prio = line.prio | ||
964 | |||
965 | def len(self): | ||
966 | return len(self.group) | ||
967 | |||
968 | |||
969 | def gather_inputlines(files): | ||
970 | """ | ||
971 | Gather all the InputLines - we want to generate them first. | ||
972 | """ | ||
973 | all_inputlines = [] | ||
974 | input_lines = [] | ||
975 | |||
976 | for file in files: | ||
977 | if isinstance(file, SubstrateFile): | ||
978 | group = None | ||
979 | basename = os.path.basename(file.filename) | ||
980 | |||
981 | codeline = conditional_filename(basename) | ||
982 | if codeline: | ||
983 | group = InputLineGroup(codeline) | ||
984 | |||
985 | have_condition = False | ||
986 | condition_to_write = None | ||
987 | for line in file.expanded_lines: | ||
988 | if isinstance(line, CodeLine): | ||
989 | have_condition = True | ||
990 | condition_to_write = line | ||
991 | continue | ||
992 | if isinstance(line, InputLine): | ||
993 | if group: | ||
994 | if condition_to_write: | ||
995 | condition_to_write.prio = line.prio | ||
996 | condition_to_write.discard = True | ||
997 | group.append(condition_to_write) | ||
998 | condition_to_write = None | ||
999 | group.append(line) | ||
1000 | else: | ||
1001 | if condition_to_write: | ||
1002 | condition_to_write.prio = line.prio | ||
1003 | condition_to_write.discard = True | ||
1004 | input_lines.append(condition_to_write) | ||
1005 | condition_to_write = None | ||
1006 | input_lines.append(line) | ||
1007 | else: | ||
1008 | if condition_to_write: | ||
1009 | condition_to_write = None | ||
1010 | if have_condition: | ||
1011 | if not line.line.strip(): | ||
1012 | line.discard = True | ||
1013 | input_lines.append(line) | ||
1014 | have_condition = False | ||
1015 | |||
1016 | if group and group.len() > 1: | ||
1017 | input_lines.append(group) | ||
1018 | |||
1019 | sort_inputlines(input_lines) | ||
1020 | process_inputline_dependencies(input_lines, all_inputlines) | ||
1021 | |||
1022 | return all_inputlines | ||
1023 | |||
1024 | |||
1025 | def run_program_lines(linelist, codedump): | ||
1026 | """ | ||
1027 | For a single file, print all the python code into a buf and execute it. | ||
1028 | """ | ||
1029 | buf = "\n".join(linelist) | ||
1030 | |||
1031 | if codedump: | ||
1032 | of = open("bspgen.out", "w") | ||
1033 | of.write(buf) | ||
1034 | of.close() | ||
1035 | exec buf | ||
1036 | |||
1037 | |||
1038 | def gen_target(files, context = None): | ||
1039 | """ | ||
1040 | Generate the python code for each file. | ||
1041 | """ | ||
1042 | for file in files: | ||
1043 | file.gen(context) | ||
1044 | |||
1045 | |||
1046 | def gen_program_header_lines(program_lines): | ||
1047 | """ | ||
1048 | Generate any imports we need. | ||
1049 | """ | ||
1050 | pass | ||
1051 | |||
1052 | |||
1053 | def gen_supplied_property_vals(properties, program_lines): | ||
1054 | """ | ||
1055 | Generate user-specified entries for input values instead of | ||
1056 | generating input prompts. | ||
1057 | """ | ||
1058 | for name, val in properties.iteritems(): | ||
1059 | program_line = name + " = \"" + val + "\"" | ||
1060 | program_lines.append(program_line) | ||
1061 | |||
1062 | |||
1063 | def gen_initial_property_vals(input_lines, program_lines): | ||
1064 | """ | ||
1065 | Generate null or default entries for input values, so we don't | ||
1066 | have undefined variables. | ||
1067 | """ | ||
1068 | for line in input_lines: | ||
1069 | if isinstance(line, InputLineGroup): | ||
1070 | gen_initial_property_vals(line.group, program_lines) | ||
1071 | continue | ||
1072 | |||
1073 | if isinstance(line, InputLine): | ||
1074 | try: | ||
1075 | name = line.props["name"] | ||
1076 | try: | ||
1077 | default_val = "\"" + line.props["default"] + "\"" | ||
1078 | except: | ||
1079 | default_val = "\"\"" | ||
1080 | program_line = name + " = " + default_val | ||
1081 | program_lines.append(program_line) | ||
1082 | except KeyError: | ||
1083 | pass | ||
1084 | |||
1085 | |||
1086 | def gen_program_input_lines(input_lines, program_lines, context, in_group = False): | ||
1087 | """ | ||
1088 | Generate only the input lines used for prompting the user. For | ||
1089 | that, we only have input lines and CodeLines that affect the next | ||
1090 | input line. | ||
1091 | """ | ||
1092 | indent = new_indent = 0 | ||
1093 | |||
1094 | for line in input_lines: | ||
1095 | if isinstance(line, InputLineGroup): | ||
1096 | gen_program_input_lines(line.group, program_lines, context, True) | ||
1097 | continue | ||
1098 | if not line.line.strip(): | ||
1099 | continue | ||
1100 | |||
1101 | genline = line.gen(context) | ||
1102 | if not genline: | ||
1103 | continue | ||
1104 | if genline.endswith(":"): | ||
1105 | new_indent += 1 | ||
1106 | else: | ||
1107 | if indent > 1 or (not in_group and indent): | ||
1108 | new_indent -= 1 | ||
1109 | |||
1110 | line.generated_line = (indent * INDENT_STR) + genline | ||
1111 | program_lines.append(line.generated_line) | ||
1112 | |||
1113 | indent = new_indent | ||
1114 | |||
1115 | |||
1116 | def gen_program_lines(target_files, program_lines): | ||
1117 | """ | ||
1118 | Generate the program lines that make up the BSP generation | ||
1119 | program. This appends the generated lines of all target_files to | ||
1120 | program_lines, and skips input lines, which are dealt with | ||
1121 | separately, or omitted. | ||
1122 | """ | ||
1123 | for file in target_files: | ||
1124 | if file.filename.endswith("noinstall"): | ||
1125 | continue | ||
1126 | |||
1127 | for line in file.expanded_lines: | ||
1128 | if isinstance(line, InputLine): | ||
1129 | continue | ||
1130 | if line.discard: | ||
1131 | continue | ||
1132 | |||
1133 | program_lines.append(line.generated_line) | ||
1134 | |||
1135 | |||
1136 | def create_context(machine, arch, scripts_path): | ||
1137 | """ | ||
1138 | Create a context object for use in deferred function invocation. | ||
1139 | """ | ||
1140 | context = {} | ||
1141 | |||
1142 | context["machine"] = machine | ||
1143 | context["arch"] = arch | ||
1144 | context["scripts_path"] = scripts_path | ||
1145 | |||
1146 | return context | ||
1147 | |||
1148 | |||
1149 | def capture_context(context): | ||
1150 | """ | ||
1151 | Create a context object for use in deferred function invocation. | ||
1152 | """ | ||
1153 | captured_context = {} | ||
1154 | |||
1155 | captured_context["machine"] = context["machine"] | ||
1156 | captured_context["arch"] = context["arch"] | ||
1157 | captured_context["scripts_path"] = context["scripts_path"] | ||
1158 | |||
1159 | return captured_context | ||
1160 | |||
1161 | |||
1162 | def expand_targets(context, bsp_output_dir): | ||
1163 | """ | ||
1164 | Expand all the tags in both the common and machine-specific | ||
1165 | 'targets'. | ||
1166 | """ | ||
1167 | target_files = [] | ||
1168 | |||
1169 | machine = context["machine"] | ||
1170 | arch = context["arch"] | ||
1171 | scripts_path = context["scripts_path"] | ||
1172 | |||
1173 | lib_path = scripts_path + '/lib' | ||
1174 | bsp_path = lib_path + '/bsp' | ||
1175 | arch_path = bsp_path + '/substrate/target/arch' | ||
1176 | |||
1177 | common = os.path.join(arch_path, "common") | ||
1178 | expand_target(common, target_files, bsp_output_dir) | ||
1179 | |||
1180 | arches = os.listdir(arch_path) | ||
1181 | if arch not in arches or arch == "common": | ||
1182 | print "Invalid karch, exiting\n" | ||
1183 | sys.exit(1) | ||
1184 | |||
1185 | target = os.path.join(arch_path, arch) | ||
1186 | expand_target(target, target_files, bsp_output_dir) | ||
1187 | |||
1188 | gen_target(target_files, context) | ||
1189 | |||
1190 | return target_files | ||
1191 | |||
1192 | |||
1193 | def yocto_bsp_create(machine, arch, scripts_path, bsp_output_dir, codedump, properties_file): | ||
1194 | """ | ||
1195 | Create bsp | ||
1196 | |||
1197 | machine - user-defined machine name | ||
1198 | arch - the arch the bsp will be based on, must be one in | ||
1199 | scripts/lib/bsp/substrate/target/arch | ||
1200 | scripts_path - absolute path to yocto /scripts dir | ||
1201 | bsp_output_dir - dirname to create for BSP | ||
1202 | codedump - dump generated code to bspgen.out | ||
1203 | properties_file - use values from here if nonempty i.e no prompting | ||
1204 | """ | ||
1205 | if os.path.exists(bsp_output_dir): | ||
1206 | print "\nBSP output dir already exists, exiting. (%s)" % bsp_output_dir | ||
1207 | sys.exit(1) | ||
1208 | |||
1209 | properties = None | ||
1210 | |||
1211 | if properties_file: | ||
1212 | try: | ||
1213 | infile = open(properties_file, "r") | ||
1214 | except IOError: | ||
1215 | print "Couldn't open properties file %s for reading, exiting" % properties_file | ||
1216 | sys.exit(1) | ||
1217 | |||
1218 | properties = json.load(infile) | ||
1219 | |||
1220 | os.mkdir(bsp_output_dir) | ||
1221 | |||
1222 | context = create_context(machine, arch, scripts_path) | ||
1223 | target_files = expand_targets(context, bsp_output_dir) | ||
1224 | |||
1225 | if not properties: | ||
1226 | input_lines = gather_inputlines(target_files) | ||
1227 | |||
1228 | program_lines = [] | ||
1229 | |||
1230 | gen_program_header_lines(program_lines) | ||
1231 | |||
1232 | if properties: | ||
1233 | gen_supplied_property_vals(properties, program_lines) | ||
1234 | else: | ||
1235 | gen_initial_property_vals(input_lines, program_lines) | ||
1236 | |||
1237 | gen_program_machine_lines(machine, program_lines) | ||
1238 | |||
1239 | if not properties: | ||
1240 | gen_program_input_lines(input_lines, program_lines, context) | ||
1241 | |||
1242 | gen_program_lines(target_files, program_lines) | ||
1243 | |||
1244 | run_program_lines(program_lines, codedump) | ||
1245 | |||
1246 | print "New %s BSP created in %s" % (arch, bsp_output_dir) | ||
1247 | |||
1248 | |||
1249 | def print_dict(items, indent = 0): | ||
1250 | """ | ||
1251 | Print the values in a possibly nested dictionary. | ||
1252 | """ | ||
1253 | for key, val in items.iteritems(): | ||
1254 | print " "*indent + "\"%s\" :" % key, | ||
1255 | if type(val) == dict: | ||
1256 | print "{" | ||
1257 | print_dict(val, indent + 1) | ||
1258 | print " "*indent + "}" | ||
1259 | else: | ||
1260 | print "%s" % val | ||
1261 | |||
1262 | |||
1263 | def get_properties(input_lines): | ||
1264 | """ | ||
1265 | Get the complete set of properties for all the input items in the | ||
1266 | BSP, as a possibly nested dictionary. | ||
1267 | """ | ||
1268 | properties = {} | ||
1269 | |||
1270 | for line in input_lines: | ||
1271 | if isinstance(line, InputLineGroup): | ||
1272 | statement = line.group[0].line | ||
1273 | group_properties = get_properties(line.group) | ||
1274 | properties[statement] = group_properties | ||
1275 | continue | ||
1276 | |||
1277 | if not isinstance(line, InputLine): | ||
1278 | continue | ||
1279 | |||
1280 | if isinstance(line, ChoiceInputLine): | ||
1281 | continue | ||
1282 | |||
1283 | props = line.props | ||
1284 | item = {} | ||
1285 | name = props["name"] | ||
1286 | for key, val in props.items(): | ||
1287 | if not key == "name": | ||
1288 | item[key] = val | ||
1289 | properties[name] = item | ||
1290 | |||
1291 | return properties | ||
1292 | |||
1293 | |||
1294 | def yocto_bsp_list_properties(arch, scripts_path, properties_file): | ||
1295 | """ | ||
1296 | List the complete set of properties for all the input items in the | ||
1297 | BSP. If properties_file is non-null, write the complete set of | ||
1298 | properties as a nested JSON object corresponding to a possibly | ||
1299 | nested dictionary. | ||
1300 | """ | ||
1301 | context = create_context("unused", arch, scripts_path) | ||
1302 | target_files = expand_targets(context, "unused") | ||
1303 | |||
1304 | input_lines = gather_inputlines(target_files) | ||
1305 | |||
1306 | properties = get_properties(input_lines) | ||
1307 | if properties_file: | ||
1308 | try: | ||
1309 | of = open(properties_file, "w") | ||
1310 | except IOError: | ||
1311 | print "Couldn't open properties file %s for writing, exiting" % properties_file | ||
1312 | sys.exit(1) | ||
1313 | |||
1314 | json.dump(properties, of) | ||
1315 | |||
1316 | print_dict(properties) | ||
1317 | |||
1318 | |||
1319 | def find_input_line(name, input_lines): | ||
1320 | """ | ||
1321 | Find the input line with the specified name. | ||
1322 | """ | ||
1323 | for line in input_lines: | ||
1324 | if isinstance(line, InputLineGroup): | ||
1325 | l = find_input_line(name, line.group) | ||
1326 | if l: | ||
1327 | return l | ||
1328 | |||
1329 | if isinstance(line, InputLine): | ||
1330 | try: | ||
1331 | if line.props["name"] == name: | ||
1332 | return line | ||
1333 | except KeyError: | ||
1334 | pass | ||
1335 | |||
1336 | return None | ||
1337 | |||
1338 | |||
1339 | def print_values(type, values_list): | ||
1340 | """ | ||
1341 | Print the values in the given list of values. | ||
1342 | """ | ||
1343 | if type == "choicelist": | ||
1344 | for value in values_list: | ||
1345 | print "[\"%s\", \"%s\"]" % (value[0], value[1]) | ||
1346 | elif type == "boolean": | ||
1347 | for value in values_list: | ||
1348 | print "[\"%s\", \"%s\"]" % (value[0], value[1]) | ||
1349 | |||
1350 | |||
1351 | def yocto_bsp_list_property_values(arch, property, scripts_path, properties_file): | ||
1352 | """ | ||
1353 | List the possible values for a given input property. If | ||
1354 | properties_file is non-null, write the complete set of properties | ||
1355 | as a JSON object corresponding to an array of possible values. | ||
1356 | """ | ||
1357 | context = create_context("unused", arch, scripts_path) | ||
1358 | context["name"] = property | ||
1359 | |||
1360 | target_files = expand_targets(context, "unused") | ||
1361 | |||
1362 | input_lines = gather_inputlines(target_files) | ||
1363 | |||
1364 | properties = get_properties(input_lines) | ||
1365 | |||
1366 | input_line = find_input_line(property, input_lines) | ||
1367 | if not input_line: | ||
1368 | print "Couldn't find values for property %s" % property | ||
1369 | return | ||
1370 | |||
1371 | values_list = [] | ||
1372 | |||
1373 | type = input_line.props["type"] | ||
1374 | if type == "boolean": | ||
1375 | values_list.append(["y", "n"]) | ||
1376 | elif type == "choicelist" or type == "checklist": | ||
1377 | try: | ||
1378 | gen_fn = input_line.props["gen"] | ||
1379 | values_list = input_line.gen_choices_list(context, False) | ||
1380 | except KeyError: | ||
1381 | for choice in input_line.choices: | ||
1382 | choicepair = [] | ||
1383 | choicepair.append(choice.val) | ||
1384 | choicepair.append(choice.desc) | ||
1385 | values_list.append(choicepair) | ||
1386 | |||
1387 | if properties_file: | ||
1388 | try: | ||
1389 | of = open(properties_file, "w") | ||
1390 | except IOError: | ||
1391 | print "Couldn't open properties file %s for writing, exiting" % properties_file | ||
1392 | sys.exit(1) | ||
1393 | |||
1394 | json.dump(values_list, of) | ||
1395 | |||
1396 | print_values(type, values_list) | ||
1397 | |||
1398 | |||
1399 | def yocto_bsp_list(args, scripts_path, properties_file): | ||
1400 | """ | ||
1401 | Print available architectures, or the complete list of properties | ||
1402 | defined by the BSP, or the possible values for a particular BSP | ||
1403 | property. | ||
1404 | """ | ||
1405 | if len(args) < 1: | ||
1406 | return False | ||
1407 | |||
1408 | if args[0] == "karch": | ||
1409 | lib_path = scripts_path + '/lib' | ||
1410 | bsp_path = lib_path + '/bsp' | ||
1411 | arch_path = bsp_path + '/substrate/target/arch' | ||
1412 | print "Architectures available:" | ||
1413 | for arch in os.listdir(arch_path): | ||
1414 | if arch == "common": | ||
1415 | continue | ||
1416 | print " %s" % arch | ||
1417 | return True | ||
1418 | else: | ||
1419 | arch = args[0] | ||
1420 | |||
1421 | if len(args) < 2 or len(args) > 3: | ||
1422 | return False | ||
1423 | |||
1424 | if len(args) == 2: | ||
1425 | if args[1] == "properties": | ||
1426 | yocto_bsp_list_properties(arch, scripts_path, properties_file) | ||
1427 | else: | ||
1428 | return False | ||
1429 | |||
1430 | if len(args) == 3: | ||
1431 | if args[1] == "property": | ||
1432 | yocto_bsp_list_property_values(arch, args[2], scripts_path, properties_file) | ||
1433 | else: | ||
1434 | return False | ||
1435 | |||
1436 | return True | ||