remove dirty components from cache
[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.repo = Repository(
32                         'repo/%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 is_dirty(self):
48                 if self.__dirty is None:
49                         self.__dirty = False
50                         for repo in (self.repo, self.meta):
51                                 if repo.dirty_files():
52                                         self.__dirty = True
53                                         break
54                 return self.__dirty
55
56         def add_depend(self, c):
57                 self.depends.add(c)
58
59         def remove_depend(self, c):
60                 self.depends.remove(c)
61                 return not self.depends
62
63         def get_depends(self):
64                 return self.depends
65
66         def add_rdepend(self, c):
67                 if self.rdepends is None:
68                         self.rdepends = set()
69
70                 self.rdepends.add(c)
71
72         def get_rdepends(self):
73                 return self.rdepends or ()
74
75         def clear_rdepends(self):
76                 if self.rdepends:
77                         del self.rdepends
78
79 class PlatformProvidedComponent(Component):
80         """A Component that is provided by the platform.
81            The sources will not be built during install."""
82
83         cached = True
84
85 def init():
86         targets = []
87         packages = {}
88
89         for c in config.components.itervalues():
90                 c.repo.init()
91                 c.meta.init()
92
93                 if not c.meta.exists():
94                         c.meta.clone()
95
96                 init_packages(c, targets, packages)
97
98         init_depends(packages)
99
100         return targets
101
102 class Package(object):
103         def __init__(self, name, component):
104                 self.name = name
105                 self.component = component
106
107                 self.depends = ''
108                 self.conflicts = ''
109                 self.architectures = []
110
111 def init_packages(c, targets, packages):
112         for path in glob(os.path.join(c.meta.path, '*.package')):
113                 name = os.path.basename(path)[:-8]
114
115                 pkg = parse_package(name, c, path)
116                 if not pkg:
117                         continue
118
119                 c.packages[name] = pkg
120                 packages[name] = pkg
121
122                 if config.debug:
123                         print 'Component', c.name, 'provides', name
124
125         if c.packages:
126                 targets.append(c.name)
127         elif config.debug:
128                 print 'Component', c.name, 'does not provide any packages'
129
130 def parse_package(name, component, path):
131         pkg = Package(name, component)
132         execfile(path, pkg.__dict__, pkg.__dict__)
133
134         if pkg.architectures:
135                 arch = config.boards[config.board].arch
136                 if arch not in pkg.architectures:
137                         return None
138
139         return pkg
140
141 class Dependency(object):
142         regex = re.compile(r'([@]?)([^\s:]+)[:]?([<>=]*)([^\s:]*)[:]?(.*)')
143
144         def __init__(self, spec):
145                 match = self.regex.match(spec)
146                 if not match:
147                         raise Error('Bad dependency specification: ' + spec)
148
149                 self.build, self.name, self.tag_op, self.tag, flags \
150                         = match.groups()
151                 self.flags = flags.split()
152
153         def check(self, packages):
154                 # TODO: check version and flags
155                 return self.name in packages
156
157 def init_depends(packages):
158         for pkg in packages.itervalues():
159                 for spec in pkg.depends.split():
160                         depname = Dependency(spec).name
161                         deppkg = packages.get(depname)
162
163                         if not deppkg:
164                                 log.error('Package %s depends on ' \
165                                           'non-existent package %s' % \
166                                           (pkg.name, depname))
167                                 continue
168
169                         if deppkg.component == pkg.component:
170                                 continue
171
172                         pkg.component.add_depend(deppkg.component)
173
174         fail = False
175         for pkg in packages.itervalues():
176                 for spec in pkg.depends.split():
177                         if not Dependency(spec).check(packages):
178                                 fail = True
179                                 log.error('Dependency %s failed for %s' % \
180                                           (spec, pkg.name))
181
182                 for spec in pkg.conflicts.split():
183                         if Dependency(spec).check(packages):
184                                 fail = True
185                                 log.error('Package %s conflicts with %s' % \
186                                           (pkg.name, spec))
187
188         if fail:
189                 raise Error('Invalid component tree')
190
191 def fill_in_depends(components):
192         depends = True
193         while depends:
194                 depends = set()
195
196                 for c in components:
197                         for dep in c.get_depends():
198                                 if dep not in components:
199                                         depends.add(dep)
200
201                 components |= depends
202
203 def init_rdepends(components):
204         for c in components:
205                 for dep in c.get_depends():
206                         dep.add_rdepend(c)