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