- add armv7l, armv6l and armv5l archs for building
[opensuse:osc.git] / osc / build.py
1 # Copyright (C) 2006 Novell Inc.  All rights reserved.
2 # This program is free software; it may be used, copied, modified
3 # and distributed under the terms of the GNU General Public Licence,
4 # either version 2, or (at your option) any later version.
5
6
7
8 import os
9 import re
10 import sys
11 import shutil
12 import urlparse
13 from tempfile import NamedTemporaryFile, mkdtemp
14 from osc.fetch import *
15 from osc.core import get_buildinfo, store_read_apiurl, store_read_project, store_read_package, meta_exists, quote_plus, get_buildconfig, is_package_dir
16 from osc.core import get_binarylist, get_binary_file
17 from osc.util import rpmquery, debquery
18 import osc.conf
19 import oscerr
20 import subprocess
21 try:
22     from xml.etree import cElementTree as ET
23 except ImportError:
24     import cElementTree as ET
25
26 from conf import config, cookiejar
27
28 change_personality = {
29             'i686':  'linux32',
30             'i586':  'linux32',
31             'i386':  'linux32',
32             'ppc':   'powerpc32',
33             's390':  's390',
34             'sparc': 'linux32',
35             'sparcv8': 'linux32',
36         }
37
38 can_also_build = {
39              'armv4l': [                                         'armv4l'                                             ],
40              'armv6l' :[                                         'armv4l', 'armv5l', 'armv6l', 'armv5el', 'armv6el'                       ],
41              'armv7l' :[                                         'armv4l', 'armv5l', 'armv6l', 'armv7l', 'armv5el', 'armv6el', 'armv7el'            ],
42              'armv5el':[                                         'armv4l', 'armv5l', 'armv5el'                                  ], # not existing arch, just for compatibility
43              'armv6el':[                                         'armv4l', 'armv5l', 'armv6l', 'armv5el', 'armv6el'                       ], # not existing arch, just for compatibility
44              'armv7el':[                                         'armv4l', 'armv5l', 'armv6l', 'armv7l', 'armv5el', 'armv6el', 'armv7el'            ], # not existing arch, just for compatibility
45              'armv7hl':[                        'armv7hl'                                                             ], # not existing arch, just for compatibility
46              'armv8el':[                                         'armv4l', 'armv5el', 'armv6el', 'armv7el', 'armv8el' ], # not existing arch, just for compatibility
47              'armv8l' :[                                         'armv4l', 'armv5el', 'armv6el', 'armv7el', 'armv8el' ], # not existing arch, just for compatibility
48              'armv5tel':[                                        'armv4l', 'armv5el',                                 'armv5tel' ], 
49              's390x':  ['s390' ],
50              'ppc64':  [                        'ppc', 'ppc64' ],
51              'sh4':    [                                                                                               'sh4' ],
52              'i386':   [        'i586',         'ppc', 'ppc64',  'armv4l', 'armv5el', 'armv5l', 'armv6l', 'armv7l', 'armv6el', 'armv7el', 'armv7hl', 'armv8el', 'sh4', 'mips', 'mipsel' ],
53              'i586':   [                'i386', 'ppc', 'ppc64',  'armv4l', 'armv5el', 'armv5l', 'armv6l', 'armv7l', 'armv6el', 'armv7el', 'armv7hl', 'armv8el', 'sh4', 'mips', 'mipsel' ],
54              'i686':   [        'i586',         'ppc', 'ppc64',  'armv4l', 'armv5el', 'armv5l', 'armv6l', 'armv7l', 'armv6el', 'armv7el', 'armv7hl', 'armv8el', 'sh4', 'mips', 'mipsel' ],
55              'x86_64': ['i686', 'i586', 'i386', 'ppc', 'ppc64',  'armv4l', 'armv5el', 'armv5l', 'armv6l', 'armv7l', 'armv6el', 'armv7el', 'armv7hl', 'armv8el', 'sh4', 'mips', 'mipsel' ],
56              'sparc64': ['sparc64v', 'sparcv9v', 'sparcv9', 'sparcv8', 'sparc'],
57              'parisc': ['hppa'],
58              }
59
60 # real arch of this machine
61 hostarch = os.uname()[4]
62 if hostarch == 'i686': # FIXME
63     hostarch = 'i586'
64
65 if hostarch == 'parisc':
66     hostarch = 'hppa'
67
68 class Buildinfo:
69     """represent the contents of a buildinfo file"""
70
71     def __init__(self, filename, apiurl, buildtype = 'spec', localpkgs = []):
72         try:
73             tree = ET.parse(filename)
74         except:
75             print >>sys.stderr, 'could not parse the buildinfo:'
76             print >>sys.stderr, open(filename).read()
77             sys.exit(1)
78
79         root = tree.getroot()
80
81         self.apiurl = apiurl
82
83         if root.find('error') != None:
84             sys.stderr.write('buildinfo is broken... it says:\n')
85             error = root.find('error').text
86             sys.stderr.write(error + '\n')
87             sys.exit(1)
88
89         if not (apiurl.startswith('https://') or apiurl.startswith('http://')):
90             raise urllib2.URLError('invalid protocol for the apiurl: \'%s\'' % apiurl)
91
92         self.buildtype = buildtype
93         self.apiurl = apiurl
94
95         # are we building .rpm or .deb?
96         # XXX: shouldn't we deliver the type via the buildinfo?
97         self.pacsuffix = 'rpm'
98         if self.buildtype == 'dsc':
99             self.pacsuffix = 'deb'
100
101         self.buildarch = root.find('arch').text
102         if root.find('release') != None:
103             self.release = root.find('release').text
104         else:
105             self.release = None
106         self.downloadurl = root.get('downloadurl')
107         self.debuginfo = 0
108         if root.find('debuginfo') != None:
109             try:
110                 self.debuginfo = int(root.find('debuginfo').text)
111             except ValueError:
112                 pass
113
114         self.deps = []
115         self.projects = {}
116         self.keys = []
117         self.prjkeys = []
118         for node in root.findall('bdep'):
119             p = Pac(node, self.buildarch, self.pacsuffix,
120                     apiurl, localpkgs)
121             if p.project:
122                 self.projects[p.project] = 1
123             self.deps.append(p)
124
125         self.vminstall_list = [ dep.name for dep in self.deps if dep.vminstall ]
126         self.cbinstall_list = [ dep.name for dep in self.deps if dep.cbinstall ]
127         self.cbpreinstall_list = [ dep.name for dep in self.deps if dep.cbpreinstall ]
128         self.preinstall_list = [ dep.name for dep in self.deps if dep.preinstall ]
129         self.runscripts_list = [ dep.name for dep in self.deps if dep.runscripts ]
130
131
132     def has_dep(self, name):
133         for i in self.deps:
134             if i.name == name:
135                 return True
136         return False
137
138     def remove_dep(self, name):
139         # we need to iterate over all deps because if this a
140         # kiwi build the same package might appear multiple times
141         for i in self.deps:
142             # only remove those which are needed for the build itself
143             if i.name == name and not i.noinstall:
144                 self.deps.remove(i)
145
146
147 class Pac:
148     """represent a package to be downloaded
149
150     We build a map that's later used to fill our URL templates
151     """
152     def __init__(self, node, buildarch, pacsuffix, apiurl, localpkgs = []):
153
154         self.mp = {}
155         for i in ['name', 'package',
156                   'version', 'release',
157                   'project', 'repository',
158                   'preinstall', 'vminstall', 'noinstall', 'runscripts',
159                   'cbinstall', 'cbpreinstall',
160                  ]:
161             self.mp[i] = node.get(i)
162
163         self.mp['buildarch']  = buildarch
164         self.mp['pacsuffix']  = pacsuffix
165
166         self.mp['arch'] = node.get('arch') or self.mp['buildarch']
167
168         # this is not the ideal place to check if the package is a localdep or not
169         localdep = self.mp['name'] in localpkgs and not self.mp['noinstall']
170         if not localdep and not (node.get('project') and node.get('repository')):
171             raise oscerr.APIError('incomplete information for package %s, may be caused by a broken project configuration.'
172                                   % self.mp['name'] )
173
174         if not localdep:
175             self.mp['extproject'] = node.get('project').replace(':', ':/')
176             self.mp['extrepository'] = node.get('repository').replace(':', ':/')
177         self.mp['repopackage'] = node.get('package') or '_repository'
178         self.mp['repoarch'] = node.get('repoarch') or self.mp['buildarch']
179
180         if pacsuffix == 'deb' and not (self.mp['name'] and self.mp['arch'] and self.mp['version']):
181             raise oscerr.APIError(
182                 "buildinfo for package %s/%s/%s is incomplete"
183                     % (self.mp['name'], self.mp['arch'], self.mp['version']))
184
185         self.mp['apiurl'] = apiurl
186
187         if pacsuffix == 'deb':
188             self.filename = debquery.DebQuery.filename(self.mp['name'], self.mp['version'], self.mp['release'], self.mp['arch'])
189         else:
190             self.filename = rpmquery.RpmQuery.filename(self.mp['name'], self.mp['version'], self.mp['release'], self.mp['arch'])
191
192         self.mp['filename'] = self.filename
193         if self.mp['repopackage'] == '_repository':
194             self.mp['repofilename'] = self.mp['name']
195         else:
196             self.mp['repofilename'] = self.mp['filename']
197
198         # make the content of the dictionary accessible as class attributes
199         self.__dict__.update(self.mp)
200
201
202     def makeurls(self, cachedir, urllist):
203
204         self.urllist = []
205
206         # build up local URL
207         # by using the urlgrabber with local urls, we basically build up a cache.
208         # the cache has no validation, since the package servers don't support etags,
209         # or if-modified-since, so the caching is simply name-based (on the assumption
210         # that the filename is suitable as identifier)
211         self.localdir = '%s/%s/%s/%s' % (cachedir, self.project, self.repository, self.arch)
212         self.fullfilename = os.path.join(self.localdir, self.filename)
213         self.url_local = 'file://%s' % self.fullfilename
214
215         # first, add the local URL
216         self.urllist.append(self.url_local)
217
218         # remote URLs
219         for url in urllist:
220             self.urllist.append(url % self.mp)
221
222     def __str__(self):
223         return self.name
224
225     def __repr__(self):
226         return "%s" % self.name
227
228
229
230 def get_built_files(pacdir, pactype):
231     if pactype == 'rpm':
232         b_built = subprocess.Popen(['find', os.path.join(pacdir, 'RPMS'),
233                                     '-name', '*.rpm'],
234                                    stdout=subprocess.PIPE).stdout.read().strip()
235         s_built = subprocess.Popen(['find', os.path.join(pacdir, 'SRPMS'),
236                                     '-name', '*.rpm'],
237                                    stdout=subprocess.PIPE).stdout.read().strip()
238     elif pactype == 'kiwi':
239         b_built = subprocess.Popen(['find', os.path.join(pacdir, 'KIWI'),
240                                     '-type', 'f'],
241                                    stdout=subprocess.PIPE).stdout.read().strip()
242     else:
243         b_built = subprocess.Popen(['find', os.path.join(pacdir, 'DEBS'),
244                                     '-name', '*.deb'],
245                                    stdout=subprocess.PIPE).stdout.read().strip()
246         s_built = subprocess.Popen(['find', os.path.join(pacdir, 'SOURCES.DEB'),
247                                     '-type', 'f'],
248                                    stdout=subprocess.PIPE).stdout.read().strip()
249     return s_built, b_built
250
251 def get_repo(path):
252     """Walks up path looking for any repodata directories.
253
254     @param path path to a directory
255     @return str path to repository directory containing repodata directory
256     """
257     oldDirectory = None
258     currentDirectory = os.path.abspath(path)
259     repositoryDirectory = None
260
261     # while there are still parent directories
262     while currentDirectory != oldDirectory:
263         children = os.listdir(currentDirectory)
264
265         if "repodata" in children:
266             repositoryDirectory = currentDirectory
267             break
268
269         # ascend
270         oldDirectory = currentDirectory
271         currentDirectory = os.path.abspath(os.path.join(oldDirectory,
272                                                         os.pardir))
273
274     return repositoryDirectory
275
276 def get_prefer_pkgs(dirs, wanted_arch, type):
277     import glob
278     from util import repodata, packagequery, cpio
279     paths = []
280     repositories = []
281
282     suffix = '*.rpm'
283     if type == 'dsc':
284         suffix = '*.deb'
285
286     for dir in dirs:
287         # check for repodata
288         repository = get_repo(dir)
289         if repository is None:
290             paths += glob.glob(os.path.join(os.path.abspath(dir), suffix))
291         else:
292             repositories.append(repository)
293
294     packageQueries = packagequery.PackageQueries(wanted_arch)
295
296     for repository in repositories:
297         repodataPackageQueries = repodata.queries(repository)
298
299         for packageQuery in repodataPackageQueries:
300             packageQueries.add(packageQuery)
301
302     for path in paths:
303         if path.endswith('src.rpm'):
304             continue
305         if path.find('-debuginfo-') > 0:
306             continue
307         packageQuery = packagequery.PackageQuery.query(path)
308         packageQueries.add(packageQuery)
309
310     prefer_pkgs = dict((name, packageQuery.path())
311                        for name, packageQuery in packageQueries.iteritems())
312
313     depfile = create_deps(packageQueries.values())
314     cpio = cpio.CpioWrite()
315     cpio.add('deps', '\n'.join(depfile))
316     return prefer_pkgs, cpio
317
318
319 def create_deps(pkgqs):
320     """
321     creates a list of requires/provides which corresponds to build's internal
322     dependency file format
323     """
324     depfile = []
325     for p in pkgqs:
326         id = '%s.%s-0/0/0: ' % (p.name(), p.arch())
327         depfile.append('R:%s%s' % (id, ' '.join(p.requires())))
328         depfile.append('P:%s%s' % (id, ' '.join(p.provides())))
329     return depfile
330
331
332 trustprompt = """Would you like to ...
333 0 - quit (default)
334 1 - trust packages from '%(project)s' always
335 2 - trust them just this time
336 ? """
337 def check_trusted_projects(apiurl, projects):
338     trusted = config['api_host_options'][apiurl]['trusted_prj']
339     tlen = len(trusted)
340     for prj in projects:
341         if not prj in trusted:
342             print "\nThe build root needs packages from project '%s'." % prj
343             print "Note that malicious packages can compromise the build result or even your system."
344             r = raw_input(trustprompt % { 'project':prj })
345             if r == '1':
346                 print "adding '%s' to ~/.oscrc: ['%s']['trusted_prj']" % (prj,apiurl)
347                 trusted.append(prj)
348             elif r != '2':
349                 print "Well, good good bye then :-)"
350                 raise oscerr.UserAbort()
351
352     if tlen != len(trusted):
353         config['api_host_options'][apiurl]['trusted_prj'] = trusted
354         conf.config_set_option(apiurl, 'trusted_prj', ' '.join(trusted))
355
356 def main(apiurl, opts, argv):
357
358     repo = argv[0]
359     arch = argv[1]
360     build_descr = argv[2]
361     xp = []
362     build_root = None
363     cache_dir  = None
364     build_uid=''
365     vm_type = config['build-type']
366
367     build_descr = os.path.abspath(build_descr)
368     build_type = os.path.splitext(build_descr)[1][1:]
369     if build_type not in ['spec', 'dsc', 'kiwi']:
370         raise oscerr.WrongArgs(
371                 'Unknown build type: \'%s\'. Build description should end in .spec, .dsc or .kiwi.' \
372                         % build_type)
373     if not os.path.isfile(build_descr):
374         raise oscerr.WrongArgs('Error: build description file named \'%s\' does not exist.' % build_descr)
375
376     buildargs = []
377     if not opts.userootforbuild:
378         buildargs.append('--norootforbuild')
379     if opts.clean:
380         buildargs.append('--clean')
381     if opts.noinit:
382         buildargs.append('--noinit')
383     if opts.nochecks:
384         buildargs.append('--no-checks')
385     if not opts.no_changelog:
386         buildargs.append('--changelog')
387     if opts.root:
388         build_root = opts.root
389     if opts.jobs:
390         buildargs.append('--jobs=%s' % opts.jobs)
391     elif config['build-jobs'] > 1:
392         buildargs.append('--jobs=%s' % config['build-jobs'])
393     if opts.icecream or config['icecream'] != '0':
394         if opts.icecream:
395             num = opts.icecream
396         else:
397             num = config['icecream']
398
399         if int(num) > 0:
400             buildargs.append('--icecream=%s' % num)
401             xp.append('icecream')
402             xp.append('gcc-c++')
403     if opts.ccache:
404         buildargs.append('--ccache')
405         xp.append('ccache')
406     if opts.linksources:
407         buildargs.append('--linksources')
408     if opts.baselibs:
409         buildargs.append('--baselibs')
410     if opts.debuginfo:
411         buildargs.append('--debug')
412     if opts._with:
413         for o in opts._with:
414             buildargs.append('--with=%s' % o)
415     if opts.without:
416         for o in opts.without:
417             buildargs.append('--without=%s' % o)
418     if opts.define:
419         for o in opts.define:
420             buildargs.append('--define=%s' % o)
421     if config['build-uid']:
422         build_uid = config['build-uid']
423     if opts.build_uid:
424         build_uid = opts.build_uid
425     if build_uid:
426         buildidre = re.compile('^[0-9]{1,5}:[0-9]{1,5}$')
427         if build_uid == 'caller':
428             buildargs.append('--uid=%s:%s' % (os.getuid(), os.getgid()))
429         elif buildidre.match(build_uid):
430             buildargs.append('--uid=%s' % build_uid)
431         else:
432             print >>sys.stderr, 'Error: build-uid arg must be 2 colon separated numerics: "uid:gid" or "caller"'
433             return 1
434     if opts.vm_type:
435         vm_type = opts.vm_type
436     if opts.alternative_project:
437         prj = opts.alternative_project
438         pac = '_repository'
439     else:
440         prj = store_read_project(os.curdir)
441         if opts.local_package:
442             pac = '_repository'
443         else:
444             pac = store_read_package(os.curdir)
445     if opts.shell:
446         buildargs.append("--shell")
447
448     # make it possible to override configuration of the rc file
449     for var in ['OSC_PACKAGECACHEDIR', 'OSC_SU_WRAPPER', 'OSC_BUILD_ROOT']:
450         val = os.getenv(var)
451         if val:
452             if var.startswith('OSC_'): var = var[4:]
453             var = var.lower().replace('_', '-')
454             if config.has_key(var):
455                 print 'Overriding config value for %s=\'%s\' with \'%s\'' % (var, config[var], val)
456             config[var] = val
457
458     pacname = pac
459     if pacname == '_repository':
460         if not opts.local_package:
461             try:
462                 pacname = store_read_package(os.curdir)
463             except oscerr.NoWorkingCopy:
464                 opts.local_package = True
465         if opts.local_package:
466             pacname = os.path.splitext(build_descr)[0]
467     apihost = urlparse.urlsplit(apiurl)[1]
468     if not build_root:
469         build_root = config['build-root'] % {'repo': repo, 'arch': arch,
470             'project': prj, 'package': pacname, 'apihost': apihost}
471
472     cache_dir = config['packagecachedir'] % {'apihost': apihost}
473
474     extra_pkgs = []
475     if not opts.extra_pkgs:
476         extra_pkgs = config['extra-pkgs']
477     elif opts.extra_pkgs != ['']:
478         extra_pkgs = opts.extra_pkgs
479
480     if xp:
481         extra_pkgs += xp
482
483     prefer_pkgs = {}
484     build_descr_data = open(build_descr).read()
485
486     # XXX: dirty hack but there's no api to provide custom defines
487     if opts.without:
488         s = ''
489         for i in opts.without:
490             s += "%%define _without_%s 1\n" % i
491             s += "%%define _with_%s 0\n" % i
492         build_descr_data = s + build_descr_data
493     if opts._with:
494         s = ''
495         for i in opts._with:
496             s += "%%define _without_%s 0\n" % i
497             s += "%%define _with_%s 1\n" % i
498         build_descr_data = s + build_descr_data
499     if opts.define:
500         s = ''
501         for i in opts.define:
502             s += "%%define %s\n" % i
503         build_descr_data = s + build_descr_data
504
505     if opts.prefer_pkgs:
506         print 'Scanning the following dirs for local packages: %s' % ', '.join(opts.prefer_pkgs)
507         prefer_pkgs, cpio = get_prefer_pkgs(opts.prefer_pkgs, arch, build_type)
508         cpio.add(os.path.basename(build_descr), build_descr_data)
509         build_descr_data = cpio.get()
510
511     # special handling for overlay and rsync-src/dest
512     specialcmdopts = []
513     if opts.rsyncsrc or opts.rsyncdest :
514         if not opts.rsyncsrc or not opts.rsyncdest:
515             raise oscerr.WrongOptions('When using --rsync-{src,dest} both parameters have to be specified.')
516         myrsyncsrc = os.path.abspath(os.path.expanduser(os.path.expandvars(opts.rsyncsrc)))
517         if not os.path.isdir(myrsyncsrc):
518             raise oscerr.WrongOptions('--rsync-src %s is no valid directory!' % opts.rsyncsrc)
519         # can't check destination - its in the target chroot ;) - but we can check for sanity
520         myrsyncdest = os.path.expandvars(opts.rsyncdest)
521         if not os.path.isabs(myrsyncdest):
522             raise oscerr.WrongOptions('--rsync-dest %s is no absolute path (starting with \'/\')!' % opts.rsyncdest)
523         specialcmdopts = ['--rsync-src='+myrsyncsrc, '--rsync-dest='+myrsyncdest]
524     if opts.overlay:
525         myoverlay = os.path.abspath(os.path.expanduser(os.path.expandvars(opts.overlay)))
526         if not os.path.isdir(myoverlay):
527             raise oscerr.WrongOptions('--overlay %s is no valid directory!' % opts.overlay)
528         specialcmdopts += ['--overlay='+myoverlay]
529
530     bi_file = None
531     bc_file = None
532     bi_filename = '_buildinfo-%s-%s.xml' % (repo, arch)
533     bc_filename = '_buildconfig-%s-%s' % (repo, arch)
534     if is_package_dir('.') and os.access(osc.core.store, os.W_OK):
535         bi_filename = os.path.join(os.getcwd(), osc.core.store, bi_filename)
536         bc_filename = os.path.join(os.getcwd(), osc.core.store, bc_filename)
537     elif not os.access('.', os.W_OK):
538         bi_file = NamedTemporaryFile(prefix=bi_filename)
539         bi_filename = bi_file.name
540         bc_file = NamedTemporaryFile(prefix=bc_filename)
541         bc_filename = bc_file.name
542     else:
543         bi_filename = os.path.abspath(bi_filename)
544         bc_filename = os.path.abspath(bc_filename)
545
546     try:
547         if opts.noinit:
548             if not os.path.isfile(bi_filename):
549                 raise oscerr.WrongOptions('--noinit is not possible, no local buildinfo file')
550             print 'Use local \'%s\' file as buildinfo' % bi_filename
551             if not os.path.isfile(bc_filename):
552                 raise oscerr.WrongOptions('--noinit is not possible, no local buildconfig file')
553             print 'Use local \'%s\' file as buildconfig' % bc_filename
554         elif opts.offline:
555             if not os.path.isfile(bi_filename):
556                 raise oscerr.WrongOptions('--offline is not possible, no local buildinfo file')
557             print 'Use local \'%s\' file as buildinfo' % bi_filename
558             if not os.path.isfile(bc_filename):
559                 raise oscerr.WrongOptions('--offline is not possible, no local buildconfig file')
560         else:
561             print 'Getting buildinfo from server and store to %s' % bi_filename
562             bi_text = ''.join(get_buildinfo(apiurl,
563                                             prj,
564                                             pac,
565                                             repo,
566                                             arch,
567                                             specfile=build_descr_data,
568                                             addlist=extra_pkgs))
569             if not bi_file:
570                 bi_file = open(bi_filename, 'w')
571             # maybe we should check for errors before saving the file
572             bi_file.write(bi_text)
573             bi_file.flush()
574             print 'Getting buildconfig from server and store to %s' % bc_filename
575             bc = get_buildconfig(apiurl, prj, repo)
576             if not bc_file:
577                 bc_file = open(bc_filename, 'w')
578             bc_file.write(bc)
579             bc_file.flush()
580     except urllib2.HTTPError, e:
581         if e.code == 404:
582             # check what caused the 404
583             if meta_exists(metatype='prj', path_args=(quote_plus(prj), ),
584                            template_args=None, create_new=False, apiurl=apiurl):
585                 pkg_meta_e = None
586                 try:
587                     # take care, not to run into double trouble.
588                     pkg_meta_e = meta_exists(metatype='pkg', path_args=(quote_plus(prj), 
589                                         quote_plus(pac)), template_args=None, create_new=False, 
590                                         apiurl=apiurl)
591                 except:
592                     pass
593
594                 if pkg_meta_e:
595                     print >>sys.stderr, 'ERROR: Either wrong repo/arch as parameter or a parse error of .spec/.dsc/.kiwi file due to syntax error'
596                 else:
597                     print >>sys.stderr, 'The package \'%s\' does not exists - please ' \
598                                         'rerun with \'--local-package\'' % pac
599             else:
600                 print >>sys.stderr, 'The project \'%s\' does not exists - please ' \
601                                     'rerun with \'--alternative-project <alternative_project>\'' % prj
602             sys.exit(1)
603         else:
604             raise
605
606     bi = Buildinfo(bi_filename, apiurl, build_type, prefer_pkgs.keys())
607
608     if bi.debuginfo and not (opts.disable_debuginfo or '--debug' in buildargs):
609         buildargs.append('--debug')
610
611     if opts.release:
612         bi.release = opts.release
613
614     if bi.release:
615         buildargs.append('--release=%s' % bi.release)
616
617     # real arch of this machine
618     # vs.
619     # arch we are supposed to build for
620     if hostarch != bi.buildarch:
621         if not bi.buildarch in can_also_build.get(hostarch, []):
622             print >>sys.stderr, 'Error: hostarch \'%s\' cannot build \'%s\'.' % (hostarch, bi.buildarch)
623             return 1
624
625     rpmlist_prefers = []
626     if prefer_pkgs:
627         print 'Evaluating preferred packages'
628         for name, path in prefer_pkgs.iteritems():
629             if bi.has_dep(name):
630                 # We remove a preferred package from the buildinfo, so that the
631                 # fetcher doesn't take care about them.
632                 # Instead, we put it in a list which is appended to the rpmlist later.
633                 # At the same time, this will make sure that these packages are
634                 # not verified.
635                 bi.remove_dep(name)
636                 rpmlist_prefers.append((name, path))
637                 print ' - %s (%s)' % (name, path)
638
639     print 'Updating cache of required packages'
640
641     urllist = []
642     if not opts.download_api_only:
643         # transform 'url1, url2, url3' form into a list
644         if 'urllist' in config:
645             if type(config['urllist']) == str:
646                 re_clist = re.compile('[, ]+')
647                 urllist = [ i.strip() for i in re_clist.split(config['urllist'].strip()) ]
648             else:
649                 urllist = config['urllist']
650
651         # OBS 1.5 and before has no downloadurl defined in buildinfo
652         if bi.downloadurl:
653             urllist.append(bi.downloadurl + '/%(extproject)s/%(extrepository)s/%(arch)s/%(filename)s')
654     if opts.disable_cpio_bulk_download:
655         urllist.append( '%(apiurl)s/build/%(project)s/%(repository)s/%(repoarch)s/%(repopackage)s/%(repofilename)s' )
656
657     fetcher = Fetcher(cache_dir,
658                       urllist = urllist,
659                       api_host_options = config['api_host_options'],
660                       offline = opts.noinit or opts.offline,
661                       http_debug = config['http_debug'],
662                       enable_cpio = not opts.disable_cpio_bulk_download,
663                       cookiejar=cookiejar)
664
665     # implicitly trust the project we are building for
666     check_trusted_projects(apiurl, [ i for i in bi.projects.keys() if not i == prj ])
667
668     # now update the package cache
669     fetcher.run(bi)
670
671     old_pkg_dir = None
672     if opts.oldpackages:
673         old_pkg_dir = opts.oldpackages
674         if not old_pkg_dir.startswith('/') and not opts.offline:
675             data = [ prj, pacname, repo, arch]
676             if old_pkg_dir == '_link':
677                 p = osc.core.findpacs(os.curdir)[0]
678                 if not p.islink():
679                     raise oscerr.WrongOptions('package is not a link')
680                 data[0] = p.linkinfo.project
681                 data[1] = p.linkinfo.package
682                 repos = osc.core.get_repositories_of_project(apiurl, data[0])
683                 # hack for links to e.g. Factory
684                 if not data[2] in repos and 'standard' in repos:
685                     data[2] = 'standard'
686             elif old_pkg_dir != '' and old_pkg_dir != '_self':
687                 a = old_pkg_dir.split('/')
688                 for i in range(0, len(a)):
689                     data[i] = a[i]
690
691             destdir = os.path.join(cache_dir, data[0], data[2], data[3])
692             old_pkg_dir = None
693             try:
694                 print "Downloading previous build from %s ..." % '/'.join(data)
695                 binaries = get_binarylist(apiurl, data[0], data[2], data[3], package=data[1], verbose=True)
696             except Exception, e:
697                 print "Error: failed to get binaries: %s" % str(e)
698                 binaries = []
699
700             if binaries:
701                 class mytmpdir:
702                     """ temporary directory that removes itself"""
703                     def __init__(self, *args, **kwargs):
704                         self.name = mkdtemp(*args, **kwargs)
705                     def cleanup(self):
706                         shutil.rmtree(self.name)
707                     def __del__(self):
708                         self.cleanup()
709                     def __exit__(self):
710                         self.cleanup()
711                     def __str__(self):
712                         return self.name
713
714                 old_pkg_dir = mytmpdir(prefix='.build.oldpackages', dir=os.path.abspath(os.curdir))
715                 if not os.path.exists(destdir):
716                     os.makedirs(destdir)
717             for i in binaries:
718                 fname = os.path.join(destdir, i.name)
719                 os.symlink(fname, os.path.join(str(old_pkg_dir), i.name))
720                 if os.path.exists(fname):
721                     st = os.stat(fname)
722                     if st.st_mtime == i.mtime and st.st_size == i.size:
723                         continue
724                 get_binary_file(apiurl,
725                                 data[0],
726                                 data[2], data[3],
727                                 i.name,
728                                 package = data[1],
729                                 target_filename = fname,
730                                 target_mtime = i.mtime,
731                                 progress_meter = True)
732
733         if old_pkg_dir != None:
734             buildargs.append('--oldpackages=%s' % old_pkg_dir)
735
736     # Make packages from buildinfo available as repos for kiwi
737     if build_type == 'kiwi':
738         if not os.path.exists('repos'):
739             os.mkdir('repos')
740         else:
741             shutil.rmtree('repos')
742             os.mkdir('repos')
743         for i in bi.deps:
744             # project
745             pdir = str(i.extproject).replace(':/', ':')
746             # repo
747             rdir = str(i.extrepository).replace(':/', ':')
748             # arch
749             adir = i.repoarch
750             # project/repo
751             prdir = "repos/"+pdir+"/"+rdir
752             # project/repo/arch
753             pradir = prdir+"/"+adir
754             # source fullfilename
755             sffn = i.fullfilename
756             print "Using package: "+sffn
757             # target fullfilename
758             tffn = pradir+"/"+sffn.split("/")[-1]
759             if not os.path.exists(os.path.join(pradir)):
760                 os.makedirs(os.path.join(pradir))
761             if not os.path.exists(tffn):
762                 if opts.linksources:
763                     os.link(sffn, tffn)
764                 else:
765                     os.symlink(sffn, tffn)
766
767     if bi.pacsuffix == 'rpm':
768         if opts.no_verify:
769             print 'Skipping verification of package signatures'
770         else:
771             print 'Verifying integrity of cached packages'
772             verify_pacs(bi)
773
774     elif bi.pacsuffix == 'deb':
775         if vm_type == "xen" or vm_type == "kvm" or vm_type == "lxc":
776             print 'Skipping verification of package signatures due to secure VM build'
777         elif opts.no_verify or opts.noinit:
778             print 'Skipping verification of package signatures'
779         else:
780             print 'WARNING: deb packages get not verified, they can compromise your system !'
781     else:
782         print 'WARNING: unknown packages get not verified, they can compromise your system !'
783
784     print 'Writing build configuration'
785
786     rpmlist = [ '%s %s\n' % (i.name, i.fullfilename) for i in bi.deps if not i.noinstall ]
787     rpmlist += [ '%s %s\n' % (i[0], i[1]) for i in rpmlist_prefers ]
788
789     rpmlist.append('preinstall: ' + ' '.join(bi.preinstall_list) + '\n')
790     rpmlist.append('vminstall: ' + ' '.join(bi.vminstall_list) + '\n')
791     rpmlist.append('cbinstall: ' + ' '.join(bi.cbinstall_list) + '\n')
792     rpmlist.append('cbpreinstall: ' + ' '.join(bi.cbpreinstall_list) + '\n')
793     rpmlist.append('runscripts: ' + ' '.join(bi.runscripts_list) + '\n')
794
795     rpmlist_file = NamedTemporaryFile(prefix='rpmlist.')
796     rpmlist_filename = rpmlist_file.name
797     rpmlist_file.writelines(rpmlist)
798     rpmlist_file.flush()
799
800     subst = { 'repo': repo, 'arch': arch, 'project' : prj, 'package' : pacname }
801     vm_options = []
802     # XXX check if build-device present
803     my_build_device = ''
804     if config['build-device']:
805         my_build_device = config['build-device'] % subst
806     else:
807         # obs worker uses /root here but that collides with the
808         # /root directory if the build root was used without vm
809         # before
810         my_build_device = build_root + '/img'
811
812     need_root = True
813     if vm_type:
814         if config['build-swap']:
815             my_build_swap = config['build-swap'] % subst
816         else:
817             my_build_swap = build_root + '/swap'
818
819         vm_options = [ '--vm-type=%s'%vm_type ]
820         if vm_type != 'lxc':
821             vm_options += [ '--vm-disk=' + my_build_device ]
822             vm_options += [ '--vm-swap=' + my_build_swap ]
823             vm_options += [ '--logfile=%s/.build.log' % build_root ]
824             if vm_type == 'kvm':
825                 if os.access(build_root, os.W_OK) and os.access('/dev/kvm', os.W_OK):
826                     # so let's hope there's also an fstab entry
827                     need_root = False
828             build_root += '/.mount'
829
830         if config['build-memory']:
831             vm_options += [ '--memory=' + config['build-memory'] ]
832         if config['build-vmdisk-rootsize']:
833             vm_options += [ '--vmdisk-rootsize=' + config['build-vmdisk-rootsize'] ]
834         if config['build-vmdisk-swapsize']:
835             vm_options += [ '--vmdisk-swapsize=' + config['build-vmdisk-swapsize'] ]
836
837     if opts.preload:
838         print "Preload done for selected repo/arch."
839         sys.exit(0)
840
841     print 'Running build'
842     cmd = [ config['build-cmd'], '--root='+build_root,
843                     '--rpmlist='+rpmlist_filename,
844                     '--dist='+bc_filename,
845                     '--arch='+bi.buildarch ]
846     cmd += specialcmdopts + vm_options + buildargs
847     cmd += [ build_descr ]
848
849     if need_root:
850         sucmd = config['su-wrapper'].split()
851         if sucmd[0] == 'su':
852             if sucmd[-1] == '-c':
853                 sucmd.pop()
854             cmd = sucmd + ['-s', cmd[0], 'root', '--' ] + cmd[1:]
855         else:
856             cmd = sucmd + cmd
857
858     # change personality, if needed
859     if hostarch != bi.buildarch and bi.buildarch in change_personality:
860         cmd = [ change_personality[bi.buildarch] ] + cmd;
861
862     try:
863         rc = subprocess.call(cmd)
864         if rc:
865             print
866             print 'The buildroot was:', build_root
867             sys.exit(rc)
868     except KeyboardInterrupt, i:
869         print "keyboard interrupt, killing build ..."
870         subprocess.call(cmd + ["--kill"])
871         raise i
872
873     pacdir = os.path.join(build_root, '.build.packages')
874     if os.path.islink(pacdir):
875         pacdir = os.readlink(pacdir)
876         pacdir = os.path.join(build_root, pacdir)
877
878     if os.path.exists(pacdir):
879         (s_built, b_built) = get_built_files(pacdir, bi.pacsuffix)
880
881         print
882         if s_built: print s_built
883         print
884         print b_built
885
886         if opts.keep_pkgs:
887             for i in b_built.splitlines() + s_built.splitlines():
888                 shutil.copy2(i, os.path.join(opts.keep_pkgs, os.path.basename(i)))
889
890     if bi_file:
891         bi_file.close()
892     if bc_file:
893         bc_file.close()
894     rpmlist_file.close()
895
896 # vim: sw=4 et