From: Ladislav Michl <[email protected]>

The bootstrap script makes ready everything PTXdist needs from the host:
- links to the GNU tools in <builddir>/bin
- kconfig in <builddir>/scripts/kconfig
All the missing prerequisites are reported at once.

Add support for out-of-tree builds to the Makefile; all generated files
ends in the build directory, 'install' and 'dist' targets are only
supported in-tree.

Signed-off-by: Ladislav Michl <[email protected]>
---
 .gitignore                   |   1 +
 Makefile.in                  |  65 +++-------
 configure.ac                 |   2 +-
 scripts/ptxdist_bootstrap.sh | 230 +++++++++++++++++++++++++++++++++++
 4 files changed, 250 insertions(+), 48 deletions(-)
 create mode 100755 scripts/ptxdist_bootstrap.sh

diff --git a/.gitignore b/.gitignore
index 1e2cd1c31..79657ecab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ man/ptxdist.1.gz
 /env/
 
 /scripts/report/__pycache__/
+/build.log
diff --git a/Makefile.in b/Makefile.in
index da8cb9d7c..5eb7b407b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,9 +1,10 @@
-version                := $(shell . ./scripts/ptxdist_version.sh && echo 
$${PTXDIST_VERSION_FULL})
+version                := $(shell PTXDIST_TOPDIR=@abs_srcdir@ && . 
@abs_srcdir@/scripts/ptxdist_version.sh && echo $${PTXDIST_VERSION_FULL})
 project                := ptxdist-$(version)
 
 prefix         := @prefix@
 exec_prefix    := @exec_prefix@
 abs_srcdir     := @abs_srcdir@
+abs_builddir   := @abs_builddir@
 libdir         := @libdir@
 bindir         := @bindir@
 instdir                := $(libdir)/$(project)
@@ -13,49 +14,13 @@ RST2MAN             := @RST2MAN@
 
 export SHELL
 
-all: kconfig environment
-       @touch .done
-
-kconfig:
-       @echo "building conf and mconf ..."
-       $(MAKE) -C "$(abs_srcdir)/scripts/kconfig"
+all:
+       @echo "preparing PTXdist host tools ..."
+       @CC="@CC@" CFLAGS="@CFLAGS@" CPPFLAGS="@CPPFLAGS@" LDFLAGS="@LDFLAGS@" \
+               LEX="@LEX@" YACC="@YACC@" \
+               "@BASH@" "$(abs_srcdir)/scripts/ptxdist_bootstrap.sh" 
"$(abs_builddir)"
        @echo "done."
 
-environment:
-       @echo -n "preparing PTXdist environment ..."
-       @ln -sf @AWK@ "$(abs_srcdir)/bin/awk"
-       @ln -sf @GNU_chmod@ "$(abs_srcdir)/bin/chmod"
-       @ln -sf @GNU_chown@ "$(abs_srcdir)/bin/chown"
-       @ln -sf @GNU_mv@ "$(abs_srcdir)/bin/mv"
-       @ln -sf @GNU_cp@ "$(abs_srcdir)/bin/cp"
-       @ln -sf @GNU_rm@ "$(abs_srcdir)/bin/rm"
-       @ln -sf @GNU_ln@ "$(abs_srcdir)/bin/ln"
-       @ln -sf @GNU_rmdir@ "$(abs_srcdir)/bin/rmdir"
-       @ln -sf @GNU_md5sum@ "$(abs_srcdir)/bin/md5sum"
-       @ln -sf @GNU_mkdir@ "$(abs_srcdir)/bin/mkdir"
-       @ln -sf @GNU_mktemp@ "$(abs_srcdir)/bin/mktemp"
-       @ln -sf @GNU_install@ "$(abs_srcdir)/bin/install"
-       @ln -sf @GNU_stat@ "$(abs_srcdir)/bin/stat"
-       @ln -sf @GNU_mknod@ "$(abs_srcdir)/bin/mknod"
-       @ln -sf @GNU_tty@ "$(abs_srcdir)/bin/tty"
-       @ln -sf @GNU_tar@ "$(abs_srcdir)/bin/tar"
-       @ln -sf @GNU_find@ "$(abs_srcdir)/bin/find"
-       @ln -sf @PTXDIST_FILE@ "$(abs_srcdir)/bin/file"
-       @ln -sf @GNU_cat@ "$(abs_srcdir)/bin/cat"
-       @ln -sf @GNU_dirname@ "$(abs_srcdir)/bin/dirname"
-       @ln -sf @GNU_readlink@ "$(abs_srcdir)/bin/readlink"
-       @ln -sf @GNU_realpath@ "$(abs_srcdir)/bin/realpath"
-       @ln -sf @GNU_sort@ "$(abs_srcdir)/bin/sort"
-       @ln -sf @SED@ "$(abs_srcdir)/bin/sed"
-       @ln -sf @GNU_xargs@ "$(abs_srcdir)/bin/xargs"
-       @ln -sf @BASH@  "$(abs_srcdir)/bin/bash"
-       @ln -sf @SYSCTL@ "$(abs_srcdir)/bin/sysctl"
-       @ln -sf @MAKE@ "$(abs_srcdir)/bin/make"
-       @if [ @SPHINX@ != "sphinx-build" ]; then \
-               ln -sf @SPHINX@ "$(abs_srcdir)/bin/sphinx-build"; \
-       fi
-       @echo " done"
-
 man: man/ptxdist.1.gz
 man/ptxdist.1: doc/ptxdist.man doc/ref_parameter.rst
        @mkdir -pv man
@@ -66,16 +31,22 @@ man/ptxdist.1.gz: man/ptxdist.1
        gzip -f $<
 
 clean:
-       @rm -f .done
-       @find "$(abs_srcdir)/bin" -type l -print0 | xargs -0 rm -f
-       @$(MAKE) -C "$(abs_srcdir)/scripts/kconfig" clean
+       @rm -f "$(abs_builddir)/.done"
+       @find "$(abs_builddir)/bin" -type l -print0 | xargs -0 rm -f
+       @$(MAKE) -C "$(abs_builddir)/scripts/kconfig" \
+               -f "$(abs_srcdir)/scripts/kconfig/Makefile" clean
+
+intree-check:
+       @if [ "$(abs_srcdir)" != "$(abs_builddir)" ]; then \
+               echo "install and dist are only supported for in-tree builds!" 
; exit 1 ; \
+       fi
 
-dirty-check:
+dirty-check: intree-check
        @case "$(version)" in \
                *-dirty) echo "refusing to install or package a dirty git 
tree!" ; exit 1 ;; \
        esac
 
-install: all dirty-check
+install: intree-check all dirty-check
        @echo "installing PTXdist to $(DESTDIR)$(prefix)..."
        @rm -fr   "$(DESTDIR)$(instdir)"
        @mkdir -p "$(DESTDIR)$(instdir)"
diff --git a/configure.ac b/configure.ac
index c368e46b1..2590ebbed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,7 +15,7 @@ dnl
 dnl Check if the user has extracted the patches archive
 dnl
 AC_MSG_CHECKING(for ptxdist patches)
-if test -d `dirname $0`/patches
+if test -d "$srcdir/patches"
 then
        AC_MSG_RESULT(yes)
 else
diff --git a/scripts/ptxdist_bootstrap.sh b/scripts/ptxdist_bootstrap.sh
new file mode 100755
index 000000000..b9f4c7a5c
--- /dev/null
+++ b/scripts/ptxdist_bootstrap.sh
@@ -0,0 +1,230 @@
+#!/bin/sh
+#
+# Prepare the host tools PTXdist needs:
+# - links to sane (GNU) tools found on the host in <builddir>/bin
+# - kconfig (conf, mconf and, if possible, nconf) in <builddir>/scripts/kconfig
+#
+# All missing prerequisites are reported at once. Only tools that PTXdist
+# itself needs are checked, everything required by some packages only is
+# left to the package build.
+#
+# usage: ptxdist_bootstrap.sh <builddir>
+#
+# CC, CFLAGS, CPPFLAGS, LDFLAGS, LEX, YACC and PKG_CONFIG from the
+# environment are used if set.
+#
+
+PTXDIST_LOG_PROMPT="ptxdist: "
+
+#
+# Everything up to the bash version check must be plain POSIX sh: the
+# script may be started by any shell and re-executes itself with bash.
+#
+if [ -z "${BASH_VERSION}" ]; then
+       bash="$(command -v bash)" || {
+               echo "${PTXDIST_LOG_PROMPT}error: bash >= 4.2 is required, 
please install it" >&2
+               exit 1
+       }
+       exec "${bash}" "${0}" "${@}"
+fi
+
+case "${BASH_VERSION}" in
+[0-3].*|4.[01].*)
+       echo "${PTXDIST_LOG_PROMPT}error: bash >= 4.2 is required, found 
${BASH_VERSION} as '${BASH}'" >&2
+       exit 1
+       ;;
+esac
+
+# e.g. when started as 'sh' with /bin/sh -> bash
+set +o posix
+
+topdir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" || exit
+builddir="${1}"
+if [ -z "${builddir}" ]; then
+       echo "usage: ${0} <builddir>" >&2
+       exit 1
+fi
+
+declare -A links
+declare -a missing
+
+#
+# $1: version found, $2: version required
+#
+version_ge() {
+       local -a have want
+       local i
+
+       IFS=. read -r -a have <<< "${1}"
+       IFS=. read -r -a want <<< "${2}"
+       for (( i = 0; i < ${#want[@]}; i++ )); do
+               if [ "${have[i]:-0}" -gt "${want[i]}" ]; then
+                       return 0
+               elif [ "${have[i]:-0}" -lt "${want[i]}" ]; then
+                       return 1
+               fi
+       done
+}
+
+#
+# find a GNU tool and register a link for it
+#
+# $1: link name
+# $2: expected '--version' output, e.g. 'GNU coreutils'
+# $3: minimum version, may be empty
+# $4: package that provides it (to print the hint)
+# $5...: names to look for in PATH
+#
+find_gnu() {
+       local link="${1}" id="${2}" min="${3}" pkg="${4}"
+       local name path version
+       shift 4
+
+       for name in "${@}"; do
+               path="$(type -P "${name}")" || continue
+               version="$("${path}" --version 2>/dev/null | head -n 1)"
+               [[ "${version}" =~ ${id} ]] || continue
+               if [ -n "${min}" ]; then
+                       [[ "${version}" =~ ([0-9]+(\.[0-9]+)+) ]] &&
+                       version_ge "${BASH_REMATCH[1]}" "${min}" || continue
+               fi
+               links[${link}]="${path}"
+               return
+       done
+       missing+=( "${id}${min:+ >= ${min}} as '${link}' (package: ${pkg})" )
+}
+
+for tool in cat chmod chown cp dirname install ln md5sum mkdir mknod \
+               mktemp mv readlink realpath rm rmdir sort stat tty; do
+       find_gnu "${tool}" "GNU coreutils" "" coreutils "g${tool}" "${tool}"
+done
+find_gnu tar   "GNU tar"       ""      tar             gtar tar
+find_gnu find  "GNU findutils" 4       findutils       gfind find
+find_gnu xargs "GNU findutils" 4       findutils       gxargs xargs
+find_gnu sed   "GNU sed"       4       sed             gsed sed
+find_gnu awk   "GNU Awk"       ""      gawk            gawk awk
+find_gnu make  "GNU Make"      3.81    make            gmake gnumake make
+
+links[bash]="${BASH}"
+
+if path="$(type -P file)"; then
+       links[file]="${path}"
+else
+       missing+=( "'file' (package: file)" )
+fi
+
+# optional
+links[sysctl]="$(type -P sysctl || type -P false)"
+if path="$(type -P sphinx-build)"; then
+       links[sphinx-build]="${path}"
+fi
+
+#
+# kconfig
+#
+if [ -z "${CC}" ]; then
+       CC="$(type -P gcc || type -P cc)" || CC=cc
+fi
+if ! type -P "${CC%% *}" > /dev/null; then
+       missing+=( "C compiler '${CC}' (package: gcc)" )
+fi
+: ${LEX:=flex} ${YACC:=bison} ${PKG_CONFIG:=pkg-config}
+if ! type -P "${LEX%% *}" > /dev/null; then
+       missing+=( "'${LEX}' (package: flex)" )
+fi
+if ! type -P "${YACC%% *}" > /dev/null; then
+       missing+=( "'${YACC}' (package: bison)" )
+fi
+
+#
+# $1: test program; possibly followed by compiler and linker flags
+#
+try_link() {
+       local prog="${1}"
+       shift
+       echo "${prog}" |
+               ${CC} ${CFLAGS} ${CPPFLAGS} -x c - -o /dev/null ${LDFLAGS} 
"${@}" > /dev/null 2>&1
+}
+
+#
+# $1: pkg-config modules (space separated, all required)
+# $2: fallback libs
+# $3: test program
+# out: libs_cflags, libs_libs
+#
+find_libs() {
+       local modules="${1}" fallback="${2}" prog="${3}"
+
+       if ${PKG_CONFIG} --exists ${modules} 2>/dev/null; then
+               libs_cflags="$(${PKG_CONFIG} --cflags ${modules})"
+               libs_libs="$(${PKG_CONFIG} --libs ${modules})"
+       else
+               libs_cflags=""
+               libs_libs="${fallback}"
+       fi
+       try_link "${prog}" ${libs_cflags} ${libs_libs}
+}
+
+ncurses_prog='#include <ncurses.h>
+int main(void) { initscr(); endwin(); return 0; }'
+nconf_prog='#include <ncurses.h>
+#include <menu.h>
+#include <panel.h>
+int main(void) { initscr(); new_panel(stdscr); free_menu(NULL); return 0; }'
+
+if type -P "${CC%% *}" > /dev/null; then
+       if find_libs "ncursesw" "-lncursesw" "${ncurses_prog}" ||
+               find_libs "ncurses" "-lncurses" "${ncurses_prog}"; then
+               mconf_cflags="${libs_cflags}"
+               mconf_libs="${libs_libs}"
+       else
+               missing+=( "ncurses development files (package: libncurses-dev 
or ncurses-devel)" )
+       fi
+       # nconf is optional
+       if find_libs "ncursesw menuw panelw" "-lmenuw -lpanelw -lncursesw" 
"${nconf_prog}" ||
+               find_libs "ncurses menu panel" "-lmenu -lpanel -lncurses" 
"${nconf_prog}"; then
+               nconf=nconf
+               nconf_libs="${libs_libs}"
+       fi
+fi
+
+if [ ${#missing[@]} -gt 0 ]; then
+       {
+               echo
+               echo "${PTXDIST_LOG_PROMPT}error: missing host tools, please 
install:"
+               echo
+               printf "  - %s\n" "${missing[@]}"
+               echo
+       } >&2
+       exit 1
+fi
+
+#
+# all prerequisites are there, set up the build directory
+#
+mkdir -p "${builddir}/bin" "${builddir}/scripts/kconfig" &&
+for link in "${!links[@]}"; do
+       ln -sfn "${links[${link}]}" "${builddir}/bin/${link}" || exit
+done
+
+if ! "${links[make]}" -C "${builddir}/scripts/kconfig" \
+       -f "${topdir}/scripts/kconfig/Makefile" \
+       CC="${CC}" \
+       ${CFLAGS:+CFLAGS="${CFLAGS}"} \
+       ${CPPFLAGS:+CPPFLAGS="${CPPFLAGS}"} \
+       ${LDFLAGS:+LDFLAGS="${LDFLAGS}"} \
+       LEX="${LEX}" \
+       YACC="${YACC}" \
+       MCONF_CFLAGS="${mconf_cflags}" \
+       MCONF_LIBS="${mconf_libs}" \
+       NCONF_LIBS="${nconf_libs}" \
+       NCONF="${nconf}" \
+       > "${builddir}/build.log" 2>&1; then
+       tail -n 20 "${builddir}/build.log" >&2
+       echo >&2
+       echo "${PTXDIST_LOG_PROMPT}error: building kconfig failed, see 
'${builddir}/build.log'" >&2
+       echo >&2
+       exit 1
+fi
+
+echo "${topdir}" > "${builddir}/.done"
-- 
2.47.3


Reply via email to