#! /usr/bin/env bash 
 | 
# icecc -- A simple distributed compiler system 
 | 
# 
 | 
# Copyright (C) 2004 by the Icecream Authors 
 | 
# GPL 
 | 
  
 | 
target_paths= 
 | 
target_aliases= 
 | 
  
 | 
# Always prints, optionally to a log file 
 | 
print_output () 
 | 
{ 
 | 
    if test -n "$log_path"; then 
 | 
        echo "$@" | tee -a "$log_path" 
 | 
    else 
 | 
        echo "$@" 
 | 
    fi 
 | 
} 
 | 
  
 | 
# Only prints if the debug flag is specified 
 | 
print_debug () 
 | 
{ 
 | 
    if test -n "$debug"; then 
 | 
        print_output "$@" 
 | 
    fi 
 | 
} 
 | 
  
 | 
is_dynamic_elf () 
 | 
{ 
 | 
    # Is the file an dynamically linked ELF executable? 
 | 
    (file -L "$1" | grep 'ELF' > /dev/null 2>&1) && (! file -L "$1" | grep 'static' > /dev/null 2>&1) 
 | 
} 
 | 
  
 | 
fix_rpath () 
 | 
{ 
 | 
    # Patches the RPATH for a file. When the program is executed in the chroot 
 | 
    # be iceccd, /proc is not mounted. As such, $ORIGIN can't be resolved. To 
 | 
    # work around this, replace all instances of $ORIGIN in RPATH with the 
 | 
    # known chroot path to the executables directory 
 | 
    local path="$1" 
 | 
    local origin="$2" 
 | 
    if ! is_dynamic_elf "$path"; then 
 | 
        return 
 | 
    fi 
 | 
    local old_rpath="`$PATCHELF --print-rpath "$path"`" 
 | 
    local new_rpath="`echo "$old_rpath" | \ 
 | 
        sed 's/.*\[\(.*\)\]/\1/g' | \ 
 | 
        sed "s,\\\$ORIGIN,/$origin,g"`" 
 | 
  
 | 
    if test -n "$new_rpath"; then 
 | 
        print_debug "Converting RPATH '$old_rpath' -> '$new_rpath'" 
 | 
        $PATCHELF --set-rpath "$new_rpath" "$path" 
 | 
    fi 
 | 
} 
 | 
  
 | 
add_path () 
 | 
{ 
 | 
    case " $target_paths " in 
 | 
        *" $1 "*) 
 | 
            return 1 
 | 
            ;; 
 | 
        *) 
 | 
            target_paths="$target_paths $1" 
 | 
            return 0 
 | 
            ;; 
 | 
    esac 
 | 
} 
 | 
  
 | 
add_alias () 
 | 
{ 
 | 
    if test "$1" != "$2"; then 
 | 
        local alias="$1=$2" 
 | 
        case " $target_aliases " in 
 | 
            *" $alias "*) 
 | 
                ;; 
 | 
            *) 
 | 
                print_debug "Adding alias '$2' -> '$1'" 
 | 
                target_aliases="$target_aliases $alias" 
 | 
                ;; 
 | 
        esac 
 | 
    fi 
 | 
} 
 | 
  
 | 
normalize_path () 
 | 
{ 
 | 
    # Normalizes the path to a file or directory, removing all "." and ".." 
 | 
    # entries. Use pwd -L to explicitly prevent symlink expansion 
 | 
    local path=$1 
 | 
    if test -f "$path"; then 
 | 
        pushd $(dirname $path) > /dev/null 2>&1 
 | 
        dir_path=$(pwd -L) 
 | 
        path=$dir_path/$(basename $path) 
 | 
        popd > /dev/null 2>&1 
 | 
    elif test -d "$path"; then 
 | 
        pushd $path > /dev/null 2>&1 
 | 
        path=$(pwd -L) 
 | 
        popd > /dev/null 2>&1 
 | 
    fi 
 | 
    echo $path 
 | 
} 
 | 
  
 | 
add_file_common() 
 | 
{ 
 | 
    local p="$1" 
 | 
    local path="$2" 
 | 
    local alias="$3" 
 | 
  
 | 
    add_alias "$path" "$p" 
 | 
    if test -n "$alias"; then 
 | 
        add_alias "$path" "$alias" 
 | 
    fi 
 | 
  
 | 
    add_path "$path" || return 1 
 | 
    print_debug "Adding file '$path'" 
 | 
  
 | 
    return 0 
 | 
} 
 | 
  
 | 
add_deps() 
 | 
{ 
 | 
    local path="$1" 
 | 
    local interp="$2" 
 | 
  
 | 
    if test -n "$interp" && test -x "$interp"; then 
 | 
        # Use the dynamic loaders --list argument to list the 
 | 
        # dependencies. The program may have a different program 
 | 
        # interpreter (typical when using uninative tarballs), which is 
 | 
        # why we can't just call ldd. 
 | 
        deps="`$interp --list "$path"`" 
 | 
    else 
 | 
        deps="`ldd "$path"`" 
 | 
    fi 
 | 
  
 | 
    print_debug "Dependencies are:" 
 | 
    print_debug "$deps" 
 | 
    if test -n "$deps"; then 
 | 
        for lib in $deps; do 
 | 
            # ldd now outputs ld as /lib/ld-linux.so.xx on current nptl 
 | 
            # based glibc this regexp parse the outputs like: 
 | 
            # ldd /usr/bin/gcc 
 | 
            #         linux-gate.so.1 =>  (0xffffe000) 
 | 
            #         libc.so.6 => /lib/tls/libc.so.6 (0xb7e81000) 
 | 
            #         /lib/ld-linux.so.2 (0xb7fe8000) 
 | 
            # covering both situations ( with => and without ) 
 | 
            lib="`echo "$lib" | sed -n 's,^[^/]*\(/[^ ]*\).*,\1,p'`" 
 | 
  
 | 
            test -f "$lib" || continue 
 | 
            # Check whether the same library also exists in the parent 
 | 
            # directory, and prefer that on the assumption that it is a 
 | 
            # more generic one. 
 | 
            local baselib=`echo "$lib" | sed 's,\(/[^/]*\)/.*\(/[^/]*\)$,\1\2,'` 
 | 
            test -f "$baselib" && lib=$baselib 
 | 
            add_dependency "$lib" "$interp" 
 | 
        done 
 | 
    fi 
 | 
} 
 | 
  
 | 
add_dependency() 
 | 
{ 
 | 
    local p=`normalize_path $1` 
 | 
    # readlink is required for Yocto, so we can use it 
 | 
    local path=`readlink -f "$p"` 
 | 
    local interp="$2" 
 | 
  
 | 
    add_file_common "$p" "$path" || return 
 | 
  
 | 
    if test -x "$path" && is_dynamic_elf "$path"; then 
 | 
        add_deps "$path" "$interp" 
 | 
    fi 
 | 
} 
 | 
  
 | 
add_file () 
 | 
{ 
 | 
    local p=`normalize_path $1` 
 | 
    # readlink is required for Yocto, so we can use it 
 | 
    local path=`readlink -f "$p"` 
 | 
  
 | 
    add_file_common "$p" "$path" "$2" || return 
 | 
  
 | 
    if test -x "$path" && is_dynamic_elf "$path"; then 
 | 
        # Request the program interpeter (dynamic loader) 
 | 
        interp=`readelf -W -l "$path" | grep "Requesting program interpreter:" | sed "s/\s*\[Requesting program interpreter:\s*\(.*\)\]/\1/g"` 
 | 
        print_debug "Interpreter is '$interp'" 
 | 
  
 | 
        add_deps "$path" "$interp" 
 | 
    fi 
 | 
} 
 | 
  
 | 
while test -n "$1"; do 
 | 
    case "$1" in 
 | 
        --respect-path) 
 | 
            # Ignore for backward compatability 
 | 
            ;; 
 | 
        --debug) 
 | 
            debug=1 
 | 
            ;; 
 | 
        --log) 
 | 
            do_log=1 
 | 
            ;; 
 | 
        --extra=*) 
 | 
            extra_tools="$extra_tools ${1#--extra=}" 
 | 
            ;; 
 | 
        *) 
 | 
            break 
 | 
            ;; 
 | 
    esac 
 | 
    shift 
 | 
done 
 | 
  
 | 
added_gcc=$1 
 | 
shift 
 | 
added_gxx=$1 
 | 
shift 
 | 
added_as=$1 
 | 
shift 
 | 
archive_name=$1 
 | 
  
 | 
if test -n "$do_log"; then 
 | 
    log_path="$archive_name.log" 
 | 
    rm -f "$log_path" 
 | 
fi 
 | 
  
 | 
if test -z "$PATCHELF"; then 
 | 
    PATCHELF=`which patchelf 2> /dev/null` 
 | 
fi 
 | 
if test -z "$PATCHELF"; then 
 | 
    PATCHELF=`which patchelf-uninative 2> /dev/null` 
 | 
fi 
 | 
if test -z "$PATCHELF"; then 
 | 
    print_output "patchelf is required" 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
if test -z "$added_gcc" || test -z "$added_gxx" ; then 
 | 
    print_output "usage: $0 <gcc_path> <g++_path>" 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
if ! test -x "$added_gcc" ; then 
 | 
    print_output "'$added_gcc' is not executable." 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
if ! test -x "$added_gxx" ; then 
 | 
    print_output "'$added_gcc' is not executable." 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
  
 | 
  
 | 
add_file $added_gcc /usr/bin/gcc 
 | 
add_file $added_gxx /usr/bin/g++ 
 | 
  
 | 
if test -z "$added_as" ; then 
 | 
    add_file /usr/bin/as /usr/bin/as 
 | 
else 
 | 
    if ! test -x "$added_as" ; then 
 | 
        print_output "'$added_as' is not executable." 
 | 
        exit 1 
 | 
    fi 
 | 
  
 | 
    add_file $added_as  /usr/bin/as 
 | 
fi 
 | 
  
 | 
add_file `$added_gcc -print-prog-name=cc1` /usr/bin/cc1 
 | 
add_file `$added_gxx -print-prog-name=cc1plus` /usr/bin/cc1plus 
 | 
specfile=`$added_gcc -print-file-name=specs` 
 | 
if test -n "$specfile" && test -e "$specfile"; then 
 | 
    add_file "$specfile" 
 | 
fi 
 | 
  
 | 
ltofile=`$added_gcc -print-prog-name=lto1` 
 | 
pluginfile=`normalize_path "${ltofile%lto1}liblto_plugin.so"` 
 | 
if test -r "$pluginfile" 
 | 
then 
 | 
    add_file $pluginfile  ${pluginfile#*usr} 
 | 
    add_file $pluginfile  /usr${pluginfile#*usr} 
 | 
fi 
 | 
  
 | 
# for testing the environment is usable at all 
 | 
if test -x /bin/true; then 
 | 
    add_file /bin/true 
 | 
elif test -x /usr/bin/true; then 
 | 
    add_file /usr/bin/true /bin/true 
 | 
else 
 | 
    print_output "'true' not found" 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
for extra in $extra_tools; do 
 | 
    if test -x "$extra"; then 
 | 
        add_file "$extra" 
 | 
    else 
 | 
        print_output "'$extra' not found" 
 | 
        exit 1 
 | 
    fi 
 | 
done 
 | 
  
 | 
link_rel () 
 | 
{ 
 | 
    local target="$1" 
 | 
    local name="$2" 
 | 
    local base="$3" 
 | 
  
 | 
    local prefix=`dirname $name` 
 | 
  
 | 
    prefix=`echo $prefix | sed 's,[^/]\+,..,g' | sed 's,^/*,,g'` 
 | 
  
 | 
    ln -s $prefix/$target $base/$name 
 | 
} 
 | 
  
 | 
tempdir=`mktemp -d /tmp/iceccenvXXXXXX` 
 | 
target_files= 
 | 
for path in $target_paths; do 
 | 
    mkdir -p $tempdir/`dirname $path` 
 | 
    cp -pH $path $tempdir/$path 
 | 
  
 | 
    if test -f $tempdir/$path -a -x $tempdir/$path; then 
 | 
        strip -s $tempdir/$path 2>/dev/null 
 | 
    fi 
 | 
  
 | 
    fix_rpath $tempdir/$path `dirname $path` 
 | 
    target_files="$target_files $path" 
 | 
done 
 | 
  
 | 
for i in $target_aliases; do 
 | 
    target=`echo $i | cut -d= -f1` 
 | 
    link_name=`echo $i | cut -d= -f2` 
 | 
  
 | 
    mkdir -p $tempdir/`dirname $link_name` 
 | 
    # Relative links are used because the files are checked for being 
 | 
    # executable outside the chroot 
 | 
    link_rel $target $link_name $tempdir 
 | 
  
 | 
    link_name=`echo $link_name | cut -b2-` 
 | 
    target_files="$target_files $link_name" 
 | 
done 
 | 
  
 | 
#sort the files 
 | 
target_files=`for i in $target_files; do echo $i; done | sort` 
 | 
  
 | 
#test if an archive name was supplied 
 | 
#if not use the md5 of all files as the archive name 
 | 
if test -z "$archive_name"; then 
 | 
    md5sum=NONE 
 | 
    for file in /usr/bin/md5sum /bin/md5 /usr/bin/md5; do 
 | 
        if test -x $file; then 
 | 
            md5sum=$file 
 | 
            break 
 | 
        fi 
 | 
    done 
 | 
  
 | 
    #calculate md5 and use it as the archive name 
 | 
    archive_name=`for i in $target_files; do test -f $tempdir/$i && $md5sum $tempdir/$i; done | sed -e 's/ .*$//' | $md5sum | sed -e 's/ .*$//'`.tar.gz || { 
 | 
        print_output "Couldn't compute MD5 sum." 
 | 
        exit 2 
 | 
    } 
 | 
    mydir=`pwd` 
 | 
else 
 | 
    mydir="`dirname "$archive_name"`" 
 | 
  
 | 
    #check if we have a full path or only a filename 
 | 
    if test "$mydir" = "." ; then 
 | 
        mydir=`pwd` 
 | 
    else 
 | 
        mydir="" 
 | 
    fi 
 | 
fi 
 | 
  
 | 
print_output "creating $archive_name" 
 | 
  
 | 
cd $tempdir 
 | 
# Add everything in the temp directory. Tar doesn't like to be given files with 
 | 
# ".." in them, which frequently happens in $target_files, and will strip off 
 | 
# the path prefix past the offending "..". This makes the archive generate 
 | 
# incorrectly 
 | 
tar -czf "$mydir/$archive_name" . || { 
 | 
    print_output "Couldn't create archive" 
 | 
    exit 3 
 | 
} 
 | 
cd .. 
 | 
rm -rf $tempdir 
 |