Blob Blame History Raw
#! /bin/bash

#############################################################################
# Copyright (c) 2006-2009 Novell, Inc.
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail,
# you may find current contact information at www.novell.com
#############################################################################

usage() {
    echo "USAGE: ${0##*/} [--(no-)symvers|--(no-)symsets|--(no-)symtypes] [--filter <filter>] rpm ..."
    echo -n "The default is"
    $do_symvers  && echo -n " --symvers"  || echo -n " --no-symvers"
    $do_symsets  && echo -n " --symsets"  || echo -n " --no-symsets"
    $do_symtypes && echo -n " --symtypes" || echo -n " --no-symtypes"
    echo
}

symsets_are_equal() {
    local a=$1 b=$2

    cmp -s <(tar tfz $a | sed -e 's:.*/::' | sort) \
	   <(tar tfz $b | sed -e 's:.*/::' | sort)
}

filters2re() {
    local f tail res sep

    if test -z "$tail"; then
        tail='/[^/]*$'
    fi
    for f in "$@"; do
        case "$f" in
        *\*)
            f="${f%\*}"
            f="^\.?$f"
            ;;
        *)
            f="^\.?$f$tail"
        esac
        res="$res$sep$f"
        sep="|"
    done
    echo "$res"
}

merge_dirs() {
    local old=$1 new=$2

    find "$old" "$new" -type f -printf '%P\n' | sort -u \
	>"$tmpdir/all"
    while read f; do
	if cmp -s "$old/$f" "$new/$f"; then
	    continue
	fi
	if ! echo "$f" | grep -Eq "$re"; then
	    continue
	fi
	if test -e "$new/$f"; then
	    cp "$new/$f" "$old/$f"
	    echo "$f added to $target"
	else
	    rm "$old/$f"
	    echo "$f removed from $target"
	fi
    done <"$tmpdir/all"
}

add_symsets() {
    set -- $tmpdir/boot/symsets-*
    if [ $# -eq 0 ]; then
	echo "No symsets file found in $rpm" >&2
	status=1
	return
    fi

    local re=$(tail='\.[0-9a-f]*(\.fake)?$' filters2re "${filters[@]//\//_}")

    for symsets in "$@"; do
	flavor=${symsets%.tar.gz}
	flavor=${flavor##*-}

	target=kabi/$arch/symsets-$flavor.tar.gz
	if [ -e $target ] && symsets_are_equal "$symsets" $target; then
	    echo "$target is unchanged"
	    continue
	fi
	if $opt_dry_run; then
	    echo "$target added"
	    continue
	fi
	mkdir -p ${target%/*}
	if test -z "${filters[*]}"; then
	    cp "$symsets" $target
	else
	    rm -rf "$tmpdir/symsets"
	    mkdir "$tmpdir/symsets"
	    tar xzf "$target" -C "$tmpdir/symsets"
	    old=$(echo "$tmpdir/symsets"/*)
	    mkdir "$tmpdir/symsets/new"
	    tar xzf "$symsets" -C "$tmpdir/symsets/new" --strip-components=1
	    new="$tmpdir/symsets/new"
	    merge_dirs "$old" "$new"
	    tar czf "$target" -C "$tmpdir/symsets" "${old##*/}"
	fi
	echo "$target added"
    done
}

unpack_symtypes() {
    local dir="$1"

    rpm/modversions --unpack "$dir"
}

pack_symtypes() {
    local dir="$1"

    rpm/modversions --pack --ext .symref "$dir"
}

unpack_symvers() {
    local dir="$1" csum sym mod rest last=
    mkdir -p "$dir"

    sort -k 3 | while read csum sym mod rest; do
	    if test "$last" != "$mod"; then
		    mkdir -p "$dir/${mod%/*}"
		    exec 3>"$dir/$mod.symvers"
		    last=$mod
	    fi
	    echo -ne "$csum\\t$sym\\t$mod" >&3
	    if test -n "$rest"; then
		    echo -ne "\\t$rest" >&3
	    fi
	    echo >&3
    done
    exec 3>&-
}

pack_symvers() {
    local dir="$1"

    find "$dir" -type f | xargs cat | sort -k 2
}

add_flat_files() {
    local type=$1

    set -- $tmpdir/boot/$type-*.gz
    if [ $# -eq 0 ]; then
        set -- $tmpdir/usr/src/linux-*-obj/*/*/Module.$type
    fi
    if [ $# -eq 0 ]; then
	echo "No $type file found in $rpm" >&2
	status=1
	return
    fi
    local file flavor target
    local re=$(filters2re "${filters[@]}")

    for file; do
        case "$file" in
        *.gz)
            gzip -cd "$file" > "${file%.gz}"
            file=${file%.gz}
            ;;
        esac
        case "$file" in
        $tmpdir/boot/*)
            flavor=${file##*-}
            ;;
        $tmpdir/usr/src/*/Module.symvers)
            flavor=${file##*-obj/}
            flavor=${flavor#*/}
            flavor=${flavor%/Module.symvers}
            ;;
        esac

	target=kabi/$arch/$type-$flavor
	if cmp -s "$file" "$target"; then
	    echo "$target is unchanged"
	    continue
	fi
	if $opt_dry_run; then
	    echo "$target added"
	    continue
	fi
	mkdir -p "${target%/*}"
	if test -z "${filters[*]}"; then
	    cp "$file" "$target"
	else
	    rm -rf "$tmpdir/$type"
	    mkdir -p "$tmpdir/$type"/{old,new}
	    old="$tmpdir/$type/old"
	    new="$tmpdir/$type/new"
	    unpack_$type "$old" <"$target" &
	    unpack_$type "$new" <"$file" &
	    wait
	    merge_dirs "$old" "$new"
	    pack_$type "$old" >"$target"
	fi
	echo "$target added"
    done
}

unpack_iso()
{
    local iso=$1 file name

    isoinfo -R -f -i "$iso" | grep -E '/kernel-[^/]*-([2-9]|[1-9][0-9]+)\.[0-9][^/]*\.rpm$' |
    while read file; do
        name=${file##*/}
        case "$name" in
        kernel-source-* | kernel-syms-* | kernel-*-debug* | kernel-*-man-*| \
        kernel-firmware-* | kernel-coverage-* | kernel-docs-* | \
        kernel-*-extra* | *src.rpm)
            continue
        esac
        isoinfo -R -i "$iso" -x "$file" >"$tmpdir/$name"
        echo "$tmpdir/$name"
    done
}

query_rpm()
{
    local rpm=$1

    exec 3< <(rpm -qp --qf '%{NAME}\n%{ARCH}\n%{VERSION}\n%{RELEASE}' "$rpm")
    read name <&3
    read arch <&3
    read version <&3
    read release <&3
    exec 3<&-
}

. rpm/config.sh
case "$IBS_PROJECT" in
SUSE:SLE-9*)
    do_symvers=true
    do_symsets=false
    do_symtypes=false
    ;;
SUSE:SLE-10-SP[012]*)
    do_symvers=true
    do_symsets=true
    do_symtypes=false
    ;;
SUSE:SLE-10* | SUSE:SLE-11*)
    do_symvers=true
    do_symsets=true
    do_symtypes=true
    ;;
*)
    do_symvers=true
    do_symsets=false
    do_symtypes=true
    ;;
esac

options="$(getopt -o h --long symvers,no-symvers,symsets,no-symsets,symtypes,no-symtypes,dry-run,filter:,help -- "$@")"
if [ $? -ne 0 ]; then
    usage >&2
    exit 1
fi
eval set -- "$options"

opt_dry_run=false
filters=()
while :; do
    arg=$1
    shift
    newval=true
    case "$arg" in
	--no-*)
	    newval=false
	    arg=${arg/--no-/--}
    esac
    case "$arg" in
	--symvers)
            do_symvers=$newval
	    ;;
	--symsets)
            do_symsets=$newval
	    ;;
	--symtypes)
            do_symtypes=$newval
	    ;;
	--dry-run)
	    opt_dry_run=true
	    ;;
	--filter)
	    filters[${#filters[@]}]=${1%/}
	    shift
	    ;;
	-h|--help)
	    usage
	    exit 0
	    ;;
	--)
	    break;
    esac
done

if [ $# -eq 0 ]; then
    usage >&2
    exit 1
fi

tmpdir=$(mktemp -td ${0##*/}.XXXXXX)
trap "rm -rf $tmpdir" EXIT

shopt -s nullglob

echo "Packages to process:"
unset ${!found_rmps_*}
while test $# -gt 0; do
    rpm=$1
    shift
    case "$rpm" in
	*.src.rpm | *.nosrc.rpm | *.noarch.rpm | *.delta.rpm)
	    continue
	    ;;
        *.iso)
            set -- $(unpack_iso "$rpm") "$@"
            continue
            ;;
    esac

    # There may be symlinks as well as regular files. Skip duplicates.
    for file in "${rpms[@]}"; do
	[ "$rpm" -ef "$file" ] && continue 2
    done

    query_rpm "$rpm"
    var=found_rmps_${arch}_${name//-/_}
    eval "$var=\"\$$var $rpm\""

    rpms[${#rpms[@]}]=$rpm
done
for rpm in ${!found_rmps_*}; do
    set -- ${!rpm}
    if [ $# -ne 1 ]; then
	echo "*** Duplicate: ${*##*/}" >&2
	failed=1
    fi
done
[ -z "$failed" ] || exit 1
for rpm in ${!found_rmps_*}; do
    set -- ${!rpm}
    echo ${1##*/}
done
echo

archs_flavors="$(scripts/guards --list < config.conf)"
for rpm in "${rpms[@]}"; do
    query_rpm "$rpm"

    # skip the main package if there is a base subpackage
    base=found_rmps_${arch}_${name//-/_}_base
    if [ -n "${!base}" ]; then
	continue
    fi
    # ... and skip both if there is a -devel subpackage
    devel=found_rmps_${arch}_${name//-/_}
    devel=${devel%_base}_devel
    if [ -n "${!devel}" ]; then
        continue
    fi

    build_arch=$arch
    case "$arch" in
	i?86)
	    arch=i386 ;;
	s390*)
            case "$IBS_PROJECT" in
            SUSE:SLE-9* | SUSE:SLE-10* | SUSE:SLE-11:*)
                arch=s390 ;;
            esac
            ;;
        ppc*)
            case "$IBS_PROJECT" in
            SUSE:SLE-10*)
                arch=powerpc ;;
            esac
            ;;
        aarch64)
            arch=arm64
            ;;
    esac
    flavor=${name#kernel-}
    flavor=${flavor%-base}
    flavor=${flavor%-devel}

    if ! echo "$archs_flavors" | grep -q "$arch/$flavor"; then
	case "$flavor" in
	source | syms | *-extra)
	    ;;
	*)
	    echo "${rpm%%*/}: not a known arch/flavor; skipping" >&2
	    ;;
	esac
	continue
    fi

    echo "[$name-$version-$release.$build_arch.rpm]"

    rm -rf $tmpdir/{boot,usr}
    rpm2cpio "$rpm" \
    | ( cd $tmpdir && cpio -dim --quiet './boot/symvers-*' './boot/symsets-*' \
        './boot/symtypes-*' './usr/src/linux-*-obj/*/*/Module.symvers' )

    if $do_symvers; then
	add_flat_files symvers
    fi
    if $do_symtypes; then
	add_flat_files symtypes
    fi
    if $do_symsets; then
	add_symsets
    fi
done

exit $status

# vim:sw=4 et