more for building sfa on debian
[build.git] / build.common
1 # -*-Shell-script-*-
2 #
3 # Common functions for build scripts used by various packages
4 # incorporated (e.g., build, bootcd, nodeimage, sliceimage)
5 #
6 # Marc E. Fiuczynski <mef@cs.princeton.edu>
7 # Copyright (C) 2007 The Trustees of Princeton University
8 # Thierry Parmentelat <thierry.parmentelat@inria.fr> INRIA
9 #
10 # supported distros f8, f10, f12, f14, f16, centos5, sl6
11 #
12 # for locating pkgs.py
13 export PATH=.:$PATH
14
15 # returns 'Fedora' or 'CentOS' for now
16 function pl_getDistro() {
17     if [ -f "/etc/redhat-release" ] ; then
18         distro=$(awk ' { print $1 } ' /etc/redhat-release)
19         case $distro in Scientific*) distro="SL" ; esac
20     elif [ -f /etc/debian_version ] ; then
21         for known in lenny squeeze wheezy jessie; do
22             grep -q $known /etc/os-release && distro=$known
23         done
24     fi
25     [ -z "$distro" ] && { echo "build.common.pl_getDistro-unknown"; exit 1; }
26     echo "$distro"
27     return 0
28 }
29
30 # returns something like 8, 10, or 5.3
31 function pl_getRelease() {
32     if [ -f "/etc/redhat-release" ] ; then
33         release=$(awk ' { if ($1=="Fedora" && $2=="Core") print $4 ; if (($1=="Fedora" && $2!="Core")||$1=="CentOS") print $3 ; if ($1=="Scientific") print $4 } ' /etc/redhat-release)
34     else
35         echo "build.common.pl_getRelease-unknown"
36         exit 1
37     fi
38     # keep only the major number
39     echo "$release" | cut -d. -f1
40     return 0
41 }
42
43 # returns distro shortname, something like 'f8' or 'centos5'
44 function pl_getReleaseName () {
45     distro=$1; shift
46     release=$1; shift
47     case $distro in
48         [Ff]edora*)
49             releasename=f$release
50             ;;
51         [Cc]entOS*)
52             old_IFS="$IFS"
53             IFS="."
54             set -- $release
55             IFS="$old_IFS"
56             releasename=centos$1
57             ;;
58         [Ss]L*)
59             releasename=sl$release
60             ;;
61         lenny|squeeze|wheezy|jessie)
62             releasename=$distro
63             ;;
64         *)
65             releasename="unknown-name-for-${pl_DISTRO}-please-edit-build.common"
66             echo 1>&2 "build.common: WARNING - releasename not set for distro=$distro" 
67             return 1
68             ;;
69     esac
70     echo "$releasename"
71     return 0
72 }
73
74 # yum exclusions are now defined in yumexclude.pkgs
75 # so they can now depend both on the linux distro and the pl distro
76 function pl_yumexclude () {
77     keyword=$1; shift
78     fcdistro=$1; shift
79     pldistro=$1; shift
80     builddir=$1; shift
81     # search for file "yumexclude.pkgs"
82     yumexclude_file=$(pl_locateDistroFile $builddir $pldistro "yumexclude.pkgs")
83     #
84     # check if pkgs.py is in PATH
85     type -p pkgs.py >& /dev/null || export PATH=$builddir:$PATH
86
87     # parse it
88     pkgs.py -a $pl_DISTRO_ARCH -f $fcdistro -d $pldistro $keyword $yumexclude_file || \
89         { echo pl_yumexclude failed with fcdistro=$fcdistro and pldistro=$pldistro; return 1 ; }
90 }
91
92 # <> fcdistro pldistro builddir
93 # node side : use the 'nodeyumexclude' keywork in yumexclude.pkgs
94 function pl_nodeyumexclude () { pl_yumexclude 'nodeyumexclude' "$@" ; }
95 # server side : use the 'plcyumexclude' keywork in yumexclude.pkgs
96 function pl_plcyumexclude () { pl_yumexclude 'plcyumexclude' "$@" ; }
97
98
99 # figure out which redhat distro we are using (fedora, centos, redhat)
100 pl_DISTRO=$(pl_getDistro)
101
102 # select basearch of the host devel environment - protected for macos for local tests
103 # try arch for testing stuff on a mac
104 pl_DISTRO_ARCH=$(uname -i 2>/dev/null || arch 2> /dev/null || echo unknownarch)
105
106 # the release number (plain number)
107 pl_DISTRO_RELEASE=$(pl_getRelease)
108
109 # the release name - something like 'f8' or 'sl6'
110 pl_DISTRO_NAME=$(pl_getReleaseName $pl_DISTRO $pl_DISTRO_RELEASE)
111
112 # get path to appropriate yumgroups.xml file
113 # Thierry: quick & dirty improvement 
114 # this file is updated by the toplevel build, from the .pkgs files
115 pl_DISTRO_YUMGROUPS="../../../RPMS/yumgroups.xml"
116
117 function pl_process_fedora_options () {
118     # Get options
119     shiftcount=0
120     while getopts "l:r:a:h" opt ; do
121         case $opt in
122             l)
123                 pl_DISTRO_URL=$OPTARG
124                 let shiftcount=$shiftcount+2
125                 ;;
126             r)
127                 pl_DISTRO_RELEASE=$OPTARG
128                 let shiftcount=$shiftcount+2
129                 ;;
130             a)
131                 pl_DISTRO_ARCH=$OPTARG
132                 let shiftcount=$shiftcount+2
133                 ;;
134             h|*)
135                 echo "Usage: $0 [OPTION]..."
136                 echo "  -l url          distro mirror location (default: $pl_DISTRO_URL)"
137                 echo "  -r release      distro release number (default: $pl_DISTRO_RELEASE)"
138                 echo "  -a arch         distro architecture (default: $pl_DISTRO_ARCH)"
139                 echo "where distro can be either fedora, centos, or redhat"
140                 echo "  -h              This message"
141                 exit 1
142                 ;;
143         esac
144     done
145     return $shiftcount
146 }
147
148 ######################################## handling a root image
149 function pl_root_rpm_macros () {
150     cat <<EOF
151 %_install_langs C:en_US:en
152 %_netsharedpath /proc:/dev/pts:/usr/share/info
153 %_excludedocs 1
154 %__file_context_path /dev/null
155 EOF
156 }
157
158 function pl_root_makedevs() {
159     vroot=$1
160     # Clean ${vroot}/dev, but only when ${vroot}!=""
161     [ -n $vroot ] && rm -rf $vroot/dev
162     
163     mkdir -p $vroot/dev
164     mknod -m 666 $vroot/dev/null c 1 3
165     mknod -m 666 $vroot/dev/zero c 1 5
166     mknod -m 666 $vroot/dev/full c 1 7
167     mknod -m 644 $vroot/dev/random c 1 8
168     mknod -m 644 $vroot/dev/urandom c 1 9
169     mknod -m 666 $vroot/dev/tty c 5 0
170     mknod -m 666 $vroot/dev/ptmx c 5 2
171     # For bash command substitution
172     ln -nsf ../proc/self/fd $vroot/dev/fd
173
174     # For df and linuxconf
175     touch $vroot/dev/hdv1
176
177     # For pseudo ttys
178     mkdir -p $vroot/dev/pts
179
180     # for tmpfs mount
181     mkdir -p $vroot/dev/shm
182
183     # For TUN/TAP
184     mkdir -p $vroot/dev/net
185     mknod -m 600 $vroot/dev/net/tun c 10 200
186
187     # For mkinitrd (in case a kernel is being installed)
188     # As well as for loop back mounting within a vm. 
189     for i in $(seq 0 255) ; do
190         mknod -m 640 $vroot/dev/loop$i b 7 $i
191     done
192 }
193
194 function mkfedora_usage() {
195     echo "Usage: pl_root_mkfedora [OPTION]... basedir pldistro pkgsfile(s)"
196     echo "      -l url          Fedora mirror location."
197     echo "                      Defaults are searched in <pldistro>.mirrors"
198     echo "      -v              Be verbose"
199     echo "      -h              This message"
200     echo " target selection (defaults based on current build VM context)"
201     echo "      -r release      Fedora release number (default: $releasever)"
202     echo "      -a arch         Fedora architecture (default: $basearch)"
203     exit 1
204 }
205
206 function pl_root_mkfedora () {
207
208     echo "* Entering pl_root_mkfedora" "$@"
209
210     if [ $UID -ne 0 ] ; then
211         echo "Error: You must run this script as root."
212         exit 1
213     fi
214
215 # Verbosity
216     verbose=0
217
218 # Release and architecture to install : defaults to current vm settings or previously parsed fedora_options
219     releasever=$pl_DISTRO_RELEASE
220     basearch=$pl_DISTRO_ARCH
221
222 # Get options
223     while getopts "vh" opt ; do
224         case $opt in
225             v) verbose=1; set -x ;;
226             h|*) mkfedora_usage ;;
227         esac
228     done
229
230     shift $(($OPTIND - 1))
231     [[ "$#" -lt 3 ]] && mkfedora_usage
232     vroot=$1 ; shift
233     pldistro=$1 ; shift
234     pkgsfile="$@"
235     vroot=$(cd $vroot && pwd -P)
236     [ -d $vroot ] || mkfedora_usage
237
238
239     # parse pkgsfile and add to local vars
240     fcdistro=${pl_DISTRO_NAME}
241     pkgs_packages=$(pkgs.py -a $pl_DISTRO_ARCH -f $fcdistro -d $pldistro package $pkgsfile) 
242     pkgs_groups=$(pkgs.py -a $pl_DISTRO_ARCH -f $fcdistro -d $pldistro group $pkgsfile)
243     # what can get trashed to save space
244     pkgs_junk=$(pkgs.py -a $pl_DISTRO_ARCH -f $fcdistro -d $pldistro junk $pkgsfile)
245     # but not this
246     pkgs_precious=$(pkgs.py -a $pl_DISTRO_ARCH -f $fcdistro -d $pldistro precious $pkgsfile)
247     # formerly related to mkfedora -k : packages to take from our own build 
248     # and thus need be excluded frem the stock repos
249     # locate builddir by looking for pkgs.py
250     builddir=$(dirname $(type -p pkgs.py))
251     SUBST_NODEYUMEXCLUDE=$(pl_nodeyumexclude $fcdistro $pldistro $builddir)
252     pkgs_yumexclude=$(pkgs.py -a $pl_DISTRO_ARCH -f $fcdistro -d $pldistro yumexclude $pkgsfile | sed -e s,@NODEYUMEXCLUDE@,"$SUBST_NODEYUMEXCLUDE",)
253     # get mirrors if not specified with -l
254     if [ -z "$mirrors" ] ; then
255         mirrorsfile=$(pl_locateDistroFile ../build/ $pldistro "$pldistro.mirrors")
256         # do not sort mirrors, keep order from input
257         mirrors=$(pkgs.py -u -a $pl_DISTRO_ARCH -f $fcdistro -d $pldistro mirror $mirrorsfile)
258     fi
259
260     yumexclude_line=""
261     [ -n "$pkgs_yumexclude" ] && yumexclude_line="exclude=$pkgs_yumexclude"
262
263     echo "$0: candidate mirrors"
264     for mirror in $mirrors ; do
265         echo "* candidate mirror $mirror"
266     done
267
268     # the repo part of the final yum.conf
269     yum_conf_repos=$vroot/xxxmkfedora-repos.confxxx
270     if ! yumconf_mirrors $yum_conf_repos ../build/ $fcdistro "$yumexclude_line" $mirrors ; then
271         echo xxx -- error ; return 1
272     fi
273     
274     # Do not tolerate errors
275     set -e
276
277     public_gpg_key=$(yumconf_gpgkey $yum_conf_repos)
278
279     ## make rpms ignore installing stuff to special fs entries like /proc
280     # Because of https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=52725
281     # you have to use at least one language beside 'C'
282     # Prevent all locales from being installed in reference image
283     mkdir -p $vroot/etc/rpm
284     pl_root_rpm_macros > $vroot/etc/rpm/macros 
285
286     # Trick rpm and yum, who read the real root /etc/rpm/macros file
287     # rather than the one installed in the reference image, despite what
288     # you might expect the --root and --installroot options to mean. Both
289     # programs always read $HOME/.rpmmacros.
290     export HOME=$vroot/tmp
291     mkdir -p $vroot/tmp
292     pl_root_rpm_macros > $vroot/tmp/.rpmmacros
293
294     function mkfedora_cleanup () {
295         umount -l $vroot/proc
296         umount -l $vroot/dev/shm
297         umount -l $vroot/dev/pts
298     }
299
300     # Clean up before exiting if anything goes wrong
301     trap "mkfedora_cleanup" ERR INT
302
303     # Mount in reference image
304     mount -t devpts none $vroot/dev/pts
305     mount -t tmpfs none $vroot/dev/shm
306     mkdir -p $vroot/proc
307     mount -t proc none $vroot/proc
308     
309     # Create a /var/lib dirs for yum & rpm
310     mkdir -p $vroot/var/lib/yum
311     mkdir -p $vroot/var/lib/rpm
312     mkdir -p $vroot/usr/share/info
313
314     # Create a dummy /etc/fstab in reference image
315     mkdir -p $vroot/etc
316     cat >$vroot/etc/fstab <<EOF
317 # This fake fstab exists only to please df and linuxconf.
318 /dev/hdv1       /       ext2    defaults        1 1
319 EOF
320     cp $vroot/etc/fstab $vroot/etc/mtab
321
322     # Necessary for some scripts
323     mkdir -p $vroot/etc/sysconfig
324     echo "NETWORKING=yes" > $vroot/etc/sysconfig/network
325
326     # Initialize RPM database in reference image
327     mkdir -p $vroot/var/lib/rpm
328     rpm --root $vroot --initdb
329     rpm --root $vroot --import $public_gpg_key
330
331     # Initialize yum in reference image
332     mkdir -p $vroot/var/cache/yum $vroot/var/log
333
334 # yum.conf is for building only - store in different location than /etc/yum.conf
335     yum_conf=$vroot/etc/mkfedora-yum.conf
336     cat > $yum_conf <<EOF
337 [main]
338 cachedir=/var/cache/yum
339 debuglevel=2
340 logfile=/var/log/yum.log
341 pkgpolicy=newest
342 multilib_policy=best
343 distroverpkg=redhat-release
344 tolerant=1
345 exactarch=1
346 retries=20
347 obsoletes=1
348 gpgcheck=0
349 # Prevent yum-2.4 from loading additional repository definitions
350 # (e.g., from /etc/yum.repos.d/)
351 reposdir=/dev/null
352 EOF
353     
354     cat $yum_conf_repos >> $yum_conf
355
356     # If we are being built as part of an automated RPM build, solve the
357     # bootstrap problem by including any just built packages in the yum
358     # configuration. This cooperates with the PlanetLab build system.
359     if [ -n "$RPM_BUILD_DIR" ] ; then
360         RPM_RPMS_DIR=$(cd $(dirname $RPM_BUILD_DIR)/RPMS && pwd -P)
361         # If run under sudo, allow user to delete the headers/ and
362         # repodata/ directories.
363         if [ -n "$SUDO_USER" ] ; then
364             chown -R $SUDO_USER $RPM_RPMS_DIR
365         fi
366         cat >> $yum_conf <<EOF
367
368 [building]
369 name=Building - $basearch - $RPM_RPMS_DIR/
370 baseurl=file://$RPM_RPMS_DIR/
371 EOF
372 fi
373
374     echo "========== Dumping $yum_conf"
375     cat $yum_conf
376     echo "========== EndDump $yum_conf"
377
378     yum_options=""
379 #    yum --help | grep verbose &> /dev/null && yum_options="$yum_options --verbose"
380     yum_options="$yum_options -y"
381     yum_options="$yum_options -c $yum_conf"
382     yum_options="$yum_options --installroot=$vroot"
383
384     # glibc must be specified explicitly for the correct arch to be
385     # chosen.
386     echo "* Installing glibc"
387     # ignore yum's return code that is basically undefined
388     yum $yum_options install glibc || :
389
390     # Go, baby, go
391     if [ -n "$pkgs_packages" ] ; then
392         echo "* Installing optional packages" $pkgs_packages
393         # ignore yum's return code that is basically undefined
394         echo "* Install options" $vroot $yum_options 
395         yum $yum_options install $pkgs_packages || :
396         if ! rpm --root $vroot -q $pkgs_packages >/dev/null ; then
397             echo "* Warning: Missing packages"
398             rpm --root $vroot -q $pkgs_packages | grep "not installed"
399         fi
400     fi
401
402     if [ -n "$pkgs_groups" ] ; then
403        ## call yum sequentially to get finer-grained info on dependencies
404         for group_plus in $pkgs_groups ; do
405             group=$(echo $group_plus | sed -e "s,+++, ,g")
406             echo "* Installing optional group $group" 
407             # ignore yum's return code that is basically undefined
408             yum $yum_options groupinstall "$group" || :
409         done
410     fi
411
412     # formerly in bootcd/prep.sh : to optimize footprint
413     if [ -n "$pkgs_junk" ] ; then
414         echo "* Removing unnecessary junk"
415         pushd $vroot
416         # Save precious files
417         [ -n "$pkgs_precious" ] && tar --ignore-failed-read -cpf precious.tar $pkgs_precious
418         # Remove unnecessary junk
419         [ -n "$pkgs_junk" ] && rm -rf $pkgs_junk
420         # Restore precious files
421         [ -n "$pkgs_precious" ] && tar -xpf precious.tar && rm -f precious.tar
422         popd
423     fi
424
425     # Clean yum cache
426     echo "* Cleaning up"
427
428     # NOTE: this hack is for Fedora >= 12.
429     # if kernel-debug is installed, clean it up
430     # we link to our version of kernel/initrd and clean up
431     # kernel-debug manually
432     if rpm --root $vroot --quiet -q kernel-debug ; then
433         echo "* Cleaning up kernel-debug - (workaround for f12)"
434         pushd $vroot/boot/
435         rm -rf kernel-boot kernel-bootsmp initrd-boot initrd-bootsmp
436         ln -s vmlinuz-*${pldistro}* kernel-boot
437         ln -s vmlinuz-*${pldistro}* kernel-bootsmp
438         ln -s initrd-*${pldistro}* initrd-boot
439         ln -s initrd-*${pldistro}* initrd-bootsmp
440         rpm --root $vroot --nodeps -e kernel-debug || :
441         popd
442     fi
443
444     # ignore yum's return code that is basically undefined
445     yum $yum_options clean all || :
446
447     # Clean RPM state
448     rm -f $vroot/var/lib/rpm/__db*
449
450     # Set time zone to UTC
451     if [ -f $vroot/usr/share/zoneinfo/UTC -a -f $vroot/etc/localtime ] ; then
452         rm -f $vroot/etc/localtime
453         ln -s /usr/share/zoneinfo/UTC $vroot/etc/localtime
454     fi
455
456     echo "Dumping current list of rpms in /etc/mkfedora-rpms.txt"
457     chroot $vroot rpm -aq | sort > $vroot/etc/mkfedora-rpms.txt
458
459     # remove trap handler, as we are about to call it directly.
460     trap - ERR INT
461
462     # Clean up
463     mkfedora_cleanup
464
465     return 0
466 }
467
468 function pl_root_tune_image () {
469     root=$1; shift
470
471     # Disable all services in reference image
472     chroot $root sh -c "/sbin/chkconfig --list | awk '{ print \$1 }' | xargs -i /sbin/chkconfig {} off"
473
474     # FC2 minilogd starts up during shutdown and makes unmounting
475     # impossible. Just get rid of it.
476     rm -f $root/sbin/minilogd
477     ln -nsf /bin/true $root/sbin/minilogd
478
479     # This tells the Boot Manager that it is okay to update
480     # /etc/resolv.conf and /etc/hosts whenever the network configuration
481     # changes. Users are free to delete this file.
482     touch $root/etc/AUTO_UPDATE_NET_FILES
483 }
484
485 # Move specified directories out of a src tree into a dst tree, and
486 # then for each moved directory create a symlink in src to dst.
487 function pl_move_dirs() {
488     root=$1
489     data=$2
490     store=$3
491     shift 3
492
493     mkdir -p $root/data
494     for datadir in "$@" ; do
495         mkdir -p ${data}${datadir}
496         if [ -d ${root}/${datadir} -a ! -h ${root}/${datadir} ] ; then
497             (cd ${root} && find ./${datadir} | cpio -p -d -u ../${data}/)
498         fi
499         rm -rf ${root}/${datadir}
500         mkdir -p $(dirname ${root}/${datadir})
501         ln -nsf ${store}/${datadir} ${root}/${datadir}
502     done
503 }
504
505 # Construct an image file from given some directory
506 # XXX in the future maybe use livecdtools?
507 function pl_make_image() {
508     root=$1
509     image=$2
510     extraspace=$3
511
512     # Leave about 100 MB free space and allow for about 20% inode overhead
513     bytes=$((($(du -sb $root | cut -f1) + $extraspace) * 120 / 100))
514     bs=4096
515     blocks=$(($bytes / $bs))
516     dd bs=$bs count=$blocks if=/dev/zero of=$image
517     mkfs.ext3 -b $bs -j -F $image
518
519     # Temporarily mount it
520     tmp=$(mktemp -d tmp.XXXXXX)
521     mount -o loop $image $tmp
522     trap "umount $tmp; rmdir $tmp" ERR INT
523
524     # Move files to it
525     (cd $root && tar cpf - .) | (cd $tmp && tar xpf -)
526
527     # Unmount it
528     umount $tmp
529     rmdir $tmp
530     trap - ERR INT
531 }
532
533 # Fix permissions on tmp directories
534 function pl_fixtmp_permissions() {
535     root=$1
536     chmod 1777 $root/tmp $root/usr/tmp $root/var/tmp
537 }
538
539 function pl_fixdirs() {
540     root=$1
541     datadirs=$2
542     for datadir in datadirs ; do
543         if [ -h ${root}/${datadir} ] ; then
544             rm -f ${root}/${datadir}
545             mkdir -p ${root}/${datadir}
546         fi
547     done
548 }
549
550 ########## .pkgs format
551 # Usage: pl_parsePkgs keyword [-a arch] fcdistro pldistro pkgs-file[..s]
552 # pkgs.py should be found in PATH, like this file build.common
553 # only usage should be for pl_getPackages and pl_getGroups,
554 # which in turn are usednow be in {node,slice}image/build.sh
555 function pl_parsePkgs () {
556     target_arch=$pl_DISTRO_ARCH
557     keyword=$1;shift
558     [ "$1" == "-a" ] && { shift; target_arch="$1"; shift; }
559     fcdistro=$1; shift
560     pldistro=$1; shift
561
562     echo 1>&2 "pl_parsePkgs: using -a $target_arch -f $fcdistro -d $pldistro $keyword $@"
563     pkgs.py -a $target_arch -f $fcdistro -d $pldistro $keyword "$@" 
564 }
565 # usage: pl_getPackages [-a arch] fcdistro pldistro pkg-file[..s]
566 function pl_getPackages() { pl_parsePkgs package "$@" ; }
567 function pl_getGroups() { pl_parsePkgs group "$@" ; }
568
569 ##############################
570
571 # locates a pldistro-dependant file
572 # tries first in build/<pldistro>/, then in build/planetlab/
573 function pl_locateDistroFile () {
574     builddir=$1; shift
575     pldistro=$1; shift
576     pkgsfile=$1; shift
577
578     pkgspath=""
579     # if config dir is missing but a .svnpath or a .gitpath exists, use it to extract the config dir
580     configdir="$builddir/config.${pldistro}"
581     if [ ! -d $configdir ] ; then
582         if [ -f "${configdir}.svnpath" -o -f "${configdir}.gitpath" ] ; then
583             echo 1>&2 "Invoking make to extract remote config.${pldistro}"
584             # we set PLDISTROTAGS here to /dev/null because when dealing with remote distros
585             # at a very early stage (like searching for devel.pkgs even before the build VM is created)
586             # then make screams because it cannot find a mandatory include file
587             # OTOH this mechanism here is not intended to depend on tags specifically
588             make 1>&2 --no-print-directory -C $builddir stage1=true config.${pldistro} PLDISTROTAGS=/dev/null
589         fi
590     fi
591     # locate it
592     paths="$builddir/config.$pldistro/$pkgsfile $builddir/config.planetlab/$pkgsfile"
593     for path in $paths; do
594         if [ -f $path ] ; then
595             pkgspath=$path
596             break
597         fi
598     done
599     if [ -z "$pkgspath" ] ; then
600         echo 1>&2 "pl_locateDistroFile - in $(pwd) : cannot locate $pkgsfile in $builddir"
601         echo 1>&2 "candidates were $paths"
602         echo "not-found-by-pl_locateDistroFile"
603         return 1
604     else
605         echo 1>&2 "pl_locateDistroFile: using $pkgspath"
606         echo $pkgspath
607         return 0
608     fi
609 }
610
611 function yumgroups_from_pkgs () {
612     builddir=$1; shift
613     pldistro=$1; shift
614     fcdistro=$1; shift
615     pkgsnames=$@
616
617     sedargs="-e s,@FCDISTRO@,$fcdistro,g"
618
619    cat <<__header
620 <?xml version="1.0"?>
621 <!DOCTYPE comps PUBLIC "-//Red Hat, Inc.//DTD Comps info//EN" "comps.dtd">
622 <comps>
623 __header
624
625     for pkgsname in $pkgsnames; do
626         pkgsfile=$(pl_locateDistroFile $builddir $pldistro $pkgsname)
627         packages=$(pl_getPackages $fcdistro $pldistro $pkgsfile)
628
629         groupname=$(pkgs.py groupname $pkgsfile | sed $sedargs)
630         groupdesc=$(pkgs.py groupdesc $pkgsfile | sed $sedargs)
631
632         if [ -z "$groupname" -o -z "$groupdesc" ] ; then
633             echo "Cannot find groupname: and groupdesc: in $pkgsfile -- skipped" 1>&2
634             continue
635         fi
636         
637         cat << __group_header
638   <group>
639     <id>$(echo $groupname|tr A-Z a-z)</id>
640     <name>$groupname</name>
641     <description>$groupdesc</description>
642     <uservisible>true</uservisible>
643     <packagelist>
644 __group_header
645         for package in $packages; do 
646             echo "<packagereq type=\"mandatory\">$package</packagereq>"
647         done
648         cat << __group_footer
649     </packagelist>
650   </group>
651 __group_footer
652     done
653 cat <<__footer
654 </comps>
655 __footer
656 }
657
658
659 function build_fetch () {
660     curl --fail --silent --max-time 60 --output /dev/null "$1" 
661 }
662
663 # tries to compute a valid yum.conf for that pldistro from the template in mirroring/
664 # returns 0 and writes <dest_yumconf> on success
665 # returns 1 on failure, in which case <dest_yumconf> is deleted
666 function yumconf_mirrors () {
667     dest_yumconf=$1; shift
668     builddir=$1; shift
669     fcdistro=$1; shift
670     yumexclude_line="$1" ; shift
671     mirrors="$@"
672
673     template=$builddir/mirroring/$fcdistro/yum.repos.d/building.repo.in
674     
675     if [ ! -f $template ] ; then
676         echo "yumconf_mirrors: cannot locate template $template"
677         rm -f $dest_yumconf
678         return 1
679     fi
680
681     for mirror in $mirrors; do
682         if yumconf_mirror $dest_yumconf $template "$yumexclude_line" $mirror; then
683             return 0
684         fi
685     done
686     echo 'yumconf_mirrors in build.common : ran out of mirrors -- BAILING OUT'
687     rm -f $dest_yumconf
688     return 1
689 }
690
691 # computes a yum.conf from the template, and checks that all baseurl defined in there are valid repos
692 # returns 0 on success and 1 on failure
693 function yumconf_mirror () {
694     dest_yumconf=$1; shift
695     template=$1; shift
696     yumexclude_line="$1" ; shift
697     mirror=$1; shift
698
699     sed -e "s,@MIRRORURL@,$mirror,g" \
700         -e "/baseurl=/i\\
701 $yumexclude_line" $template > $dest_yumconf
702     
703     # capture all lines defining baseurl
704     baseurl_defs=$(grep '^baseurl=' $dest_yumconf)
705     if [ -z "$baseurl_defs" ] ; then
706         return 1
707     fi
708
709     for baseurl_def in $baseurl_defs; do
710         baseurl=$(echo $baseurl_def | sed \
711             -e s,baseurl=,, \
712             -e 's,$basearch,'"$pl_DISTRO_ARCH",g)
713         repomd=$baseurl/repodata/repomd.xml
714
715         echo "* Trying to fetch $repomd"
716         if ! build_fetch $repomd ; then
717             echo "* Failed to fetch $repomd"
718             return 1
719         fi
720     done
721     echo "* Selected mirror $mirror"
722     return 0
723 }
724
725 # from a yum.conf as generated above, computes the gpgkey urls
726 function yumconf_gpgkey () {
727     dest_yumconf=$1; shift
728
729     values=$(grep -h '^gpgkey=' $dest_yumconf | sed -e s,gpgkey=,, | sed -e 's,$basearch,'"$pl_DISTRO_ARCH",g | sed -e 's, ,\n,g' | sort | uniq | xargs)
730     [ -n "$values" ] || return 1
731     echo $values
732     return 0
733 }
734
735 # patches a yum conf to insert an exclude line in each declared repo
736 function yumconf_exclude () {
737     repo=$1; shift
738     yumexclude_line="$1" ; shift
739     
740     sed -i -e "/#baseurl=.*$/i\\
741 $yumexclude_line" $repo
742 }