#!/bin/bash # ----------------------------------------------------------------------------- # # Copyright (c) 2011 - OpenSLX GmbH # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # # This file contains the functions needed to setup a target. # The main function is 'generate_target' which will go over all # activated modules (symlinks found under remote/targets/) # and run the module-specific 'fetch_source', 'build' and 'post_copy' # functions of the modules. # # The functions defined here are module-independent, meaning they # can process any module, as long as following requirements are met: # # - Modules need to have a build-script and a config file, # i.e. remote/module/kernel/kernel.build # and remote/module/kernel/kernel.conf # as these will be read by 'process_module'. # # - Modules need to generate a 'build'-directory in their # own sub-directory (i.e. remote/modules/sshd/build/ # as 'copy_files_with_deps' will look for the files in # that directory ONLY. # # - Several information have to be set in the config file of # a module: # * REQUIRED_BINARIES - list of binaries # * REQUIRED_FILES - list of regular files # * REQUIRED_DIRECTORIES - list of directories # * REQUIRED_SYSTEM_FILES - files to be copied directly from the system # * REQUIRED_INSTALLED_PACKAGES - list of packages needed at build-time # * REQUIRED_CONTENT_PACKAGES - list of packages where the contents are to be copied # * (optional variables needed by the module-build-script) # # NOTE: REQUIRED_BINARIES, REQUIRED_FILES, REQUIRED_FILES will be ONLY looked # for in the module's own build directory! # # Optionally, a module can have a static data directory under # remote/modules//data which will be copied as is to the target build directory. # # Moreover modules do not need to specify the dynamic dependencies of given binaries # as these will be determined automaticly by the helper function 'get_dynamic_dependencies' # ----------------------------------------------------------------------------- # MODE_DIR="${ROOT_DIR}/remote" MODULES_DIR="${MODE_DIR}/modules" EXPORT_DIR="/export/build" # Keep track of processed modules PROCESSED_MODULES="" initial_checks () { : } # # generic function to read the config file of the current $MODULE # read_config () { # unset previous variables from other config files for VARNAME in ${!REQUIRED_*}; do unset $VARNAME done local MODULE_CONFIG="${MODULE_DIR}/${MODULE}.conf" # sanity checks [ ! -e "${MODULE_CONFIG}" ] && perror "Config for '$MODULE' not found." # source the config file . "${MODULE_CONFIG}" || perror "Sourcing '${MODULE_CONFIG}' failed." if [ -e "${MODULE_CONFIG}.${PACKET_MANAGER}" ]; then # a specific tool.conf seems to exist, use it to override certain vars . "${MODULE_CONFIG}.${PACKET_MANAGER}" || perror "Sourcing '${MODULE_CONFIG}.${PACKET_MANAGER}' failed." fi } # # generic function to read the build script of the current $MODULE # read_build () { local BUILD_SCRIPT="${MODULE_DIR}/${MODULE}.build" [ ! -e "${BUILD_SCRIPT}" ] && perror "Build script for specified tool not found." . "${BUILD_SCRIPT}" || perror "Sourcing '${BUILD_SCRIPT}' failed." } export_builds() { [ ! -d "${MODE_DIR}/builds" ] && perror "No ${MODE_DIR}/builds, nothing to export." pinfo "Mounting ${MODE_DIR}/builds to ${EXPORT_DIR}. This will make the local builds syncable from another machine." [ ! -d "${EXPORT_DIR}" ] && mkdir -p "${EXPORT_DIR}" mount --bind ${MODE_DIR}/builds ${EXPORT_DIR} || perror "Failed to bind mount ${MODE_DIR}/builds to ${EXPORT_DIR}" } # # main function which copies all files, directories, binaries and external libraries to TARGET_BUILD_DIR # copy_files_with_deps () { [ ! -d "$MODULE_BUILD_DIR" ] && pinfo "No build directory found, skipping dependency copying" && return 0 cd "$MODULE_BUILD_DIR" COPYFILES_LIST="list_wanted_stage3.2" [ -e "${COPYFILES_LIST}" ] && rm "${COPYFILES_LIST}" [ ! -z "${REQUIRED_BINARIES}" ] && pinfo "Gathering required binaries from config file..." for FILENAME in ${REQUIRED_BINARIES} do local FILE_CANDIDATES=$( find . -name "${FILENAME}" -a \( -type f -o -type l \) ) # only do if more than one candidate found if [ "$(echo $FILE_CANDIDATES | wc -w)" -gt 1 ]; then pdebug "Candidates for $FILENAME are: $(echo $FILE_CANDIDATES)" local FINAL_LIST="" for FILE in $FILE_CANDIDATES; do local TESTFILE="$(readlink -f "$FILE")" pdebug " $FILE leads to $TESTFILE" [ -f "$TESTFILE" -a -x "$TESTFILE" ] && [ "x$(grep -l -E '^(.ELF|#!)' "$TESTFILE")" != "x" ] && FINAL_LIST="$FINAL_LIST $FILE" done FINAL_LIST=$(trim "$FINAL_LIST") if [ -z "$FINAL_LIST" ]; then pwarning "\tNo Binary found for ${FILENAME}. Skipping." continue fi if [[ "$FINAL_LIST" == *" "* ]]; then pwarning "Found more than one match for required file '$FILENAME': $FINAL_LIST" else pdebug "\tFound ${FILENAME} at ${FILE}" fi elif [ "$(echo $FILE_CANDIDATES | wc -w)" -eq 0 ]; then perror "Could not find required binary $FILENAME" else # one candidate FINAL_LIST=${FILE_CANDIDATES} fi for FILE in $FINAL_LIST; do pdebug "* $FILE" strip "$FILE" || pwarning "Could not strip '${FILE}'" get_link_chain "${MODULE_BUILD_DIR}/${FILE}" "${MODULE_BUILD_DIR}" >> "${COPYFILES_LIST}" get_dynamic_dependencies -l "${MODULE_BUILD_DIR}" "${FILE}" >> "${COPYFILES_LIST}" done done [ ! -z "${REQUIRED_LIBRARIES}" ] && pinfo "Gathering required libraries from config file..." for LIB in ${REQUIRED_LIBRARIES} do for LOCATION in $(find . -name ${LIB}.so\*) do pdebug "* $LOCATION" strip "$LOCATION" || pwarning "Could not strip '${LOCATION}'" get_link_chain "${MODULE_BUILD_DIR}/${LOCATION}" "${MODULE_BUILD_DIR}" >> "${COPYFILES_LIST}" get_dynamic_dependencies -l "${MODULE_BUILD_DIR}" "${LOCATION}" >> "${COPYFILES_LIST}" done done [ ! -z "${REQUIRED_DIRECTORIES}" ] && pinfo "Gathering required directories from config file..." local CURRENT_PWD=$(pwd) # Prevent calling pwd 50000 times inside the loop below for ENTRY in ${REQUIRED_DIRECTORIES} do pdebug "* .$ENTRY" echo "./${ENTRY}" >> "${COPYFILES_LIST}" for BIN in $(find ".${ENTRY}" -type f -not -name '*.a' | xargs grep -l '^.ELF') do #pdebug "\tSearching libs for ${BIN}..." get_link_chain "${MODULE_BUILD_DIR}/${BIN}" "${MODULE_BUILD_DIR}" >> "${COPYFILES_LIST}" get_dynamic_dependencies -l "${MODULE_BUILD_DIR}" "${BIN}" >> "${COPYFILES_LIST}" done done [ ! -z "${REQUIRED_FILES}" ] && pinfo "Gathering required files from config file..." for ENTRY in ${REQUIRED_FILES} do get_link_chain "${MODULE_BUILD_DIR}/${ENTRY}" "${MODULE_BUILD_DIR}" >> "${COPYFILES_LIST}" get_dynamic_dependencies -l "${MODULE_BUILD_DIR}" "${MODULE_BUILD_DIR}/.${ENTRY}" >> "${COPYFILES_LIST}" done #copy to initramfsdir pdebug "File list generated at ${MODULE_BUILD_DIR}/${COPYFILES_LIST}." if [ -s "$COPYFILES_LIST" ]; then local CLISTCOUNT=$(cat "$COPYFILES_LIST" | wc -l) pinfo "Copying $CLISTCOUNT files to '${TARGET_BUILD_DIR}'." tarcopy "$(cat "$COPYFILES_LIST"|sort -u)" "${TARGET_BUILD_DIR}" fi } # # # main public function. Requires the TARGET to be given as argument. # this will simply go over all the modules as found in the remote/target/ # and run following functions: # # generate_target() { initial_checks TARGET=$1 && shift TARGET_DIR="${MODE_DIR}/targets/${TARGET}" TARGET_BUILD_DIR="${MODE_DIR}/builds/${TARGET}" [ -e "${ROOT_DIR}/logs/${TARGET}.size" ] && . "${ROOT_DIR}/logs/${TARGET}.size" || echo "declare -A BUILD_SIZE" >> "${ROOT_DIR}/logs/${TARGET}.size" [ -d "$TARGET_DIR" ] || perror "Given target directory does not exist: $TARGET_DIR" [[ "$TARGET" == "builds" || "$TARGET" == "modules" ]] && \ perror "Target directory cannot be named 'builds' or 'modules'." pinfo "Generating '$TARGET_BUILD_DIR' for '$TARGET'" # if no arguments assume all. if [ "x$1" = "x" -o "x$1" = "xall" ]; then MODULES=$(ls ${TARGET_DIR}) set -- $MODULES else # tools = arguments given MODULES=$@ fi pinfo "Activated modules in '${TARGET}':" pinfo "\t$(echo ${MODULES})" # copy basic libs pinfo "Copying libc and ld-linux used by ${SHELL}" tarcopy "$(list_basic_libs)" "${TARGET_BUILD_DIR}" [ -d "$TARGET_BUILD_DIR" ] && TARGET_BUILD_SIZE=$(du -bc "${TARGET_BUILD_DIR}" | awk 'END {print $1}') || TARGET_BUILD_SIZE=0 # now iterate over given tools and copy them while (( "$#" )); do process_module "$1" shift done post_process_target pinfo "Target completed. Total size: $(du -bsh "${TARGET_BUILD_DIR}" | awk 'END {print $1}')" TOOL_STR="" } process_module() { [ "$#" -ne "1" ] && perror "process_module: want 1 param." local MODULE="$1" [[ "$PROCESSED_MODULES" == *"!${MODULE}!"* ]] && return # Already processed this module PROCESSED_MODULES="${PROCESSED_MODULES}!${MODULE}!" local MODULE_DIR="${TARGET_DIR}/${MODULE}" local MODULE_BUILD_DIR="${MODULE_DIR}/build" local TOOL_STR="" pinfo ">>>>>>>>>>>>>>>>> Processing module [ $MODULE ]" TOOL_STR="[${MODULE}]" if [ -d "${MODULE_DIR}" ]; then #[ "x$DEBUG" != "x1" ] \ # && echo "Logging to ${TOOL_DIR}/${TOOL}/stage32.log" \ # && exec 6>&1 > ${TOOL_DIR}/${TOOL}/stage32.log # TODO: Make above work with the new logging system (add function to logging.inc to switch logfile) cd "${MODULE_DIR}" || perror "Module dir '${MODULE_DIR}' seems to exist, but cd to it failed." pinfo "## Reading config" read_config # Check if this module has a dependency that wasn't built yet: if [ ! -z "$REQUIRED_MODULES" ]; then pinfo "$MODULE depends on ${REQUIRED_MODULES}...." for DEP in $REQUIRED_MODULES; do process_module "$DEP" done # Read old config again, as it got overwritten by the deps cd "${MODULE_DIR}" || perror "Tool dir '${MODULE_DIR}' seems to exist, but cd to it failed (after building deps)." read_config pinfo "<<<<<<<<<<<<<<<<< Dependency modules processed, back to module [ $MODULE ]" fi pinfo "## Reading build" read_build pinfo "## Installing dependencies" install_dependencies pinfo "## Fetching source" [ -e "${MODULE_DIR}/.fetched_source" ] || { fetch_source && touch "${MODULE_DIR}/.fetched_source"; } if [ -e "${MODULE_DIR}/.built" -a -d "${MODULE_BUILD_DIR}" ]; then pinfo "# built-flag is set, skipping build..." else pinfo "## Building" mkdir -p "${MODULE_BUILD_DIR}" || perror "Could not create build dir" build # calls perror if something fails, no need to do that here touch "${MODULE_DIR}/.built" || pwarning "Error setting built-flag" fi # remove *.la files as they might confuse libtool/linker of other tool packages [ -d "${MODULE_BUILD_DIR}" ] && find "${MODULE_BUILD_DIR}" -name '*.la' -exec rm -f {} \; [ -d "${TARGET_BUILD_DIR}" ] && TARGET_BUILD_SIZE=$(du -bc "${TARGET_BUILD_DIR}" | awk 'END {print $1}') || TARGET_BUILD_SIZE=0 pinfo "## Copying files with dependencies" copy_files_with_deps pinfo "## Copying required system files" copy_system_files pinfo "## Copying static module files" copy_static_data pinfo "## Post copy" post_copy # reset pipes #[ "x$DEBUG" != "x1" ] && exec 1>&6 6>&- # TODO calc_size #sets MODULE_BUILD_SIZE pinfo "Module completed. Total size: ${MODULE_BUILD_SIZE}" else pwarning "Module directory for '$MODULE' not found." fi } post_process_target() { local TOOL_STR="$TOOL_STR post_process_target:" #move systemd files from /lib to /etc/lib pinfo "move systemd files from /lib/systemd to /etc/systemd" if [ -d "${TARGET_BUILD_DIR}/lib/systemd" ]; then cd "${TARGET_BUILD_DIR}/lib" tarcopy "systemd" "${TARGET_BUILD_DIR}/etc" cd - &> /dev/null rm -r "${TARGET_BUILD_DIR}/lib/systemd" fi # figure out all relevant ld-paths local LDTEMP="$TARGET_BUILD_DIR/ldconf-temp" grep -h '^\s*/' /etc/ld.so.conf /etc/ld.so.conf.d/* > "$LDTEMP" # TODO: Echo other stuff to $LDTEMP (but figure out if first or last match wins) ldconfig -v -r "${TARGET_BUILD_DIR}" -f "/ldconf-temp" unlink "$LDTEMP" } clean_modules() { TARGET=$1 shift TARGET_DIR="${MODE_DIR}/targets/${TARGET}" TARGET_BUILD_DIR="${MODE_DIR}/builds/${TARGET}" [ -d $TARGET_DIR ] || perror "Given target directory does not exist: $TARGET_DIR" if [ "x$1" = "x" -o "x$1" = "xall" ]; then if [ -e ${ROOT_DIR}/logs/${TARGET}.size ]; then rm "${ROOT_DIR}/logs/${TARGET}.size" || perror "Could not delete logs/${TARGET}.size" fi if [ -d ${TARGET_BUILD_DIR} ]; then pinfo "Cleaning '${TARGET_BUILD_DIR}'" \ && rm -rf "${TARGET_BUILD_DIR}"/* \ || perror "Error deleting $TARGET_BUILD_DIR" fi set -- $(ls ${TARGET_DIR} | grep -v kernel) fi cd ${TARGET_DIR} while (( "$#" )); do clean_module $(readlink -f $1) shift done cd - &> /dev/null } clean_module() { # if kernel is to be cleaned, do it separately and return [ "x$(basename $1)" == "xkernel" ] && clean_kernel_module && return pinfo "Cleaning '$1'..." local MODULE_DIR=$1 if [ -e ${MODULE_DIR}/.built ]; then rm "${MODULE_DIR}/.built" || perror "Could not clear built flag" fi if [ -e ${MODULE_DIR}/.fetched_source ]; then rm "${MODULE_DIR}/.fetched_source" || perror "Could not clear fetched_source flag" fi if [ -d ${MODULE_DIR}/build ]; then rm -rf "${MODULE_DIR}/build" || perror "Could not delete build path" fi if [ -d ${MODULE_DIR}/src ]; then rm -rf "${MODULE_DIR}/src" || perror "Could not delete src path" fi if [ -e ${MODULE_DIR}/list_dpkg_output ]; then rm "${MODULE_DIR}/list_dpkg_output" || perror "Could not delete list_dpkg_output" fi if [ -e ${MODULE_DIR}/list_binaries_and_files ]; then rm "${MODULE_DIR}/list_binaries_and_files" || perror "Could not delete list_binaries_and_files" fi } clean_kernel_module() { pinfo "Cleaning kernel module (including sources and compiled stuff)." pinfo "It will take a long time to rebuild, are you sure? [y/N]" read USER_INPUT if [ "x$USER_INPUT" == "xy" ]; then pinfo "Confirmed, going on..." # clean kernel module cd "${TARGET_DIR}/kernel" || perror "Could not cd to ${TARGET_DIR}/kernel" if [ -e build ]; then rm -rf build || perror "Could not delete ${TARGET_DIR}/kernel/build" fi # little workaround to actually list names of entries # (if only a dir exists, ls kernel/linux-* would just list # the contents of that directory....) for FILE in $(ls | grep linux); do rm -rf $FILE || perror "Could not delete $TARGET_DIR}/$FILE" done # finally clean the config generated if [ -e openslx.config ]; then rm openslx.config || perror "Could not delete ${TARGET_DIR}/kernel/openslx.config" fi if [ -e .fetched_source ]; then rm .fetched_source || perror "Could not delete ${TARGET_DIR}/kernel/.fetched_source" fi if [ -e .built ]; then rm .built || perror "Could not delete ${TARGET_DIR}/kernel/.built" fi if [ -e ksrc ]; then unlink ksrc || perror "Could not unlink ${TARGET_DIR}/kernel/ksrc." fi else pinfo "Not confirmed, not cleaning kernel module." fi pinfo "Done cleaning kernel." }