use Repository.__str__() in messages
[matrix.git] / matrix / matrix.py
1 # Copyright (C) 2006-2008 Movial Oy
2 # Authors: Timo Savola <tsavola@movial.fi>
3 #          Toni Timonen
4 #          Kalle Vahlman <kalle.vahlman@movial.fi>
5 #          Tuomas Kulve <tuomas.kulve@movial.fi>
6
7 import os
8 import sys
9 from sets import Set as set
10
11 import cache
12 import components
13 import config
14 import git
15 import log
16 from build import build
17 from rootfs import RootFS
18
19 Error = RuntimeError
20
21 def main():
22         parse_config('config.local')
23
24         command, params, targets = parse_args(sys.argv)
25
26         parse_config('config')
27         parse_config('boards')
28         parse_config('components')
29
30         if 'global-cache' in config.flags:
31                 config.cache_dir = config.global_cache_dir
32
33         if not os.path.exists(config.cache_dir):
34                 os.makedirs(config.cache_dir)
35
36         all_targets = components.init()
37
38         for i in xrange(len(targets)):
39                 name = targets[i]
40                 if name.startswith('src' + os.path.sep):
41                         name = name.split(os.path.sep)[1]
42                         targets[i] = name
43
44                 if name not in config.components:
45                         raise Error('Component "%s" does not exist' % name)
46
47         if not targets:
48                 targets = all_targets
49
50         if command == 'install':
51                 build(targets, **params)
52         elif command == 'clone':
53                 clone(targets)
54         elif command == 'clean':
55                 clean(targets)
56         elif command == 'rebase':
57                 rebase(targets)
58         elif command == 'pull':
59                 pull(targets)
60         elif command == 'source-dist':
61                 source_dist(targets)
62         elif command == 'rootfs':
63                 build_rootfs(**params)
64         else:
65                 raise Error('Invalid command: ' + command)
66
67 def help(file, args):
68         print >>file, '''
69 Copyright (C) 2006-2008 Movial Oy
70
71 Usage: %(progname)s [<options>] <command> [<params>] [<components>]
72
73 If no components are specified, all of them will be targeted.  All component
74 metadata will be downloaded regardless of the specified command and components.
75
76 Options:
77         -v              Verbose output.
78         -d              Debug output.
79         -r URL          Specify the root for component git repos to clone from.
80                         If this option is not given, the roots specified in the
81                         components file will be used.  This option may be
82                         specified multiple times.
83         -f              Build components with dirty files.
84         -h, --help      Print this help text.
85
86 Commands and parameters:
87         clone           Download the components' git repositories.
88         install         Download, build and install the components.
89                 -j N            Execute N build jobs in parallel.
90                 -mj N           Run component builds with make -j N.
91         clean           Remove all non-tracked files from the component git
92                         repository directories.
93         rebase          Update repositories from server by rebasing.
94         pull            Update repositories from server by merging.
95         source-dist     Download and package the component sources.
96         rootfs          Build a rootfs from the current target installation.
97                 -n              Do not clean env.faked and stripped directory.
98                 -r              Only generate a stripped rootfs.
99                 -j              Only generate a jffs2 image
100                 -d              Only generate a rootstrap (development rootfs).
101 ''' % {'progname': args[0]}
102
103 def parse_args(args):
104         def parse_jobs(arg):
105                 jobs = int(arg)
106                 if jobs <= 0:
107                         raise Error('Please specify a valid number of jobs')
108                 return jobs
109
110         command = None
111         params = {}
112         targets = []
113
114         i = 1
115         while i < len(args):
116                 if args[i].startswith('-'):
117                         if not command:
118                                 if args[i] == '-v':
119                                         config.verbose = True
120                                 elif args[i] == '-d':
121                                         config.debug = True
122                                 elif args[i] == '-r':
123                                         i += 1
124                                         config.roots.append(args[i])
125                                 elif args[i] == '-f':
126                                         config.force = True
127                                 elif args[i] in ('-h', '--help'):
128                                         help(sys.stdout, args)
129                                         sys.exit(0)
130                                 else:
131                                         raise Error('Bad option: ' + args[i])
132                         elif command == 'install':
133                                 if args[i] == '-j':
134                                         i += 1
135                                         params['build_jobs'] = parse_jobs(args[i])
136                                 elif args[i] == '-mj':
137                                         i += 1
138                                         params['make_jobs'] = parse_jobs(args[i])
139                                 else:
140                                         raise Error('Bad parameter: ' + args[i])
141                         elif command == 'rootfs':
142                                 if args[i] == '-n':
143                                         i += 1
144                                         params['clean'] = False
145                                 elif args[i] == '-r':
146                                         i += 1
147                                         params['rootfs_only'] = True
148                                 elif args[i] == '-j':
149                                         i += 1
150                                         params['jffs2_only'] = True
151                                 elif args[i] == '-d':
152                                         i += 1
153                                         params['devrootfs_only'] = True
154                         else:
155                                 raise Error('Command takes no parameters')
156                 elif not command:
157                         command = args[i]
158                 else:
159                         targets.append(args[i])
160                 i += 1
161
162         if not command:
163                 help(sys.stderr, args)
164                 sys.exit(1)
165
166         return command, params, targets
167
168 def parse_config(path):
169         if os.path.isabs(path):
170                 path = os.path.join(config.top_dir, path)
171
172         if os.path.exists(path):
173                 if config.debug:
174                         print 'Reading', path
175
176                 execfile(path, config.__dict__, config.__dict__)
177
178 def execute(args):
179         if config.debug:
180                 print 'Executing:', ' '.join(args)
181
182         if os.spawnvp(os.P_WAIT, args[0], args) != 0:
183                 raise Error('Failed: ' + ' '.join(args))
184
185 def remove_tree(path):
186         execute(['rm', '-rf', path])
187
188 def clone(targets):
189         for name in targets:
190                 c = config.components[name]
191                 clone_component(c)
192
193 def clone_component(c, overwrite=False):
194         have_repo = c.repo.exists()
195         have_meta = c.meta.exists()
196
197         if not overwrite and have_repo and have_meta:
198                 return
199
200         if overwrite and os.path.exists(c.repo.path):
201                 print 'Removing', c.repo
202
203                 remove_tree(c.repo.path)
204                 have_repo = False
205                 have_meta = False
206
207         if not have_repo:
208                 c.repo.clone()
209         if not have_meta:
210                 c.meta.clone()
211
212 def clean(targets):
213         for name in targets:
214                 c = config.components[name]
215                 c.repo.clean()
216                 cache.remove(c)
217
218                 for repo in (c.repo, c.meta):
219                         if repo.dirty_files():
220                                 log.error('Dirty files left in %s' % repo.path)
221
222 def for_each_repository(func, targets=None):
223         for name in targets:
224                 c = config.components[name]
225                 if c.repo.exists():
226                         func(c.repo)
227                 func(c.meta)
228
229 def rebase(targets):
230         for_each_repository(lambda repo: repo.rebase(), targets)
231
232 def pull(targets):
233         for_each_repository(lambda repo: repo.pull(), targets)
234
235 def source_dist(targets):
236         location = 'dist'
237         if not os.path.exists(location):
238                 os.makedirs(location)
239
240         for name in targets:
241                 c = config.components[name]
242                 dist_changes(c, location)
243                 dist_sources(c, location)
244
245 def dist_changes(c, location):
246         path = os.path.join(location, c.name) + '.changes'
247         c.repo.dump_log(path)
248
249 def dist_sources(c, location):
250         rev = c.repo.describe()
251         if rev:
252                 rev = '_' + rev
253         else:
254                 rev = ''
255
256         name = c.name + rev
257         path = os.path.join(location, name) + '.tar.bz2'
258         if os.path.exists(path):
259                 os.remove(path)
260
261         c.repo.archive(name, path)
262
263 def build_rootfs(clean=True, rootfs_only=False, jffs2_only=False, devrootfs_only=False):
264         parse_config('rootfs')
265
266         f = os.popen('sb-conf cu')
267         target = f.read()
268         f.close()
269
270         rootfs = RootFS(target.strip())
271         rootfs.include_paths(config.include_paths)
272         rootfs.include_files(config.include_files)
273         rootfs.filter_paths(config.exclude_paths)
274         rootfs.filter_files(config.exclude_files)
275         rootfs.filter_expressions(config.exclude_expressions)
276         rootfs.add_paths(config.created_paths)
277         rootfs.set_devices(config.devices)
278         rootfs.set_change_owner(config.change_owner)
279         rootfs.set_erase_size(config.boards[config.board].flash_erase_size)
280         rootfs.set_pad_size(config.boards[config.board].flash_pad_size)
281
282         if rootfs_only:
283                 rootfs.generate(clean, build_target="rootfs")
284         elif jffs2_only:
285                 rootfs.generate(clean, build_target="jffs2")
286         elif devrootfs_only:
287                 rootfs.generate(clean, build_target="devrootfs")
288         else:
289                 rootfs.generate(clean, build_target="all")