dep_file.py 7.83 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#!/usr/bin/python
#
# 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/>.
#

22 23
"""Module providing the Classes used to provide and handle dependable files"""

24 25
from __future__ import absolute_import
from __future__ import print_function
26
import os
27
import logging
28 29

from .util import path as path_mod
30
import six
31 32 33


class DepRelation(object):
34 35 36

    """Class used to create instances representing HDL dependency relations"""

Paweł Szostek's avatar
Paweł Szostek committed
37
    # direction
38 39 40
    PROVIDE = 1
    USE = 2

Paweł Szostek's avatar
Paweł Szostek committed
41
    # rel_type
42 43 44
    ENTITY = 1
    PACKAGE = 2
    INCLUDE = 3
45
    ARCHITECTURE = 4
46
    MODULE = ENTITY
47 48

    def __init__(self, obj_name, direction, rel_type):
Paweł Szostek's avatar
Paweł Szostek committed
49
        assert direction in [DepRelation.PROVIDE, DepRelation.USE]
50 51 52 53 54 55
        assert rel_type in [
            DepRelation.ENTITY,
            DepRelation.PACKAGE,
            DepRelation.INCLUDE,
            DepRelation.ARCHITECTURE,
            DepRelation.MODULE]
56 57
        self.direction = direction
        self.rel_type = rel_type
58
        self.obj_name = obj_name.lower()
59 60

    def satisfies(self, rel_b):
61
        """Check if the current dependency relation matches the provided one"""
62
        if (rel_b.direction == DepRelation.PROVIDE or
63
                self.direction == DepRelation.USE):
64 65 66 67 68 69
            return False
        if rel_b.rel_type == self.rel_type and rel_b.obj_name == self.obj_name:
            return True
        return False

    def library(self):
70 71
        """If the current relation type is PACKAGE, it returns the base name of
        the library, e.g. for work.counter it returns work."""
72 73 74
        if self.rel_type == DepRelation.PACKAGE:
            libdotpackage = self.obj_name
            try:
75
                return libdotpackage.split('.')[0]
76 77 78 79 80
            except ValueError:
                return None
        else:
            return None

81
    def __repr__(self):
82
        dstr = {self.USE: "Use", self.PROVIDE: "Provide"}
83 84 85 86 87 88
        ostr = {
            self.ENTITY: "entity",
            self.PACKAGE: "package",
            self.INCLUDE: "include/header",
            self.ARCHITECTURE: "architecture",
            self.MODULE: "module"}
89 90 91
        return "%s %s '%s'" % (dstr[self.direction],
                               ostr[self.rel_type],
                               self.obj_name)
92

93 94 95
    def __hash__(self):
        return hash(self.__repr__())

96 97 98 99 100 101 102 103 104
    def __eq__(self, other):
        return (isinstance(other, self.__class__)
                and self.__dict__ == other.__dict__)

    def __ne__(self, other):
        return not self.__eq__(other)


class File(object):
105

106 107
    """This is the base class for all of the different files in HDLMake"""

108 109
    def __init__(self, path, module=None):
        self.path = path
110
        assert not isinstance(module, six.string_types)
111
        self.module = module
112 113 114

    @property
    def name(self):
115 116
        """Property defined as a method that gets the basename of the file
        path, i.e. it strips the path and takes the full file name"""
117 118 119 120
        return os.path.basename(self.path)

    @property
    def purename(self):
121 122
        """Property defined as a method that gets the name of the file
        and strips put the extension from the file"""
123 124 125 126
        return os.path.splitext(self.name)[0]

    @property
    def dirname(self):
127 128
        """Property defined as a method that gets the name of the directory
        in which the file is stored"""
129 130
        return os.path.dirname(self.path)

131 132 133 134 135 136 137
    def rel_path(self, directory=None):
        """Returns the relative path for the file calculated with (directory)
        as the origin reference -- if none, it will be defaulted to current
        folder from which we are launching the program"""
        if directory is None:
            directory = os.getcwd()
        return path_mod.relpath(self.path, directory)
138 139 140 141 142

    def __str__(self):
        return self.path

    def __eq__(self, other):
143 144 145 146
        _not_found = object()
        path_self, path_other = [getattr(obj, "path", _not_found)
                                 for obj in [self, other]]
        if path_self is _not_found or path_other is _not_found:
147
            return False
148
        elif path_self != path_other:
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
            return False
        return True

    def __hash__(self):
        return hash(self.path)

    def __cmp__(self, other):
        if self.path < other.path:
            return -1
        if self.path == other.path:
            return 0
        if self.path > other.path:
            return 1

    def __ne__(self, other):
        return not self.__eq__(other)

    def isdir(self):
167
        """Check if the defined file path is a directory"""
168 169 170
        return os.path.isdir(self.path)

    def show(self):
171
        """Print the file path to stdout"""
172
        print(self.path)
173 174

    def extension(self):
175
        """Method that gets the extension for the file instance"""
176
        tmp = self.path.rsplit('.')
177
        ext = tmp[len(tmp) - 1]
178 179 180 181
        return ext


class DepFile(File):
182

183 184 185
    """Class that serves as base to all those HDL files that can be
    parsed and solved (Verilog, SystemVerilog, VHDL)"""

186
    def __init__(self, file_path, module):
187
        assert isinstance(file_path, six.string_types)
188 189
        File.__init__(self, path=file_path, module=module)
        self.file_path = file_path
190
        self.rels = set()
191
        self.depends_on = set()
192
        self.dep_level = None
193
        self.is_parsed = False
194
        self.file_path = file_path
195
        self.include_paths = []
196

197
    def add_relation(self, rel):
198 199
        """Add a new relation to the set provided by the file"""
        self.rels.add(rel)
200 201

    def satisfies(self, rel_b):
202 203
        """Check if any of the file object relations match any of the relations
        listed in the parameter (rel_b)"""
204
        assert isinstance(rel_b, DepRelation)
205
        # self._parse_if_needed()
206
        return any([x.satisfies(rel_b) for x in self.rels])
207 208

    def show_relations(self):
209
        """Print the file relations to stdout: can be used for logging"""
210
        # self._parse_if_needed()
211
        for relation in self.rels:
212
            print(str(relation))
213 214 215

    @property
    def filename(self):
216 217
        """Property defined as a method that checks the basename of the file
        path in the host, i.e. the name of the last directory on the path"""
218
        return os.path.basename(self.file_path)
219 220

    def get_dep_level(self):
221 222
        """Get the dependency level for the file instance, so we can order
        later the full fileset"""
223
        if self.dep_level is None:
224 225 226
            if len(self.depends_on) == 0:
                self.dep_level = 0
            else:
227 228
                # set dep_level to a negative value so we can detect
                # if the recusion below brings us back to
229 230
                # this file in a circular reference, that would otherwise
                # result in an infinite loop.
Will's avatar
Will committed
231
                self.dep_level = -1
232
                # recurse, to find the largest number of levels below.
233 234
                self.dep_level = 1 + \
                    max([dep.get_dep_level() for dep in self.depends_on])
Will's avatar
Will committed
235
        elif self.dep_level < 0:
236 237 238 239
            logging.warning("Probably run into a circular reference of file "
                            "dependencies. It appears %s depends on itself, "
                            "indirectly via atleast one other file.",
                            self.file_path)
240
        return self.dep_level