optimize dirty files check
[matrix.git] / matrix / components.py
1 # Copyright (C) 2006-2008 Movial Oy
2 # Authors: Timo Savola <tsavola@movial.fi>
3
4 import os
5 import re
6 from glob import glob
7 from sets import Set as set
8
9 import config
10 import log
11 from repositories import Repository
12
13 Error = RuntimeError
14
15 class Component(object):
16         __dirty = None
17
18         cached = None
19
20         rdepends = None
21         weight = None
22         state = None
23
24         def __init__(self, name, tag='master', tags={}, flags=[]):
25                 commits = {}
26                 commits.update(tags)
27                 commits[None] = tag
28
29                 self.name = name
30
31                 self.source = Repository(
32                         'source/%s' % name,
33                         os.path.join(config.top_dir, 'src', name),
34                         commits,
35                         exclude=['meta'])
36
37                 self.meta = Repository(
38                         'meta/%s' % name,
39                         os.path.join(config.top_dir, 'src', name, 'meta'),
40                         commits)
41
42                 self.flags = flags
43
44                 self.packages = {}
45                 self.depends = set()
46
47         def __str__(self):
48                 return str(self.source)
49
50         def is_dirty(self):
51                 if self.__dirty is None:
52                         for repo in (self.source, self.meta):
53                                 if repo.is_dirty():
54                                         self.__dirty = True
55                                         break
56
57                         if self.__dirty is None:
58                                 self.__dirty = False
59
60                 return self.__dirty
61
62         def add_depend(self, c):
63                 self.depends.add(c)
64
65         def remove_depend(self, c):
66                 self.depends.remove(c)
67                 return not self.depends
68
69         def get_depends(self):
70                 return self.depends
71
72         def add_rdepend(self, c):
73                 if self.rdepends is None:
74                         self.rdepends = set()
75
76                 self.rdepends.add(c)
77
78         def get_rdepends(self):
79                 return self.rdepends or ()
80
81         def clear_rdepends(self):
82                 if self.rdepends:
83                         del self.rdepends
84
85 class PlatformProvidedComponent(Component):
86         """A Component that is provided by the platform.
87            The sources will not be built during install."""
88
89         cached = True
90
91 def init():
92         targets = []
93         packages = {}
94
95         for c in config.components.itervalues():
96                 if not c.meta.exists():
97                         c.meta.clone()
98
99                 init_packages(c, targets, packages)
100
101         init_depends(packages)
102
103         targets.sort()
104         return targets
105
106 class Package(object):
107         def __init__(self, name, component):
108                 self.name = name
109                 self.component = component
110
111                 self.depends = ''
112                 self.conflicts = ''
113                 self.architectures = []
114
115 def init_packages(c, targets, packages):
116         for path in glob(os.path.join(c.meta.path, '*.package')):
117                 name = os.path.basename(path)[:-8]
118
119                 pkg = parse_package(name, c, path)
120                 if not pkg:
121                         continue
122
123                 c.packages[name] = pkg
124                 packages[name] = pkg
125
126                 if config.debug:
127                         print 'Component', c.name, 'provides', name
128
129         if c.packages:
130                 targets.append(c.name)
131         elif config.debug:
132                 print 'Component', c.name, 'does not provide any packages'
133
134 def parse_package(name, component, path):
135         pkg = Package(name, component)
136         execfile(path, pkg.__dict__, pkg.__dict__)
137
138         if pkg.architectures:
139                 arch = config.boards[config.board].arch
140                 if arch not in pkg.architectures:
141                         return None
142
143         return pkg
144
145 class Dependency(object):
146         regex = re.compile(r'([@]?)([^\s:]+)[:]?([<>=]*)([^\s:]*)[:]?(.*)')
147
148         def __init__(self, spec):
149                 match = self.regex.match(spec)
150                 if not match:
151                         raise Error('Bad dependency specification: ' + spec)
152
153                 self.build, self.name, self.tag_op, self.tag, flags \
154                         = match.groups()
155                 self.flags = flags.split()
156
157         def check(self, packages):
158                 # TODO: check version and flags
159                 return self.name in packages
160
161 def init_depends(packages):
162         for pkg in packages.itervalues():
163                 for spec in pkg.depends.split():
164                         depname = Dependency(spec).name
165                         deppkg = packages.get(depname)
166
167                         if not deppkg:
168                                 log.error('Package %s depends on ' \
169                                           'non-existent package %s' % \
170                                           (pkg.name, depname))
171                                 continue
172
173                         if deppkg.component == pkg.component:
174                                 continue
175
176                         pkg.component.add_depend(deppkg.component)
177
178         fail = False
179         for pkg in packages.itervalues():
180                 for spec in pkg.depends.split():
181                         if not Dependency(spec).check(packages):
182                                 fail = True
183                                 log.error('Dependency %s failed for %s' % \
184                                           (spec, pkg.name))
185
186                 for spec in pkg.conflicts.split():
187                         if Dependency(spec).check(packages):
188                                 fail = True
189                                 log.error('Package %s conflicts with %s' % \
190                                           (pkg.name, spec))
191
192         if fail:
193                 raise Error('Invalid component tree')
194
195 def fill_in_depends(components):
196         depends = True
197         while depends:
198                 depends = set()
199
200                 for c in components:
201                         for dep in c.get_depends():
202                                 if dep not in components:
203                                         depends.add(dep)
204
205                 components |= depends
206
207 def init_rdepends(components):
208         for c in components:
209                 for dep in c.get_depends():
210                         dep.add_rdepend(c)