root URL must be accompanied with branch name
[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 log
10 from config import config
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, branch=None, flags=[]):
25                 self.name = name
26
27                 self.source = Repository(
28                         'source/%s' % name,
29                         os.path.join(config.top_dir, 'src', name),
30                         branch,
31                         exclude=['meta'])
32
33                 self.meta = Repository(
34                         'meta/%s' % name,
35                         os.path.join(config.top_dir, 'src', name, 'meta'),
36                         branch)
37
38                 self.flags = flags
39
40                 self.packages = {}
41                 self.depends = set()
42
43         def __str__(self):
44                 return str(self.source)
45
46         def is_dirty(self):
47                 if self.__dirty is None:
48                         for repo in (self.source, self.meta):
49                                 if repo.is_dirty():
50                                         self.__dirty = True
51                                         break
52
53                         if self.__dirty is None:
54                                 self.__dirty = False
55
56                 return self.__dirty
57
58         def add_depend(self, c):
59                 self.depends.add(c)
60
61         def remove_depend(self, c):
62                 self.depends.remove(c)
63                 return not self.depends
64
65         def get_depends(self):
66                 return self.depends
67
68         def add_rdepend(self, c):
69                 if self.rdepends is None:
70                         self.rdepends = set()
71
72                 self.rdepends.add(c)
73
74         def get_rdepends(self):
75                 return self.rdepends or ()
76
77         def clear_rdepends(self):
78                 if self.rdepends:
79                         del self.rdepends
80
81 class PlatformProvidedComponent(Component):
82         """A Component that is provided by the platform.
83            The sources will not be built during install."""
84
85         cached = True
86
87 def init():
88         targets = []
89         packages = {}
90
91         for c in config.components.itervalues():
92                 if not c.meta.exists():
93                         c.meta.clone()
94
95                 init_packages(c, targets, packages)
96
97         init_depends(packages)
98
99         targets.sort()
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)