Mercurial > lcfOS
comparison python/zcc.py @ 331:a78b41ff6ad2
Added better recipe files
author | Windel Bouwman |
---|---|
date | Fri, 07 Feb 2014 12:39:59 +0100 |
parents | 8f6f3ace4e78 |
children | 87feb8a23b4d |
comparison
equal
deleted
inserted
replaced
330:a79ac866732f | 331:a78b41ff6ad2 |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 | 2 |
3 import sys | 3 import sys |
4 import os | |
4 import argparse | 5 import argparse |
5 import logging | 6 import logging |
7 import yaml | |
6 | 8 |
7 from ppci.c3 import AstPrinter | |
8 from ppci.buildtasks import Compile | 9 from ppci.buildtasks import Compile |
9 from ppci.tasks import TaskRunner | 10 from ppci.tasks import TaskRunner |
11 from ppci.report import RstFormatter | |
10 import outstream | 12 import outstream |
11 from utils import HexFile | |
12 import target | |
13 from target.target_list import target_list | 13 from target.target_list import target_list |
14 from ppci import irutils | 14 import ppci |
15 import io | |
16 | |
17 | |
18 logformat='%(asctime)s|%(levelname)s|%(name)s|%(message)s' | |
19 | 15 |
20 | 16 |
21 def logLevel(s): | 17 def logLevel(s): |
22 """ Converts a string to a valid logging level """ | 18 """ Converts a string to a valid logging level """ |
23 numeric_level = getattr(logging, s.upper(), None) | 19 numeric_level = getattr(logging, s.upper(), None) |
24 if not isinstance(numeric_level, int): | 20 if not isinstance(numeric_level, int): |
25 raise ValueError('Invalid log level: {}'.format(s)) | 21 raise ValueError('Invalid log level: {}'.format(s)) |
26 return numeric_level | 22 return numeric_level |
27 | 23 |
28 | 24 |
29 class RstFormatter(logging.Formatter): | |
30 """ Formatter that tries to create an rst document """ | |
31 def __init__(self): | |
32 super().__init__(fmt=logformat) | |
33 | |
34 def format(self, record): | |
35 s = super().format(record) | |
36 s += '\n' | |
37 if hasattr(record, 'c3_ast'): | |
38 f = io.StringIO() | |
39 print('', file=f) | |
40 print('', file=f) | |
41 print('.. code::', file=f) | |
42 print('', file=f) | |
43 AstPrinter().printAst(record.c3_ast, f) | |
44 print('', file=f) | |
45 s += '\n' + f.getvalue() | |
46 if hasattr(record, 'ircode'): | |
47 f = io.StringIO() | |
48 print('', file=f) | |
49 print('', file=f) | |
50 print('.. code::', file=f) | |
51 print('', file=f) | |
52 Writer(' ').write(record.ircode, f) | |
53 print('', file=f) | |
54 s += '\n' + f.getvalue() | |
55 if hasattr(record, 'irfunc'): | |
56 f = io.StringIO() | |
57 print('', file=f) | |
58 print('', file=f) | |
59 print('.. code::', file=f) | |
60 print('', file=f) | |
61 Writer(' ').write_function(record.irfunc, f) | |
62 print('', file=f) | |
63 s += '\n' + f.getvalue() | |
64 if hasattr(record, 'ppci_frame'): | |
65 f = io.StringIO() | |
66 frame = record.ppci_frame | |
67 print('', file=f) | |
68 print('.. code::', file=f) | |
69 print('', file=f) | |
70 print(' {}'.format(frame.name), file=f) | |
71 for i in frame.instructions: | |
72 print(' {}'.format(i),file=f) | |
73 print('', file=f) | |
74 s += '\n' + f.getvalue() | |
75 if hasattr(record, 'ra_cfg'): | |
76 f = io.StringIO() | |
77 print('', file=f) | |
78 print('', file=f) | |
79 print('.. graphviz::', file=f) | |
80 print('', file=f) | |
81 print(' digraph G {', file=f) | |
82 print(' size="8,80";', file=f) | |
83 cfg = record.ra_cfg | |
84 cfg.to_dot(f) | |
85 print(' }', file=f) | |
86 print('', file=f) | |
87 s += '\n' + f.getvalue() | |
88 if hasattr(record, 'ra_ig'): | |
89 f = io.StringIO() | |
90 print('', file=f) | |
91 print('', file=f) | |
92 print('.. graphviz::', file=f) | |
93 print('', file=f) | |
94 print(' digraph G {', file=f) | |
95 print(' ratio="compress";', file=f) | |
96 print(' size="8,80";', file=f) | |
97 ig = record.ra_ig | |
98 ig.to_dot(f) | |
99 print(' }', file=f) | |
100 print('', file=f) | |
101 s += '\n' + f.getvalue() | |
102 if hasattr(record, 'zcc_outs'): | |
103 f = io.StringIO() | |
104 print('', file=f) | |
105 print('', file=f) | |
106 print('.. code::', file=f) | |
107 print('', file=f) | |
108 outstream.OutputStreamWriter(' ').dump(record.zcc_outs, f) | |
109 print('', file=f) | |
110 s += '\n' + f.getvalue() | |
111 return s | |
112 | |
113 | |
114 targets = {t.name: t for t in target_list} | 25 targets = {t.name: t for t in target_list} |
115 targetnames = list(targets.keys()) | 26 targetnames = list(targets.keys()) |
116 | 27 |
117 # Parse arguments: | 28 def make_parser(): |
118 parser = argparse.ArgumentParser(description='lcfos Compiler') | 29 parser = argparse.ArgumentParser(description='lcfos Compiler') |
119 parser.add_argument('source', type=argparse.FileType('r'), \ | |
120 help='the source file to build', nargs="+") | |
121 parser.add_argument('-i', '--imp', type=argparse.FileType('r'), \ | |
122 help='Possible import module', action='append', default=[]) | |
123 | 30 |
124 #sub_parsers = parser.add_subparsers() | 31 parser.add_argument('--log', help='Log level (INFO,DEBUG,[WARN])', |
125 #recipe = sub_parsers.add_parser('recipe') | 32 type=logLevel, default='WARN') |
126 parser.add_argument('--optimize', action='store_true', help="Optimize") | 33 sub_parsers = parser.add_subparsers(title='commands', |
127 parser.add_argument('--target', help="Backend selection", | 34 description='possible commands', dest='command') |
128 choices=targetnames, required=True) | 35 recipe_parser = sub_parsers.add_parser('recipe', help="Bake recipe") |
129 parser.add_argument('-o', '--output', help='Output file', metavar='filename') | 36 recipe_parser.add_argument('recipe_file', help='recipe file') |
130 parser.add_argument('--log', help='Log level (INFO,DEBUG,[WARN])', | 37 |
131 type=logLevel, default='WARN') | 38 compile_parser = sub_parsers.add_parser('compile', help="compile source") |
132 parser.add_argument('--report', | 39 compile_parser.add_argument('source', type=argparse.FileType('r'), |
133 help='Specify a file to write the compile report to', | 40 help='the source file to build', nargs="+") |
134 type=argparse.FileType('w')) | 41 compile_parser.add_argument('-i', '--imp', type=argparse.FileType('r'), |
42 help='Possible import module', action='append', default=[]) | |
43 compile_parser.add_argument('--target', help="Backend selection", | |
44 choices=targetnames, required=True) | |
45 compile_parser.add_argument('-o', '--output', help='Output file', | |
46 metavar='filename') | |
47 compile_parser.add_argument('--report', | |
48 help='Specify a file to write the compile report to', | |
49 type=argparse.FileType('w')) | |
50 return parser | |
51 | |
52 | |
53 class RecipeLoader: | |
54 def load_file(self, recipe_file, runner): | |
55 """ Loads a recipe dictionary into a task runner """ | |
56 self.recipe_dir = os.path.abspath(os.path.dirname(recipe_file)) | |
57 with open(recipe_file, 'r') as f: | |
58 recipe = yaml.load(f) | |
59 self.load_dict(recipe, runner) | |
60 | |
61 def relpath(self, filename): | |
62 return os.path.join(self.recipe_dir, filename) | |
63 | |
64 def openfile(self, filename): | |
65 return open(self.relpath(filename), 'r') | |
66 | |
67 def load_dict(self, recipe, runner): | |
68 for command, value in recipe.items(): | |
69 if command == 'compile': | |
70 sources = [self.openfile(s) for s in value['sources']] | |
71 includes = [self.openfile(i) for i in value['includes']] | |
72 target = targets[value['machine']] | |
73 output = outstream.TextOutputStream() | |
74 runner.add_task(Compile(sources, includes, target, output)) | |
75 elif command == 'link': | |
76 self.load_dict(value['inputs'], runner) | |
77 #runner.add_task(Link()) | |
78 elif command == 'assemble': | |
79 pass | |
80 elif command == 'apps': | |
81 for a in value: | |
82 self.load_dict(a, runner) | |
83 else: | |
84 raise NotImplementedError(command) | |
135 | 85 |
136 | 86 |
137 def main(args): | 87 def main(args): |
138 # Configure some logging: | 88 # Configure some logging: |
139 logging.getLogger().setLevel(logging.DEBUG) | 89 logging.getLogger().setLevel(logging.DEBUG) |
140 ch = logging.StreamHandler() | 90 ch = logging.StreamHandler() |
141 ch.setFormatter(logging.Formatter(logformat)) | 91 ch.setFormatter(logging.Formatter(ppci.logformat)) |
142 ch.setLevel(args.log) | 92 ch.setLevel(args.log) |
143 logging.getLogger().addHandler(ch) | 93 logging.getLogger().addHandler(ch) |
144 if args.report: | |
145 fh = logging.StreamHandler(stream=args.report) | |
146 fh.setFormatter(RstFormatter()) | |
147 logging.getLogger().addHandler(fh) | |
148 | 94 |
149 tg = targets[args.target] | 95 runner = TaskRunner() |
150 outs = outstream.TextOutputStream() | 96 if args.command == 'compile': |
97 tg = targets[args.target] | |
98 outs = outstream.TextOutputStream() | |
99 runner.add_task(Compile(args.source, args.imp, tg, outs)) | |
100 elif args.command == 'recipe': | |
101 recipe_loader = RecipeLoader() | |
102 recipe_loader.load_file(args.recipe_file, runner) | |
103 else: | |
104 raise NotImplementedError('Invalid option') | |
151 | 105 |
152 tr = TaskRunner() | 106 res = runner.run_tasks() |
153 tr.add_task(Compile(args.source, args.imp, tg, outs)) | |
154 | 107 |
155 res = tr.run_tasks() | |
156 if res > 0: | |
157 return 1 | |
158 logging.info('Assembly created', extra={'zcc_outs':outs}) | |
159 | |
160 code_bytes = outs.sections['code'].to_bytes() | |
161 if args.output: | |
162 output_filename = args.output | |
163 with open(output_filename, 'wb') as f: | |
164 f.write(code_bytes) | |
165 | |
166 if args.report: | |
167 logging.getLogger().removeHandler(fh) | |
168 logging.getLogger().removeHandler(ch) | 108 logging.getLogger().removeHandler(ch) |
169 return 0 | 109 return res |
170 | 110 |
171 | 111 |
172 if __name__ == '__main__': | 112 if __name__ == '__main__': |
113 parser = make_parser() | |
173 arguments = parser.parse_args() | 114 arguments = parser.parse_args() |
115 if not arguments.command: | |
116 parser.print_usage() | |
117 sys.exit(1) | |
174 sys.exit(main(arguments)) | 118 sys.exit(main(arguments)) |