#!/usr/bin/env bash # # Script for various external toolchain tasks, refer to # the --help output for more information. # # Copyright (C) 2012 Jo-Philipp Wich # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA CC="" CXX="" CPP="" CFLAGS="" TOOLCHAIN="." LIBC_TYPE="" # Library specs LIB_SPECS=" c: ld-* lib{anl,c,cidn,crypt,dl,m,nsl,nss_dns,nss_files,resolv,util} rt: librt-* librt pthread: libpthread-* libpthread stdcpp: libstdc++ thread_db: libthread-db gcc: libgcc_s ssp: libssp gfortran: libgfortran gomp: libgomp " # Binary specs BIN_SPECS=" ldd: ldd ldconfig: ldconfig gdb: gdb gdbserver: gdbserver " OVERWRITE_CONFIG="" test_c() { cat <<-EOT | "${CC:-false}" $CFLAGS -o /dev/null -x c - 2>/dev/null #include int main(int argc, char **argv) { printf("Hello, world!\n"); return 0; } EOT } test_cxx() { cat <<-EOT | "${CXX:-false}" $CFLAGS -o /dev/null -x c++ - 2>/dev/null #include using namespace std; int main() { cout << "Hello, world!" << endl; return 0; } EOT } test_softfloat() { cat <<-EOT | "$CC" $CFLAGS -msoft-float -o /dev/null -x c - 2>/dev/null int main(int argc, char **argv) { double a = 0.1; double b = 0.2; double c = (a + b) / (a * b); return 1; } EOT } test_uclibc() { local sysroot="$("$CC" $CFLAGS -print-sysroot 2>/dev/null)" if [ -d "${sysroot:-$TOOLCHAIN}" ]; then local lib for lib in "${sysroot:-$TOOLCHAIN}"/{lib,usr/lib,usr/local/lib}/ld*-uClibc*.so*; do if [ -f "$lib" ] && [ ! -h "$lib" ]; then return 0 fi done fi return 1 } test_feature() { local feature="$1"; shift # find compilers, libc type probe_cc probe_cxx probe_libc # common toolchain feature tests case "$feature" in c) test_c; return $? ;; c++) test_cxx; return $? ;; soft*) test_softfloat; return $? ;; esac # assume eglibc/glibc supports all libc features if [ "$LIBC_TYPE" != "uclibc" ]; then return 0 fi # uclibc feature tests local inc local sysroot="$("$CC" "$@" -muclibc -print-sysroot 2>/dev/null)" for inc in "include" "usr/include" "usr/local/include"; do local conf="${sysroot:-$TOOLCHAIN}/$inc/bits/uClibc_config.h" if [ -f "$conf" ]; then case "$feature" in lfs) grep -q '__UCLIBC_HAS_LFS__ 1' "$conf"; return $?;; ipv6) grep -q '__UCLIBC_HAS_IPV6__ 1' "$conf"; return $?;; rpc) grep -q '__UCLIBC_HAS_RPC__ 1' "$conf"; return $?;; locale) grep -q '__UCLIBC_HAS_LOCALE__ 1' "$conf"; return $?;; wchar) grep -q '__UCLIBC_HAS_WCHAR__ 1' "$conf"; return $?;; threads) grep -q '__UCLIBC_HAS_THREADS__ 1' "$conf"; return $?;; esac fi done return 1 } find_libs() { local spec="$(echo "$LIB_SPECS" | sed -ne "s#^[[:space:]]*$1:##ip")" if [ -n "$spec" ] && probe_cpp; then local libdir libdirs for libdir in $( "$CPP" $CFLAGS -v -x c /dev/null 2>&1 | \ sed -ne 's#:# #g; s#^LIBRARY_PATH=##p' ); do if [ -d "$libdir" ]; then libdirs="$libdirs $(cd "$libdir"; pwd)/" fi done local pattern for pattern in $(eval echo $spec); do find $libdirs -name "$pattern.so*" | sort -u done return 0 fi return 1 } find_bins() { local spec="$(echo "$BIN_SPECS" | sed -ne "s#^[[:space:]]*$1:##ip")" if [ -n "$spec" ] && probe_cpp; then local sysroot="$("$CPP" -print-sysroot)" local bindir bindirs for bindir in $( echo "${sysroot:-$TOOLCHAIN}/bin"; echo "${sysroot:-$TOOLCHAIN}/usr/bin"; echo "${sysroot:-$TOOLCHAIN}/usr/local/bin"; "$CPP" $CFLAGS -v -x c /dev/null 2>&1 | \ sed -ne 's#:# #g; s#^COMPILER_PATH=##p' ); do if [ -d "$bindir" ]; then bindirs="$bindirs $(cd "$bindir"; pwd)/" fi done local pattern for pattern in $(eval echo $spec); do find $bindirs -name "$pattern" | sort -u done return 0 fi return 1 } wrap_bin_cc() { local out="$1" local bin="$2" echo '#!/bin/sh' > "$out" echo 'for arg in "$@"; do' >> "$out" echo ' case "$arg" in -l*|-L*|-shared|-static)' >> "$out" echo -n ' exec "'"$bin"'" '"$CFLAGS"' ${STAGING_DIR:+' >> "$out" echo -n '-idirafter "$STAGING_DIR/usr/include" ' >> "$out" echo -n '-L "$STAGING_DIR/usr/lib" ' >> "$out" echo '-Wl,-rpath-link,"$STAGING_DIR/usr/lib"} "$@" ;;' >> "$out" echo ' esac' >> "$out" echo 'done' >> "$out" echo -n 'exec "'"$bin"'" '"$CFLAGS"' ${STAGING_DIR:+' >> "$out" echo '-idirafter "$STAGING_DIR/usr/include"} "$@"' >> "$out" chmod +x "$out" } wrap_bin_ld() { local out="$1" local bin="$2" echo '#!/bin/sh' > "$out" echo -n 'exec "'"$bin"'" ${STAGING_DIR:+' >> "$out" echo -n '-L "$STAGING_DIR/usr/lib" ' >> "$out" echo '-rpath-link "$STAGING_DIR/usr/lib"} "$@"' >> "$out" chmod +x "$out" } wrap_bin_other() { local out="$1" local bin="$2" echo '#!/bin/sh' > "$out" echo 'exec "'"$bin"'" "$@"' >> "$out" chmod +x "$out" } wrap_bins() { if probe_cc; then mkdir -p "$1" || return 1 local cmd for cmd in "${CC%-*}-"*; do if [ -x "$cmd" ]; then local out="$1/${cmd##*/}" local bin="$cmd" if [ -x "$out" ] && ! grep -q STAGING_DIR "$out"; then mv "$out" "$out.bin" bin='$(dirname "$0")/'"${out##*/}"'.bin' fi case "${cmd##*/}" in *-*cc|*-*cc-*|*-*++|*-*++-*|*-cpp) wrap_bin_cc "$out" "$bin" ;; *-ld) wrap_bin_ld "$out" "$bin" ;; *) wrap_bin_other "$out" "$bin" ;; esac fi done return 0 fi return 1 } print_config() { local mktarget="$1" local mksubtarget local target="$("$CC" $CFLAGS -dumpmachine)" local version="$("$CC" $CFLAGS -dumpversion)" local cpuarch="${target%%-*}" # get CC; strip version; strip gcc and add - suffix local prefix="${CC##*/}"; prefix="${prefix%-$version}"; prefix="${prefix%-*}-" local config="${0%/scripts/*}/.config" # if no target specified, print choice list and exit if [ -z "$mktarget" ]; then # prepare metadata if [ ! -f "${0%/scripts/*}/tmp/.targetinfo" ]; then "${0%/*}/scripts/config/mconf" prepare-tmpinfo fi local mktargets=$( sed -ne " /^Target: / { h }; /^Target-Arch: $cpuarch\$/ { x; s#^Target: ##p } " "${0%/scripts/*}/tmp/.targetinfo" | sort -u ) for mktarget in $mktargets; do case "$mktarget" in */*) mktargets=$(echo "$mktargets" | sed -e "/^${mktarget%/*}\$/d") esac done if [ -n "$mktargets" ]; then echo "Available targets:" >&2 echo $mktargets >&2 else echo -e "Could not find a suitable OpenWrt target for " >&2 echo -e "CPU architecture '$cpuarch' - you need to " >&2 echo -e "define one first!" >&2 fi return 1 fi # bail out if there is a .config already if [ -f "$config" ]; then if [ "$OVERWRITE_CONFIG" == "" ]; then echo "There already is a .config file, refusing to overwrite!" >&2 return 1 else echo "There already is a .config file, trying to overwrite!" fi fi case "$mktarget" in */*) mksubtarget="${mktarget#*/}" mktarget="${mktarget%/*}" ;; esac if [ ! -f "$config" ]; then touch "$config" fi echo "CONFIG_TARGET_${mktarget}=y" >> "$config" if [ -n "$mksubtarget" ]; then echo "CONFIG_TARGET_${mktarget}_${mksubtarget}=y" >> "$config" fi if test_feature "softfloat"; then echo "CONFIG_SOFT_FLOAT=y" >> "$config" else echo "# CONFIG_SOFT_FLOAT is not set" >> "$config" fi if test_feature "ipv6"; then echo "CONFIG_IPV6=y" >> "$config" else echo "# CONFIG_IPV6 is not set" >> "$config" fi if test_feature "locale"; then echo "CONFIG_BUILD_NLS=y" >> "$config" else echo "# CONFIG_BUILD_NLS is not set" >> "$config" fi echo "CONFIG_DEVEL=y" >> "$config" echo "CONFIG_EXTERNAL_TOOLCHAIN=y" >> "$config" echo "CONFIG_TOOLCHAIN_ROOT=\"$TOOLCHAIN\"" >> "$config" echo "CONFIG_TOOLCHAIN_PREFIX=\"$prefix\"" >> "$config" echo "CONFIG_TARGET_NAME=\"$target\"" >> "$config" if [ -f "$config" ]; then sed -i '/CONFIG_EXTERNAL_TOOLCHAIN_LIBC_USE_MUSL/d' "$config" sed -i '/CONFIG_EXTERNAL_TOOLCHAIN_LIBC_USE_GLIBC/d' "$config" fi if [ "$LIBC_TYPE" == glibc ]; then echo "CONFIG_EXTERNAL_TOOLCHAIN_LIBC_USE_GLIBC=y" >> "$config" elif [ "$LIBC_TYPE" == musl ]; then echo "CONFIG_EXTERNAL_TOOLCHAIN_LIBC_USE_MUSL=y" >> "$config" else echo "Can't detect LIBC type. Aborting!" >&2 return 1 fi local lib for lib in C RT PTHREAD GCC STDCPP SSP GFORTRAN GOMP; do local file local spec="" local llib="$(echo "$lib" | sed -e 's#.*#\L&#')" for file in $(find_libs "$lib"); do spec="${spec:+$spec }$(echo "$file" | sed -e "s#^$TOOLCHAIN#.#")" done if [ -n "$spec" ]; then echo "CONFIG_PACKAGE_lib${llib}=y" >> "$config" echo "CONFIG_LIB${lib}_FILE_SPEC=\"$spec\"" >> "$config" else echo "# CONFIG_PACKAGE_lib${llib} is not set" >> "$config" fi done local bin for bin in LDD LDCONFIG; do local file local spec="" local lbin="$(echo "$bin" | sed -e 's#.*#\L&#')" for file in $(find_bins "$bin"); do spec="${spec:+$spec }$(echo "$file" | sed -e "s#^$TOOLCHAIN#.#")" done if [ -n "$spec" ]; then echo "CONFIG_PACKAGE_${lbin}=y" >> "$config" echo "CONFIG_${bin}_FILE_SPEC=\"$spec\"" >> "$config" else echo "# CONFIG_PACKAGE_${lbin} is not set" >> "$config" fi done # inflate make -C "${0%/scripts/*}" defconfig return 0 } probe_cc() { if [ -z "$CC" ]; then local bin for bin in "bin" "usr/bin" "usr/local/bin"; do local cmd for cmd in "$TOOLCHAIN/$bin/"*-*cc*; do if [ -x "$cmd" ] && [ ! -h "$cmd" ]; then CC="$(cd "${cmd%/*}"; pwd)/${cmd##*/}" return 0 fi done done return 1 fi return 0 } probe_cxx() { if [ -z "$CXX" ]; then local bin for bin in "bin" "usr/bin" "usr/local/bin"; do local cmd for cmd in "$TOOLCHAIN/$bin/"*-*++*; do if [ -x "$cmd" ] && [ ! -h "$cmd" ]; then CXX="$(cd "${cmd%/*}"; pwd)/${cmd##*/}" return 0 fi done done return 1 fi return 0 } probe_cpp() { if [ -z "$CPP" ]; then local bin for bin in "bin" "usr/bin" "usr/local/bin"; do local cmd for cmd in "$TOOLCHAIN/$bin/"*-cpp*; do if [ -x "$cmd" ] && [ ! -h "$cmd" ]; then CPP="$(cd "${cmd%/*}"; pwd)/${cmd##*/}" return 0 fi done done return 1 fi return 0 } probe_libc() { if [ -f $TOOLCHAIN/info.mk ]; then LIBC_TYPE=$(grep LIBC_TYPE $TOOLCHAIN/info.mk | sed 's/LIBC_TYPE=//') return 0 fi echo "Warning! Can't find info.mk, trying to detect with alternative way." if [ -z "$LIBC_TYPE" ]; then if test_uclibc; then LIBC_TYPE="uclibc" else LIBC_TYPE="glibc" fi fi return 0 } while [ -n "$1" ]; do arg="$1"; shift case "$arg" in --toolchain) [ -d "$1" ] || { echo "Toolchain directory '$1' does not exist." >&2 exit 1 } TOOLCHAIN="$(cd "$1"; pwd)"; shift ;; --cflags) CFLAGS="${CFLAGS:+$CFLAGS }$1"; shift ;; --print-libc) if probe_cc; then probe_libc echo "$LIBC_TYPE" exit 0 fi echo "No C compiler found in '$TOOLCHAIN'." >&2 exit 1 ;; --print-target) if probe_cc; then exec "$CC" $CFLAGS -dumpmachine fi echo "No C compiler found in '$TOOLCHAIN'." >&2 exit 1 ;; --print-bin) if [ -z "$1" ]; then echo "Available programs:" >&2 echo $(echo "$BIN_SPECS" | sed -ne 's#:.*$##p') >&2 exit 1 fi find_bins "$1" || exec "$0" --toolchain "$TOOLCHAIN" --print-bin exit 0 ;; --print-libs) if [ -z "$1" ]; then echo "Available libraries:" >&2 echo $(echo "$LIB_SPECS" | sed -ne 's#:.*$##p') >&2 exit 1 fi find_libs "$1" || exec "$0" --toolchain "$TOOLCHAIN" --print-libs exit 0 ;; --test) test_feature "$1" exit $? ;; --wrap) [ -n "$1" ] || exec "$0" --help wrap_bins "$1" exit $? ;; --overwrite-config) OVERWRITE_CONFIG=y ;; --config) if probe_cc; then probe_libc print_config "$1" exit $? fi echo "No C compiler found in '$TOOLCHAIN'." >&2 exit 1 ;; -h|--help) me="$(basename "$0")" echo -e "\nUsage:\n" >&2 echo -e " $me --toolchain {directory} --print-libc" >&2 echo -e " Print the libc implementation and exit.\n" >&2 echo -e " $me --toolchain {directory} --print-target" >&2 echo -e " Print the GNU target name and exit.\n" >&2 echo -e " $me --toolchain {directory} --print-bin {program}" >&2 echo -e " Print executables belonging to given program," >&2 echo -e " omit program argument to get a list of names.\n" >&2 echo -e " $me --toolchain {directory} --print-libs {library}" >&2 echo -e " Print shared objects belonging to given library," >&2 echo -e " omit library argument to get a list of names.\n" >&2 echo -e " $me --toolchain {directory} --test {feature}" >&2 echo -e " Test given feature, exit code indicates success." >&2 echo -e " Possible features are 'c', 'c++', 'softfloat'," >&2 echo -e " 'lfs', 'rpc', 'ipv6', 'wchar', 'locale' and " >&2 echo -e " 'threads'.\n" >&2 echo -e " $me --toolchain {directory} --wrap {directory}" >&2 echo -e " Create wrapper scripts for C and C++ compiler, " >&2 echo -e " linker, assembler and other key executables in " >&2 echo -e " the directory given with --wrap.\n" >&2 echo -e " $me --toolchain {directory} --config {target}" >&2 echo -e " Analyze the given toolchain and print a suitable" >&2 echo -e " .config for the given target. Omit target " >&2 echo -e " argument to get a list of names.\n" >&2 echo -e " $me --help" >&2 echo -e " Display this help text and exit.\n\n" >&2 echo -e " Most commands also take a --cflags parameter which " >&2 echo -e " is used to specify C flags to be passed to the " >&2 echo -e " cross compiler when performing tests." >&2 echo -e " This parameter may be repeated multiple times." >&2 echo -e " Use --overwrite-config before --config to overwrite" >&2 echo -e " an already present config with the required changes.">&2 exit 1 ;; *) echo "Unknown argument '$arg'" >&2 exec $0 --help ;; esac done exec $0 --help ='n380' href='#n380'>380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421