action.py 9.05 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2013 CERN
# Author: Pawel Szostek (pawel.szostek@cern.ch)
#
# This file is part of Hdlmake.
#
# Hdlmake is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Hdlmake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Hdlmake.  If not, see <http://www.gnu.org/licenses/>.
21

22 23
"""This module provides the common stuff for the different supported actions"""

24 25

from __future__ import print_function
26
from __future__ import absolute_import
27
import os
28
import logging
29 30
import sys

31
from hdlmake.tools import load_syn_tool, load_sim_tool
32
from hdlmake.util import shell
33
from hdlmake.util.termcolor import colored
34
from hdlmake import new_dep_solver as dep_solver
35
from hdlmake.srcfile import SourceFileSet, VHDLFile, VerilogFile, SVFile
36

37 38 39 40
def set_logging_level(options):
    """Set the log level and config (A.K.A. log verbosity)"""
    numeric_level = getattr(logging, options.log.upper(), None)
    if not isinstance(numeric_level, int):
41
        raise Exception('Invalid log level: %s' % options.log)
42

43
    if not shell.check_windows() and options.logfile == None:
44 45 46 47 48 49 50 51 52 53 54 55
        logging.basicConfig(
            format=colored(
                "%(levelname)s",
                "yellow") + colored(
                "\t%(filename)s:%(lineno)d: %(funcName)s()\t",
                "blue") + "%(message)s",
            level=numeric_level)
    else:
        logging.basicConfig(
            format="%(levelname)s" +
                   "\t%(filename)s:%(lineno)d: %(funcName)s()\t" +
                   "%(message)s",
56 57
            level=numeric_level,
            filename=options.logfile)
58 59 60
    logging.debug(str(options))


61
class Action(list):
62

63
    """This is the base class providing the common Action methods"""
64

65
    def __init__(self, options):
66
        super(Action, self).__init__()
67
        self.top_module = None
68 69
        self.parseable_fileset = SourceFileSet()
        self.privative_fileset = SourceFileSet()
70
        self._deps_solved = False
71
        self.options = options
72
        set_logging_level(options)
73 74
        self.new_module(parent=None,
                         url=os.getcwd(),
75
                         source=None,
76
                         fetchto=".")
77
        self.config = self._get_config_dict()
78 79
        action = self.config.get("action")
        if action == None:
80 81
            self.tool = None            
            self.top_entity = self.config.get("top_module", None)
82 83
        elif action == "simulation":
            self.tool = load_sim_tool(self.config.get("sim_tool"))
84 85 86
            if (self.config.get("sim_top") == None and
                    not self.config.get("top_module") == None):
                self.config["sim_top"] = self.config["top_module"]
87
            self.top_entity = self.config.get("sim_top")
88 89
        elif action == "synthesis":
            self.tool = load_syn_tool(self.config.get("syn_tool"))
90 91 92
            if (self.config.get("syn_top") == None and
                    not self.config.get("top_module") == None):
                self.config["syn_top"] = self.config["top_module"]
93
            self.top_entity = self.config.get("syn_top")
94
        else:
95
            raise Exception("Unknown requested action: %s", action)
96

97 98 99 100 101 102 103 104 105 106 107
    def new_module(self, parent, url, source, fetchto):
        """Add new module to the pool.

        This is the only way to add new modules to the pool
        Thanks to it the pool can easily control its content

        NOTE: the first module added to the pool will become the top_module!.
        """
        from hdlmake.module import Module, ModuleArgs
        self._deps_solved = False
        new_module_args = ModuleArgs()
108
        new_module_args.set_args(parent, url, source, fetchto)
109 110 111 112 113 114 115 116
        new_module = Module(new_module_args, self)
        if not self.__contains(new_module):
            self._add(new_module)
            if not self.top_module:
                self.top_module = new_module
                new_module.parse_manifest()
        return new_module

117
    def _check_manifest_variable_is_set(self, name):
118
        """Method to check if a specific manifest variable is set"""
119
        if getattr(self.top_module, name) is None:
120
            raise Exception(
121 122 123
                "Variable %s must be set in the manifest "
                "to perform current action (%s)",
                name, self.__class__.__name__)
124

125 126 127 128 129 130 131 132
    def _check_manifest_variable_value(self, name, value):
        """Method to check if a manifest variable is set to a specific value"""
        variable_match = False
        manifest_value = getattr(self.top_module, name)
        if manifest_value == value:
            variable_match = True

        if variable_match is False:
133
            raise Exception(
134
                "Variable %s must be set in the manifest and equal to '%s'.",
135
                name, value)
136 137 138 139 140 141 142 143 144 145

    def build_complete_file_set(self):
        """Build file set with all the files listed in the complete pool"""
        logging.debug("Begin build complete file set")
        all_manifested_files = SourceFileSet()
        for module in self:
            all_manifested_files.add(module.files)
        logging.debug("End build complete file set")
        return all_manifested_files

146
    def solve_file_set(self):
147 148
        """Build file set with only those files required by the top entity"""
        if not self._deps_solved:
149 150 151 152 153
            if self.tool == None:
                dep_solver.solve(self.parseable_fileset)
            else:
                dep_solver.solve(self.parseable_fileset,
                                 self.tool.get_standard_libs())
154
            self._deps_solved = True
155 156
        if self.options.all_files:
            return
157 158
        solved_files = SourceFileSet()
        solved_files.add(dep_solver.make_dependency_set(
159 160
            self.parseable_fileset, self.top_entity,
            self.config.get("extra_modules")))
161 162 163 164 165 166
        self.parseable_fileset = solved_files

    def build_file_set(self):
        """Initialize the parseable and privative fileset contents"""
        total_files = self.build_complete_file_set()
        for file_aux in total_files:
167 168 169 170 171 172
            if self.tool == None:
                if any(isinstance(file_aux, file_type)
                       for file_type in [VHDLFile, VerilogFile, SVFile]):
                    self.parseable_fileset.add(file_aux)
                else:
                    self.privative_fileset.add(file_aux)
173
            else:
174 175 176 177 178 179 180 181 182
                if any(isinstance(file_aux, file_type)
                       for file_type in self.tool.get_parseable_files()):
                    self.parseable_fileset.add(file_aux)
                elif any(isinstance(file_aux, file_type)
                       for file_type in self.tool.get_privative_files()):
                    self.privative_fileset.add(file_aux)
                else:
                    logging.debug("File not supported by the tool: %s",
                                  file_aux.path)
183 184 185 186 187 188
        if len(self.privative_fileset) > 0:
            logging.info("Detected %d supported files that are not parseable",
                         len(self.privative_fileset))
        if len(self.parseable_fileset) > 0:
            logging.info("Detected %d supported files that can be parsed",
                         len(self.parseable_fileset))
189 190 191 192 193

    def get_top_module(self):
        """Get the Top module from the pool"""
        return self.top_module

194
    def _get_config_dict(self):
195 196 197 198
        """Get the combined hierarchical Manifest dictionary from the pool"""
        config_dict = {}
        for mod in self:
            manifest_dict_tmp = mod.manifest_dict
199 200 201 202 203 204 205
            if not manifest_dict_tmp == None:
                if 'fetchto' in manifest_dict_tmp:
                    manifest_dict_tmp['fetchto'] = os.path.relpath(os.path.join(
                        mod.path,
                        mod.manifest_dict['fetchto']))
                manifest_dict_tmp.update(config_dict)
                config_dict = manifest_dict_tmp
206 207
        return config_dict

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    def _add(self, new_module):
        """Add the given new module if this is not already in the pool"""
        from hdlmake.module import Module
        if not isinstance(new_module, Module):
            raise RuntimeError("Expecting a Module instance")
        if self.__contains(new_module):
            return False
        if new_module.isfetched:
            for mod in new_module.submodules():
                self._add(mod)
        self.append(new_module)
        return True

    def __contains(self, module):
        """Check if the pool contains the given module by checking the URL"""
        for mod in self:
            if mod.url == module.url:
                return True
        return False

    def __str__(self):
        """Cast the module list as a list of strings"""
        return str([str(m) for m in self])