diff options
Diffstat (limited to 'scripts/lib/bsp/engine.py')
-rw-r--r-- | scripts/lib/bsp/engine.py | 1947 |
1 files changed, 1947 insertions, 0 deletions
diff --git a/scripts/lib/bsp/engine.py b/scripts/lib/bsp/engine.py new file mode 100644 index 0000000000..7d6be239da --- /dev/null +++ b/scripts/lib/bsp/engine.py | |||
@@ -0,0 +1,1947 @@ | |||
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 | import subprocess | ||
41 | import shutil | ||
42 | |||
43 | class Line(): | ||
44 | """ | ||
45 | Generic (abstract) container representing a line that will appear | ||
46 | in the BSP-generating program. | ||
47 | """ | ||
48 | __metaclass__ = ABCMeta | ||
49 | |||
50 | def __init__(self, line): | ||
51 | self.line = line | ||
52 | self.generated_line = "" | ||
53 | self.prio = sys.maxint | ||
54 | self.discard = False | ||
55 | |||
56 | @abstractmethod | ||
57 | def gen(self, context = None): | ||
58 | """ | ||
59 | Generate the final executable line that will appear in the | ||
60 | BSP-generation program. | ||
61 | """ | ||
62 | pass | ||
63 | |||
64 | def escape(self, line): | ||
65 | """ | ||
66 | Escape single and double quotes and backslashes until I find | ||
67 | something better (re.escape() escapes way too much). | ||
68 | """ | ||
69 | return line.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") | ||
70 | |||
71 | def parse_error(self, msg, lineno, line): | ||
72 | raise SyntaxError("%s: %s" % (msg, line)) | ||
73 | |||
74 | |||
75 | class NormalLine(Line): | ||
76 | """ | ||
77 | Container for normal (non-tag) lines. | ||
78 | """ | ||
79 | def __init__(self, line): | ||
80 | Line.__init__(self, line) | ||
81 | self.is_filename = False | ||
82 | self.is_dirname = False | ||
83 | self.out_filebase = None | ||
84 | |||
85 | def gen(self, context = None): | ||
86 | if self.is_filename: | ||
87 | line = "current_file = \"" + os.path.join(self.out_filebase, self.escape(self.line)) + "\"; of = open(current_file, \"w\")" | ||
88 | elif self.is_dirname: | ||
89 | dirname = os.path.join(self.out_filebase, self.escape(self.line)) | ||
90 | line = "if not os.path.exists(\"" + dirname + "\"): os.mkdir(\"" + dirname + "\")" | ||
91 | else: | ||
92 | line = "of.write(\"" + self.escape(self.line) + "\\n\")" | ||
93 | return line | ||
94 | |||
95 | |||
96 | class CodeLine(Line): | ||
97 | """ | ||
98 | Container for Python code tag lines. | ||
99 | """ | ||
100 | def __init__(self, line): | ||
101 | Line.__init__(self, line) | ||
102 | |||
103 | def gen(self, context = None): | ||
104 | return self.line | ||
105 | |||
106 | |||
107 | class Assignment: | ||
108 | """ | ||
109 | Representation of everything we know about {{=name }} tags. | ||
110 | Instances of these are used by Assignment lines. | ||
111 | """ | ||
112 | def __init__(self, start, end, name): | ||
113 | self.start = start | ||
114 | self.end = end | ||
115 | self.name = name | ||
116 | |||
117 | |||
118 | class AssignmentLine(NormalLine): | ||
119 | """ | ||
120 | Container for normal lines containing assignment tags. Assignment | ||
121 | tags must be in ascending order of 'start' value. | ||
122 | """ | ||
123 | def __init__(self, line): | ||
124 | NormalLine.__init__(self, line) | ||
125 | self.assignments = [] | ||
126 | |||
127 | def add_assignment(self, start, end, name): | ||
128 | self.assignments.append(Assignment(start, end, name)) | ||
129 | |||
130 | def gen(self, context = None): | ||
131 | line = self.escape(self.line) | ||
132 | |||
133 | for assignment in self.assignments: | ||
134 | replacement = "\" + " + assignment.name + " + \"" | ||
135 | idx = line.find(ASSIGN_TAG) | ||
136 | line = line[:idx] + replacement + line[idx + assignment.end - assignment.start:] | ||
137 | if self.is_filename: | ||
138 | return "current_file = \"" + os.path.join(self.out_filebase, line) + "\"; of = open(current_file, \"w\")" | ||
139 | elif self.is_dirname: | ||
140 | dirname = os.path.join(self.out_filebase, line) | ||
141 | return "if not os.path.exists(\"" + dirname + "\"): os.mkdir(\"" + dirname + "\")" | ||
142 | else: | ||
143 | return "of.write(\"" + line + "\\n\")" | ||
144 | |||
145 | |||
146 | class InputLine(Line): | ||
147 | """ | ||
148 | Base class for Input lines. | ||
149 | """ | ||
150 | def __init__(self, props, tag, lineno): | ||
151 | Line.__init__(self, tag) | ||
152 | self.props = props | ||
153 | self.lineno = lineno | ||
154 | |||
155 | try: | ||
156 | self.prio = int(props["prio"]) | ||
157 | except KeyError: | ||
158 | self.prio = sys.maxint | ||
159 | |||
160 | def gen(self, context = None): | ||
161 | try: | ||
162 | depends_on = self.props["depends-on"] | ||
163 | try: | ||
164 | depends_on_val = self.props["depends-on-val"] | ||
165 | except KeyError: | ||
166 | self.parse_error("No 'depends-on-val' for 'depends-on' property", | ||
167 | self.lineno, self.line) | ||
168 | except KeyError: | ||
169 | pass | ||
170 | |||
171 | |||
172 | class EditBoxInputLine(InputLine): | ||
173 | """ | ||
174 | Base class for 'editbox' Input lines. | ||
175 | |||
176 | props: | ||
177 | name: example - "Load address" | ||
178 | msg: example - "Please enter the load address" | ||
179 | result: | ||
180 | Sets the value of the variable specified by 'name' to | ||
181 | whatever the user typed. | ||
182 | """ | ||
183 | def __init__(self, props, tag, lineno): | ||
184 | InputLine.__init__(self, props, tag, lineno) | ||
185 | |||
186 | def gen(self, context = None): | ||
187 | InputLine.gen(self, context) | ||
188 | name = self.props["name"] | ||
189 | if not name: | ||
190 | self.parse_error("No input 'name' property found", | ||
191 | self.lineno, self.line) | ||
192 | msg = self.props["msg"] | ||
193 | if not msg: | ||
194 | self.parse_error("No input 'msg' property found", | ||
195 | self.lineno, self.line) | ||
196 | |||
197 | try: | ||
198 | default_choice = self.props["default"] | ||
199 | except KeyError: | ||
200 | default_choice = "" | ||
201 | |||
202 | msg += " [default: " + default_choice + "]" | ||
203 | |||
204 | line = name + " = default(raw_input(\"" + msg + " \"), " + name + ")" | ||
205 | |||
206 | return line | ||
207 | |||
208 | |||
209 | class GitRepoEditBoxInputLine(EditBoxInputLine): | ||
210 | """ | ||
211 | Base class for 'editbox' Input lines for user input of remote git | ||
212 | repos. This class verifies the existence and connectivity of the | ||
213 | specified git repo. | ||
214 | |||
215 | props: | ||
216 | name: example - "Load address" | ||
217 | msg: example - "Please enter the load address" | ||
218 | result: | ||
219 | Sets the value of the variable specified by 'name' to | ||
220 | whatever the user typed. | ||
221 | """ | ||
222 | def __init__(self, props, tag, lineno): | ||
223 | EditBoxInputLine.__init__(self, props, tag, lineno) | ||
224 | |||
225 | def gen(self, context = None): | ||
226 | EditBoxInputLine.gen(self, context) | ||
227 | name = self.props["name"] | ||
228 | if not name: | ||
229 | self.parse_error("No input 'name' property found", | ||
230 | self.lineno, self.line) | ||
231 | msg = self.props["msg"] | ||
232 | if not msg: | ||
233 | self.parse_error("No input 'msg' property found", | ||
234 | self.lineno, self.line) | ||
235 | |||
236 | try: | ||
237 | default_choice = self.props["default"] | ||
238 | except KeyError: | ||
239 | default_choice = "" | ||
240 | |||
241 | msg += " [default: " + default_choice + "]" | ||
242 | |||
243 | line = name + " = get_verified_git_repo(\"" + msg + "\"," + name + ")" | ||
244 | |||
245 | return line | ||
246 | |||
247 | |||
248 | class FileEditBoxInputLine(EditBoxInputLine): | ||
249 | """ | ||
250 | Base class for 'editbox' Input lines for user input of existing | ||
251 | files. This class verifies the existence of the specified file. | ||
252 | |||
253 | props: | ||
254 | name: example - "Load address" | ||
255 | msg: example - "Please enter the load address" | ||
256 | result: | ||
257 | Sets the value of the variable specified by 'name' to | ||
258 | whatever the user typed. | ||
259 | """ | ||
260 | def __init__(self, props, tag, lineno): | ||
261 | EditBoxInputLine.__init__(self, props, tag, lineno) | ||
262 | |||
263 | def gen(self, context = None): | ||
264 | EditBoxInputLine.gen(self, context) | ||
265 | name = self.props["name"] | ||
266 | if not name: | ||
267 | self.parse_error("No input 'name' property found", | ||
268 | self.lineno, self.line) | ||
269 | msg = self.props["msg"] | ||
270 | if not msg: | ||
271 | self.parse_error("No input 'msg' property found", | ||
272 | self.lineno, self.line) | ||
273 | |||
274 | try: | ||
275 | default_choice = self.props["default"] | ||
276 | except KeyError: | ||
277 | default_choice = "" | ||
278 | |||
279 | msg += " [default: " + default_choice + "]" | ||
280 | |||
281 | line = name + " = get_verified_file(\"" + msg + "\"," + name + ", True)" | ||
282 | |||
283 | return line | ||
284 | |||
285 | |||
286 | class BooleanInputLine(InputLine): | ||
287 | """ | ||
288 | Base class for boolean Input lines. | ||
289 | props: | ||
290 | name: example - "keyboard" | ||
291 | msg: example - "Got keyboard?" | ||
292 | result: | ||
293 | Sets the value of the variable specified by 'name' to "yes" or "no" | ||
294 | example - keyboard = "yes" | ||
295 | """ | ||
296 | def __init__(self, props, tag, lineno): | ||
297 | InputLine.__init__(self, props, tag, lineno) | ||
298 | |||
299 | def gen(self, context = None): | ||
300 | InputLine.gen(self, context) | ||
301 | name = self.props["name"] | ||
302 | if not name: | ||
303 | self.parse_error("No input 'name' property found", | ||
304 | self.lineno, self.line) | ||
305 | msg = self.props["msg"] | ||
306 | if not msg: | ||
307 | self.parse_error("No input 'msg' property found", | ||
308 | self.lineno, self.line) | ||
309 | |||
310 | try: | ||
311 | default_choice = self.props["default"] | ||
312 | except KeyError: | ||
313 | default_choice = "" | ||
314 | |||
315 | msg += " [default: " + default_choice + "]" | ||
316 | |||
317 | line = name + " = boolean(raw_input(\"" + msg + " \"), " + name + ")" | ||
318 | |||
319 | return line | ||
320 | |||
321 | |||
322 | class ListInputLine(InputLine): | ||
323 | """ | ||
324 | Base class for List-based Input lines. e.g. Choicelist, Checklist. | ||
325 | """ | ||
326 | __metaclass__ = ABCMeta | ||
327 | |||
328 | def __init__(self, props, tag, lineno): | ||
329 | InputLine.__init__(self, props, tag, lineno) | ||
330 | self.choices = [] | ||
331 | |||
332 | def gen_choicepair_list(self): | ||
333 | """Generate a list of 2-item val:desc lists from self.choices.""" | ||
334 | if not self.choices: | ||
335 | return None | ||
336 | |||
337 | choicepair_list = list() | ||
338 | |||
339 | for choice in self.choices: | ||
340 | choicepair = [] | ||
341 | choicepair.append(choice.val) | ||
342 | choicepair.append(choice.desc) | ||
343 | choicepair_list.append(choicepair) | ||
344 | |||
345 | return choicepair_list | ||
346 | |||
347 | def gen_degenerate_choicepair_list(self, choices): | ||
348 | """Generate a list of 2-item val:desc with val=desc from passed-in choices.""" | ||
349 | choicepair_list = list() | ||
350 | |||
351 | for choice in choices: | ||
352 | choicepair = [] | ||
353 | choicepair.append(choice) | ||
354 | choicepair.append(choice) | ||
355 | choicepair_list.append(choicepair) | ||
356 | |||
357 | return choicepair_list | ||
358 | |||
359 | def exec_listgen_fn(self, context = None): | ||
360 | """ | ||
361 | Execute the list-generating function contained as a string in | ||
362 | the "gen" property. | ||
363 | """ | ||
364 | retval = None | ||
365 | try: | ||
366 | fname = self.props["gen"] | ||
367 | modsplit = fname.split('.') | ||
368 | mod_fn = modsplit.pop() | ||
369 | mod = '.'.join(modsplit) | ||
370 | |||
371 | __import__(mod) | ||
372 | # python 2.7 has a better way to do this using importlib.import_module | ||
373 | m = sys.modules[mod] | ||
374 | |||
375 | fn = getattr(m, mod_fn) | ||
376 | if not fn: | ||
377 | self.parse_error("couldn't load function specified for 'gen' property ", | ||
378 | self.lineno, self.line) | ||
379 | retval = fn(context) | ||
380 | if not retval: | ||
381 | self.parse_error("function specified for 'gen' property returned nothing ", | ||
382 | self.lineno, self.line) | ||
383 | except KeyError: | ||
384 | pass | ||
385 | |||
386 | return retval | ||
387 | |||
388 | def gen_choices_str(self, choicepairs): | ||
389 | """ | ||
390 | Generate a numbered list of choices from a list of choicepairs | ||
391 | for display to the user. | ||
392 | """ | ||
393 | choices_str = "" | ||
394 | |||
395 | for i, choicepair in enumerate(choicepairs): | ||
396 | choices_str += "\t" + str(i + 1) + ") " + choicepair[1] + "\n" | ||
397 | |||
398 | return choices_str | ||
399 | |||
400 | def gen_choices_val_str(self, choicepairs): | ||
401 | """ | ||
402 | Generate an array of choice values corresponding to the | ||
403 | numbered list generated by gen_choices_str(). | ||
404 | """ | ||
405 | choices_val_list = "[" | ||
406 | |||
407 | for i, choicepair in enumerate(choicepairs): | ||
408 | choices_val_list += "\"" + choicepair[0] + "\"," | ||
409 | choices_val_list += "]" | ||
410 | |||
411 | return choices_val_list | ||
412 | |||
413 | def gen_choices_val_list(self, choicepairs): | ||
414 | """ | ||
415 | Generate an array of choice values corresponding to the | ||
416 | numbered list generated by gen_choices_str(). | ||
417 | """ | ||
418 | choices_val_list = [] | ||
419 | |||
420 | for i, choicepair in enumerate(choicepairs): | ||
421 | choices_val_list.append(choicepair[0]) | ||
422 | |||
423 | return choices_val_list | ||
424 | |||
425 | def gen_choices_list(self, context = None, checklist = False): | ||
426 | """ | ||
427 | Generate an array of choice values corresponding to the | ||
428 | numbered list generated by gen_choices_str(). | ||
429 | """ | ||
430 | choices = self.exec_listgen_fn(context) | ||
431 | if choices: | ||
432 | if len(choices) == 0: | ||
433 | self.parse_error("No entries available for input list", | ||
434 | self.lineno, self.line) | ||
435 | choicepairs = self.gen_degenerate_choicepair_list(choices) | ||
436 | else: | ||
437 | if len(self.choices) == 0: | ||
438 | self.parse_error("No entries available for input list", | ||
439 | self.lineno, self.line) | ||
440 | choicepairs = self.gen_choicepair_list() | ||
441 | |||
442 | return choicepairs | ||
443 | |||
444 | def gen_choices(self, context = None, checklist = False): | ||
445 | """ | ||
446 | Generate an array of choice values corresponding to the | ||
447 | numbered list generated by gen_choices_str(), display it to | ||
448 | the user, and process the result. | ||
449 | """ | ||
450 | msg = self.props["msg"] | ||
451 | if not msg: | ||
452 | self.parse_error("No input 'msg' property found", | ||
453 | self.lineno, self.line) | ||
454 | |||
455 | try: | ||
456 | default_choice = self.props["default"] | ||
457 | except KeyError: | ||
458 | default_choice = "" | ||
459 | |||
460 | msg += " [default: " + default_choice + "]" | ||
461 | |||
462 | choicepairs = self.gen_choices_list(context, checklist) | ||
463 | |||
464 | choices_str = self.gen_choices_str(choicepairs) | ||
465 | choices_val_list = self.gen_choices_val_list(choicepairs) | ||
466 | if checklist: | ||
467 | choiceval = default(find_choicevals(raw_input(msg + "\n" + choices_str), choices_val_list), default_choice) | ||
468 | else: | ||
469 | choiceval = default(find_choiceval(raw_input(msg + "\n" + choices_str), choices_val_list), default_choice) | ||
470 | |||
471 | return choiceval | ||
472 | |||
473 | |||
474 | def find_choiceval(choice_str, choice_list): | ||
475 | """ | ||
476 | Take number as string and return val string from choice_list, | ||
477 | empty string if oob. choice_list is a simple python list. | ||
478 | """ | ||
479 | choice_val = "" | ||
480 | |||
481 | try: | ||
482 | choice_idx = int(choice_str) | ||
483 | if choice_idx <= len(choice_list): | ||
484 | choice_idx -= 1 | ||
485 | choice_val = choice_list[choice_idx] | ||
486 | except ValueError: | ||
487 | pass | ||
488 | |||
489 | return choice_val | ||
490 | |||
491 | |||
492 | def find_choicevals(choice_str, choice_list): | ||
493 | """ | ||
494 | Take numbers as space-separated string and return vals list from | ||
495 | choice_list, empty list if oob. choice_list is a simple python | ||
496 | list. | ||
497 | """ | ||
498 | choice_vals = [] | ||
499 | |||
500 | choices = choice_str.split() | ||
501 | for choice in choices: | ||
502 | choice_vals.append(find_choiceval(choice, choice_list)) | ||
503 | |||
504 | return choice_vals | ||
505 | |||
506 | |||
507 | def default(input_str, name): | ||
508 | """ | ||
509 | Return default if no input_str, otherwise stripped input_str. | ||
510 | """ | ||
511 | if not input_str: | ||
512 | return name | ||
513 | |||
514 | return input_str.strip() | ||
515 | |||
516 | |||
517 | def verify_git_repo(giturl): | ||
518 | """ | ||
519 | Verify that the giturl passed in can be connected to. This can be | ||
520 | used as a check for the existence of the given repo and/or basic | ||
521 | git remote connectivity. | ||
522 | |||
523 | Returns True if the connection was successful, fals otherwise | ||
524 | """ | ||
525 | if not giturl: | ||
526 | return False | ||
527 | |||
528 | gitcmd = "git ls-remote %s > /dev/null 2>&1" % (giturl) | ||
529 | rc = subprocess.call(gitcmd, shell=True) | ||
530 | if rc == 0: | ||
531 | return True | ||
532 | |||
533 | return False | ||
534 | |||
535 | |||
536 | def get_verified_git_repo(input_str, name): | ||
537 | """ | ||
538 | Return git repo if verified, otherwise loop forever asking user | ||
539 | for filename. | ||
540 | """ | ||
541 | msg = input_str.strip() + " " | ||
542 | |||
543 | giturl = default(raw_input(msg), name) | ||
544 | |||
545 | while True: | ||
546 | if verify_git_repo(giturl): | ||
547 | return giturl | ||
548 | giturl = default(raw_input(msg), name) | ||
549 | |||
550 | |||
551 | def get_verified_file(input_str, name, filename_can_be_null): | ||
552 | """ | ||
553 | Return filename if the file exists, otherwise loop forever asking | ||
554 | user for filename. | ||
555 | """ | ||
556 | msg = input_str.strip() + " " | ||
557 | |||
558 | filename = default(raw_input(msg), name) | ||
559 | |||
560 | while True: | ||
561 | if not filename and filename_can_be_null: | ||
562 | return filename | ||
563 | if os.path.isfile(filename): | ||
564 | return filename | ||
565 | filename = default(raw_input(msg), name) | ||
566 | |||
567 | |||
568 | def replace_file(replace_this, with_this): | ||
569 | """ | ||
570 | Replace the given file with the contents of filename, retaining | ||
571 | the original filename. | ||
572 | """ | ||
573 | try: | ||
574 | replace_this.close() | ||
575 | shutil.copy(with_this, replace_this.name) | ||
576 | except IOError: | ||
577 | pass | ||
578 | |||
579 | |||
580 | def boolean(input_str, name): | ||
581 | """ | ||
582 | Return lowercase version of first char in string, or value in name. | ||
583 | """ | ||
584 | if not input_str: | ||
585 | return name | ||
586 | |||
587 | str = input_str.lower().strip() | ||
588 | if str and str[0] == "y" or str[0] == "n": | ||
589 | return str[0] | ||
590 | else: | ||
591 | return name | ||
592 | |||
593 | |||
594 | def strip_base(input_str): | ||
595 | """ | ||
596 | strip '/base' off the end of input_str, so we can use 'base' in | ||
597 | the branch names we present to the user. | ||
598 | """ | ||
599 | if input_str and input_str.endswith("/base"): | ||
600 | return input_str[:-len("/base")] | ||
601 | return input_str.strip() | ||
602 | |||
603 | |||
604 | deferred_choices = {} | ||
605 | |||
606 | def gen_choices_defer(input_line, context, checklist = False): | ||
607 | """ | ||
608 | Save the context hashed the name of the input item, which will be | ||
609 | passed to the gen function later. | ||
610 | """ | ||
611 | name = input_line.props["name"] | ||
612 | |||
613 | try: | ||
614 | nameappend = input_line.props["nameappend"] | ||
615 | except KeyError: | ||
616 | nameappend = "" | ||
617 | |||
618 | try: | ||
619 | branches_base = input_line.props["branches_base"] | ||
620 | except KeyError: | ||
621 | branches_base = "" | ||
622 | |||
623 | filename = input_line.props["filename"] | ||
624 | |||
625 | closetag_start = filename.find(CLOSE_TAG) | ||
626 | |||
627 | if closetag_start != -1: | ||
628 | filename = filename[closetag_start + len(CLOSE_TAG):] | ||
629 | |||
630 | filename = filename.strip() | ||
631 | filename = os.path.splitext(filename)[0] | ||
632 | |||
633 | captured_context = capture_context(context) | ||
634 | context["filename"] = filename | ||
635 | captured_context["filename"] = filename | ||
636 | context["nameappend"] = nameappend | ||
637 | captured_context["nameappend"] = nameappend | ||
638 | context["branches_base"] = branches_base | ||
639 | captured_context["branches_base"] = branches_base | ||
640 | |||
641 | deferred_choice = (input_line, captured_context, checklist) | ||
642 | key = name + "_" + filename + "_" + nameappend | ||
643 | deferred_choices[key] = deferred_choice | ||
644 | |||
645 | |||
646 | def invoke_deferred_choices(name): | ||
647 | """ | ||
648 | Invoke the choice generation function using the context hashed by | ||
649 | 'name'. | ||
650 | """ | ||
651 | deferred_choice = deferred_choices[name] | ||
652 | input_line = deferred_choice[0] | ||
653 | context = deferred_choice[1] | ||
654 | checklist = deferred_choice[2] | ||
655 | |||
656 | context["name"] = name | ||
657 | |||
658 | choices = input_line.gen_choices(context, checklist) | ||
659 | |||
660 | return choices | ||
661 | |||
662 | |||
663 | class ChoicelistInputLine(ListInputLine): | ||
664 | """ | ||
665 | Base class for choicelist Input lines. | ||
666 | props: | ||
667 | name: example - "xserver_choice" | ||
668 | msg: example - "Please select an xserver for this machine" | ||
669 | result: | ||
670 | Sets the value of the variable specified by 'name' to whichever Choice was chosen | ||
671 | example - xserver_choice = "xserver_vesa" | ||
672 | """ | ||
673 | def __init__(self, props, tag, lineno): | ||
674 | ListInputLine.__init__(self, props, tag, lineno) | ||
675 | |||
676 | def gen(self, context = None): | ||
677 | InputLine.gen(self, context) | ||
678 | |||
679 | gen_choices_defer(self, context) | ||
680 | name = self.props["name"] | ||
681 | nameappend = context["nameappend"] | ||
682 | filename = context["filename"] | ||
683 | |||
684 | try: | ||
685 | default_choice = self.props["default"] | ||
686 | except KeyError: | ||
687 | default_choice = "" | ||
688 | |||
689 | line = name + " = default(invoke_deferred_choices(\"" + name + "_" + filename + "_" + nameappend + "\"), \"" + default_choice + "\")" | ||
690 | |||
691 | return line | ||
692 | |||
693 | |||
694 | class ListValInputLine(InputLine): | ||
695 | """ | ||
696 | Abstract base class for choice and checkbox Input lines. | ||
697 | """ | ||
698 | def __init__(self, props, tag, lineno): | ||
699 | InputLine.__init__(self, props, tag, lineno) | ||
700 | |||
701 | try: | ||
702 | self.val = self.props["val"] | ||
703 | except KeyError: | ||
704 | self.parse_error("No input 'val' property found", self.lineno, self.line) | ||
705 | |||
706 | try: | ||
707 | self.desc = self.props["msg"] | ||
708 | except KeyError: | ||
709 | self.parse_error("No input 'msg' property found", self.lineno, self.line) | ||
710 | |||
711 | |||
712 | class ChoiceInputLine(ListValInputLine): | ||
713 | """ | ||
714 | Base class for choicelist item Input lines. | ||
715 | """ | ||
716 | def __init__(self, props, tag, lineno): | ||
717 | ListValInputLine.__init__(self, props, tag, lineno) | ||
718 | |||
719 | def gen(self, context = None): | ||
720 | return None | ||
721 | |||
722 | |||
723 | class ChecklistInputLine(ListInputLine): | ||
724 | """ | ||
725 | Base class for checklist Input lines. | ||
726 | """ | ||
727 | def __init__(self, props, tag, lineno): | ||
728 | ListInputLine.__init__(self, props, tag, lineno) | ||
729 | |||
730 | def gen(self, context = None): | ||
731 | InputLine.gen(self, context) | ||
732 | |||
733 | gen_choices_defer(self, context, True) | ||
734 | name = self.props["name"] | ||
735 | nameappend = context["nameappend"] | ||
736 | filename = context["filename"] | ||
737 | |||
738 | try: | ||
739 | default_choice = self.props["default"] | ||
740 | except KeyError: | ||
741 | default_choice = "" | ||
742 | |||
743 | line = name + " = default(invoke_deferred_choices(\"" + name + "_" + filename + "_" + nameappend + "\"), \"" + default_choice + "\")" | ||
744 | |||
745 | return line | ||
746 | |||
747 | |||
748 | class CheckInputLine(ListValInputLine): | ||
749 | """ | ||
750 | Base class for checklist item Input lines. | ||
751 | """ | ||
752 | def __init__(self, props, tag, lineno): | ||
753 | ListValInputLine.__init__(self, props, tag, lineno) | ||
754 | |||
755 | def gen(self, context = None): | ||
756 | return None | ||
757 | |||
758 | |||
759 | dirname_substitutions = {} | ||
760 | |||
761 | class SubstrateBase(object): | ||
762 | """ | ||
763 | Base class for both expanded and unexpanded file and dir container | ||
764 | objects. | ||
765 | """ | ||
766 | def __init__(self, filename, filebase, out_filebase): | ||
767 | self.filename = filename | ||
768 | self.filebase = filebase | ||
769 | self.translated_filename = filename | ||
770 | self.out_filebase = out_filebase | ||
771 | self.raw_lines = [] | ||
772 | self.expanded_lines = [] | ||
773 | self.prev_choicelist = None | ||
774 | |||
775 | def parse_error(self, msg, lineno, line): | ||
776 | raise SyntaxError("%s: [%s: %d]: %s" % (msg, self.filename, lineno, line)) | ||
777 | |||
778 | def expand_input_tag(self, tag, lineno): | ||
779 | """ | ||
780 | Input tags consist of the word 'input' at the beginning, | ||
781 | followed by name:value property pairs which are converted into | ||
782 | a dictionary. | ||
783 | """ | ||
784 | propstr = tag[len(INPUT_TAG):] | ||
785 | |||
786 | props = dict(prop.split(":", 1) for prop in shlex.split(propstr)) | ||
787 | props["filename"] = self.filename | ||
788 | |||
789 | input_type = props[INPUT_TYPE_PROPERTY] | ||
790 | if not props[INPUT_TYPE_PROPERTY]: | ||
791 | self.parse_error("No input 'type' property found", lineno, tag) | ||
792 | |||
793 | if input_type == "boolean": | ||
794 | return BooleanInputLine(props, tag, lineno) | ||
795 | if input_type == "edit": | ||
796 | return EditBoxInputLine(props, tag, lineno) | ||
797 | if input_type == "edit-git-repo": | ||
798 | return GitRepoEditBoxInputLine(props, tag, lineno) | ||
799 | if input_type == "edit-file": | ||
800 | return FileEditBoxInputLine(props, tag, lineno) | ||
801 | elif input_type == "choicelist": | ||
802 | self.prev_choicelist = ChoicelistInputLine(props, tag, lineno) | ||
803 | return self.prev_choicelist | ||
804 | elif input_type == "choice": | ||
805 | if not self.prev_choicelist: | ||
806 | self.parse_error("Found 'choice' input tag but no previous choicelist", | ||
807 | lineno, tag) | ||
808 | choice = ChoiceInputLine(props, tag, lineno) | ||
809 | self.prev_choicelist.choices.append(choice) | ||
810 | return choice | ||
811 | elif input_type == "checklist": | ||
812 | return ChecklistInputLine(props, tag, lineno) | ||
813 | elif input_type == "check": | ||
814 | return CheckInputLine(props, tag, lineno) | ||
815 | |||
816 | def expand_assignment_tag(self, start, line, lineno): | ||
817 | """ | ||
818 | Expand all tags in a line. | ||
819 | """ | ||
820 | expanded_line = AssignmentLine(line.rstrip()) | ||
821 | |||
822 | while start != -1: | ||
823 | end = line.find(CLOSE_TAG, start) | ||
824 | if end == -1: | ||
825 | self.parse_error("No close tag found for assignment tag", lineno, line) | ||
826 | else: | ||
827 | name = line[start + len(ASSIGN_TAG):end].strip() | ||
828 | expanded_line.add_assignment(start, end + len(CLOSE_TAG), name) | ||
829 | start = line.find(ASSIGN_TAG, end) | ||
830 | |||
831 | return expanded_line | ||
832 | |||
833 | def expand_tag(self, line, lineno): | ||
834 | """ | ||
835 | Returns a processed tag line, or None if there was no tag | ||
836 | |||
837 | The rules for tags are very simple: | ||
838 | - No nested tags | ||
839 | - Tags start with {{ and end with }} | ||
840 | - An assign tag, {{=, can appear anywhere and will | ||
841 | be replaced with what the assignment evaluates to | ||
842 | - Any other tag occupies the whole line it is on | ||
843 | - if there's anything else on the tag line, it's an error | ||
844 | - if it starts with 'input', it's an input tag and | ||
845 | will only be used for prompting and setting variables | ||
846 | - anything else is straight Python | ||
847 | - tags are in effect only until the next blank line or tag or 'pass' tag | ||
848 | - we don't have indentation in tags, but we need some way to end a block | ||
849 | forcefully without blank lines or other tags - that's the 'pass' tag | ||
850 | - todo: implement pass tag | ||
851 | - directories and filenames can have tags as well, but only assignment | ||
852 | and 'if' code lines | ||
853 | - directories and filenames are the only case where normal tags can | ||
854 | coexist with normal text on the same 'line' | ||
855 | """ | ||
856 | start = line.find(ASSIGN_TAG) | ||
857 | if start != -1: | ||
858 | return self.expand_assignment_tag(start, line, lineno) | ||
859 | |||
860 | start = line.find(OPEN_TAG) | ||
861 | if start == -1: | ||
862 | return None | ||
863 | |||
864 | end = line.find(CLOSE_TAG, 0) | ||
865 | if end == -1: | ||
866 | self.parse_error("No close tag found for open tag", lineno, line) | ||
867 | |||
868 | tag = line[start + len(OPEN_TAG):end].strip() | ||
869 | |||
870 | if not tag.lstrip().startswith(INPUT_TAG): | ||
871 | return CodeLine(tag) | ||
872 | |||
873 | return self.expand_input_tag(tag, lineno) | ||
874 | |||
875 | def append_translated_filename(self, filename): | ||
876 | """ | ||
877 | Simply append filename to translated_filename | ||
878 | """ | ||
879 | self.translated_filename = os.path.join(self.translated_filename, filename) | ||
880 | |||
881 | def get_substituted_file_or_dir_name(self, first_line, tag): | ||
882 | """ | ||
883 | If file or dir names contain name substitutions, return the name | ||
884 | to substitute. Note that this is just the file or dirname and | ||
885 | doesn't include the path. | ||
886 | """ | ||
887 | filename = first_line.find(tag) | ||
888 | if filename != -1: | ||
889 | filename += len(tag) | ||
890 | substituted_filename = first_line[filename:].strip() | ||
891 | this = substituted_filename.find(" this") | ||
892 | if this != -1: | ||
893 | head, tail = os.path.split(self.filename) | ||
894 | substituted_filename = substituted_filename[:this + 1] + tail | ||
895 | if tag == DIRNAME_TAG: # get rid of .noinstall in dirname | ||
896 | substituted_filename = substituted_filename.split('.')[0] | ||
897 | |||
898 | return substituted_filename | ||
899 | |||
900 | def get_substituted_filename(self, first_line): | ||
901 | """ | ||
902 | If a filename contains a name substitution, return the name to | ||
903 | substitute. Note that this is just the filename and doesn't | ||
904 | include the path. | ||
905 | """ | ||
906 | return self.get_substituted_file_or_dir_name(first_line, FILENAME_TAG) | ||
907 | |||
908 | def get_substituted_dirname(self, first_line): | ||
909 | """ | ||
910 | If a dirname contains a name substitution, return the name to | ||
911 | substitute. Note that this is just the dirname and doesn't | ||
912 | include the path. | ||
913 | """ | ||
914 | return self.get_substituted_file_or_dir_name(first_line, DIRNAME_TAG) | ||
915 | |||
916 | def substitute_filename(self, first_line): | ||
917 | """ | ||
918 | Find the filename in first_line and append it to translated_filename. | ||
919 | """ | ||
920 | substituted_filename = self.get_substituted_filename(first_line) | ||
921 | self.append_translated_filename(substituted_filename); | ||
922 | |||
923 | def substitute_dirname(self, first_line): | ||
924 | """ | ||
925 | Find the dirname in first_line and append it to translated_filename. | ||
926 | """ | ||
927 | substituted_dirname = self.get_substituted_dirname(first_line) | ||
928 | self.append_translated_filename(substituted_dirname); | ||
929 | |||
930 | def is_filename_substitution(self, line): | ||
931 | """ | ||
932 | Do we have a filename subustition? | ||
933 | """ | ||
934 | if line.find(FILENAME_TAG) != -1: | ||
935 | return True | ||
936 | return False | ||
937 | |||
938 | def is_dirname_substitution(self, line): | ||
939 | """ | ||
940 | Do we have a dirname subustition? | ||
941 | """ | ||
942 | if line.find(DIRNAME_TAG) != -1: | ||
943 | return True | ||
944 | return False | ||
945 | |||
946 | def translate_dirname(self, first_line): | ||
947 | """ | ||
948 | Just save the first_line mapped by filename. The later pass | ||
949 | through the directories will look for a dirname.noinstall | ||
950 | match and grab the substitution line. | ||
951 | """ | ||
952 | dirname_substitutions[self.filename] = first_line | ||
953 | |||
954 | def translate_dirnames_in_path(self, path): | ||
955 | """ | ||
956 | Translate dirnames below this file or dir, not including tail. | ||
957 | dirname_substititions is keyed on actual untranslated filenames. | ||
958 | translated_path contains the subsititutions for each element. | ||
959 | """ | ||
960 | remainder = path[len(self.filebase)+1:] | ||
961 | translated_path = untranslated_path = self.filebase | ||
962 | |||
963 | untranslated_dirs = remainder.split(os.sep) | ||
964 | |||
965 | for dir in untranslated_dirs: | ||
966 | key = os.path.join(untranslated_path, dir + '.noinstall') | ||
967 | try: | ||
968 | first_line = dirname_substitutions[key] | ||
969 | except KeyError: | ||
970 | translated_path = os.path.join(translated_path, dir) | ||
971 | untranslated_path = os.path.join(untranslated_path, dir) | ||
972 | continue | ||
973 | substituted_dir = self.get_substituted_dirname(first_line) | ||
974 | translated_path = os.path.join(translated_path, substituted_dir) | ||
975 | untranslated_path = os.path.join(untranslated_path, dir) | ||
976 | |||
977 | return translated_path | ||
978 | |||
979 | def translate_file_or_dir_name(self): | ||
980 | """ | ||
981 | Originally we were allowed to use open/close/assign tags and python | ||
982 | code in the filename, which fit in nicely with the way we | ||
983 | processed the templates and generated code. Now that we can't | ||
984 | do that, we make those tags proper file contents and have this | ||
985 | pass substitute the nice but non-functional names with those | ||
986 | 'strange' ones, and then proceed as usual. | ||
987 | |||
988 | So, if files or matching dir<.noinstall> files contain | ||
989 | filename substitutions, this function translates them into the | ||
990 | corresponding 'strange' names, which future passes will expand | ||
991 | as they always have. The resulting pathname is kept in the | ||
992 | file or directory's translated_filename. Another way to think | ||
993 | about it is that self.filename is the input filename, and | ||
994 | translated_filename is the output filename before expansion. | ||
995 | """ | ||
996 | # remove leaf file or dirname | ||
997 | head, tail = os.path.split(self.filename) | ||
998 | translated_path = self.translate_dirnames_in_path(head) | ||
999 | self.translated_filename = translated_path | ||
1000 | |||
1001 | # This is a dirname - does it have a matching .noinstall with | ||
1002 | # a substitution? If so, apply the dirname subsititution. | ||
1003 | if not os.path.isfile(self.filename): | ||
1004 | key = self.filename + ".noinstall" | ||
1005 | try: | ||
1006 | first_line = dirname_substitutions[key] | ||
1007 | except KeyError: | ||
1008 | self.append_translated_filename(tail) | ||
1009 | return | ||
1010 | self.substitute_dirname(first_line) | ||
1011 | return | ||
1012 | |||
1013 | f = open(self.filename) | ||
1014 | first_line = f.readline() | ||
1015 | f.close() | ||
1016 | |||
1017 | # This is a normal filename not needing translation, just use | ||
1018 | # it as-is. | ||
1019 | if not first_line or not first_line.startswith("#"): | ||
1020 | self.append_translated_filename(tail) | ||
1021 | return | ||
1022 | |||
1023 | # If we have a filename substitution (first line in the file | ||
1024 | # is a FILENAME_TAG line) do the substitution now. If we have | ||
1025 | # a dirname substitution (DIRNAME_TAG in dirname.noinstall | ||
1026 | # meta-file), hash it so we can apply it when we see the | ||
1027 | # matching dirname later. Otherwise we have a regular | ||
1028 | # filename, just use it as-is. | ||
1029 | if self.is_filename_substitution(first_line): | ||
1030 | self.substitute_filename(first_line) | ||
1031 | elif self.is_dirname_substitution(first_line): | ||
1032 | self.translate_dirname(first_line) | ||
1033 | else: | ||
1034 | self.append_translated_filename(tail) | ||
1035 | |||
1036 | def expand_file_or_dir_name(self): | ||
1037 | """ | ||
1038 | Expand file or dir names into codeline. Dirnames and | ||
1039 | filenames can only have assignments or if statements. First | ||
1040 | translate if statements into CodeLine + (dirname or filename | ||
1041 | creation). | ||
1042 | """ | ||
1043 | lineno = 0 | ||
1044 | |||
1045 | line = self.translated_filename[len(self.filebase):] | ||
1046 | if line.startswith("/"): | ||
1047 | line = line[1:] | ||
1048 | opentag_start = -1 | ||
1049 | |||
1050 | start = line.find(OPEN_TAG) | ||
1051 | while start != -1: | ||
1052 | if not line[start:].startswith(ASSIGN_TAG): | ||
1053 | opentag_start = start | ||
1054 | break | ||
1055 | start += len(ASSIGN_TAG) | ||
1056 | start = line.find(OPEN_TAG, start) | ||
1057 | |||
1058 | if opentag_start != -1: | ||
1059 | end = line.find(CLOSE_TAG, opentag_start) | ||
1060 | if end == -1: | ||
1061 | self.parse_error("No close tag found for open tag", lineno, line) | ||
1062 | # we have a {{ tag i.e. code | ||
1063 | tag = line[opentag_start + len(OPEN_TAG):end].strip() | ||
1064 | if not tag.lstrip().startswith(IF_TAG): | ||
1065 | self.parse_error("Only 'if' tags are allowed in file or directory names", | ||
1066 | lineno, line) | ||
1067 | self.expanded_lines.append(CodeLine(tag)) | ||
1068 | |||
1069 | # everything after }} is the actual filename (possibly with assignments) | ||
1070 | # everything before is the pathname | ||
1071 | line = line[:opentag_start] + line[end + len(CLOSE_TAG):].strip() | ||
1072 | |||
1073 | assign_start = line.find(ASSIGN_TAG) | ||
1074 | if assign_start != -1: | ||
1075 | assignment_tag = self.expand_assignment_tag(assign_start, line, lineno) | ||
1076 | if isinstance(self, SubstrateFile): | ||
1077 | assignment_tag.is_filename = True | ||
1078 | assignment_tag.out_filebase = self.out_filebase | ||
1079 | elif isinstance(self, SubstrateDir): | ||
1080 | assignment_tag.is_dirname = True | ||
1081 | assignment_tag.out_filebase = self.out_filebase | ||
1082 | self.expanded_lines.append(assignment_tag) | ||
1083 | return | ||
1084 | |||
1085 | normal_line = NormalLine(line) | ||
1086 | if isinstance(self, SubstrateFile): | ||
1087 | normal_line.is_filename = True | ||
1088 | normal_line.out_filebase = self.out_filebase | ||
1089 | elif isinstance(self, SubstrateDir): | ||
1090 | normal_line.is_dirname = True | ||
1091 | normal_line.out_filebase = self.out_filebase | ||
1092 | self.expanded_lines.append(normal_line) | ||
1093 | |||
1094 | def expand(self): | ||
1095 | """ | ||
1096 | Expand the file or dir name first, eventually this ends up | ||
1097 | creating the file or dir. | ||
1098 | """ | ||
1099 | self.translate_file_or_dir_name() | ||
1100 | self.expand_file_or_dir_name() | ||
1101 | |||
1102 | |||
1103 | class SubstrateFile(SubstrateBase): | ||
1104 | """ | ||
1105 | Container for both expanded and unexpanded substrate files. | ||
1106 | """ | ||
1107 | def __init__(self, filename, filebase, out_filebase): | ||
1108 | SubstrateBase.__init__(self, filename, filebase, out_filebase) | ||
1109 | |||
1110 | def read(self): | ||
1111 | if self.raw_lines: | ||
1112 | return | ||
1113 | f = open(self.filename) | ||
1114 | self.raw_lines = f.readlines() | ||
1115 | |||
1116 | def expand(self): | ||
1117 | """Expand the contents of all template tags in the file.""" | ||
1118 | SubstrateBase.expand(self) | ||
1119 | self.read() | ||
1120 | |||
1121 | for lineno, line in enumerate(self.raw_lines): | ||
1122 | # only first line can be a filename substitition | ||
1123 | if lineno == 0 and line.startswith("#") and FILENAME_TAG in line: | ||
1124 | continue # skip it - we've already expanded it | ||
1125 | expanded_line = self.expand_tag(line, lineno + 1) # humans not 0-based | ||
1126 | if not expanded_line: | ||
1127 | expanded_line = NormalLine(line.rstrip()) | ||
1128 | self.expanded_lines.append(expanded_line) | ||
1129 | |||
1130 | def gen(self, context = None): | ||
1131 | """Generate the code that generates the BSP.""" | ||
1132 | base_indent = 0 | ||
1133 | |||
1134 | indent = new_indent = base_indent | ||
1135 | |||
1136 | for line in self.expanded_lines: | ||
1137 | genline = line.gen(context) | ||
1138 | if not genline: | ||
1139 | continue | ||
1140 | if isinstance(line, InputLine): | ||
1141 | line.generated_line = genline | ||
1142 | continue | ||
1143 | if genline.startswith(OPEN_START): | ||
1144 | if indent == 1: | ||
1145 | base_indent = 1 | ||
1146 | if indent: | ||
1147 | if genline == BLANKLINE_STR or (not genline.startswith(NORMAL_START) | ||
1148 | and not genline.startswith(OPEN_START)): | ||
1149 | indent = new_indent = base_indent | ||
1150 | if genline.endswith(":"): | ||
1151 | new_indent = base_indent + 1 | ||
1152 | line.generated_line = (indent * INDENT_STR) + genline | ||
1153 | indent = new_indent | ||
1154 | |||
1155 | |||
1156 | class SubstrateDir(SubstrateBase): | ||
1157 | """ | ||
1158 | Container for both expanded and unexpanded substrate dirs. | ||
1159 | """ | ||
1160 | def __init__(self, filename, filebase, out_filebase): | ||
1161 | SubstrateBase.__init__(self, filename, filebase, out_filebase) | ||
1162 | |||
1163 | def expand(self): | ||
1164 | SubstrateBase.expand(self) | ||
1165 | |||
1166 | def gen(self, context = None): | ||
1167 | """Generate the code that generates the BSP.""" | ||
1168 | indent = new_indent = 0 | ||
1169 | for line in self.expanded_lines: | ||
1170 | genline = line.gen(context) | ||
1171 | if not genline: | ||
1172 | continue | ||
1173 | if genline.endswith(":"): | ||
1174 | new_indent = 1 | ||
1175 | else: | ||
1176 | new_indent = 0 | ||
1177 | line.generated_line = (indent * INDENT_STR) + genline | ||
1178 | indent = new_indent | ||
1179 | |||
1180 | |||
1181 | def expand_target(target, all_files, out_filebase): | ||
1182 | """ | ||
1183 | Expand the contents of all template tags in the target. This | ||
1184 | means removing tags and categorizing or creating lines so that | ||
1185 | future passes can process and present input lines and generate the | ||
1186 | corresponding lines of the Python program that will be exec'ed to | ||
1187 | actually produce the final BSP. 'all_files' includes directories. | ||
1188 | """ | ||
1189 | for root, dirs, files in os.walk(target): | ||
1190 | for file in files: | ||
1191 | if file.endswith("~") or file.endswith("#"): | ||
1192 | continue | ||
1193 | f = os.path.join(root, file) | ||
1194 | sfile = SubstrateFile(f, target, out_filebase) | ||
1195 | sfile.expand() | ||
1196 | all_files.append(sfile) | ||
1197 | |||
1198 | for dir in dirs: | ||
1199 | d = os.path.join(root, dir) | ||
1200 | sdir = SubstrateDir(d, target, out_filebase) | ||
1201 | sdir.expand() | ||
1202 | all_files.append(sdir) | ||
1203 | |||
1204 | |||
1205 | def gen_program_machine_lines(machine, program_lines): | ||
1206 | """ | ||
1207 | Use the input values we got from the command line. | ||
1208 | """ | ||
1209 | line = "machine = \"" + machine + "\"" | ||
1210 | program_lines.append(line) | ||
1211 | |||
1212 | line = "layer_name = \"" + machine + "\"" | ||
1213 | program_lines.append(line) | ||
1214 | |||
1215 | |||
1216 | def sort_inputlines(input_lines): | ||
1217 | """Sort input lines according to priority (position).""" | ||
1218 | input_lines.sort(key = lambda l: l.prio) | ||
1219 | |||
1220 | |||
1221 | def find_parent_dependency(lines, depends_on): | ||
1222 | for i, line in lines: | ||
1223 | if isinstance(line, CodeLine): | ||
1224 | continue | ||
1225 | if line.props["name"] == depends_on: | ||
1226 | return i | ||
1227 | |||
1228 | return -1 | ||
1229 | |||
1230 | |||
1231 | def process_inputline_dependencies(input_lines, all_inputlines): | ||
1232 | """If any input lines depend on others, put the others first.""" | ||
1233 | for line in input_lines: | ||
1234 | if isinstance(line, InputLineGroup): | ||
1235 | group_inputlines = [] | ||
1236 | process_inputline_dependencies(line.group, group_inputlines) | ||
1237 | line.group = group_inputlines | ||
1238 | all_inputlines.append(line) | ||
1239 | continue | ||
1240 | |||
1241 | if isinstance(line, CodeLine) or isinstance(line, NormalLine): | ||
1242 | all_inputlines.append(line) | ||
1243 | continue | ||
1244 | |||
1245 | try: | ||
1246 | depends_on = line.props["depends-on"] | ||
1247 | depends_codeline = "if " + line.props["depends-on"] + " == \"" + line.props["depends-on-val"] + "\":" | ||
1248 | all_inputlines.append(CodeLine(depends_codeline)) | ||
1249 | all_inputlines.append(line) | ||
1250 | except KeyError: | ||
1251 | all_inputlines.append(line) | ||
1252 | |||
1253 | |||
1254 | def conditional_filename(filename): | ||
1255 | """ | ||
1256 | Check if the filename itself contains a conditional statement. If | ||
1257 | so, return a codeline for it. | ||
1258 | """ | ||
1259 | opentag_start = filename.find(OPEN_TAG) | ||
1260 | |||
1261 | if opentag_start != -1: | ||
1262 | if filename[opentag_start:].startswith(ASSIGN_TAG): | ||
1263 | return None | ||
1264 | end = filename.find(CLOSE_TAG, opentag_start) | ||
1265 | if end == -1: | ||
1266 | print "No close tag found for open tag in filename %s" % filename | ||
1267 | sys.exit(1) | ||
1268 | |||
1269 | # we have a {{ tag i.e. code | ||
1270 | tag = filename[opentag_start + len(OPEN_TAG):end].strip() | ||
1271 | if not tag.lstrip().startswith(IF_TAG): | ||
1272 | print "Only 'if' tags are allowed in file or directory names, filename: %s" % filename | ||
1273 | sys.exit(1) | ||
1274 | |||
1275 | return CodeLine(tag) | ||
1276 | |||
1277 | return None | ||
1278 | |||
1279 | |||
1280 | class InputLineGroup(InputLine): | ||
1281 | """ | ||
1282 | InputLine that does nothing but group other input lines | ||
1283 | corresponding to all the input lines in a SubstrateFile so they | ||
1284 | can be generated as a group. prio is the only property used. | ||
1285 | """ | ||
1286 | def __init__(self, codeline): | ||
1287 | InputLine.__init__(self, {}, "", 0) | ||
1288 | self.group = [] | ||
1289 | self.prio = sys.maxint | ||
1290 | self.group.append(codeline) | ||
1291 | |||
1292 | def append(self, line): | ||
1293 | self.group.append(line) | ||
1294 | if line.prio < self.prio: | ||
1295 | self.prio = line.prio | ||
1296 | |||
1297 | def len(self): | ||
1298 | return len(self.group) | ||
1299 | |||
1300 | |||
1301 | def gather_inputlines(files): | ||
1302 | """ | ||
1303 | Gather all the InputLines - we want to generate them first. | ||
1304 | """ | ||
1305 | all_inputlines = [] | ||
1306 | input_lines = [] | ||
1307 | |||
1308 | for file in files: | ||
1309 | if isinstance(file, SubstrateFile): | ||
1310 | group = None | ||
1311 | basename = os.path.basename(file.translated_filename) | ||
1312 | |||
1313 | codeline = conditional_filename(basename) | ||
1314 | if codeline: | ||
1315 | group = InputLineGroup(codeline) | ||
1316 | |||
1317 | have_condition = False | ||
1318 | condition_to_write = None | ||
1319 | for line in file.expanded_lines: | ||
1320 | if isinstance(line, CodeLine): | ||
1321 | have_condition = True | ||
1322 | condition_to_write = line | ||
1323 | continue | ||
1324 | if isinstance(line, InputLine): | ||
1325 | if group: | ||
1326 | if condition_to_write: | ||
1327 | condition_to_write.prio = line.prio | ||
1328 | condition_to_write.discard = True | ||
1329 | group.append(condition_to_write) | ||
1330 | condition_to_write = None | ||
1331 | group.append(line) | ||
1332 | else: | ||
1333 | if condition_to_write: | ||
1334 | condition_to_write.prio = line.prio | ||
1335 | condition_to_write.discard = True | ||
1336 | input_lines.append(condition_to_write) | ||
1337 | condition_to_write = None | ||
1338 | input_lines.append(line) | ||
1339 | else: | ||
1340 | if condition_to_write: | ||
1341 | condition_to_write = None | ||
1342 | if have_condition: | ||
1343 | if not line.line.strip(): | ||
1344 | line.discard = True | ||
1345 | input_lines.append(line) | ||
1346 | have_condition = False | ||
1347 | |||
1348 | if group and group.len() > 1: | ||
1349 | input_lines.append(group) | ||
1350 | |||
1351 | sort_inputlines(input_lines) | ||
1352 | process_inputline_dependencies(input_lines, all_inputlines) | ||
1353 | |||
1354 | return all_inputlines | ||
1355 | |||
1356 | |||
1357 | def run_program_lines(linelist, codedump): | ||
1358 | """ | ||
1359 | For a single file, print all the python code into a buf and execute it. | ||
1360 | """ | ||
1361 | buf = "\n".join(linelist) | ||
1362 | |||
1363 | if codedump: | ||
1364 | of = open("bspgen.out", "w") | ||
1365 | of.write(buf) | ||
1366 | of.close() | ||
1367 | exec buf | ||
1368 | |||
1369 | |||
1370 | def gen_target(files, context = None): | ||
1371 | """ | ||
1372 | Generate the python code for each file. | ||
1373 | """ | ||
1374 | for file in files: | ||
1375 | file.gen(context) | ||
1376 | |||
1377 | |||
1378 | def gen_program_header_lines(program_lines): | ||
1379 | """ | ||
1380 | Generate any imports we need. | ||
1381 | """ | ||
1382 | program_lines.append("current_file = \"\"") | ||
1383 | |||
1384 | |||
1385 | def gen_supplied_property_vals(properties, program_lines): | ||
1386 | """ | ||
1387 | Generate user-specified entries for input values instead of | ||
1388 | generating input prompts. | ||
1389 | """ | ||
1390 | for name, val in properties.iteritems(): | ||
1391 | program_line = name + " = \"" + val + "\"" | ||
1392 | program_lines.append(program_line) | ||
1393 | |||
1394 | |||
1395 | def gen_initial_property_vals(input_lines, program_lines): | ||
1396 | """ | ||
1397 | Generate null or default entries for input values, so we don't | ||
1398 | have undefined variables. | ||
1399 | """ | ||
1400 | for line in input_lines: | ||
1401 | if isinstance(line, InputLineGroup): | ||
1402 | gen_initial_property_vals(line.group, program_lines) | ||
1403 | continue | ||
1404 | |||
1405 | if isinstance(line, InputLine): | ||
1406 | try: | ||
1407 | name = line.props["name"] | ||
1408 | try: | ||
1409 | default_val = "\"" + line.props["default"] + "\"" | ||
1410 | except: | ||
1411 | default_val = "\"\"" | ||
1412 | program_line = name + " = " + default_val | ||
1413 | program_lines.append(program_line) | ||
1414 | except KeyError: | ||
1415 | pass | ||
1416 | |||
1417 | |||
1418 | def gen_program_input_lines(input_lines, program_lines, context, in_group = False): | ||
1419 | """ | ||
1420 | Generate only the input lines used for prompting the user. For | ||
1421 | that, we only have input lines and CodeLines that affect the next | ||
1422 | input line. | ||
1423 | """ | ||
1424 | indent = new_indent = 0 | ||
1425 | |||
1426 | for line in input_lines: | ||
1427 | if isinstance(line, InputLineGroup): | ||
1428 | gen_program_input_lines(line.group, program_lines, context, True) | ||
1429 | continue | ||
1430 | if not line.line.strip(): | ||
1431 | continue | ||
1432 | |||
1433 | genline = line.gen(context) | ||
1434 | if not genline: | ||
1435 | continue | ||
1436 | if genline.endswith(":"): | ||
1437 | new_indent += 1 | ||
1438 | else: | ||
1439 | if indent > 1 or (not in_group and indent): | ||
1440 | new_indent -= 1 | ||
1441 | |||
1442 | line.generated_line = (indent * INDENT_STR) + genline | ||
1443 | program_lines.append(line.generated_line) | ||
1444 | |||
1445 | indent = new_indent | ||
1446 | |||
1447 | |||
1448 | def gen_program_lines(target_files, program_lines): | ||
1449 | """ | ||
1450 | Generate the program lines that make up the BSP generation | ||
1451 | program. This appends the generated lines of all target_files to | ||
1452 | program_lines, and skips input lines, which are dealt with | ||
1453 | separately, or omitted. | ||
1454 | """ | ||
1455 | for file in target_files: | ||
1456 | if file.filename.endswith("noinstall"): | ||
1457 | continue | ||
1458 | |||
1459 | for line in file.expanded_lines: | ||
1460 | if isinstance(line, InputLine): | ||
1461 | continue | ||
1462 | if line.discard: | ||
1463 | continue | ||
1464 | |||
1465 | program_lines.append(line.generated_line) | ||
1466 | |||
1467 | |||
1468 | def create_context(machine, arch, scripts_path): | ||
1469 | """ | ||
1470 | Create a context object for use in deferred function invocation. | ||
1471 | """ | ||
1472 | context = {} | ||
1473 | |||
1474 | context["machine"] = machine | ||
1475 | context["arch"] = arch | ||
1476 | context["scripts_path"] = scripts_path | ||
1477 | |||
1478 | return context | ||
1479 | |||
1480 | |||
1481 | def capture_context(context): | ||
1482 | """ | ||
1483 | Create a context object for use in deferred function invocation. | ||
1484 | """ | ||
1485 | captured_context = {} | ||
1486 | |||
1487 | captured_context["machine"] = context["machine"] | ||
1488 | captured_context["arch"] = context["arch"] | ||
1489 | captured_context["scripts_path"] = context["scripts_path"] | ||
1490 | |||
1491 | return captured_context | ||
1492 | |||
1493 | |||
1494 | def expand_targets(context, bsp_output_dir, expand_common=True): | ||
1495 | """ | ||
1496 | Expand all the tags in both the common and machine-specific | ||
1497 | 'targets'. | ||
1498 | |||
1499 | If expand_common is False, don't expand the common target (this | ||
1500 | option is used to create special-purpose layers). | ||
1501 | """ | ||
1502 | target_files = [] | ||
1503 | |||
1504 | machine = context["machine"] | ||
1505 | arch = context["arch"] | ||
1506 | scripts_path = context["scripts_path"] | ||
1507 | |||
1508 | lib_path = scripts_path + '/lib' | ||
1509 | bsp_path = lib_path + '/bsp' | ||
1510 | arch_path = bsp_path + '/substrate/target/arch' | ||
1511 | |||
1512 | if expand_common: | ||
1513 | common = os.path.join(arch_path, "common") | ||
1514 | expand_target(common, target_files, bsp_output_dir) | ||
1515 | |||
1516 | arches = os.listdir(arch_path) | ||
1517 | if arch not in arches or arch == "common": | ||
1518 | print "Invalid karch, exiting\n" | ||
1519 | sys.exit(1) | ||
1520 | |||
1521 | target = os.path.join(arch_path, arch) | ||
1522 | expand_target(target, target_files, bsp_output_dir) | ||
1523 | |||
1524 | gen_target(target_files, context) | ||
1525 | |||
1526 | return target_files | ||
1527 | |||
1528 | |||
1529 | def yocto_common_create(machine, target, scripts_path, layer_output_dir, codedump, properties_file, properties_str="", expand_common=True): | ||
1530 | """ | ||
1531 | Common layer-creation code | ||
1532 | |||
1533 | machine - user-defined machine name (if needed, will generate 'machine' var) | ||
1534 | target - the 'target' the layer will be based on, must be one in | ||
1535 | scripts/lib/bsp/substrate/target/arch | ||
1536 | scripts_path - absolute path to yocto /scripts dir | ||
1537 | layer_output_dir - dirname to create for layer | ||
1538 | codedump - dump generated code to bspgen.out | ||
1539 | properties_file - use values from this file if nonempty i.e no prompting | ||
1540 | properties_str - use values from this string if nonempty i.e no prompting | ||
1541 | expand_common - boolean, use the contents of (for bsp layers) arch/common | ||
1542 | """ | ||
1543 | if os.path.exists(layer_output_dir): | ||
1544 | print "\nlayer output dir already exists, exiting. (%s)" % layer_output_dir | ||
1545 | sys.exit(1) | ||
1546 | |||
1547 | properties = None | ||
1548 | |||
1549 | if properties_file: | ||
1550 | try: | ||
1551 | infile = open(properties_file, "r") | ||
1552 | except IOError: | ||
1553 | print "Couldn't open properties file %s for reading, exiting" % properties_file | ||
1554 | sys.exit(1) | ||
1555 | |||
1556 | properties = json.load(infile) | ||
1557 | |||
1558 | if properties_str and not properties: | ||
1559 | properties = json.loads(properties_str) | ||
1560 | |||
1561 | os.mkdir(layer_output_dir) | ||
1562 | |||
1563 | context = create_context(machine, target, scripts_path) | ||
1564 | target_files = expand_targets(context, layer_output_dir, expand_common) | ||
1565 | |||
1566 | input_lines = gather_inputlines(target_files) | ||
1567 | |||
1568 | program_lines = [] | ||
1569 | |||
1570 | gen_program_header_lines(program_lines) | ||
1571 | |||
1572 | gen_initial_property_vals(input_lines, program_lines) | ||
1573 | |||
1574 | if properties: | ||
1575 | gen_supplied_property_vals(properties, program_lines) | ||
1576 | |||
1577 | gen_program_machine_lines(machine, program_lines) | ||
1578 | |||
1579 | if not properties: | ||
1580 | gen_program_input_lines(input_lines, program_lines, context) | ||
1581 | |||
1582 | gen_program_lines(target_files, program_lines) | ||
1583 | |||
1584 | run_program_lines(program_lines, codedump) | ||
1585 | |||
1586 | |||
1587 | def yocto_layer_create(layer_name, scripts_path, layer_output_dir, codedump, properties_file, properties=""): | ||
1588 | """ | ||
1589 | Create yocto layer | ||
1590 | |||
1591 | layer_name - user-defined layer name | ||
1592 | scripts_path - absolute path to yocto /scripts dir | ||
1593 | layer_output_dir - dirname to create for layer | ||
1594 | codedump - dump generated code to bspgen.out | ||
1595 | properties_file - use values from this file if nonempty i.e no prompting | ||
1596 | properties - use values from this string if nonempty i.e no prompting | ||
1597 | """ | ||
1598 | yocto_common_create(layer_name, "layer", scripts_path, layer_output_dir, codedump, properties_file, properties, False) | ||
1599 | |||
1600 | print "\nNew layer created in %s.\n" % (layer_output_dir) | ||
1601 | print "Don't forget to add it to your BBLAYERS (for details see %s\README)." % (layer_output_dir) | ||
1602 | |||
1603 | |||
1604 | def yocto_bsp_create(machine, arch, scripts_path, bsp_output_dir, codedump, properties_file, properties=None): | ||
1605 | """ | ||
1606 | Create bsp | ||
1607 | |||
1608 | machine - user-defined machine name | ||
1609 | arch - the arch the bsp will be based on, must be one in | ||
1610 | scripts/lib/bsp/substrate/target/arch | ||
1611 | scripts_path - absolute path to yocto /scripts dir | ||
1612 | bsp_output_dir - dirname to create for BSP | ||
1613 | codedump - dump generated code to bspgen.out | ||
1614 | properties_file - use values from this file if nonempty i.e no prompting | ||
1615 | properties - use values from this string if nonempty i.e no prompting | ||
1616 | """ | ||
1617 | yocto_common_create(machine, arch, scripts_path, bsp_output_dir, codedump, properties_file, properties) | ||
1618 | |||
1619 | print "\nNew %s BSP created in %s" % (arch, bsp_output_dir) | ||
1620 | |||
1621 | |||
1622 | def print_dict(items, indent = 0): | ||
1623 | """ | ||
1624 | Print the values in a possibly nested dictionary. | ||
1625 | """ | ||
1626 | for key, val in items.iteritems(): | ||
1627 | print " "*indent + "\"%s\" :" % key, | ||
1628 | if type(val) == dict: | ||
1629 | print "{" | ||
1630 | print_dict(val, indent + 1) | ||
1631 | print " "*indent + "}" | ||
1632 | else: | ||
1633 | print "%s" % val | ||
1634 | |||
1635 | |||
1636 | def get_properties(input_lines): | ||
1637 | """ | ||
1638 | Get the complete set of properties for all the input items in the | ||
1639 | BSP, as a possibly nested dictionary. | ||
1640 | """ | ||
1641 | properties = {} | ||
1642 | |||
1643 | for line in input_lines: | ||
1644 | if isinstance(line, InputLineGroup): | ||
1645 | statement = line.group[0].line | ||
1646 | group_properties = get_properties(line.group) | ||
1647 | properties[statement] = group_properties | ||
1648 | continue | ||
1649 | |||
1650 | if not isinstance(line, InputLine): | ||
1651 | continue | ||
1652 | |||
1653 | if isinstance(line, ChoiceInputLine): | ||
1654 | continue | ||
1655 | |||
1656 | props = line.props | ||
1657 | item = {} | ||
1658 | name = props["name"] | ||
1659 | for key, val in props.items(): | ||
1660 | if not key == "name": | ||
1661 | item[key] = val | ||
1662 | properties[name] = item | ||
1663 | |||
1664 | return properties | ||
1665 | |||
1666 | |||
1667 | def yocto_layer_list_properties(arch, scripts_path, properties_file, expand_common=True): | ||
1668 | """ | ||
1669 | List the complete set of properties for all the input items in the | ||
1670 | layer. If properties_file is non-null, write the complete set of | ||
1671 | properties as a nested JSON object corresponding to a possibly | ||
1672 | nested dictionary. | ||
1673 | """ | ||
1674 | context = create_context("unused", arch, scripts_path) | ||
1675 | target_files = expand_targets(context, "unused", expand_common) | ||
1676 | |||
1677 | input_lines = gather_inputlines(target_files) | ||
1678 | |||
1679 | properties = get_properties(input_lines) | ||
1680 | if properties_file: | ||
1681 | try: | ||
1682 | of = open(properties_file, "w") | ||
1683 | except IOError: | ||
1684 | print "Couldn't open properties file %s for writing, exiting" % properties_file | ||
1685 | sys.exit(1) | ||
1686 | |||
1687 | json.dump(properties, of) | ||
1688 | |||
1689 | print_dict(properties) | ||
1690 | |||
1691 | |||
1692 | def split_nested_property(property): | ||
1693 | """ | ||
1694 | A property name of the form x.y describes a nested property | ||
1695 | i.e. the property y is contained within x and can be addressed | ||
1696 | using standard JSON syntax for nested properties. Note that if a | ||
1697 | property name itself contains '.', it should be contained in | ||
1698 | double quotes. | ||
1699 | """ | ||
1700 | splittable_property = "" | ||
1701 | in_quotes = False | ||
1702 | for c in property: | ||
1703 | if c == '.' and not in_quotes: | ||
1704 | splittable_property += '\n' | ||
1705 | continue | ||
1706 | if c == '"': | ||
1707 | in_quotes = not in_quotes | ||
1708 | splittable_property += c | ||
1709 | |||
1710 | split_properties = splittable_property.split('\n') | ||
1711 | |||
1712 | if len(split_properties) > 1: | ||
1713 | return split_properties | ||
1714 | |||
1715 | return None | ||
1716 | |||
1717 | |||
1718 | def find_input_line_group(substring, input_lines): | ||
1719 | """ | ||
1720 | Find and return the InputLineGroup containing the specified substring. | ||
1721 | """ | ||
1722 | for line in input_lines: | ||
1723 | if isinstance(line, InputLineGroup): | ||
1724 | if substring in line.group[0].line: | ||
1725 | return line | ||
1726 | |||
1727 | return None | ||
1728 | |||
1729 | |||
1730 | def find_input_line(name, input_lines): | ||
1731 | """ | ||
1732 | Find the input line with the specified name. | ||
1733 | """ | ||
1734 | for line in input_lines: | ||
1735 | if isinstance(line, InputLineGroup): | ||
1736 | l = find_input_line(name, line.group) | ||
1737 | if l: | ||
1738 | return l | ||
1739 | |||
1740 | if isinstance(line, InputLine): | ||
1741 | try: | ||
1742 | if line.props["name"] == name: | ||
1743 | return line | ||
1744 | if line.props["name"] + "_" + line.props["nameappend"] == name: | ||
1745 | return line | ||
1746 | except KeyError: | ||
1747 | pass | ||
1748 | |||
1749 | return None | ||
1750 | |||
1751 | |||
1752 | def print_values(type, values_list): | ||
1753 | """ | ||
1754 | Print the values in the given list of values. | ||
1755 | """ | ||
1756 | if type == "choicelist": | ||
1757 | for value in values_list: | ||
1758 | print "[\"%s\", \"%s\"]" % (value[0], value[1]) | ||
1759 | elif type == "boolean": | ||
1760 | for value in values_list: | ||
1761 | print "[\"%s\", \"%s\"]" % (value[0], value[1]) | ||
1762 | |||
1763 | |||
1764 | def yocto_layer_list_property_values(arch, property, scripts_path, properties_file, expand_common=True): | ||
1765 | """ | ||
1766 | List the possible values for a given input property. If | ||
1767 | properties_file is non-null, write the complete set of properties | ||
1768 | as a JSON object corresponding to an array of possible values. | ||
1769 | """ | ||
1770 | context = create_context("unused", arch, scripts_path) | ||
1771 | context["name"] = property | ||
1772 | |||
1773 | target_files = expand_targets(context, "unused", expand_common) | ||
1774 | |||
1775 | input_lines = gather_inputlines(target_files) | ||
1776 | |||
1777 | properties = get_properties(input_lines) | ||
1778 | |||
1779 | nested_properties = split_nested_property(property) | ||
1780 | if nested_properties: | ||
1781 | # currently the outer property of a nested property always | ||
1782 | # corresponds to an input line group | ||
1783 | input_line_group = find_input_line_group(nested_properties[0], input_lines) | ||
1784 | if input_line_group: | ||
1785 | input_lines[:] = input_line_group.group[1:] | ||
1786 | # The inner property of a nested property name is the | ||
1787 | # actual property name we want, so reset to that | ||
1788 | property = nested_properties[1] | ||
1789 | |||
1790 | input_line = find_input_line(property, input_lines) | ||
1791 | if not input_line: | ||
1792 | print "Couldn't find values for property %s" % property | ||
1793 | return | ||
1794 | |||
1795 | values_list = [] | ||
1796 | |||
1797 | type = input_line.props["type"] | ||
1798 | if type == "boolean": | ||
1799 | values_list.append(["y", "n"]) | ||
1800 | elif type == "choicelist" or type == "checklist": | ||
1801 | try: | ||
1802 | gen_fn = input_line.props["gen"] | ||
1803 | if nested_properties: | ||
1804 | context["filename"] = nested_properties[0] | ||
1805 | try: | ||
1806 | context["branches_base"] = input_line.props["branches_base"] | ||
1807 | except KeyError: | ||
1808 | context["branches_base"] = None | ||
1809 | values_list = input_line.gen_choices_list(context, False) | ||
1810 | except KeyError: | ||
1811 | for choice in input_line.choices: | ||
1812 | choicepair = [] | ||
1813 | choicepair.append(choice.val) | ||
1814 | choicepair.append(choice.desc) | ||
1815 | values_list.append(choicepair) | ||
1816 | |||
1817 | if properties_file: | ||
1818 | try: | ||
1819 | of = open(properties_file, "w") | ||
1820 | except IOError: | ||
1821 | print "Couldn't open properties file %s for writing, exiting" % properties_file | ||
1822 | sys.exit(1) | ||
1823 | |||
1824 | json.dump(values_list, of) | ||
1825 | |||
1826 | print_values(type, values_list) | ||
1827 | |||
1828 | |||
1829 | def yocto_bsp_list(args, scripts_path, properties_file): | ||
1830 | """ | ||
1831 | Print available architectures, or the complete list of properties | ||
1832 | defined by the BSP, or the possible values for a particular BSP | ||
1833 | property. | ||
1834 | """ | ||
1835 | if len(args) < 1: | ||
1836 | return False | ||
1837 | |||
1838 | if args[0] == "karch": | ||
1839 | lib_path = scripts_path + '/lib' | ||
1840 | bsp_path = lib_path + '/bsp' | ||
1841 | arch_path = bsp_path + '/substrate/target/arch' | ||
1842 | print "Architectures available:" | ||
1843 | for arch in os.listdir(arch_path): | ||
1844 | if arch == "common" or arch == "layer": | ||
1845 | continue | ||
1846 | print " %s" % arch | ||
1847 | return True | ||
1848 | else: | ||
1849 | arch = args[0] | ||
1850 | |||
1851 | if len(args) < 2 or len(args) > 3: | ||
1852 | return False | ||
1853 | |||
1854 | if len(args) == 2: | ||
1855 | if args[1] == "properties": | ||
1856 | yocto_layer_list_properties(arch, scripts_path, properties_file) | ||
1857 | else: | ||
1858 | return False | ||
1859 | |||
1860 | if len(args) == 3: | ||
1861 | if args[1] == "property": | ||
1862 | yocto_layer_list_property_values(arch, args[2], scripts_path, properties_file) | ||
1863 | else: | ||
1864 | return False | ||
1865 | |||
1866 | return True | ||
1867 | |||
1868 | |||
1869 | def yocto_layer_list(args, scripts_path, properties_file): | ||
1870 | """ | ||
1871 | Print the complete list of input properties defined by the layer, | ||
1872 | or the possible values for a particular layer property. | ||
1873 | """ | ||
1874 | if len(args) < 1: | ||
1875 | return False | ||
1876 | |||
1877 | if len(args) < 1 or len(args) > 2: | ||
1878 | return False | ||
1879 | |||
1880 | if len(args) == 1: | ||
1881 | if args[0] == "properties": | ||
1882 | yocto_layer_list_properties("layer", scripts_path, properties_file, False) | ||
1883 | else: | ||
1884 | return False | ||
1885 | |||
1886 | if len(args) == 2: | ||
1887 | if args[0] == "property": | ||
1888 | yocto_layer_list_property_values("layer", args[1], scripts_path, properties_file, False) | ||
1889 | else: | ||
1890 | return False | ||
1891 | |||
1892 | return True | ||
1893 | |||
1894 | |||
1895 | def map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): | ||
1896 | """ | ||
1897 | Return the linux-yocto bsp branch to use with the specified | ||
1898 | kbranch. This handles the -standard variants for 3.4 and 3.8; the | ||
1899 | other variants don't need mappings. | ||
1900 | """ | ||
1901 | if need_new_kbranch == "y": | ||
1902 | kbranch = new_kbranch | ||
1903 | else: | ||
1904 | kbranch = existing_kbranch | ||
1905 | |||
1906 | if kbranch.startswith("standard/common-pc-64"): | ||
1907 | return "bsp/common-pc-64/common-pc-64-standard.scc" | ||
1908 | if kbranch.startswith("standard/common-pc"): | ||
1909 | return "bsp/common-pc/common-pc-standard.scc" | ||
1910 | else: | ||
1911 | return "ktypes/standard/standard.scc" | ||
1912 | |||
1913 | |||
1914 | def map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): | ||
1915 | """ | ||
1916 | Return the linux-yocto bsp branch to use with the specified | ||
1917 | kbranch. This handles the -preempt-rt variants for 3.4 and 3.8; | ||
1918 | the other variants don't need mappings. | ||
1919 | """ | ||
1920 | if need_new_kbranch == "y": | ||
1921 | kbranch = new_kbranch | ||
1922 | else: | ||
1923 | kbranch = existing_kbranch | ||
1924 | |||
1925 | if kbranch.startswith("standard/preempt-rt/common-pc-64"): | ||
1926 | return "bsp/common-pc-64/common-pc-64-preempt-rt.scc" | ||
1927 | if kbranch.startswith("standard/preempt-rt/common-pc"): | ||
1928 | return "bsp/common-pc/common-pc-preempt-rt.scc" | ||
1929 | else: | ||
1930 | return "ktypes/preempt-rt/preempt-rt.scc" | ||
1931 | |||
1932 | |||
1933 | def map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): | ||
1934 | """ | ||
1935 | Return the linux-yocto bsp branch to use with the specified | ||
1936 | kbranch. This handles the -tiny variants for 3.4 and 3.8; the | ||
1937 | other variants don't need mappings. | ||
1938 | """ | ||
1939 | if need_new_kbranch == "y": | ||
1940 | kbranch = new_kbranch | ||
1941 | else: | ||
1942 | kbranch = existing_kbranch | ||
1943 | |||
1944 | if kbranch.startswith("standard/tiny/common-pc"): | ||
1945 | return "bsp/common-pc/common-pc-tiny.scc" | ||
1946 | else: | ||
1947 | return "ktypes/tiny/tiny.scc" | ||