#!/bin/sh set -e ## usage # Simply display the builder usage. Though it would be nice if some of this # information was pushed down into the sub-commands. usage() { cat< [command-opts] [all|/ ...] Options ------- -v, --version Display the builder version. -d, --debug Enable debug logging. -h, --help Display the builder help and exit (may appear anywhere on the command line). Commands -------- query The query is used internally by builder, while allowing one to query various packages from the builder repository. options ------- -b, --build-deps -d, --depends -n, --name -d, --description -v, --version -u, --source-uri -s, --summary source Create a copy/checkout of the package sources inside of packages///source. The source/ directory within a package takes precendences over the SOURCE_URI, allowing in-place development of various packages. This is particularly useful if the SOURCE_URI is an SCM. fetch Fetch the sources for a package from the SOURCE_URI and store them into the sources/ top-level directory. This is done automatically for all commands which depend on it. package Prep, compile and construct a binary "artifact" file for a given package. This command is performed automatically for all commands which depend on it. install Install a binary artifact into the sysroot. This action is performed automatically for any packages which the current target depends on. If necessary produce binary artifacts for all package deps. clean Clean specified package from sysroot and artifacts. distclean Clean up specified package from sysroot, artifacts, and sources. EOF } ## mkenv # prepare the environment structure for the current package mkenv() { for dir in "${W}" "${L}" "${E}" "${D}" "${T}"; do if [ ! -d "${dir}" ]; then mkdir -p "${dir}" fi done unset dir if [ "$#" -gt "0" ]; then cat /dev/null > "${L}/${1}.log" set > "${E}/${1}.env" fi } ## cleanup # deal with temporary junk when we exit cleanup() { test -f "${BUILDER_MAKEFILE}" && rm -f "${BUILDER_MAKEFILE}" test -p "${BUILDER_PIPE}" && rm -f "${BUILDER_PIPE}" } ## error # displays the supplied on stderr error() { echo "error: $*" >&2 } ## die # display the supplied and exit with an error die() { error "$*" exit 1 } ## import # import a package into the current program space import() { # Evaluate our environment data # There is a bit of a performance penalty here in that the query # routine will source the build-rules to finish collecting data, so we # end up sourcing the Buildrules twice. eval $(build-query --environ "${1}") RELEASE= S= # Source in the category Buildrules if [ -f "${BUILDER_PKGDIR}/${CATEGORY}/.buildrules" ]; then . "${BUILDER_PKGDIR}/${CATEGORY}/.buildrules" fi # Source in the package Buildrules . "${RULESFILE}" if [ "${CATEGORY}/${NAME}" != "${1}" ]; then die "Buildrules can not set the package name" fi if [ -z "${VERSION}" ]; then die "missing version in '${NAME}'" fi if [ -z "${DESCRIPTION}" ]; then die "missing description in '${NAME}'" fi if test -z "${RELEASE}"; then RELEASE="${VERSION#*-}" if ! test -z "${RELEASE}" && test "${RELEASE}" != "${VERSION}"; then VERSION="${VERSION%-${RELEASE}}" if test -z "${S}"; then S="${W}/${NAME}-${VERSION}-${RELEASE}" fi else RELEASE='0' fi fi if test -z "${S}"; then S="${W}/${NAME}-${VERSION}" fi } if [ ! -z "${BUILDER_DEBUG}" ]; then set -x fi ## # Check to see if we are wrapping a sub-command # FIXME support custom commands BUILDER_COMMAND= if [ -f "${1}" ]; then case "${1}" in (*build-*) BUILDER_COMMAND="${1}" shift . "${BUILDER_COMMAND}" # exit with the exit status of the last command from within the # sub-script. This is normal shell behavior, we are just # making it explicit. exit $? ;; esac fi for arg in "$@"; do case "${arg}" in (-h|-help|--help) usage; exit 0;; esac done unset arg DEBUG="0" while [ "$#" -gt "0" ]; do case "$1" in (-v|-version|--version) version; exit 0;; (-t|-target|--target) shift 1 if [ "$#" -eq "0" ]; then echo "error: no target specified" >&2 exit 1 fi BUILDER_TARGET="${1}" shift 1 ;; (-d|-debug|--debug) BUILDER_DEBUG=1 export BUILDER_DEBUG set -x ;; (-*) echo "error: unknown option '${1}'" >&2 echo "try '${0} --help'" >&2 exit 1 ;; (*) break ;; esac shift 1 echo "$@" done ## # Done with arguments, time for setting up the enviroment for the build TOPDIR="${PWD}" while [ ! -z "${TOPDIR}" ]; do [ -d "${TOPDIR}/.builder" ] && break TOPDIR="${TOPDIR%/*}" done if [ -z "${TOPDIR}" ]; then echo "error: current path not in a build-tree" >&2 exit 1 fi # set the builtin defaults based on TOPDIR. We export TOPDIR as BUILDER_TOPDIR # to avoid stepping on the potential usage of TOPDIR within package Makefiles BUILDER_TOPDIR="${TOPDIR}" BUILDER_PKGDIR="${TOPDIR}/packages" BUILDER_SRCDIR="${TOPDIR}/sources" BUILDER_ATFDIR="${TOPDIR}/artifacts" BUILDER_LIBDIR="${TOPDIR}/scripts/builder" export BUILDER_CFGDIR BUILDER_PKGDIR BUILDER_SRCDIR export BUILDER_ATFDIR BUILDER_LIBDIR BUILDER_TOPDIR BUILDER_TMPDIR="${TOPDIR}/tmp" if ! test -d "${BUILDER_TMPDIR}"; then mkdir -p "${BUILDER_TMPDIR}" fi export BUILDER_TMPDIR # We save the pre-config PATH as BUILDER_PATH to be used by downstream tools. BUILDER_PATH="${BUILDER_LIBDIR}:${PATH}" PATH="${BUILDER_PATH}" export BUILDER_PATH PATH # The default SYSROOT SYSROOT="${TOPDIR}/sysroot" ## # This may be a little bit confusing to most. The core issue here is the # ability to deal with compiled-in defaults, user-defined defaults, environment # settings, and target settings. CFGDIR="${TOPDIR}/.builder" # grab the default and target settings if available, let the target settings # override the user-defined defaults. if [ -f "${CFGDIR}/config" ]; then . "${CFGDIR}/config" fi TARGET="${TARGET:-config}" if [ "${TARGET}" != "config" ]; then if [ ! -f "${CFGDIR}/${TARGET}" ]; then echo "error: invalid target '${TARGET}'" >&2 exit 1 fi . "${CFGDIR}/${TARGET}" fi export TARGET export SYSROOT # If the MAKE_OPTS are not set then find the number of CPU's and set them. if [ -z "${MAKE_OPTS}" ]; then NUM_CPUS="$(awk '/^processor/{print$3}'< /proc/cpuinfo|sort|uniq|tail -n 1)" NUM_CPUS="$((${NUM_CPUS} + 1))" MAKE_OPTS="-j$((${NUM_CPUS} + 1))" fi export MAKE_OPTS # Available to be set in the config PROJECT="${PROJECT:-platform}" ARCHIVE_FORMAT="${ARCHIVE_FORMAT:-tar.bz2}" export PROJECT ARCHIVE_FORMAT # If unspecified go ahead and ask gcc # FIXME there has to be a more robust way to figure this out. if [ -z "${CBUILD}" ]; then if command -v gcc > /dev/null 2>&1; then CBUILD="$(gcc -dumpmachine)" else CBUILD="$(build-dumpmachine)" fi fi export CBUILD # FIXME this stuff needs to be detected in a more reliable fashion ARCH="${ARCH:-$(uname -m)}" export ARCH CHOST="${CHOST:-${CBUILD}}" export CHOST # if we aren't given an action then we do everything ACTION="install" if [ "$#" -gt "0" ]; then ACTION="${1}" shift 1 fi # FIXME Support custom commands somehow.. if [ ! -x "${BUILDER_LIBDIR}/build-${ACTION}" ]; then error "unknown action '${ACTION}'" echo "try '${0} --help'" >&2 exit 1 fi # query is a special case # FIXME this is semi-busted if [ "${ACTION}" = "query" ]; then exec "${BUILDER_LIBDIR}/build-query" "${@}" fi # If no target is given, then base our target on the current working directory, # falling back to "${PROJECT}/all" as our default. if [ "$#" -lt "1" ]; then # Are we somewhere within the pkg structure. If this test succeeds # then we are at least in a category directory within the pkgdir. Just # being in pkgdir is not enough to change our default argument list # handling. NAME="all" if [ "${PWD##${BUILDER_PKGDIR}/}" != "${PWD}" ]; then category="${PWD##${BUILDER_PKGDIR}/}" if [ "${category%%/*}" != "${category}" ]; then name="${category#*/}" category="${category%%/*}" NAME="${category}/${name%%/*}" else NAME="${category}/all" fi unset category unset name fi set -- "${NAME}" fi for package in "$@"; do # If all is specified anywhere in the argument list than just discard # everything else. if [ "${package}" = "all" ]; then continue fi CATEGORY="${package%%/*}" if [ ! -d "${BUILDER_PKGDIR}/${CATEGORY}" ]; then die "invalid package category '${CATEGORY}'" fi if [ "${package##*/}" != "all" ]; then if ! build-query --exists "${package}"; then exit 1 fi fi done # sort/uniq the argument list set -- $(for package in "$@"; do echo "${package}" ; done | sort | uniq) # build the Makefile trap cleanup EXIT BUILDER_MAKEFILE="$(mktemp ${BUILDER_TMPDIR}/builder_makefile.XXXXXXXX)" if [ ! -f "${BUILDER_MAKEFILE}" ]; then die "failed to generate build dependencies" fi export BUILDER_MAKEFILE "${BUILDER_LIBDIR}/build-makedeps" || die "failed generate build dependencies" packages= for package in "$@"; do if [ "${package##*/}" != "all" ]; then package="$(build-query --pkgname "${package}")" fi package="$(echo "${package}"|tr '/-' '__')" packages="${packages} ${package}_${ACTION}" done set -- ${packages} unset packages # The 'tee' command will discard the exit status from 'make', so we have to # jump through a few hoops to capture the exit status in a portable fashion. BUILDER_PIPE="`mktemp "${BUILDER_TMPDIR}/builder_pipe.XXXXXXXX"`" test -f "${BUILDER_PIPE}" || die 'failed to generate log-pipe placeholder' rm -f "${BUILDER_PIPE}" && mkfifo "${BUILDER_PIPE}" || die 'failed to create log-pipe' tee "${BUILDER_TMPDIR}/builder.log" < "${BUILDER_PIPE}" & BUILDER_LOGGER="$!" make -r -f "${BUILDER_MAKEFILE}" "${@}" > "${BUILDER_PIPE}" 2>&1 exit $?