From 0e0a7b8e649283837c3126c1160c977b32191656 Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Tue, 31 Jul 2012 09:49:00 -0400 Subject: Rewrite opkbuild, add natural language support. --- (limited to 'src') diff --git a/src/opkbuild.sh b/src/opkbuild.sh index 114eeed..865c350 100644 --- a/src/opkbuild.sh +++ b/src/opkbuild.sh @@ -19,395 +19,496 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -. @@LIBDIR@@/opkhelper/controlfields -. @@LIBDIR@@/opkhelper/architecture -. @@LIBDIR@@/opkhelper/archive +. @@LIBDIR@@/opkhelper/messages +. @@LIBDIR@@/opkhelper/strings/en +. @@LIBDIR@@/opkhelper/changelog + +# Environment variables: +export OH_SOURCE= +export OH_SOURCE_VERSION= +export OH_SOURCE_VERSION_UPSTREAM= +export OH_BINARY_VERSION= +export OH_BUILD_ARCH= +export OH_BUILD_ARCH_CPU= +export OH_BUILD_ARCH_KERNEL= +export OH_BUILD_ARCH_LIBS= +export OH_BUILD_PLATFORM= +export OH_BUILD_ARCH_GNU= +export OH_HOST_ARCH= +export OH_HOST_ARCH_CPU= +export OH_HOST_ARCH_KERNEL= +export OH_HOST_ARCH_LIBS= +export OH_HOST_PLATFORM= +export OH_HOST_ARCH_GNU= +export OH_TOOL_PREFIX= +export AR= +export AS= +export CC= +export CPP= +export CXXLD= +export NM= +export OBJCOPY= +export OBJDUMP= +export RANLIB= +export READELF= +export SIZE= +export STRINGS= +export STRIP= + +# Constant global parameters: +SOURCE_VERSION_ID_RE='^' +SOURCE_VERSION_ID_RE="${SOURCE_VERSION_ID_RE}"'[0-9a-z.~]+' +SOURCE_VERSION_ID_RE="${SOURCE_VERSION_ID_RE}"'(\+sip[1-9][0-9]*)?' +SOURCE_VERSION_ID_RE="${SOURCE_VERSION_ID_RE}"'(-[1-9][0-9]*)?' +SOURCE_VERSION_ID_RE="${SOURCE_VERSION_ID_RE}"'(\+[a-z0-9]+-[1-9][0-9]*)?' +SOURCE_VERSION_ID_RE="${SOURCE_VERSION_ID_RE}"'$' + +# Global parameters: +OPT_BUILD= +OPT_TARGET= +OPT_HOST_ARCH= +OPT_HOST_PLAT= +OPT_CHECK_BUILD_DEPS= +OPT_CLEAN= +OPT_UID0_CMD= + +main() +{ + get_options + + test_uid0_cmd + + find_sanity + oh_changelog_parse changelog setup_package + + if [ "${OPT_BUILD}" = 'source' -o "${OPT_BUILD}" = 'full' ]; then + build_source + fi + + if [ "${OPT_BUILD}" != 'source' ]; then + "${OPT_CHECK_BUILD_DEPS}" && oh-checkbuilddeps + setup_build + print_arch_stats + if [ -n "${OH_CV_STEP}" ]; then + "step_${OH_CV_STEP}" + else + make_work_area + fi + fi +} -print_usage() +get_options() { - cat <. -This is free software: you are free to change and redistribute it. -There is NO WARRANTY, to the extent permitted by law. - -Written by P. J. McDermott. -EOF + # Verify that the UID 0 command works. + test_uid=$("${OPT_UID_CMD}" id -u) + if [ "${?}" -ne 0 ]; then + oh_error "${oh_str_uid0_cmd_not_found}" "${OPT_UID_CMD}" + fi + if [ "${test_uid}" -ne 0 ]; then + oh_error "${oh_str_uid0_cmd_bad_uid}" "${OPT_UID_CMD}" + fi } -error() +find_sanity() { - [ ${#} -eq 1 ] && printf 'opkbuild: [%s] Error\n' "${1}" >&2 - if ${dbg}; then - printf 'opkbuild: Starting debugging shell...\n' >&2 - @@SHELL@@ + oh_info "${oh_str_find_sanity}" + + format="$(cat format 2>/dev/null)" + if [ "${?}" -ne 0 ]; then + oh_error "${oh_str_no_format}" fi - cd .. - rm -Rf tmp 2> /dev/null - exit 1 + if [ "${format}" -ne 0 ]; then + oh_error "${oh_str_unsupported_format}" + fi + + # Check for all files immediately required by SPF 2.0. + [ ! -f 'control' ] && oh_error "${oh_str_no_control}" + [ ! -x 'config' ] && oh_error "${oh_str_no_config}" + [ ! -f 'copyright' ] && oh_error "${oh_str_no_copyright}" + [ ! -f 'changelog' ] && oh_error "${oh_str_no_changelog}" } -source_only=false -binary_only=false -opts=$(getopt -n "${0}" -o 'Sbr:a:p:ds:V' -- "${@}") -if [ ${?} -ne 0 ]; then - print_usage "${0}" >&2 - exit 1; -fi -eval set -- "${opts}" -while true; do - case "${1}" in - -S) - source_only=true - shift - ;; - -b) - binary_only=true - shift - ;; - -r) - uid0_cmd=${2} - shift 2 - ;; - -a) - host_arch=${2} - shift 2 - ;; - -p) - host_platform=${2} - shift 2 - ;; - -d) - dbg=true - shift - ;; - -s) - status_override=${2} - shift 2 - ;; - -V) - print_version - exit 0 - ;; - --) - shift - break - ;; - *) - print_usage "${0}" >&2 - exit 1 - ;; - esac -done - -if ${source_only} && ${binary_only}; then - printf 'opkbuild: Error: Cannot combine -S and -b\n' >&2 - exit 1 -fi -if [ -z "${uid0_cmd}" ]; then - uid0_cmd=fakeroot -fi -${uid0_cmd} true -if [ ${?} -ne 0 ]; then - printf 'opkbuild: Error running command to gain UID 0\n' >&2 - exit 1 -fi -if [ -z "${dbg}" ]; then - dbg=false -fi - -# Sanity checks. -printf 'opkbuild: Checking for sanity...\n' -if [ ! -f format -o ! -f build -o ! -f control ]; then - printf 'Error: Current working directory is not a source package.\n' >&2 - exit 1 -fi -if [ "$(cat format)" != '1.0' ]; then - printf 'Error: Source package is of an incompatible version.\n' >&2 - exit 1 -fi - -# Attempt to detect the build architecture. -build_arch=$(opkg print-architecture | sed -n \ - 's/^arch \([^ -][^ -]*-[^ -][^ -]*-[^ -][^ -]*\) [0-9][0-9]*$/\1/p') -if [ -z "${build_arch}" ]; then - printf 'opkbuild: Error: No installable architecture found\n' >&2 - exit 1 -elif [ $(echo "${build_arch}" | wc -l) -gt 1 ]; then - printf 'opkbuild: Error: Multiple installable architectures found\n' >&2 - exit 1 -fi - -# If a host architecture is not specified, natively build. -if [ -z "${host_arch}" ]; then - host_arch="${build_arch}" -fi - -# Attempt to detect the build platform. -build_platform=$(opkg print-architecture | sed -n \ - 's/^arch \([^ -][^ -]*\) [0-9][0-9]*$/\1/p' | \ - grep -v 'all') -if [ -z "${build_platform}" ]; then - printf 'opkbuild: Error: No build platforms found\n' >&2 - exit 1 -elif [ $(echo "${build_platform}" | wc -l) -gt 1 ]; then - printf 'opkbuild: Error: Multiple build platforms found\n' >&2 - exit 1 -fi - -# If there no config files to copy, build platform-independent packages. -if [ ! -f config ]; then - host_platform='' -# If a host platform was not specified, natively configure. -elif [ -z "${host_platform}" ]; then - host_platform="${build_platform}" -fi - -# Resolve status override file path. -pkg_dir="${PWD}" -cd "$(dirname "${status_override}")" -status_override="${PWD}/$(basename "${status_override}")" -cd "${pkg_dir}" - -# Make work area. -printf 'opkbuild: Making work area...\n' -mkdir tmp -cd tmp - -# Get source package name and version. -printf 'opkbuild: Reading source package control fields...\n' -srcpkg=$(oh_get_field Source) -version=$(oh_get_field Version) -export OH_SRCPKG="${srcpkg}" -export OH_PKGVER=${version} - -# Set environment variables for the build architecture. -export OH_BUILD_ARCH_DIST="${build_arch}" -IFS=- read OH_BUILD_ARCH_CPU OH_BUILD_ARCH_KERNEL OH_BUILD_ARCH_LIBS </dev/null 2>&1 + if [ "${?}" -ne 0 ]; then + oh_error "${oh_str_bad_source_version}" "${OH_SOURCE_VERSION}" + fi -# Set environment variables for the host architecture and platform. -export OH_HOST_ARCH_DIST="${host_arch}" -IFS=- read OH_HOST_ARCH_CPU OH_HOST_ARCH_KERNEL OH_HOST_ARCH_LIBS <&2 - exit 1 -fi - -archtab_bre='[ \t][ \t]*\([^ \t][^ \t]*[ \t][ \t]*[^ \t][^ \t]*\)' - -# TODO: kbuild doesn't have standardized architecture names. For example, IA-32 -# is known as "x86" in Linux and "i386" in BusyBox. So we should support a -# comma-separated list of possible values and try to find one in src/arch once -# the software source code is copied into the work area. - -# TODO: Ideally, we should have a tool like debhelper's dh_auto_configure that -# detects the build system in use and configures the build automatically, -# including cross building options. - -# Look up build GNU and kbuild architecture names. -archtab_row_build="$(sed -n \ - "s/^${OH_BUILD_ARCH_DIST}${archtab_bre}\$/\\1/p" \ - "@@DATADIR@@/opkhelper/archtab")" -if [ -z "${archtab_row_build}" ]; then - printf 'opkbuild: Error: "%s" not found in architecture table.\n' \ - "${OH_BUILD_ARCH_DIST}" >&2 - exit 1 -fi -read OH_BUILD_ARCH_GNU OH_BUILD_ARCH_KBUILD <&2 - exit 1 -fi -read OH_HOST_ARCH_GNU OH_HOST_ARCH_KBUILD <}" - -# Check build dependencies. -oh-checkbuilddeps -s "${status_override}" || error "${srcpkg}-src" - -printf '\n' - -# TODO: This should maybe go in the library. - -if ! ${binary_only}; then - -# Build *-src package. -printf 'opkbuild: Attempting to build package "%s"...\n' "${srcpkg}-src" -printf 'opkbuild: Installing files for package "%s"...\n' "${srcpkg}-src" -${uid0_cmd} mkdir -p ${srcpkg}-src.data/usr/src/${srcpkg}_${version} || - error "${srcpkg}-src" -for file in ../*; do - case ${file} in - ../tmp) - ;; - ../*) - ${uid0_cmd} cp -R ${file} \ - ${srcpkg}-src.data/usr/src/${srcpkg}_${version} || - error "${srcpkg}-src" - ;; - esac -done -( ${uid0_cmd} oh-gencontrol -s;) || error "${srcpkg}-src" -( ${uid0_cmd} oh-buildopk -s;) || error "${srcpkg}-src" -( ${uid0_cmd} rm -Rf ${srcpkg}-src.data;) || error "${srcpkg}-src" -printf 'opkbuild: Package "%s" complete!\n\n' "${srcpkg}-src" - -fi - -# TODO: This should maybe go in the library. - -if ! ${source_only}; then - -# Build other binary packages. -for binpkgdir in ../*.pkg/; do - binpkg=${binpkgdir#'../'} - binpkg=${binpkg%'.pkg/'} - export OH_BINPKG="${binpkg}" - # Check architecture. - oh_is_buildable "${binpkg}" - if [ ${?} -eq 0 ]; then - printf 'opkbuild: Attempting to build package "%s"...\n' "${binpkg}" - # Copy or extract software sources to src. - if [ -d ../src ]; then - printf 'opkbuild: Copying sources into work area...\n' - cp -Rp ../src src || error "${binpkg}" - else - printf 'opkbuild: Extracting sources into work area...\n' - oh_archive_extract_source - case ${?} in - 1) - printf 'opkbuild: Error: No source archive found\n' >&2 - error - ;; - 2) - printf 'opkbuild: Error: Multiple source archives found\n' \ - >&2 - error - ;; - 3) - printf 'opkbuild: Error: %s\n' \ - 'Unsupported archive compression format detected' >&2 - error - ;; - 4) - printf 'opkbuild: Error: %s\n' \ - 'No directories found in source archive' >&2 - error - ;; - 5) - printf 'opkbuild: Error: %s\n' \ - 'Multiple top-level directories found in source archive' >&2 - error - ;; - 6) - printf 'opkbuild: Error: %s\n' \ - 'Failed to extract source archive' >&2 - error - ;; - 7) - printf 'opkbuild: Error: %s\n' \ - 'Failed to move extracted sources' >&2 - error - ;; - esac - fi - # Make installation directory. - # TODO: Maybe this should be an FHS-compliant filesystem hierarchy. - mkdir dest || error "${binpkg}" - # Apply patches. - oh-applypatches - # Copy platform config files. - oh-copyconfig || error "${binpkg}" - # Build the package. - ${uid0_cmd} ../build ${binpkg} || error "${binpkg}" - printf 'opkbuild: Package "%s" complete!\n\n' "${binpkg}" - # Clean up everything except the build stamps. - for file in *; do - case ${file} in - *stamp) - ;; - *) - ${uid0_cmd} rm -Rf ${file} || error "${binpkg}" - ;; - esac +} + +setup_host_platform() +{ + if [ -n "${OPT_HOST_PLAT}" ]; then + OH_HOST_PLATFORM="${OPT_HOST_PLAT}" + else + OH_HOST_PLATFORM="${OH_BUILD_PLATFORM}" + fi +} + +setup_toolchain() +{ + OH_TOOL_PREFIX="${OH_HOST_ARCH}-" + + # Set toolchain environment variables. + # NB: This is written for GNU Binutils and GCC and won't work for, e.g., LLVM. + AR="${OH_TOOL_PREFIX}ar" + AS="${OH_TOOL_PREFIX}as" + CC="${OH_TOOL_PREFIX}gcc" + CPP="${OH_TOOL_PREFIX}cpp" + CXX="${OH_TOOL_PREFIX}g++" + LD="${OH_TOOL_PREFIX}ld" + NM="${OH_TOOL_PREFIX}nm" + OBJCOPY="${OH_TOOL_PREFIX}objcopy" + OBJDUMP="${OH_TOOL_PREFIX}objdump" + RANLIB="${OH_TOOL_PREFIX}ranlib" + READELF="${OH_TOOL_PREFIX}readelf" + SIZE="${OH_TOOL_PREFIX}size" + STRINGS="${OH_TOOL_PREFIX}strings" + STRIP="${OH_TOOL_PREFIX}strip" +} + +print_arch_stats() +{ + oh_info "${oh_str_build_arch_stat_header}" + oh_info "${oh_str_arch_stat_arch}" "${OH_BUILD_ARCH}" + oh_info "${oh_str_arch_stat_plat}" "${OH_BUILD_PLATFORM}" + oh_info "${oh_str_host_arch_stat_header}" + oh_info "${oh_str_arch_stat_arch}" "${OH_HOST_ARCH}" + oh_info "${oh_str_arch_stat_plat}" "${OH_HOST_PLATFORM}" +} + +make_work_area() +{ + mkdir tmp || oh_error "${oh_str_cant_make_work_area}" + cd tmp || oh_error "${oh_str_cant_enter_work_area}" + + step_unpack +} + +step_unpack() +{ + oh_cv_set step unpack + + # TODO: Put this in oh-unpacksource. + if [ -d ../src ]; then + oh_info "${oh_str_unpacking_native}" + cp -Rp ../src src || oh_error "${oh_str_cant_unpack_native}" + else + archive_base_name="${OH_SOURCE}-${OH_SOURCE_VERSION_UPSTREAM}.orig.tar." + archive_matches=$(find .. -name "${archive_base_name}*") + case "$(echo "${archive_matches}" | wc -l)" in + 0) + oh_error "${oh_str_no_sources}" + ;; + 1) + ;; + *) + oh_error "${oh_str_multiple_upstream_sources}" + ;; + esac + z_ext="${archive_matches#../${archive_base_name}}" + case ${z_ext} in + gz) + z=z + ;; + bz2) + z=j + ;; + lz) + z=a + ;; + Z) + z=Z + ;; + *) + oh_error "${oh_str_unsupported_archive_compression}" "${z_ext}" + ;; + esac + sed 's@^./@@' | grep '^[^/]*/$' + top_dirs=$(tar "-t${z}f" "${archive_matches}${z_ext}" | \ + sed -n 's@^\./\([^/]*\)$@\1@p') + case "$(echo "${top_dirs}" | wc -l)" in + 0) + oh_error "${oh_str_no_upstream_dirs}" + ;; + 1) + ;; + *) + oh_error "${oh_str_multiple_top_upstream_dirs}" + esac + oh_info "${oh_str_unpacking_upstream}" + tar "-x${z}f" "${archive_matches}${z_ext}" || \ + oh_error "${oh_str_cant_unpack_upstream}" + mv "${top_dirs}" src || oh_error "${oh_str_cant_move_native}" + fi + + step_patch +} + +step_patch() +{ + oh_cv_set step patch + + # TODO: Put this in oh-applypatches. + applied=false + if [ -d ../patches ]; then + # Iterate over patches ordered alphabetically by name. + for patch in $(ls -1 ../patches | sort); do + oh_info "${oh_str_applying_patch}" "${patch}" + patch -N -p 1 -u -d src -i "../../patches/${patch}" || \ + oh_error "${oh_str_cant_apply_patch}" "${patch}" + applied=true done fi -done + ${applied} || oh_info "${oh_str_no_patches}" + + step_config +} + +step_config() +{ + oh_cv_set step config + + ./config + + step_platconf +} + +step_platconf() +{ + oh_cv_set step platconf + + # TODO: Put this in oh-copyconfig. + if [ -f ../config ]; then + while read -r src dest; do + if [ -z "${src}" -o -z "${dest}" ]; then + oh_error + fi + # Make sure the destination directory exists. + mkdir -p "${dest%/*}" || oh_error + # Find the config package files. + # TODO: Don't hardcode path. + config_dir_base="/usr/share/config/${OH_HOST_PLATFORM}/${OH_SOURCE}" + if [ -d "${config_dir_base}-${OH_SOURCE_VERSION_UPSTREAM}" ]; then + src_base="${config_dir_base}-${OH_SOURCE_VERSION_UPSTREAM}" + elif [ -d "${config_dir_base}" ]; then + src_base="${config_dir_base}" + else + # This shouldn't happen unless the package maintainer neglected + # to add the config package to the package's Build-Depends + # field. + oh_error + fi + # Copy the config file(s). + oh_info + cp -p "${src_base}/${src}" "${dest}" || oh_error + done <../config + fi + for bin_config in ../*.pkg/config; do + while read -r src dest; do + if [ -z "${src}" -o -z "${dest}" ]; then + oh_error + fi + # Make sure the destination directory exists. + mkdir -p "dest/${dest%/*}" || oh_error + # Find the config package files. + # TODO: Don't hardcode path. + config_dir_base="/usr/share/config/${OH_HOST_PLATFORM}/${OH_SOURCE}" + if [ -d "${config_dir_base}-${OH_SOURCE_VERSION_UPSTREAM}" ]; then + src_base="${config_dir_base}-${OH_SOURCE_VERSION_UPSTREAM}" + elif [ -d "${config_dir_base}" ]; then + src_base="${config_dir_base}" + else + # This shouldn't happen unless the package maintainer neglected + # to add the config package to the package's Build-Depends + # field. + oh_error + fi + # Copy the config file(s). + oh_info + cp -p "${src_base}/${src}" "dest/${dest}" || oh_error + done <"${bin_config}" + done + + step_build +} + +step_build() +{ + oh_cv_set step build -fi + ./build "${OPT_TARGET}" + + "${OPT_CLEAN}" && step_clean +} + +step_clean() +{ + oh_cv_set step clean + + ./config clean + + step_cleanwork +} + +step_cleanwork() +{ + oh_cv_clear + + cd .. + rm -Rf tmp +} -# Clean up tmp. -cd .. -rm -Rf tmp +main "${@}" -- cgit v0.9.1