123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 |
- #! /bin/sh -
- # Builder of custom stages (cross compilers, GNU/Linux distributions)
- #
- # Copyright (c) 2014-2022 Matias Fonzo, <selk@dragora.org>.
- #
- # 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 3 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, see <http://www.gnu.org/licenses/>.
- # EXIT STATUS
- # 0 = Successful completion
- # 1 = Minor common errors (e.g: help usage, support not available)
- # 2 = Command execution error
- # 3 = Integrity check error for compressed files
- PROGRAM="${0##*/}"
- # Override locale settings
- LC_ALL=C
- LANGUAGE=C
- export LC_ALL LANGUAGE
- # Get physical working directory (absolute path)
- CWD="$(CDPATH='' cd -P -- "$(dirname -- "$0")" && pwd -P)" || exit $?
- ### Functions
- usage()
- {
- printf '%s' \
- "Usage: $PROGRAM [OPTIONS] [FILE]...
- Builder of custom stages (cross compilers, GNU/Linux distributions).
- Where FILE is any shell script (as long as it is executable) from
- a stage number. Without FILE, it loads all the found scripts from
- the stage number. Stage numbers come from the stages directory
- (${CWD}/stages).
- Defaults for the options are specified in brackets.
- Options:
- -a Target architecture [${arch}]
- -j Parallel jobs for the compiler [${jobs}]
- -k Keep (don't delete) source directory
- -o Output directory [${output}]
- -s Stage number to build [${stage}]
- -h Display this help and exit
- -V Print version information and exit
- Some influential environment variables:
- TMPDIR Temporary directory for sources [${TMPDIR}]
- BTCC C compiler command [${BTCC}]
- BTCXX C++ compiler command [${BTCXX}]
- BTCFLAGS C compiler flags [${BTCFLAGS}]
- BTCXXFLAGS C++ compiler flags [${BTCXXFLAGS}]
- BTLDFLAGS Linker flags [${BTLDFLAGS}]
- VENDOR Vendor name to reflect on the target triplet
- Available targets from ${CWD}/targets ...
- "
- for name in "${CWD}/targets"/*
- do
- sed -e '2q;d' "$name"
- done
- unset -v name
- echo ""
- }
- version()
- {
- printf '%s' \
- "$PROGRAM 3.32
- Copyright (C) 2014-2022 Matias Andres Fonzo.
- License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
- This is free software: you are free to change and redistribute it.
- There is NO WARRANTY, to the extent permitted by law.
- "
- }
- warn()
- {
- printf '%s\n' "$@" 1>&2
- }
- chkstatus_or_exit()
- {
- status=$?
- if test $status -ne 0
- then
- echo "Return status = $status" 1>&2
- exit "${1-2}"; # If not given, defaults to 2
- fi
- unset -v status
- }
- # Function to test and extract compressed files
- unpack()
- {
- for file in "$@"
- do
- case $file in
- *.tar)
- tar tf "$file" > /dev/null && \
- tar xpf "$file"
- chkstatus_or_exit 3
- ;;
- *.tar.gz | *.tgz | *.tar.Z )
- gzip -cd "$file" | tar tf - > /dev/null && \
- gzip -cd "$file" | tar xpf -
- chkstatus_or_exit 3
- ;;
- *.tar.bz2 | *.tbz2 | *.tbz )
- bzip2 -cd "$file" | tar tf - > /dev/null && \
- bzip2 -cd "$file" | tar xpf -
- chkstatus_or_exit 3
- ;;
- *.tar.lz | *.tlz )
- lzip -cd "$file" | tar tf - > /dev/null && \
- lzip -cd "$file" | tar xpf -
- chkstatus_or_exit 3
- ;;
- *.tar.xz | *.txz )
- xz -cd "$file" | tar tf - > /dev/null && \
- xz -cd "$file" | tar xpf -
- chkstatus_or_exit 3
- ;;
- *.tar.zst | *.tzst )
- zstd -cd "$file" | tar -tf - > /dev/null && \
- zstd -cd "$file" | tar -xpf -
- chkstatus_or_exit 3
- ;;
- *.zip | *.ZIP )
- unzip -t "$file" > /dev/null && \
- unzip "$file" > /dev/null
- chkstatus_or_exit 3
- ;;
- *.gz)
- gzip -t "$file" && \
- gzip -cd "$file" > "$(basename -- "$file" .gz)"
- chkstatus_or_exit 3
- ;;
- *.Z)
- gzip -t "$file" && \
- gzip -cd "$file" > "$(basename -- "$file" .Z)"
- chkstatus_or_exit 3
- ;;
- *.bz2)
- bzip2 -t "$file" && \
- bzip2 -cd "$file" > "$(basename -- "$file" .bz2)"
- chkstatus_or_exit 3
- ;;
- *.lz)
- lzip -t "$file" && \
- lzip -cd "$file" > "$(basename -- "$file" .lz)"
- chkstatus_or_exit 3
- ;;
- *.xz)
- xz -t "$file" && \
- xz -cd "$file" > "$(basename -- "$file" .xz)"
- chkstatus_or_exit 3
- ;;
- *.zst)
- zstd -qt "$file" && \
- zstd -cd "$file" > "$(basename -- "$file" .zst)"
- chkstatus_or_exit 3
- ;;
- *)
- warn "${PROGRAM}: cannot unpack ${file}: Unsupported extension"
- exit 1
- esac
- done
- unset -v file
- }
- # Print a warning for good practices.
- #
- # Recommended practices is to set variables
- # in front of `configure' or make(1), see:
- #
- # http://www.gnu.org/software/make/manual/html_node/Environment.html
- # http://gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/html_node/Defining-Variables.html
- # http://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/html_node/Setting-Output-Variables.html
- warn_flags()
- {
- warn "" \
- "WARNING: Environment variable '$1' is set." \
- "This will be unset to avoid possible risks." \
- ""
- }
- ### Default values
- BTCC="${BTCC:=gcc}"
- BTCXX="${BTCXX:=g++}"
- BTCFLAGS="${BTCFLAGS:=-O2}"
- BTCXXFLAGS="${BTCXXFLAGS:=-O2}"
- BTLDFLAGS="${BTLDFLAGS:=}"
- opt_keep=opt_keep.off
- stage=0
- libSuffix=""
- arch="$(uname -m)" || chkstatus_or_exit
- jobs=1
- worktree="$CWD"
- output="${worktree}/OUTPUT.${PROGRAM}"
- TMPDIR="${TMPDIR:=${output}/sources}"
- # Compose vendor name adding "-" as suffix
- test -n "$VENDOR" && VENDOR="${VENDOR}-"
- ### Parse options
- while getopts :ha:j:ko:s:V name
- do
- case $name in
- h)
- usage
- exit 0
- ;;
- a)
- arch="$OPTARG"
- ;;
- j)
- jobs="$OPTARG"
- ;;
- k)
- opt_keep=opt_keep
- ;;
- o)
- output="$OPTARG"
- ;;
- s)
- stage="$OPTARG"
- ;;
- V)
- version
- exit 0
- ;;
- :)
- warn "Option '-${OPTARG}' requires an argument"
- usage
- exit 1
- ;;
- \?)
- warn "Illegal option -- '-${OPTARG}'"
- usage
- exit 1
- ;;
- esac
- done
- shift $(( OPTIND - 1 ))
- unset -f usage version
- # Check for environment variables, print warning, unset in any case
- test -n "$CC" && warn_flags CC
- test -n "$CXX" && warn_flags CXX
- test -n "$CFLAGS" && warn_flags CFLAGS
- test -n "$CXXFLAGS" && warn_flags CXXFLAGS
- test -n "$LDFLAGS" && warn_flags LDFLAGS
- unset CC CXX CFLAGS CXXFLAGS LDFLAGS warn_flags
- # Load target architecture-file
- if test -f "${worktree}/targets/$arch"
- then
- echo "${PROGRAM}: Loading target $arch ..."
- . "${worktree}/targets/$arch"
- else
- warn \
- "${PROGRAM}: Target name not recognized -- '${arch}'" \
- "See '$0 -h' for more information"
- exit 1
- fi
- # Determine the host to use.
- #
- # If the host and the target are the same triplet, it won't work.
- # We are changing the host if it is really needed
- host="$(${BTCC} -dumpmachine)" || chkstatus_or_exit
- if test "$host" = "$target"
- then
- # Rename VENDOR to 'cross'. If empty, 'cross-linux' is added
- case "${host%-*-*}" in
- *-*)
- host="$(echo "$host" | sed -e 's/-[^-]*/-cross/')"
- ;;
- *)
- host="$(echo "$host" | sed -e 's/-[^-]*/-cross-linux/')"
- ;;
- esac
- fi
- # Compose variables for the physical output,
- # printing some useful information
- crossdir="${output}/cross/${target}"
- rootdir="${output}/stage${stage}"
- printf '%s\n' \
- "BTCC: $BTCC" \
- "BTCXX: $BTCXX" \
- "BTCFLAGS: $BTCFLAGS" \
- "BTCXXFLAGS: $BTCXXFLAGS" \
- "BTLDFLAGS: $BTLDFLAGS" \
- "Host: $host" \
- "Target: $target" \
- "Output directory: $output" \
- "Cross directory: $crossdir" \
- "Root directory: $rootdir"
- # Remove write permission for group and other
- umask 022
- # Create required directories
- mkdir -p -- "$output" "$TMPDIR"
- chkstatus_or_exit
- # Set default PATH, propagate variables
- PATH="${crossdir}/bin:${PATH}"
- export PATH VENDOR TMPDIR
- # Main loop
- for file in ${CWD}/stages/${stage}/${@:-??-*}
- do
- file="${file##*/}"
- if test ! -f "${CWD}/stages/${stage}/$file"
- then
- warn "${PROGRAM}: ${stage}/${file}: No such file or stage number"
- exit 1
- fi
- if test ! -x "${CWD}/stages/${stage}/$file"
- then
- warn "${PROGRAM}: ${stage}/${file}: File not executable, dodging"
- continue;
- fi
- ### Stage pre-settings
- # Create sysroot directory, recreating the symlink for the stage 0
- if test "$stage" = 0
- then
- mkdir -p -- "${crossdir}/$target" || chkstatus_or_exit
- if test ! -L "${crossdir}/${target}/usr"
- then
- ln -s -f . "${crossdir}/${target}/usr" || chkstatus_or_exit
- fi
- # Ensure toolchain sanity for multilib purposes
- case $arch in
- aarch64* | arm* | x32 | x86_64 | i?86 | riscv* )
- if test ! -L "${crossdir}/lib" && test -n "$libSuffix"
- then
- ln -s -f lib${libSuffix} "${crossdir}/lib" || chkstatus_or_exit
- fi
- ;;
- esac
- fi
- # Create "tools" directory, recreating the symlink for the stage 1
- if test "$stage" = 1
- then
- if test ! -d "${rootdir}/tools"
- then
- mkdir -p -- "${rootdir}/tools" || chkstatus_or_exit
- fi
- # Check and make required symlink
- if test ! -L /tools
- then
- ln -s -f "${rootdir}/tools" / || chkstatus_or_exit
- fi
- fi
- # Createating the symlink for the stage 2
- if test "$stage" = 2
- then
- # Check and make required symlink
- if test ! -L "${rootdir}"
- then
- ln -s -f -d -r "${output}/stage1" "${rootdir}" || chkstatus_or_exit
- fi
- fi
- echo "${PROGRAM}: Processing $file from stages/${stage} ..."
- # Set trap before to run the script in order to
- # catch the return status, exit code 2 if fails
- trap 'chkstatus_or_exit 2' EXIT HUP INT QUIT ABRT TERM
- # Exit immediately on any error
- set -e
- . "${CWD}/stages/${stage}/$file"
- # Deactivate shell option(s)
- set +e
- # Reset given signals
- trap - EXIT HUP INT QUIT ABRT TERM
- # Delete declared directories on the cleanup() function
- if test "$opt_keep" != opt_keep
- then
- if type cleanup 1> /dev/null 2> /dev/null
- then
- cleanup
- chkstatus_or_exit 2
- unset -f cleanup
- fi
- fi
- # Back to the current working directory
- cd -- "$CWD" || chkstatus_or_exit
- done
|