initialize repository url on demand
[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                 if not c.meta.exists():
91                         c.meta.clone()
92
93                 init_packages(c, targets, packages)
94
95         init_depends(packages)
96
97         return targets
98
99 class Package(object):
100         def __init__(self, name, component):
101                 self.name = name
102                 self.component = component
103
104                 self.depends = ''
105                 self.conflicts = ''
106                 self.architectures = []
107
108 def init_packages(c, targets, packages):
109         for path in glob(os.path.join(c.meta.path, '*.package')):
110                 name = os.path.basename(path)[:-8]
111
112                 pkg = parse_package(name, c, path)
113                 if not pkg:
114                         continue
115
116                 c.packages[name] = pkg
117                 packages[name] = pkg
118
119                 if config.debug:
120                         print 'Component', c.name, 'provides', name
121
122         if c.packages:
123                 targets.append(c.name)
124         elif config.debug:
125                 print 'Component', c.name, 'does not provide any packages'
126
127 def parse_package(name, component, path):
128         pkg = Package(name, component)
129         execfile(path, pkg.__dict__, pkg.__dict__)
130
131         if pkg.architectures:
132                 arch = config.boards[config.board].arch
133                 if arch not in pkg.architectures:
134                         return None
135
136         return pkg
137
138 class Dependency(object):
139         regex = re.compile(r'([@]?)([^\s:]+)[:]?([<>=]*)([^\s:]*)[:]?(.*)')
140
141         def __init__(self, spec):
142                 match = self.regex.match(spec)
143                 if not match:
144                         raise Error('Bad dependency specification: ' + spec)
145
146                 self.build, self.name, self.tag_op, self.tag, flags \
147                         = match.groups()
148                 self.flags = flags.split()
149
150         def check(self, packages):
151                 # TODO: check version and flags
152                 return self.name in packages
153
154 def init_depends(packages):
155         for pkg in packages.itervalues():
156                 for spec in pkg.depends.split():
157                         depname = Dependency(spec).name
158                         deppkg = packages.get(depname)
159
160                         if not deppkg:
161                                 log.error('Package %s depends on ' \
162                                           'non-existent package %s' % \
163                                           (pkg.name, depname))
164                                 continue
165
166                         if deppkg.component == pkg.component:
167                                 continue
168
169                         pkg.component.add_depend(deppkg.component)
170
171         fail = False
172         for pkg in packages.itervalues():
173                 for spec in pkg.depends.split():
174                         if not Dependency(spec).check(packages):
175                                 fail = True
176                                 log.error('Dependency %s failed for %s' % \
177                                           (spec, pkg.name))
178
179                 for spec in pkg.conflicts.split():
180                         if Dependency(spec).check(packages):
181                                 fail = True
182                                 log.error('Package %s conflicts with %s' % \
183                                           (pkg.name, spec))
184
185         if fail:
186                 raise Error('Invalid component tree')
187
188 def fill_in_depends(components):
189         depends = True
190         while depends:
191                 depends = set()
192
193                 for c in components:
194                         for dep in c.get_depends():
195                                 if dep not in components:
196                                         depends.add(dep)
197
198                 components |= depends
199
200 def init_rdepends(components):
201         for c in components:
202                 for dep in c.get_depends():
203                         dep.add_rdepend(c)