view python/zcc.py @ 336:d1ecc493384e

Added spiffy armtoken class for bit fiddeling. Added cool test that checks for build repeatability
author Windel Bouwman
date Wed, 19 Feb 2014 22:32:15 +0100
parents 6f4753202b9a
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))