1#! /usr/bin/env bash 2# icecc -- A simple distributed compiler system 3# 4# Copyright (C) 2004 by the Icecream Authors 5# GPL 6 7target_paths= 8target_aliases= 9 10# Always prints, optionally to a log file 11print_output () 12{ 13 if test -n "$log_path"; then 14 echo "$@" | tee -a "$log_path" 15 else 16 echo "$@" 17 fi 18} 19 20# Only prints if the debug flag is specified 21print_debug () 22{ 23 if test -n "$debug"; then 24 print_output "$@" 25 fi 26} 27 28is_dynamic_elf () 29{ 30 # Is the file an dynamically linked ELF executable? 31 (file -L "$1" | grep 'ELF' > /dev/null 2>&1) && (! file -L "$1" | grep 'static' > /dev/null 2>&1) 32} 33 34fix_rpath () 35{ 36 # Patches the RPATH for a file. When the program is executed in the chroot 37 # be iceccd, /proc is not mounted. As such, $ORIGIN can't be resolved. To 38 # work around this, replace all instances of $ORIGIN in RPATH with the 39 # known chroot path to the executables directory 40 local path="$1" 41 local origin="$2" 42 if ! is_dynamic_elf "$path"; then 43 return 44 fi 45 local old_rpath="`$PATCHELF --print-rpath "$path"`" 46 local new_rpath="`echo "$old_rpath" | \ 47 sed 's/.*\[\(.*\)\]/\1/g' | \ 48 sed "s,\\\$ORIGIN,/$origin,g"`" 49 50 if test -n "$new_rpath"; then 51 print_debug "Converting RPATH '$old_rpath' -> '$new_rpath'" 52 $PATCHELF --set-rpath "$new_rpath" "$path" 53 fi 54} 55 56add_path () 57{ 58 case " $target_paths " in 59 *" $1 "*) 60 return 1 61 ;; 62 *) 63 target_paths="$target_paths $1" 64 return 0 65 ;; 66 esac 67} 68 69add_alias () 70{ 71 if test "$1" != "$2"; then 72 local alias="$1=$2" 73 case " $target_aliases " in 74 *" $alias "*) 75 ;; 76 *) 77 print_debug "Adding alias '$2' -> '$1'" 78 target_aliases="$target_aliases $alias" 79 ;; 80 esac 81 fi 82} 83 84normalize_path () 85{ 86 # Normalizes the path to a file or directory, removing all "." and ".." 87 # entries. Use pwd -L to explicitly prevent symlink expansion 88 local path=$1 89 if test -f "$path"; then 90 pushd $(dirname $path) > /dev/null 2>&1 91 dir_path=$(pwd -L) 92 path=$dir_path/$(basename $path) 93 popd > /dev/null 2>&1 94 elif test -d "$path"; then 95 pushd $path > /dev/null 2>&1 96 path=$(pwd -L) 97 popd > /dev/null 2>&1 98 fi 99 echo $path 100} 101 102add_file_common() 103{ 104 local p="$1" 105 local path="$2" 106 local alias="$3" 107 108 add_alias "$path" "$p" 109 if test -n "$alias"; then 110 add_alias "$path" "$alias" 111 fi 112 113 add_path "$path" || return 1 114 print_debug "Adding file '$path'" 115 116 return 0 117} 118 119add_deps() 120{ 121 local path="$1" 122 local interp="$2" 123 124 if test -n "$interp" && test -x "$interp"; then 125 # Use the dynamic loaders --list argument to list the 126 # dependencies. The program may have a different program 127 # interpreter (typical when using uninative tarballs), which is 128 # why we can't just call ldd. 129 deps="`$interp --list "$path"`" 130 else 131 deps="`ldd "$path"`" 132 fi 133 134 print_debug "Dependencies are:" 135 print_debug "$deps" 136 if test -n "$deps"; then 137 for lib in $deps; do 138 # ldd now outputs ld as /lib/ld-linux.so.xx on current nptl 139 # based glibc this regexp parse the outputs like: 140 # ldd /usr/bin/gcc 141 # linux-gate.so.1 => (0xffffe000) 142 # libc.so.6 => /lib/tls/libc.so.6 (0xb7e81000) 143 # /lib/ld-linux.so.2 (0xb7fe8000) 144 # covering both situations ( with => and without ) 145 lib="`echo "$lib" | sed -n 's,^[^/]*\(/[^ ]*\).*,\1,p'`" 146 147 test -f "$lib" || continue 148 # Check whether the same library also exists in the parent 149 # directory, and prefer that on the assumption that it is a 150 # more generic one. 151 local baselib=`echo "$lib" | sed 's,\(/[^/]*\)/.*\(/[^/]*\)$,\1\2,'` 152 test -f "$baselib" && lib=$baselib 153 add_dependency "$lib" "$interp" 154 done 155 fi 156} 157 158add_dependency() 159{ 160 local p=`normalize_path $1` 161 # readlink is required for Yocto, so we can use it 162 local path=`readlink -f "$p"` 163 local interp="$2" 164 165 add_file_common "$p" "$path" || return 166 167 if test -x "$path" && is_dynamic_elf "$path"; then 168 add_deps "$path" "$interp" 169 fi 170} 171 172add_file () 173{ 174 local p=`normalize_path $1` 175 # readlink is required for Yocto, so we can use it 176 local path=`readlink -f "$p"` 177 178 add_file_common "$p" "$path" "$2" || return 179 180 if test -x "$path" && is_dynamic_elf "$path"; then 181 # Request the program interpeter (dynamic loader) 182 interp=`readelf -W -l "$path" | grep "Requesting program interpreter:" | sed "s/\s*\[Requesting program interpreter:\s*\(.*\)\]/\1/g"` 183 print_debug "Interpreter is '$interp'" 184 185 add_deps "$path" "$interp" 186 fi 187} 188 189while test -n "$1"; do 190 case "$1" in 191 --respect-path) 192 # Ignore for backward compatability 193 ;; 194 --debug) 195 debug=1 196 ;; 197 --log) 198 do_log=1 199 ;; 200 --extra=*) 201 extra_tools="$extra_tools ${1#--extra=}" 202 ;; 203 *) 204 break 205 ;; 206 esac 207 shift 208done 209 210added_gcc=$1 211shift 212added_gxx=$1 213shift 214added_as=$1 215shift 216archive_name=$1 217 218if test -n "$do_log"; then 219 log_path="$archive_name.log" 220 rm -f "$log_path" 221fi 222 223if test -z "$PATCHELF"; then 224 PATCHELF=`which patchelf 2> /dev/null` 225fi 226if test -z "$PATCHELF"; then 227 PATCHELF=`which patchelf-uninative 2> /dev/null` 228fi 229if test -z "$PATCHELF"; then 230 print_output "patchelf is required" 231 exit 1 232fi 233 234if test -z "$added_gcc" || test -z "$added_gxx" ; then 235 print_output "usage: $0 <gcc_path> <g++_path>" 236 exit 1 237fi 238 239if ! test -x "$added_gcc" ; then 240 print_output "'$added_gcc' is not executable." 241 exit 1 242fi 243 244if ! test -x "$added_gxx" ; then 245 print_output "'$added_gcc' is not executable." 246 exit 1 247fi 248 249 250 251add_file $added_gcc /usr/bin/gcc 252add_file $added_gxx /usr/bin/g++ 253 254if test -z "$added_as" ; then 255 add_file /usr/bin/as /usr/bin/as 256else 257 if ! test -x "$added_as" ; then 258 print_output "'$added_as' is not executable." 259 exit 1 260 fi 261 262 add_file $added_as /usr/bin/as 263fi 264 265add_file `$added_gcc -print-prog-name=cc1` /usr/bin/cc1 266add_file `$added_gxx -print-prog-name=cc1plus` /usr/bin/cc1plus 267specfile=`$added_gcc -print-file-name=specs` 268if test -n "$specfile" && test -e "$specfile"; then 269 add_file "$specfile" 270fi 271 272ltofile=`$added_gcc -print-prog-name=lto1` 273pluginfile=`normalize_path "${ltofile%lto1}liblto_plugin.so"` 274if test -r "$pluginfile" 275then 276 add_file $pluginfile ${pluginfile#*usr} 277 add_file $pluginfile /usr${pluginfile#*usr} 278fi 279 280# for testing the environment is usable at all 281if test -x /bin/true; then 282 add_file /bin/true 283elif test -x /usr/bin/true; then 284 add_file /usr/bin/true /bin/true 285else 286 print_output "'true' not found" 287 exit 1 288fi 289 290for extra in $extra_tools; do 291 if test -x "$extra"; then 292 add_file "$extra" 293 else 294 print_output "'$extra' not found" 295 exit 1 296 fi 297done 298 299link_rel () 300{ 301 local target="$1" 302 local name="$2" 303 local base="$3" 304 305 local prefix=`dirname $name` 306 307 prefix=`echo $prefix | sed 's,[^/]\+,..,g' | sed 's,^/*,,g'` 308 309 ln -s $prefix/$target $base/$name 310} 311 312tempdir=`mktemp -d /tmp/iceccenvXXXXXX` 313target_files= 314for path in $target_paths; do 315 mkdir -p $tempdir/`dirname $path` 316 cp -pH $path $tempdir/$path 317 318 if test -f $tempdir/$path -a -x $tempdir/$path; then 319 strip -s $tempdir/$path 2>/dev/null 320 fi 321 322 fix_rpath $tempdir/$path `dirname $path` 323 target_files="$target_files $path" 324done 325 326for i in $target_aliases; do 327 target=`echo $i | cut -d= -f1` 328 link_name=`echo $i | cut -d= -f2` 329 330 mkdir -p $tempdir/`dirname $link_name` 331 # Relative links are used because the files are checked for being 332 # executable outside the chroot 333 link_rel $target $link_name $tempdir 334 335 link_name=`echo $link_name | cut -b2-` 336 target_files="$target_files $link_name" 337done 338 339#sort the files 340target_files=`for i in $target_files; do echo $i; done | sort` 341 342#test if an archive name was supplied 343#if not use the md5 of all files as the archive name 344if test -z "$archive_name"; then 345 md5sum=NONE 346 for file in /usr/bin/md5sum /bin/md5 /usr/bin/md5; do 347 if test -x $file; then 348 md5sum=$file 349 break 350 fi 351 done 352 353 #calculate md5 and use it as the archive name 354 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 || { 355 print_output "Couldn't compute MD5 sum." 356 exit 2 357 } 358 mydir=`pwd` 359else 360 mydir="`dirname "$archive_name"`" 361 362 #check if we have a full path or only a filename 363 if test "$mydir" = "." ; then 364 mydir=`pwd` 365 else 366 mydir="" 367 fi 368fi 369 370print_output "creating $archive_name" 371 372cd $tempdir 373# Add everything in the temp directory. Tar doesn't like to be given files with 374# ".." in them, which frequently happens in $target_files, and will strip off 375# the path prefix past the offending "..". This makes the archive generate 376# incorrectly 377tar -czf "$mydir/$archive_name" . || { 378 print_output "Couldn't create archive" 379 exit 3 380} 381cd .. 382rm -rf $tempdir 383