comparison python/ppci/buildtasks.py @ 377:9667d78ba79e

Switched to xml for project description
author Windel Bouwman
date Fri, 11 Apr 2014 15:47:50 +0200
parents 39bf68bf1891
children 6df89163e114
comparison
equal deleted inserted replaced
376:1e951e71d3f1 377:9667d78ba79e
3 Defines task classes that can compile, link etc.. 3 Defines task classes that can compile, link etc..
4 Task can depend upon one another. 4 Task can depend upon one another.
5 """ 5 """
6 6
7 import logging 7 import logging
8 import json
8 9
9 from .c3 import Builder 10 from .tasks import Task, TaskError, register_task
10 from .irutils import Verifier 11 from .buildfunctions import c3compile, link, assemble
11 from .codegen import CodeGenerator 12 from pyyacc import ParserException
12 from .transform import CleanPass, RemoveAddZero 13 from . import CompilerError
13 from .tasks import Task, TaskError
14 from . import DiagnosticsManager, CompilerError
15 from .assembler import Assembler
16 from .objectfile import ObjectFile
17 from .linker import Linker
18 from .outstream import BinaryOutputStream, MasterOutputStream, LoggerOutputStream
19 from .target.target_list import targets
20 from .target import Target
21 14
22 15
23 def fix_target(tg): 16 @register_task("empty")
24 """ Try to return an instance of the Target class """ 17 class EmptyTask(Task):
25 if isinstance(tg, Target): 18 """ Basic task that does nothing """
26 return tg 19 def run(self):
27 elif isinstance(tg, str): 20 pass
28 if tg in targets:
29 return targets[tg]
30 else:
31 raise TaskError('Target {} not found'.format(tg))
32 raise TaskError('Invalid target {}'.format(tg))
33 21
34 22
35 def fix_file(f): 23 @register_task("echo")
36 """ Determine if argument is a file like object or make it so! """ 24 class EchoTask(Task):
37 if hasattr(f, 'read'): 25 """ Simple task that echoes a message """
38 # Assume this is a file like object 26 def run(self):
39 return f 27 message = self.arguments['message']
40 elif isinstance(f, str): 28 print(message)
41 return open(f, 'r')
42 else:
43 raise TaskError('cannot use {} as input'.format(f))
44
45 def fix_object(o):
46 if isinstance(o, ObjectFile):
47 return o
48 else:
49 raise TaskError('Cannot use {} as objectfile'.format(o))
50
51 class BuildTask(Task):
52 """ Base task for all kind of weird build tasks """
53 def __init__(self, name):
54 super().__init__(name)
55 self.logger = logging.getLogger('buildtask')
56 29
57 30
58 class Assemble(BuildTask): 31 @register_task("property")
32 class Property(Task):
33 """ Sets a property to a value """
34 def run(self):
35 name = self.arguments['name']
36 value = self.arguments['value']
37 self.target.project.set_property(name, value)
38
39
40 @register_task("assemble")
41 class AssembleTask(Task):
59 """ Task that can runs the assembler over the source and enters the 42 """ Task that can runs the assembler over the source and enters the
60 output into an object file """ 43 output into an object file """
61 def __init__(self, source, target, output_object):
62 super().__init__('Assemble')
63 self.source = fix_file(source)
64 self.output = output_object
65 o2 = BinaryOutputStream(self.output)
66 o1 = LoggerOutputStream()
67 self.ostream = MasterOutputStream([o1, o2])
68 self.assembler = Assembler(fix_target(target))
69 44
70 def run(self): 45 def run(self):
71 self.logger.debug('Assembling into code section') 46 target = self.get_argument('target')
72 self.ostream.select_section('code') 47 source = self.relpath(self.get_argument('source'))
73 self.assembler.assemble(self.source, self.ostream) 48 output_filename = self.relpath(self.get_argument('output'))
49
50 try:
51 output = assemble(source, target)
52 except ParserException as e:
53 raise TaskError('Error during assembly:' + str(e))
54 except CompilerError as e:
55 raise TaskError('Error during assembly:' + str(e))
56 with open(output_filename, 'w') as f:
57 output.save(f)
74 self.logger.debug('Assembling finished') 58 self.logger.debug('Assembling finished')
75 59
76 60
77 class Compile(BuildTask): 61 @register_task("compile")
62 class C3cTask(Task):
78 """ Task that compiles C3 source for some target into an object file """ 63 """ Task that compiles C3 source for some target into an object file """
79 def __init__(self, sources, includes, target, output_object): 64 def run(self):
80 super().__init__('Compile') 65 target = self.get_argument('target')
81 self.sources = list(map(fix_file, sources)) 66 sources = self.open_file_set(self.arguments['sources'])
82 self.includes = list(map(fix_file, includes)) 67 output_filename = self.relpath(self.get_argument('output'))
83 self.target = fix_target(target) 68 if 'includes' in self.arguments:
84 self.output = output_object 69 includes = self.open_file_set(self.arguments['includes'])
70 else:
71 includes = []
85 72
86 def run(self): 73 output = c3compile(sources, includes, target)
87 self.logger.debug('Compile started') 74 # Store output:
88 diag = DiagnosticsManager() 75 with open(output_filename, 'w') as f:
89 c3b = Builder(diag, self.target) 76 output.save(f)
90 cg = CodeGenerator(self.target)
91
92 for ircode in c3b.build(self.sources, self.includes):
93 if not ircode:
94 # Something went wrong, do not continue the code generation
95 continue
96
97 d = {'ircode':ircode}
98 self.logger.debug('Verifying code {}'.format(ircode), extra=d)
99 Verifier().verify(ircode)
100
101 # Optimization passes:
102 CleanPass().run(ircode)
103 Verifier().verify(ircode)
104 RemoveAddZero().run(ircode)
105 Verifier().verify(ircode)
106 CleanPass().run(ircode)
107 Verifier().verify(ircode)
108
109 # Code generation:
110 d = {'ircode':ircode}
111 self.logger.debug('Starting code generation for {}'.format(ircode), extra=d)
112
113 o2 = BinaryOutputStream(self.output)
114 o1 = LoggerOutputStream()
115 o = MasterOutputStream([o1, o2])
116 cg.generate(ircode, o)
117
118 if not c3b.ok:
119 diag.printErrors()
120 raise TaskError('Compile errors')
121 77
122 78
123 class Link(BuildTask): 79 def make_num(txt):
80 if txt.startswith('0x'):
81 return int(txt[2:], 16)
82 else:
83 return int(txt)
84
85
86 def load_layout(filename):
87 """ Load a linker layout file which contains directives where sections
88 must be placed into memory. """
89 try:
90 with open(filename, 'r') as f:
91 layout = json.load(f)
92 except OSError as e:
93 raise TaskError(str(e))
94 for s in layout:
95 layout[s] = make_num(layout[s])
96 return layout
97
98
99 @register_task("link")
100 class LinkTask(Task):
124 """ Link together a collection of object files """ 101 """ Link together a collection of object files """
125 def __init__(self, objects, layout, output_file): 102 def run(self):
126 super().__init__('Link') 103 layout = load_layout(self.relpath(self.get_argument('layout')))
127 self.objects = list(map(fix_object, objects)) 104 objects = self.open_file_set(self.get_argument('objects'))
128 self.linker = Linker() 105 output_file = self.relpath(self.get_argument('output'))
129 self.duration = 0.1337
130 self.layout = layout
131 self.output_file = output_file
132 106
133 def run(self):
134 try: 107 try:
135 output_obj = self.linker.link(self.objects, self.layout) 108 output_obj = link(objects, layout)
136 except CompilerError as e: 109 except CompilerError as e:
137 raise TaskError(e.msg) 110 raise TaskError(e.msg)
111 # TODO: use layout here:
138 code = output_obj.get_section('code').data 112 code = output_obj.get_section('code').data
139 with open(self.output_file, 'wb') as f: 113 with open(output_file, 'wb') as f:
140 f.write(code) 114 f.write(code)
141 115
142 116
143 class ObjCopy(BuildTask): 117 class ObjCopyTask(Task):
144 def __init__(self, objects, output_file): 118 def run(self):
145 super().__init__('ObjCopy') 119 pass
146 120