view python/ppci/buildtasks.py @ 366:39bf68bf1891

Fix sample tests and deterministic build
author Windel Bouwman
date Fri, 21 Mar 2014 09:43:01 +0100
parents 5477e499b039
children 9667d78ba79e
line wrap: on
line source


"""
Defines task classes that can compile, link etc..
Task can depend upon one another.
"""

import logging

from .c3 import Builder
from .irutils import Verifier
from .codegen import CodeGenerator
from .transform import CleanPass, RemoveAddZero
from .tasks import Task, TaskError
from . import DiagnosticsManager, CompilerError
from .assembler import Assembler
from .objectfile import ObjectFile
from .linker import Linker
from .outstream import BinaryOutputStream, MasterOutputStream, LoggerOutputStream
from .target.target_list import targets
from .target import Target


def fix_target(tg):
    """ Try to return an instance of the Target class """
    if isinstance(tg, Target):
        return tg
    elif isinstance(tg, str):
        if tg in targets:
            return targets[tg]
        else:
            raise TaskError('Target {} not found'.format(tg))
    raise TaskError('Invalid target {}'.format(tg))


def fix_file(f):
    """ Determine if argument is a file like object or make it so! """
    if hasattr(f, 'read'):
        # Assume this is a file like object
        return f
    elif isinstance(f, str):
        return open(f, 'r')
    else:
        raise TaskError('cannot use {} as input'.format(f))

def fix_object(o):
    if isinstance(o, ObjectFile):
        return o
    else:
        raise TaskError('Cannot use {} as objectfile'.format(o))

class BuildTask(Task):
    """ Base task for all kind of weird build tasks """
    def __init__(self, name):
        super().__init__(name)
        self.logger = logging.getLogger('buildtask')


class Assemble(BuildTask):
    """ Task that can runs the assembler over the source and enters the 
        output into an object file """
    def __init__(self, source, target, output_object):
        super().__init__('Assemble')
        self.source = fix_file(source)
        self.output = output_object
        o2 = BinaryOutputStream(self.output)
        o1 = LoggerOutputStream()
        self.ostream = MasterOutputStream([o1, o2])
        self.assembler = Assembler(fix_target(target))

    def run(self):
        self.logger.debug('Assembling into code section')
        self.ostream.select_section('code')
        self.assembler.assemble(self.source, self.ostream)
        self.logger.debug('Assembling finished')


class Compile(BuildTask):
    """ Task that compiles C3 source for some target into an object file """
    def __init__(self, sources, includes, target, output_object):
        super().__init__('Compile')
        self.sources = list(map(fix_file, sources))
        self.includes = list(map(fix_file, includes))
        self.target = fix_target(target)
        self.output = output_object

    def run(self):
        self.logger.debug('Compile started')
        diag = DiagnosticsManager()
        c3b = Builder(diag, self.target)
        cg = CodeGenerator(self.target)

        for ircode in c3b.build(self.sources, self.includes):
            if not ircode:
                # Something went wrong, do not continue the code generation
                continue

            d = {'ircode':ircode}
            self.logger.debug('Verifying code {}'.format(ircode), extra=d)
            Verifier().verify(ircode)

            # Optimization passes:
            CleanPass().run(ircode)
            Verifier().verify(ircode)
            RemoveAddZero().run(ircode)
            Verifier().verify(ircode)
            CleanPass().run(ircode)
            Verifier().verify(ircode)

            # Code generation:
            d = {'ircode':ircode}
            self.logger.debug('Starting code generation for {}'.format(ircode), extra=d)

            o2 = BinaryOutputStream(self.output)
            o1 = LoggerOutputStream()
            o = MasterOutputStream([o1, o2])
            cg.generate(ircode, o)

        if not c3b.ok:
            diag.printErrors()
            raise TaskError('Compile errors')


class Link(BuildTask):
    """ Link together a collection of object files """
    def __init__(self, objects, layout, output_file):
        super().__init__('Link')
        self.objects = list(map(fix_object, objects))
        self.linker = Linker()
        self.duration = 0.1337
        self.layout = layout
        self.output_file = output_file

    def run(self):
        try:
            output_obj = self.linker.link(self.objects, self.layout)
        except CompilerError as e:
            raise TaskError(e.msg)
        code = output_obj.get_section('code').data
        with open(self.output_file, 'wb') as f:
            f.write(code)


class ObjCopy(BuildTask):
    def __init__(self, objects, output_file):
        super().__init__('ObjCopy')