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))