view python/zcc.py @ 338:8eb4a6fe8fc8

Added testcase with emulator
author Windel Bouwman
date Fri, 21 Feb 2014 13:31:29 +0100
parents d1ecc493384e
children 86b02c98a717
line wrap: on
line source

#!/usr/bin/env python

import sys
import os
import argparse
import logging
import yaml

from ppci.buildtasks import Compile, Assemble, Link
from ppci.tasks import TaskRunner
from ppci.report import RstFormatter
from ppci.objectfile import ObjectFile
from target.target_list import target_list
import ppci


def logLevel(s):
    """ Converts a string to a valid logging level """
    numeric_level = getattr(logging, s.upper(), None)
    if not isinstance(numeric_level, int):
        raise ValueError('Invalid log level: {}'.format(s))
    return numeric_level


targets = {t.name: t for t in target_list}
targetnames = list(targets.keys())

def make_parser():
    parser = argparse.ArgumentParser(description='lcfos Compiler')

    parser.add_argument('--log', help='Log level (INFO,DEBUG,[WARN])',
                        type=logLevel, default='INFO')
    parser.add_argument('--display-build-steps', action='store_true')
    sub_parsers = parser.add_subparsers(title='commands',
         description='possible commands', dest='command')
    recipe_parser = sub_parsers.add_parser('recipe', help="Bake recipe")
    recipe_parser.add_argument('recipe_file', help='recipe file')

    compile_parser = sub_parsers.add_parser('compile', help="compile source")
    compile_parser.add_argument('source', type=argparse.FileType('r'),
      help='the source file to build', nargs="+")
    compile_parser.add_argument('-i', '--imp', type=argparse.FileType('r'),
      help='Possible import module', action='append', default=[])
    compile_parser.add_argument('--target', help="Backend selection",
        choices=targetnames, required=True)
    compile_parser.add_argument('-o', '--output', help='Output file',
        metavar='filename')
    compile_parser.add_argument('--report',
                help='Specify a file to write the compile report to',
                type=argparse.FileType('w'))
    return parser


class RecipeLoader:
    """ Loads a recipe into a runner from a dictionary or file """
    def __init__(self):
        self.directive_handlers = {}
        for a in dir(self):
            if a.startswith('handle_'):
                f = getattr(self, a)
                self.directive_handlers[a[7:]] = f

    def load_file(self, recipe_file, runner):
        """ Loads a recipe dictionary into a task runner """
        self.recipe_dir = os.path.abspath(os.path.dirname(recipe_file))
        with open(recipe_file, 'r') as f:
            recipe = yaml.load(f)
        self.runner = runner
        self.load_dict(recipe)

    def relpath(self, filename):
        return os.path.join(self.recipe_dir, filename)

    def openfile(self, filename):
        return open(self.relpath(filename), 'r')

    def handle_compile(self, value):
        sources = [self.openfile(s) for s in value['sources']]
        includes = [self.openfile(i) for i in value['includes']]
        target = targets[value['machine']]
        output = ObjectFile()
        task = Compile(sources, includes, target, output)
        self.runner.add_task(task)
        return task

    def handle_assemble(self, value):
        asm_src = self.openfile(value['source'])
        target = targets[value['machine']]
        output = ObjectFile()
        task = Assemble(asm_src, target, output)
        self.runner.add_task(task)
        return task

    def handle_link(self, value):
        inputs = value['inputs']
        objs = []
        for i in inputs:
            task = self.load_dict(i)
            objs.append(task.output)
        layout = value['layout']
        output = self.relpath(value['output'])
        self.runner.add_task(Link(objs, layout, output))

    def handle_apps(self, value):
        for a in value:
            self.load_dict(a)

    def load_dict(self, recipe):
        for command, value in recipe.items():
            return self.directive_handlers[command](value)


def main(args):
    # Configure some logging:
    logging.getLogger().setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setFormatter(logging.Formatter(ppci.logformat))
    ch.setLevel(args.log)
    logging.getLogger().addHandler(ch)

    runner = TaskRunner()
    if args.command == 'compile':
        tg = targets[args.target]
        output = ObjectFile()
        runner.add_task(Compile(args.source, args.imp, tg, output))
    elif args.command == 'recipe':
        recipe_loader = RecipeLoader()
        recipe_loader.load_file(args.recipe_file, runner)
    else:
        raise NotImplementedError('Invalid option')

    if args.display_build_steps:
        runner.display()
        res = 0
    else:
        res = runner.run_tasks()

    logging.getLogger().removeHandler(ch)
    return res


if __name__ == '__main__':
    parser = make_parser()
    arguments = parser.parse_args()
    if not arguments.command:
        parser.print_usage()
        sys.exit(1)
    sys.exit(main(arguments))