cache cache status
[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         cached = None
17
18         state = None
19
20         def __init__(self, name, tag='master', tags={}, flags=[]):
21                 commits = {}
22                 commits.update(tags)
23                 commits[None] = tag
24
25                 self.name = name
26
27                 self.repo = Repository(
28                         'repo/%s' % name,
29                         os.path.join(config.top_dir, 'src', name),
30                         commits,
31                         exclude=['meta'])
32
33                 self.meta = Repository(
34                         'meta/%s' % name,
35                         os.path.join(config.top_dir, 'src', name, 'meta'),
36                         commits)
37
38                 self.flags = flags
39
40                 self.packages = {}
41                 self.depends = set()
42
43 class PlatformProvidedComponent(Component):
44         """A Component that is provided by the platform.
45            The sources will not be built during install."""
46
47         cached = True
48
49 def init():
50         targets = []
51         packages = {}
52
53         for c in config.components.itervalues():
54                 c.repo.init()
55                 c.meta.init()
56
57                 if not c.meta.exists():
58                         c.meta.clone()
59
60                 init_packages(c, targets, packages)
61
62         init_depends(packages)
63
64         return targets
65
66 class Package(object):
67         def __init__(self, name, component):
68                 self.name = name
69                 self.component = component
70
71                 self.depends = ''
72                 self.conflicts = ''
73                 self.architectures = []
74
75 def init_packages(c, targets, packages):
76         for path in glob(os.path.join(c.meta.path, '*.package')):
77                 name = os.path.basename(path)[:-8]
78
79                 pkg = parse_package(name, c, path)
80                 if not pkg:
81                         continue
82
83                 c.packages[name] = pkg
84                 packages[name] = pkg
85
86                 if config.debug:
87                         print 'Component', c.name, 'provides', name
88
89         if c.packages:
90                 targets.append(c.name)
91         elif config.debug:
92                 print 'Component', c.name, 'does not provide any packages'
93
94 def parse_package(name, component, path):
95         pkg = Package(name, component)
96         execfile(path, pkg.__dict__, pkg.__dict__)
97
98         if pkg.architectures:
99                 arch = config.boards[config.board].arch
100                 if arch not in pkg.architectures:
101                         return None
102
103         return pkg
104
105 class Dependency(object):
106         regex = re.compile(r'([@]?)([^\s:]+)[:]?([<>=]*)([^\s:]*)[:]?(.*)')
107
108         def __init__(self, spec):
109                 match = self.regex.match(spec)
110                 if not match:
111                         raise Error('Bad dependency specification: ' + spec)
112
113                 self.build, self.name, self.tag_op, self.tag, flags \
114                         = match.groups()
115                 self.flags = flags.split()
116
117         def check(self, packages):
118                 # TODO: check version and flags
119                 return self.name in packages
120
121 def init_depends(packages):
122         for pkg in packages.itervalues():
123                 for spec in pkg.depends.split():
124                         depname = Dependency(spec).name
125                         deppkg = packages.get(depname)
126
127                         if not deppkg:
128                                 log.error('Package %s depends on ' \
129                                           'non-existent package %s' % \
130                                           (pkg.name, depname))
131                                 continue
132
133                         if deppkg.component == pkg.component:
134                                 continue
135
136                         pkg.component.depends.add(deppkg.component)
137
138         fail = False
139         for pkg in packages.itervalues():
140                 for spec in pkg.depends.split():
141                         if not Dependency(spec).check(packages):
142                                 fail = True
143                                 log.error('Dependency %s failed for %s' % \
144                                           (spec, pkg.name))
145
146                 for spec in pkg.conflicts.split():
147                         if Dependency(spec).check(packages):
148                                 fail = True
149                                 log.error('Package %s conflicts with %s' % \
150                                           (pkg.name, spec))
151
152         if fail:
153                 raise Error('Invalid component tree')