diff --git a/build/pack.c b/build/pack.c index f9e5a7ad97..5c3b4fa71b 100644 --- a/build/pack.c +++ b/build/pack.c @@ -347,6 +347,12 @@ static char *getIOFlags(Package pkg) } else if (rstreq(s+1, "lzdio")) { compr = "lzma"; (void) rpmlibNeedsFeature(pkg, "PayloadIsLzma", "4.4.6-1"); +#endif +#if HAVE_ZSTD_H + } else if (rstreq(s+1, "zstdio")) { + compr = "zstd"; + /* Add prereq on rpm version that understands zstd payloads */ + (void) rpmlibNeedsFeature(pkg, "PayloadIsZstd", "5.4.18-1"); #endif } else { rpmlog(RPMLOG_ERR, _("Unknown payload compression: %s\n"), diff --git a/build/parsePrep.c b/build/parsePrep.c index 182056ffc2..a62123e257 100644 --- a/build/parsePrep.c +++ b/build/parsePrep.c @@ -210,6 +210,9 @@ static char *doUntar(rpmSpec spec, uint32_t c, int quietly) t = "%{__7zip} x"; needtar = 0; break; + case COMPRESSED_ZSTD: + t = "%{__zstd} -dc"; + break; case COMPRESSED_GEM: t = "%{__gem} unpack"; needtar = 0; diff --git a/configure.ac b/configure.ac index 017a908882..874941efe8 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,7 @@ AC_PATH_PROG(__SED, sed, /bin/sed, $MYPATH) AC_PATH_PROG(__SEMODULE, semodule, /usr/bin/semodule, $MYPATH) AC_PATH_PROG(__SSH, ssh, /usr/bin/ssh, $MYPATH) AC_PATH_PROG(__TAR, tar, /bin/tar, $MYPATH) +AC_PATH_PROG(__ZSTD, zstd, /usr/bin/zstd, $MYPATH) AC_PATH_PROG(__LD, ld, /usr/bin/ld, $MYPATH) AC_PATH_PROG(__NM, nm, /usr/bin/nm, $MYPATH) @@ -202,6 +203,20 @@ AC_CHECK_HEADERS([lzma.h],[ ]) AC_SUBST(WITH_LZMA_LIB) +#================= +# Check for zstd library. + +dnl # Facebook Zstd +RPM_CHECK_LIB( + [Facebook Zstd], [zstd], + [zstd], [ZSTD_versionNumber], [zstd.h], + [yes,external:none], [zstd:lib:lib], + [ + ], []) + +WITH_ZSTD_LIB="$WITH_ZSTD" +AC_SUBST(WITH_ZSTD_LIB) + #================= dnl diff --git a/lib/rpmds.c b/lib/rpmds.c index 0e4417fc74..03b63ecc6b 100644 --- a/lib/rpmds.c +++ b/lib/rpmds.c @@ -1278,6 +1278,11 @@ static const struct rpmlibProvides_s rpmlibProvides[] = { { "rpmlib(RichDependencies)", "4.12.0-1", ( RPMSENSE_EQUAL), N_("support for rich dependencies.") }, +#if HAVE_ZSTD_H + { "rpmlib(PayloadIsZstd)", "5.4.18-1", + (RPMSENSE_RPMLIB|RPMSENSE_EQUAL), + N_("package payload can be compressed using zstd.") }, +#endif { NULL, NULL, 0, NULL } }; diff --git a/m4/acinclude.m4 b/m4/acinclude.m4 new file mode 100644 index 0000000000..412b456b80 --- /dev/null +++ b/m4/acinclude.m4 @@ -0,0 +1,537 @@ +dnl ## +dnl ## acinclude.m4 -- manually provided local Autoconf macros +dnl ## + +dnl ## +dnl ## NAME: +dnl ## AC_MSG_TITLE -- Display a configuration title +dnl ## +dnl ## USAGE: +dnl ## AC_MSG_TITLE(, ) +dnl ## + +AC_DEFUN([AC_MSG_TITLE],[ + _AS_ECHO([Configuring $1, Version $2]) +]) + +dnl ## +dnl ## NAME: +dnl ## AC_MSG_HEADER -- Display a configuration header +dnl ## +dnl ## USAGE: +dnl ## AC_MSG_HEADER() +dnl ## + +AC_DEFUN([AC_MSG_HEADER],[ + _AS_ECHO([]) + _AS_ECHO([=== $1 ===]) +]) + +dnl ## +dnl ## NAME: +dnl ## AC_MSG_VERBOSE -- Display a message under --verbose +dnl ## +dnl ## USAGE: +dnl ## AC_MSG_VERBOSE() +dnl ## + +AC_DEFUN([AC_MSG_VERBOSE], [ + if test ".$verbose" = .yes; then + _AS_ECHO([$1]) + fi +]) + +dnl ## +dnl ## NAME: +dnl ## RPM_CHECK_LIB -- Check for third-party libraries +dnl ## +dnl ## COPYRIGHT +dnl ## Copyright (c) 2007 Ralf S. Engelschall +dnl ## +dnl ## DESCRIPTION: +dnl ## This is a rather complex Autoconf macro for sophisticated +dnl ## checking the availability of third-party libraries and +dnl ## extending the build environment for correctly building +dnl ## against it. +dnl ## +dnl ## It especially supports the following particular features: +dnl ## - is aware of old-style [lib]-config style scripts +dnl ## - is aware of new-style pkg-config(1) [lib].pc configuration files +dnl ## - searches under standard sub-directories "include", "lib", etc. +dnl ## - searches under arbitrary sub-areas of a tree like ".libs", etc. +dnl ## - searches in standard system locations (implicitly) +dnl ## - supports searching for function in multiple libraries +dnl ## - supports searching for multiple headers +dnl ## - supports multiple search locations (fallbacks!) +dnl ## +dnl ## USAGE: +dnl ## - configure.in: +dnl ## RPM_CHECK_LIB( +dnl ## , -- [$1] e.g. GNU bzip2 +dnl ## , -- [$2] e.g. bzip2 +dnl ## , -- [$3] e.g. bz2 +dnl ## , -- [$4] e.g. BZ2_bzlibVersion +dnl ## , -- [$5] e.g. bzlib.h +dnl ## [, -- [$6] e.g. yes,external:internal:none +dnl ## [, -- [$7] e.g. lib/bzip2:include:src +dnl ## [, -- [$8] e.g. AC_DEFINE(USE_BZIP2, 1, [...]) +dnl ## -- [$9] e.g. AC_MSG_ERROR([...]) +dnl ## ]]]) +dnl ## +dnl ## - Makefile.in: +dnl ## top_srcdir = @top_srcdir@ +dnl ## srcdir = @srcdir@ +dnl ## WITH_ = @WITH_@ +dnl ## WITH__SUBDIR = @WITH__SUBDIR@ +dnl ## CPPFLAGS = @CPPFLAGS@ +dnl ## CFLAGS = @CFLAGS@ +dnl ## LDFLAGS = @LDFLAGS@ +dnl ## LIBS = @LIBS@ +dnl ## +dnl ## - CLI: +dnl ## $ ./configure \ +dnl ## --with-[=] +dnl ## [...] +dnl ## +dnl ## SYNTAX: +dnl ## ::= | +dnl ## ::= "," +dnl ## ::= "yes" | "no" +dnl ## ::= ":" +dnl ## | +dnl ## | "system" +dnl ## | "external" +dnl ## | "internal" +dnl ## | "none" +dnl ## ::= [...] /* valid arg for test(1) option "-d" */ +dnl ## +dnl ## + +dnl # public API macro +AC_DEFUN([RPM_CHECK_LIB], [ + dnl ## + dnl ## PROLOG + dnl ## + + dnl # parse into default enable mode and default locations + m4_define([__rcl_default_enable], [m4_substr([$6], 0, m4_index([$6], [,]))]) + m4_define([__rcl_default_locations], [m4_substr([$6], m4_eval(m4_index([$6], [,]) + 1))]) + + dnl # provide defaults + if test ".${with_$2+set}" != .set; then + dnl # initialize to default enable mode + with_$2="__rcl_default_enable" + AC_MSG_VERBOSE([++ assuming --with-$2=$with_$2]) + fi + if test ".${with_$2}" = .yes; then + dnl # map simple "--with-foo=yes" to an enabled default location path + with_$2="__rcl_default_locations" + AC_MSG_VERBOSE([++ mapping --with-$2=yes to --with-$2="$with_$2"]) + fi + + dnl ## + dnl ## HANDLING + dnl ## + + __rcl_result_hint="" + __rcl_location_$2="" + __rcl_location_last="" + m4_if([$7],,, [ + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_SUBDIR="" + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="" + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS="" + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LIBS="" + ]) + AC_ARG_WITH($2, + AS_HELP_STRING([--with-$2=ARG], [build with $1 library (__rcl_default_enable) (location path: "__rcl_default_locations")]), [dnl + if test ".${with_$2}" != .no; then + dnl # iterate over location path specification for searching purposes + __rcl_IFS="${IFS}"; IFS=":" + for __rcl_location in ${with_$2}; do + IFS="${__rcl_IFS}" + __rcl_location_last="${__rcl_location}" + AC_MSG_VERBOSE([++ searching location: ${__rcl_location}]) + if test ".${__rcl_location}" = .none; then + dnl # no operation in loop, ignore failure later, too. + AC_MSG_VERBOSE([-- no operation]) + m4_if([$7],,, [ elif test ".${__rcl_location}" = .internal; then + dnl # optional support for feature + m4_define([__rcl_subdir], + [m4_if(m4_index([$7], [:]), -1, [$7], + m4_substr([$7], 0, m4_index([$7], [:])))]) + if test -d ${srcdir}/__rcl_subdir; then + AC_MSG_VERBOSE([-- activating local sub-directory: __rcl_subdir]) + if test -f ${srcdir}/__rcl_subdir/configure; then + AC_CONFIG_SUBDIRS(__rcl_subdir) + fi + dnl # NOTICE: an internal copy of the third-party library is a tricky thing + dnl # because for the following two major reasons we cannot just extend + dnl # CPPFLAGS, LDFLAGS and LIBS in this case: + dnl # 1. at _this_ "configure" time at least the library file (libfoo.a) + dnl # is still not existing, so extending LIBS with "-lfoo" would cause + dnl # following Autoconf checks to fail. + dnl # 2. even deferring the extension of LIBS doesn't work, because although + dnl # this works around the problem under (1), it will fail if more than + dnl # one internal third-party library exists: LIBS would contains "-lfoo + dnl # -lbar" and if build "foo", "bar" usually still isn't built (or vice + dnl # versa). Hence, the linking of programs (tools, tests, etc) in "foo" + dnl # would fail. + dnl # 3. information in at least LDFLAGS and LIBS is usually passed-through + dnl # to applications via xxx-config(1) scripts or pkg-config(1) + dnl # specifications. As the path to the internal copy is usually just a + dnl # temporary path, this will break there, too. + dnl # So, internal copies of third-party libraries inherently have to be + dnl # handled explicitly by the build environment and for this we can only + dnl # provide the necessary information in dedicated per-library variables. + WITH_]m4_translit([$2],[a-z],[A-Z])[_SUBDIR="__rcl_subdir" + __rcl_location_$2=internal + AC_MSG_VERBOSE([++ post-adjustments for --with-$2 (${__rcl_location_$2})]) + __rcl_dirs_inc=`echo '$7' | sed -e 's/^[[^:]]*://' -e 's/:[[^:]]*[$]//'` + __rcl_dirs_lib=`echo '$7' | sed -e 's/^[[^:]]*:[[^:]]*://'` + __rcl_srcdir="\[$](top_srcdir)/\[$](WITH_[]m4_translit([$2],[a-z],[A-Z])[]_SUBDIR)" + __rcl_builddir="\[$](top_builddir)/\[$](WITH_[]m4_translit([$2],[a-z],[A-Z])[]_SUBDIR)" + __rcl_firstlib="m4_if(m4_index([$3], [ ]), -1, [$3], m4_substr([$3], 0, m4_index([$3], [ ])))" + if test ".${__rcl_dirs_inc}" != ".$7"; then + __rcl_IFS="${IFS}"; IFS="," + for __rcl_dir in ${__rcl_dirs_inc}; do + IFS="${__rcl_IFS}" + test ".${__rcl_dir}" = . && continue + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS: -I${__rcl_srcdir}/${__rcl_dir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS} -I${__rcl_srcdir}/${__rcl_dir}" + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS: -I${__rcl_builddir}/${__rcl_dir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS} -I${__rcl_builddir}/${__rcl_dir}" + done + IFS="${__rcl_IFS}" + fi + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS: -I${__rcl_srcdir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS} -I${__rcl_srcdir}" + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS: -I${__rcl_builddir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS} -I${__rcl_builddir}" + if test ".${__rcl_dirs_lib}" != ".$7"; then + __rcl_IFS="${IFS}"; IFS="," + for __rcl_dir in ${__rcl_dirs_lib}; do + IFS="${__rcl_IFS}" + test ".${__rcl_dir}" = . && continue + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS: -L${__rcl_builddir}/${__rcl_dir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS} -L${__rcl_builddir}/${__rcl_dir}" + done + IFS="${__rcl_IFS}" + fi + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS: -L${__rcl_builddir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS} -L${__rcl_builddir}" + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LIBS: -l${__rcl_firstlib}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LIBS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LIBS} -l${__rcl_firstlib}" + __rcl_result_hint="internal: sub-directory __rcl_subdir" + break + else + AC_MSG_VERBOSE([-- skipping not existing local sub-directory: __rcl_subdir]) + fi + ]) + elif test ".${__rcl_location}" = .external; then + dnl # detection of library in arbitrary external location + if (pkg-config --exists $2 || pkg-config --exists lib$2) 2>/dev/null; then + dnl # via pkg-config(1) script in PATH + m4_define([__rcl_query_pkgconfig], [ + __rcl_flags="" + for __rcl_query in $][2; do + if test ".$__rcl_flags" != .; then + __rcl_flags="${__rcl_flags} `($][1 $__rcl_query) 2>/dev/null`" + else + __rcl_flags="`($][1 $__rcl_query) 2>/dev/null`" + fi + done + if test ".${__rcl_flags}" != .; then + AC_MSG_VERBOSE(-- extending $][3: ${__rcl_flags}) + $][3="${$][3} ${__rcl_flags}" + fi + ]) + if (pkg-config --exists $2) 2>/dev/null; then + __rcl_query_pkgconfig([pkg-config $2], [--cflags-only-other], [CFLAGS]) + __rcl_query_pkgconfig([pkg-config $2], [--cflags-only-I], [CPPFLAGS]) + __rcl_query_pkgconfig([pkg-config $2], [--libs-only-L --libs-only-other], [LDFLAGS]) + __rcl_query_pkgconfig([pkg-config $2], [--libs-only-l], [LIBS]) + __rcl_result_hint="external: via pkg-config $2" + else + __rcl_query_pkgconfig([pkg-config lib$2], [--cflags-only-other], [CFLAGS]) + __rcl_query_pkgconfig([pkg-config lib$2], [--cflags-only-I], [CPPFLAGS]) + __rcl_query_pkgconfig([pkg-config lib$2], [--libs-only-L --libs-only-other], [LDFLAGS]) + __rcl_query_pkgconfig([pkg-config lib$2], [--libs-only-l], [LIBS]) + __rcl_result_hint="external: via pkg-config lib$2" + fi + break + elif test ".`($2-config --version; lib$2-config --version) 2>/dev/null`" != .; then + dnl # via config script in PATH + m4_define([__rcl_query_config], [ + __rcl_flags="`($][1 $][2) 2>/dev/null`" + if test ".${__rcl_flags}" != .; then + AC_MSG_VERBOSE(-- extending $][3: ${__rcl_flags}) + $][3="${$][3} ${__rcl_flags}" + fi + ]) + if test ".`($2-config --version) 2>/dev/null`" != .; then + __rcl_query_config([$2-config], [--cppflags], [CPPFLAGS]) + __rcl_query_config([$2-config], [--cflags], [CFLAGS]) + __rcl_query_config([$2-config], [--ldflags], [LDFLAGS]) + __rcl_query_config([$2-config], [--libs], [LIBS]) + __rcl_result_hint="external: via $2-config" + else + __rcl_query_config([lib$2-config], [--cppflags], [CPPFLAGS]) + __rcl_query_config([lib$2-config], [--cflags], [CFLAGS]) + __rcl_query_config([lib$2-config], [--ldflags], [LDFLAGS]) + __rcl_query_config([lib$2-config], [--libs], [LIBS]) + __rcl_result_hint="external: via lib$2-config" + fi + break + elif test ".${__rcl_found}" = .no; then + dnl # via implicit flags attribution of previous checks or + dnl # in standard system locations (usually /usr/include and /usr/lib) + __rcl_found_hdr=no + __rcl_found_lib=no + AC_PREPROC_IFELSE([AC_LANG_SOURCE([@%:@include <$5>])], [ __rcl_found_hdr=yes ]) + m4_foreach_w([__rcl_lib], [$3], [ + __rcl_safe_LIBS="${LIBS}" + LIBS="-l[]m4_defn([__rcl_lib]) ${LIBS}" + AC_LINK_IFELSE([AC_LANG_CALL([], [$4])], [ __rcl_found_lib=yes ]) + LIBS="${__rcl_safe_LIBS}" + ]) + if test ".${__rcl_found_hdr}:${__rcl_found_lib}" = ".yes:yes"; then + __rcl_result_hint="external: implicit or default location" + break + fi + fi + elif test ".${__rcl_location}" = .system; then + dnl # detection of library in external locations controlled + dnl # by explicit build flags or in standard system locations + dnl # (usually /usr/include and /usr/lib) + __rcl_found_hdr=no + __rcl_found_lib=no + AC_PREPROC_IFELSE([AC_LANG_SOURCE([@%:@include <$5>])], [ __rcl_found_hdr=yes ]) + m4_foreach_w([__rcl_lib], [$3], [ + __rcl_safe_LIBS="${LIBS}" + LIBS="-l[]m4_defn([__rcl_lib]) ${LIBS}" + AC_LINK_IFELSE([AC_LANG_CALL([], [$4])], [ __rcl_found_lib=yes ]) + LIBS="${__rcl_safe_LIBS}" + ]) + if test ".${__rcl_found_hdr}:${__rcl_found_lib}" = ".yes:yes"; then + __rcl_result_hint="system: explicitly controlled or system location" + break + fi + elif test -d "${__rcl_location}"; then + dnl # detection of library in particular external location + __rcl_found=no + dnl # via config script + for __rcl_dir in ${__rcl_location}/bin ${__rcl_location}; do + if test -f "${__rcl_dir}/$2-config" && test ! -f "${__rcl_dir}/$2-config.in"; then + if test ".`(${__rcl_dir}/$2-config --version) 2>/dev/null`" != .; then + __rcl_query_config([${__rcl_dir}/$2-config], [--cppflags], [CPPFLAGS]) + __rcl_query_config([${__rcl_dir}/$2-config], [--cflags], [CFLAGS]) + __rcl_query_config([${__rcl_dir}/$2-config], [--ldflags], [LDFLAGS]) + __rcl_query_config([${__rcl_dir}/$2-config], [--libs], [LIBS]) + __rcl_result_hint="external: via ${__rcl_dir}/$2-config" + __rcl_found=yes + break + fi + elif test -f "${__rcl_dir}/lib$2-config" && test ! -f "${__rcl_dir}/lib$2-config.in"; then + if test ".`(${__rcl_dir}/lib$2-config --version) 2>/dev/null`" != .; then + __rcl_query_config([${__rcl_dir}/lib$2-config], [--cppflags], [CPPFLAGS]) + __rcl_query_config([${__rcl_dir}/lib$2-config], [--cflags], [CFLAGS]) + __rcl_query_config([${__rcl_dir}/lib$2-config], [--ldflags], [LDFLAGS]) + __rcl_query_config([${__rcl_dir}/lib$2-config], [--libs], [LIBS]) + __rcl_result_hint="external: via ${__rcl_dir}/lib$2-config" + __rcl_found=yes + break + fi + fi + done + dnl # via pkg-config(1) script + if test ".${__rcl_found}" = .no; then + for __rcl_dir in ${__rcl_location}/bin ${__rcl_location}; do + if test -f "${__rcl_dir}/pkg-config"; then + if (${__rcl_dir}/pkg-config --exists $2) 2>/dev/null; then + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config $2], [--cflags-only-other], [CFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config $2], [--cflags-only-I], [CPPFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config $2], [--libs-only-L --libs-only-other], [LDFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config $2], [--libs-only-l], [LIBS]) + __rcl_result_hint="external: via ${__rcl_dir}/pkg-config $2" + __rcl_found=yes + break + elif (${__rcl_dir}/pkg-config --exists lib$2) 2>/dev/null; then + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config lib$2], [--cflags-only-other], [CFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config lib$2], [--cflags-only-I], [CPPFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config lib$2], [--libs-only-L --libs-only-other], [LDFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config lib$2], [--libs-only-l], [LIBS]) + __rcl_result_hint="external: via ${__rcl_dir}/pkg-config lib$2" + __rcl_found=yes + break + fi + fi + done + fi + dnl # in standard sub-areas + if test ".${__rcl_found}" = .no; then + for __rcl_dir in ${__rcl_location}/include/$2 ${__rcl_location}/include ${__rcl_location}; do + if test -f "${__rcl_dir}/$5"; then + if test ".${__rcl_dir}" != "./usr/include"; then + AC_MSG_VERBOSE([-- extending CPPFLAGS: -I${__rcl_dir}]) + CPPFLAGS="${CPPFLAGS} -I${__rcl_dir}" + fi + __rcl_found=yes + break + fi + done + if test ".${__rcl_found}" = .yes; then + __rcl_found=no + for __rcl_dir in ${__rcl_location}/lib/$2 ${__rcl_location}/lib ${__rcl_location}; do + m4_foreach_w([__rcl_lib], [$3], [ + if test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).la" && \ + test -d "${__rcl_dir}/.libs"; then + if test ".${__rcl_dir}" != "./usr/lib"; then + AC_MSG_VERBOSE([-- extending LDFLAGS: -L${__rcl_dir}]) + AC_MSG_VERBOSE([-- extending LDFLAGS: -L${__rcl_dir}/.libs]) + LDFLAGS="${LDFLAGS} -L${__rcl_dir} -L${__rcl_dir}/.libs" + fi + __rcl_found=yes + break + fi + if test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).a" || \ + test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).so" || \ + test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).sl" || \ + test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).dylib"; then + if test ".${__rcl_dir}" != "./usr/lib"; then + AC_MSG_VERBOSE([-- extending LDFLAGS: -L${__rcl_dir}]) + LDFLAGS="${LDFLAGS} -L${__rcl_dir}" + fi + __rcl_found=yes + break + fi + ]) + done + fi + if test ".${__rcl_found}" = .yes; then + __rcl_result_hint="external: directory ${__rcl_location}" + fi + fi + dnl # in any sub-area + if test ".${__rcl_found}" = .no; then + for __rcl_file in _ `find ${__rcl_location} -name "$5" -type f -print 2>/dev/null`; do + test .${__rcl_file} = ._ && continue + __rcl_dir=`echo ${__rcl_file} | sed -e 's;[[^/]]*[$];;' -e 's;\(.\)/[$];\1;'` + if test ".${__rcl_dir}" != "./usr/include"; then + AC_MSG_VERBOSE([-- extending CPPFLAGS: -I${__rcl_dir}]) + CPPFLAGS="${CPPFLAGS} -I${__rcl_dir}" + fi + __rcl_found=yes + break + done + if test ".${__rcl_found}" = .yes; then + __rcl_found=no + m4_foreach_w([__rcl_lib], [$3], [ + for __rcl_file in _ `find ${__rcl_location} -name "lib[]m4_defn([__rcl_lib]).*" -type f -print 2>/dev/null | \ + egrep '\.(a|so|sl|dylib)$'`; do + test .${__rcl_file} = ._ && continue + __rcl_dir=`echo ${__rcl_file} | sed -e 's;[[^/]]*[$];;' -e 's;\(.\)/[$];\1;'` + if test ".${__rcl_dir}" != "./usr/lib"; then + AC_MSG_VERBOSE([-- extending LDFLAGS: -L${__rcl_dir}]) + LDFLAGS="${LDFLAGS} -L${__rcl_dir}" + fi + __rcl_found=yes + break + done + ]) + fi + if test ".${__rcl_found}" = .yes; then + __rcl_result_hint="external: tree ${__rcl_location}" + fi + fi + if test ".${__rcl_found}" = .yes; then + break + fi + else + AC_MSG_ERROR([Unknown location specification $__rcl_location]) + fi + done + IFS="${__rcl_IFS}" + + dnl # check for actual availability of library + m4_if([$7],,, [ if test ".${__rcl_location_last}" = .internal; then + dnl # special case: still not existing "internal" library + dnl # cannot be checked (and usually has not to be checked anyway) + with_$2=yes + if test ".${__rcl_location_$2}" != .internal; then + AC_MSG_ERROR([unable to find internal $1 library]) + fi + else ]) + dnl # regular case: use standard Autoconf facilities + dnl # and try to make sure both header and library is found + __rcl_found=yes + dnl # check for C header (in set of optionally multiple possibilities) + AC_CHECK_HEADERS([$5], [], [ __rcl_found=no ]) + dnl # check for C library (in set of optionally multiple possibilities) + dnl # stop on first found library + __rcl_found_lib=no + while true; do + m4_foreach_w([__rcl_lib], [$3], [ + AC_CHECK_LIB(m4_defn([__rcl_lib]), [$4]) + dnl # manually check for success (do not use third argument to AC_CHECK_LIB + dnl # here as this would no longer set the LIBS variable (the default action) + test ".${m4_translit(ac_cv_lib_[]m4_defn([__rcl_lib])_$4,[.-,],[___])}" = .yes && __rcl_found_lib=yes && break + ]) + break + done + test ".${__rcl_found_lib}" = .no && __rcl_found="no" + dnl # determine final results + with_$2=${__rcl_found} + m4_if([$7],,, [ fi ]) + if test ".${with_$2}" = .yes && test ".${__rcl_result_hint}" = .; then + dnl # was not found explicitly via searching above, but + dnl # implicitly in standard location or via extended + dnl # flags from previous searches + __rcl_result_hint="external: implicitly" + fi + fi + ]) + + dnl ## + dnl ## EPILOG + dnl ## + + dnl # provide results + if test ".${with_$2}" = .yes; then + AC_DEFINE([WITH_]m4_translit([$2],[a-z],[A-Z]), 1, [Define as 1 if building with $1 library]) + fi + [WITH_]m4_translit([$2],[a-z],[A-Z])="${with_$2}" + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])) + m4_if([$7],,, [ + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])[_SUBDIR]) + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])[_CPPFLAGS]) + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])[_LDFLAGS]) + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])[_LIBS]) + ]) + + dnl # report results + AC_MSG_CHECKING(whether to build with $1 library) + __rcl_msg="${with_$2}" + if test ".${with_$2}" = .yes && test ".${__rcl_result_hint}" != .; then + __rcl_msg="${__rcl_msg} (${__rcl_result_hint})" + fi + AC_MSG_RESULT([${__rcl_msg}]) + + dnl # perform actions + RPM_CHECK_LIB_LOCATION="${__rcl_location_last}" + if test ".${with_$2}" = .yes; then + AC_DEFINE([WITH_]m4_translit([$2],[a-z],[A-Z]), 1, [Define as 1 if building with $1 library]) + dnl # support optional + AC_MSG_VERBOSE([++ executing success action]) + m4_if([$8],, :, [$8]) + else + dnl # support optional + AC_MSG_VERBOSE([++ executing failure action]) + m4_if([$9],, [ + if test ".${RPM_CHECK_LIB_LOCATION}" != . && \ + test ".${RPM_CHECK_LIB_LOCATION}" != .none; then + AC_MSG_ERROR([unable to find usable $1 library]) + fi + ], [$9]) + fi + ${as_unset} RPM_CHECK_LIB_LOCATION +]) diff --git a/m4/rpm_check_lib.m4 b/m4/rpm_check_lib.m4 new file mode 100644 index 0000000000..cdc921b626 --- /dev/null +++ b/m4/rpm_check_lib.m4 @@ -0,0 +1,494 @@ +dnl ## +dnl ## NAME: +dnl ## RPM_CHECK_LIB -- Check for third-party libraries +dnl ## +dnl ## COPYRIGHT +dnl ## Copyright (c) 2007 Ralf S. Engelschall +dnl ## +dnl ## DESCRIPTION: +dnl ## This is a rather complex Autoconf macro for sophisticated +dnl ## checking the availability of third-party libraries and +dnl ## extending the build environment for correctly building +dnl ## against it. +dnl ## +dnl ## It especially supports the following particular features: +dnl ## - is aware of old-style [lib]-config style scripts +dnl ## - is aware of new-style pkg-config(1) [lib].pc configuration files +dnl ## - searches under standard sub-directories "include", "lib", etc. +dnl ## - searches under arbitrary sub-areas of a tree like ".libs", etc. +dnl ## - searches in standard system locations (implicitly) +dnl ## - supports searching for function in multiple libraries +dnl ## - supports searching for multiple headers +dnl ## - supports multiple search locations (fallbacks!) +dnl ## +dnl ## USAGE: +dnl ## - configure.in: +dnl ## RPM_CHECK_LIB( +dnl ## , -- [$1] e.g. GNU bzip2 +dnl ## , -- [$2] e.g. bzip2 +dnl ## , -- [$3] e.g. bz2 +dnl ## , -- [$4] e.g. BZ2_bzlibVersion +dnl ## , -- [$5] e.g. bzlib.h +dnl ## [, -- [$6] e.g. yes,external:internal:none +dnl ## [, -- [$7] e.g. lib/bzip2:include:src +dnl ## [, -- [$8] e.g. AC_DEFINE(USE_BZIP2, 1, [...]) +dnl ## -- [$9] e.g. AC_MSG_ERROR([...]) +dnl ## ]]]) +dnl ## +dnl ## - Makefile.in: +dnl ## top_srcdir = @top_srcdir@ +dnl ## srcdir = @srcdir@ +dnl ## WITH_ = @WITH_@ +dnl ## WITH__SUBDIR = @WITH__SUBDIR@ +dnl ## CPPFLAGS = @CPPFLAGS@ +dnl ## CFLAGS = @CFLAGS@ +dnl ## LDFLAGS = @LDFLAGS@ +dnl ## LIBS = @LIBS@ +dnl ## +dnl ## - CLI: +dnl ## $ ./configure \ +dnl ## --with-[=] +dnl ## [...] +dnl ## +dnl ## SYNTAX: +dnl ## ::= | +dnl ## ::= "," +dnl ## ::= "yes" | "no" +dnl ## ::= ":" +dnl ## | +dnl ## | "system" +dnl ## | "external" +dnl ## | "internal" +dnl ## | "none" +dnl ## ::= [...] /* valid arg for test(1) option "-d" */ +dnl ## +dnl ## + +dnl # public API macro +AC_DEFUN([RPM_CHECK_LIB], [ + dnl ## + dnl ## PROLOG + dnl ## + + dnl # parse into default enable mode and default locations + m4_define([__rcl_default_enable], [m4_substr([$6], 0, m4_index([$6], [,]))]) + m4_define([__rcl_default_locations], [m4_substr([$6], m4_eval(m4_index([$6], [,]) + 1))]) + + dnl # provide defaults + if test ".${with_$2+set}" != .set; then + dnl # initialize to default enable mode + with_$2="__rcl_default_enable" + AC_MSG_VERBOSE([++ assuming --with-$2=$with_$2]) + fi + if test ".${with_$2}" = .yes; then + dnl # map simple "--with-foo=yes" to an enabled default location path + with_$2="__rcl_default_locations" + AC_MSG_VERBOSE([++ mapping --with-$2=yes to --with-$2="$with_$2"]) + fi + + dnl ## + dnl ## HANDLING + dnl ## + + __rcl_result_hint="" + __rcl_location_$2="" + __rcl_location_last="" + m4_if([$7],,, [ + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_SUBDIR="" + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="" + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS="" + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LIBS="" + ]) + AC_ARG_WITH($2, + AS_HELP_STRING([--with-$2=ARG], [build with $1 library (__rcl_default_enable) (location path: "__rcl_default_locations")]), [dnl + if test ".${with_$2}" != .no; then + dnl # iterate over location path specification for searching purposes + __rcl_IFS="${IFS}"; IFS=":" + for __rcl_location in ${with_$2}; do + IFS="${__rcl_IFS}" + __rcl_location_last="${__rcl_location}" + AC_MSG_VERBOSE([++ searching location: ${__rcl_location}]) + if test ".${__rcl_location}" = .none; then + dnl # no operation in loop, ignore failure later, too. + AC_MSG_VERBOSE([-- no operation]) + m4_if([$7],,, [ elif test ".${__rcl_location}" = .internal; then + dnl # optional support for feature + m4_define([__rcl_subdir], + [m4_if(m4_index([$7], [:]), -1, [$7], + m4_substr([$7], 0, m4_index([$7], [:])))]) + if test -d ${srcdir}/__rcl_subdir; then + AC_MSG_VERBOSE([-- activating local sub-directory: __rcl_subdir]) + if test -f ${srcdir}/__rcl_subdir/configure; then + AC_CONFIG_SUBDIRS(__rcl_subdir) + fi + dnl # NOTICE: an internal copy of the third-party library is a tricky thing + dnl # because for the following two major reasons we cannot just extend + dnl # CPPFLAGS, LDFLAGS and LIBS in this case: + dnl # 1. at _this_ "configure" time at least the library file (libfoo.a) + dnl # is still not existing, so extending LIBS with "-lfoo" would cause + dnl # following Autoconf checks to fail. + dnl # 2. even deferring the extension of LIBS doesn't work, because although + dnl # this works around the problem under (1), it will fail if more than + dnl # one internal third-party library exists: LIBS would contains "-lfoo + dnl # -lbar" and if build "foo", "bar" usually still isn't built (or vice + dnl # versa). Hence, the linking of programs (tools, tests, etc) in "foo" + dnl # would fail. + dnl # 3. information in at least LDFLAGS and LIBS is usually passed-through + dnl # to applications via xxx-config(1) scripts or pkg-config(1) + dnl # specifications. As the path to the internal copy is usually just a + dnl # temporary path, this will break there, too. + dnl # So, internal copies of third-party libraries inherently have to be + dnl # handled explicitly by the build environment and for this we can only + dnl # provide the necessary information in dedicated per-library variables. + WITH_]m4_translit([$2],[a-z],[A-Z])[_SUBDIR="__rcl_subdir" + __rcl_location_$2=internal + AC_MSG_VERBOSE([++ post-adjustments for --with-$2 (${__rcl_location_$2})]) + __rcl_dirs_inc=`echo '$7' | sed -e 's/^[[^:]]*://' -e 's/:[[^:]]*[$]//'` + __rcl_dirs_lib=`echo '$7' | sed -e 's/^[[^:]]*:[[^:]]*://'` + __rcl_srcdir="\[$](top_srcdir)/\[$](WITH_[]m4_translit([$2],[a-z],[A-Z])[]_SUBDIR)" + __rcl_builddir="\[$](top_builddir)/\[$](WITH_[]m4_translit([$2],[a-z],[A-Z])[]_SUBDIR)" + __rcl_firstlib="m4_if(m4_index([$3], [ ]), -1, [$3], m4_substr([$3], 0, m4_index([$3], [ ])))" + if test ".${__rcl_dirs_inc}" != ".$7"; then + __rcl_IFS="${IFS}"; IFS="," + for __rcl_dir in ${__rcl_dirs_inc}; do + IFS="${__rcl_IFS}" + test ".${__rcl_dir}" = . && continue + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS: -I${__rcl_srcdir}/${__rcl_dir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS} -I${__rcl_srcdir}/${__rcl_dir}" + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS: -I${__rcl_builddir}/${__rcl_dir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS} -I${__rcl_builddir}/${__rcl_dir}" + done + IFS="${__rcl_IFS}" + fi + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS: -I${__rcl_srcdir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS} -I${__rcl_srcdir}" + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS: -I${__rcl_builddir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_CPPFLAGS} -I${__rcl_builddir}" + if test ".${__rcl_dirs_lib}" != ".$7"; then + __rcl_IFS="${IFS}"; IFS="," + for __rcl_dir in ${__rcl_dirs_lib}; do + IFS="${__rcl_IFS}" + test ".${__rcl_dir}" = . && continue + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS: -L${__rcl_builddir}/${__rcl_dir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS} -L${__rcl_builddir}/${__rcl_dir}" + done + IFS="${__rcl_IFS}" + fi + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS: -L${__rcl_builddir}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LDFLAGS} -L${__rcl_builddir}" + AC_MSG_VERBOSE([-- extending WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LIBS: -l${__rcl_firstlib}]) + WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LIBS="${WITH_[]m4_translit([$2],[a-z],[A-Z])[]_LIBS} -l${__rcl_firstlib}" + __rcl_result_hint="internal: sub-directory __rcl_subdir" + break + else + AC_MSG_VERBOSE([-- skipping not existing local sub-directory: __rcl_subdir]) + fi + ]) + elif test ".${__rcl_location}" = .external; then + dnl # detection of library in arbitrary external location + if (pkg-config --exists $2 || pkg-config --exists lib$2) 2>/dev/null; then + dnl # via pkg-config(1) script in PATH + m4_define([__rcl_query_pkgconfig], [ + __rcl_flags="" + for __rcl_query in $][2; do + if test ".$__rcl_flags" != .; then + __rcl_flags="${__rcl_flags} `($][1 $__rcl_query) 2>/dev/null`" + else + __rcl_flags="`($][1 $__rcl_query) 2>/dev/null`" + fi + done + if test ".${__rcl_flags}" != .; then + AC_MSG_VERBOSE(-- extending $][3: ${__rcl_flags}) + $][3="${$][3} ${__rcl_flags}" + fi + ]) + if (pkg-config --exists $2) 2>/dev/null; then + __rcl_query_pkgconfig([pkg-config $2], [--cflags-only-other], [CFLAGS]) + __rcl_query_pkgconfig([pkg-config $2], [--cflags-only-I], [CPPFLAGS]) + __rcl_query_pkgconfig([pkg-config $2], [--libs-only-L --libs-only-other], [LDFLAGS]) + __rcl_query_pkgconfig([pkg-config $2], [--libs-only-l], [LIBS]) + __rcl_result_hint="external: via pkg-config $2" + else + __rcl_query_pkgconfig([pkg-config lib$2], [--cflags-only-other], [CFLAGS]) + __rcl_query_pkgconfig([pkg-config lib$2], [--cflags-only-I], [CPPFLAGS]) + __rcl_query_pkgconfig([pkg-config lib$2], [--libs-only-L --libs-only-other], [LDFLAGS]) + __rcl_query_pkgconfig([pkg-config lib$2], [--libs-only-l], [LIBS]) + __rcl_result_hint="external: via pkg-config lib$2" + fi + break + elif test ".`($2-config --version; lib$2-config --version) 2>/dev/null`" != .; then + dnl # via config script in PATH + m4_define([__rcl_query_config], [ + __rcl_flags="`($][1 $][2) 2>/dev/null`" + if test ".${__rcl_flags}" != .; then + AC_MSG_VERBOSE(-- extending $][3: ${__rcl_flags}) + $][3="${$][3} ${__rcl_flags}" + fi + ]) + if test ".`($2-config --version) 2>/dev/null`" != .; then + __rcl_query_config([$2-config], [--cppflags], [CPPFLAGS]) + __rcl_query_config([$2-config], [--cflags], [CFLAGS]) + __rcl_query_config([$2-config], [--ldflags], [LDFLAGS]) + __rcl_query_config([$2-config], [--libs], [LIBS]) + __rcl_result_hint="external: via $2-config" + else + __rcl_query_config([lib$2-config], [--cppflags], [CPPFLAGS]) + __rcl_query_config([lib$2-config], [--cflags], [CFLAGS]) + __rcl_query_config([lib$2-config], [--ldflags], [LDFLAGS]) + __rcl_query_config([lib$2-config], [--libs], [LIBS]) + __rcl_result_hint="external: via lib$2-config" + fi + break + elif test ".${__rcl_found}" = .no; then + dnl # via implicit flags attribution of previous checks or + dnl # in standard system locations (usually /usr/include and /usr/lib) + __rcl_found_hdr=no + __rcl_found_lib=no + AC_PREPROC_IFELSE([AC_LANG_SOURCE([@%:@include <$5>])], [ __rcl_found_hdr=yes ]) + m4_foreach_w([__rcl_lib], [$3], [ + __rcl_safe_LIBS="${LIBS}" + LIBS="-l[]m4_defn([__rcl_lib]) ${LIBS}" + AC_LINK_IFELSE([AC_LANG_CALL([], [$4])], [ __rcl_found_lib=yes ]) + LIBS="${__rcl_safe_LIBS}" + ]) + if test ".${__rcl_found_hdr}:${__rcl_found_lib}" = ".yes:yes"; then + __rcl_result_hint="external: implicit or default location" + break + fi + fi + elif test ".${__rcl_location}" = .system; then + dnl # detection of library in external locations controlled + dnl # by explicit build flags or in standard system locations + dnl # (usually /usr/include and /usr/lib) + __rcl_found_hdr=no + __rcl_found_lib=no + AC_PREPROC_IFELSE([AC_LANG_SOURCE([@%:@include <$5>])], [ __rcl_found_hdr=yes ]) + m4_foreach_w([__rcl_lib], [$3], [ + __rcl_safe_LIBS="${LIBS}" + LIBS="-l[]m4_defn([__rcl_lib]) ${LIBS}" + AC_LINK_IFELSE([AC_LANG_CALL([], [$4])], [ __rcl_found_lib=yes ]) + LIBS="${__rcl_safe_LIBS}" + ]) + if test ".${__rcl_found_hdr}:${__rcl_found_lib}" = ".yes:yes"; then + __rcl_result_hint="system: explicitly controlled or system location" + break + fi + elif test -d "${__rcl_location}"; then + dnl # detection of library in particular external location + __rcl_found=no + dnl # via config script + for __rcl_dir in ${__rcl_location}/bin ${__rcl_location}; do + if test -f "${__rcl_dir}/$2-config" && test ! -f "${__rcl_dir}/$2-config.in"; then + if test ".`(${__rcl_dir}/$2-config --version) 2>/dev/null`" != .; then + __rcl_query_config([${__rcl_dir}/$2-config], [--cppflags], [CPPFLAGS]) + __rcl_query_config([${__rcl_dir}/$2-config], [--cflags], [CFLAGS]) + __rcl_query_config([${__rcl_dir}/$2-config], [--ldflags], [LDFLAGS]) + __rcl_query_config([${__rcl_dir}/$2-config], [--libs], [LIBS]) + __rcl_result_hint="external: via ${__rcl_dir}/$2-config" + __rcl_found=yes + break + fi + elif test -f "${__rcl_dir}/lib$2-config" && test ! -f "${__rcl_dir}/lib$2-config.in"; then + if test ".`(${__rcl_dir}/lib$2-config --version) 2>/dev/null`" != .; then + __rcl_query_config([${__rcl_dir}/lib$2-config], [--cppflags], [CPPFLAGS]) + __rcl_query_config([${__rcl_dir}/lib$2-config], [--cflags], [CFLAGS]) + __rcl_query_config([${__rcl_dir}/lib$2-config], [--ldflags], [LDFLAGS]) + __rcl_query_config([${__rcl_dir}/lib$2-config], [--libs], [LIBS]) + __rcl_result_hint="external: via ${__rcl_dir}/lib$2-config" + __rcl_found=yes + break + fi + fi + done + dnl # via pkg-config(1) script + if test ".${__rcl_found}" = .no; then + for __rcl_dir in ${__rcl_location}/bin ${__rcl_location}; do + if test -f "${__rcl_dir}/pkg-config"; then + if (${__rcl_dir}/pkg-config --exists $2) 2>/dev/null; then + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config $2], [--cflags-only-other], [CFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config $2], [--cflags-only-I], [CPPFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config $2], [--libs-only-L --libs-only-other], [LDFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config $2], [--libs-only-l], [LIBS]) + __rcl_result_hint="external: via ${__rcl_dir}/pkg-config $2" + __rcl_found=yes + break + elif (${__rcl_dir}/pkg-config --exists lib$2) 2>/dev/null; then + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config lib$2], [--cflags-only-other], [CFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config lib$2], [--cflags-only-I], [CPPFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config lib$2], [--libs-only-L --libs-only-other], [LDFLAGS]) + __rcl_query_pkgconfig([${__rcl_dir}/pkg-config lib$2], [--libs-only-l], [LIBS]) + __rcl_result_hint="external: via ${__rcl_dir}/pkg-config lib$2" + __rcl_found=yes + break + fi + fi + done + fi + dnl # in standard sub-areas + if test ".${__rcl_found}" = .no; then + for __rcl_dir in ${__rcl_location}/include/$2 ${__rcl_location}/include ${__rcl_location}; do + if test -f "${__rcl_dir}/$5"; then + if test ".${__rcl_dir}" != "./usr/include"; then + AC_MSG_VERBOSE([-- extending CPPFLAGS: -I${__rcl_dir}]) + CPPFLAGS="${CPPFLAGS} -I${__rcl_dir}" + fi + __rcl_found=yes + break + fi + done + if test ".${__rcl_found}" = .yes; then + __rcl_found=no + for __rcl_dir in ${__rcl_location}/lib/$2 ${__rcl_location}/lib ${__rcl_location}; do + m4_foreach_w([__rcl_lib], [$3], [ + if test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).la" && \ + test -d "${__rcl_dir}/.libs"; then + if test ".${__rcl_dir}" != "./usr/lib"; then + AC_MSG_VERBOSE([-- extending LDFLAGS: -L${__rcl_dir}]) + AC_MSG_VERBOSE([-- extending LDFLAGS: -L${__rcl_dir}/.libs]) + LDFLAGS="${LDFLAGS} -L${__rcl_dir} -L${__rcl_dir}/.libs" + fi + __rcl_found=yes + break + fi + if test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).a" || \ + test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).so" || \ + test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).sl" || \ + test -f "${__rcl_dir}/lib[]m4_defn([__rcl_lib]).dylib"; then + if test ".${__rcl_dir}" != "./usr/lib"; then + AC_MSG_VERBOSE([-- extending LDFLAGS: -L${__rcl_dir}]) + LDFLAGS="${LDFLAGS} -L${__rcl_dir}" + fi + __rcl_found=yes + break + fi + ]) + done + fi + if test ".${__rcl_found}" = .yes; then + __rcl_result_hint="external: directory ${__rcl_location}" + fi + fi + dnl # in any sub-area + if test ".${__rcl_found}" = .no; then + for __rcl_file in _ `find ${__rcl_location} -name "$5" -type f -print 2>/dev/null`; do + test .${__rcl_file} = ._ && continue + __rcl_dir=`echo ${__rcl_file} | sed -e 's;[[^/]]*[$];;' -e 's;\(.\)/[$];\1;'` + if test ".${__rcl_dir}" != "./usr/include"; then + AC_MSG_VERBOSE([-- extending CPPFLAGS: -I${__rcl_dir}]) + CPPFLAGS="${CPPFLAGS} -I${__rcl_dir}" + fi + __rcl_found=yes + break + done + if test ".${__rcl_found}" = .yes; then + __rcl_found=no + m4_foreach_w([__rcl_lib], [$3], [ + for __rcl_file in _ `find ${__rcl_location} -name "lib[]m4_defn([__rcl_lib]).*" -type f -print 2>/dev/null | \ + egrep '\.(a|so|sl|dylib)$'`; do + test .${__rcl_file} = ._ && continue + __rcl_dir=`echo ${__rcl_file} | sed -e 's;[[^/]]*[$];;' -e 's;\(.\)/[$];\1;'` + if test ".${__rcl_dir}" != "./usr/lib"; then + AC_MSG_VERBOSE([-- extending LDFLAGS: -L${__rcl_dir}]) + LDFLAGS="${LDFLAGS} -L${__rcl_dir}" + fi + __rcl_found=yes + break + done + ]) + fi + if test ".${__rcl_found}" = .yes; then + __rcl_result_hint="external: tree ${__rcl_location}" + fi + fi + if test ".${__rcl_found}" = .yes; then + break + fi + else + AC_MSG_ERROR([Unknown location specification $__rcl_location]) + fi + done + IFS="${__rcl_IFS}" + + dnl # check for actual availability of library + m4_if([$7],,, [ if test ".${__rcl_location_last}" = .internal; then + dnl # special case: still not existing "internal" library + dnl # cannot be checked (and usually has not to be checked anyway) + with_$2=yes + if test ".${__rcl_location_$2}" != .internal; then + AC_MSG_ERROR([unable to find internal $1 library]) + fi + else ]) + dnl # regular case: use standard Autoconf facilities + dnl # and try to make sure both header and library is found + __rcl_found=yes + dnl # check for C header (in set of optionally multiple possibilities) + AC_CHECK_HEADERS([$5], [], [ __rcl_found=no ]) + dnl # check for C library (in set of optionally multiple possibilities) + dnl # stop on first found library + __rcl_found_lib=no + while true; do + m4_foreach_w([__rcl_lib], [$3], [ + AC_CHECK_LIB(m4_defn([__rcl_lib]), [$4]) + dnl # manually check for success (do not use third argument to AC_CHECK_LIB + dnl # here as this would no longer set the LIBS variable (the default action) + test ".${m4_translit(ac_cv_lib_[]m4_defn([__rcl_lib])_$4,[.-,],[___])}" = .yes && __rcl_found_lib=yes && break + ]) + break + done + test ".${__rcl_found_lib}" = .no && __rcl_found="no" + dnl # determine final results + with_$2=${__rcl_found} + m4_if([$7],,, [ fi ]) + if test ".${with_$2}" = .yes && test ".${__rcl_result_hint}" = .; then + dnl # was not found explicitly via searching above, but + dnl # implicitly in standard location or via extended + dnl # flags from previous searches + __rcl_result_hint="external: implicitly" + fi + fi + ]) + + dnl ## + dnl ## EPILOG + dnl ## + + dnl # provide results + if test ".${with_$2}" = .yes; then + AC_DEFINE([WITH_]m4_translit([$2],[a-z],[A-Z]), 1, [Define as 1 if building with $1 library]) + fi + [WITH_]m4_translit([$2],[a-z],[A-Z])="${with_$2}" + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])) + m4_if([$7],,, [ + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])[_SUBDIR]) + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])[_CPPFLAGS]) + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])[_LDFLAGS]) + AC_SUBST([WITH_]m4_translit([$2],[a-z],[A-Z])[_LIBS]) + ]) + + dnl # report results + AC_MSG_CHECKING(whether to build with $1 library) + __rcl_msg="${with_$2}" + if test ".${with_$2}" = .yes && test ".${__rcl_result_hint}" != .; then + __rcl_msg="${__rcl_msg} (${__rcl_result_hint})" + fi + AC_MSG_RESULT([${__rcl_msg}]) + + dnl # perform actions + RPM_CHECK_LIB_LOCATION="${__rcl_location_last}" + if test ".${with_$2}" = .yes; then + AC_DEFINE([WITH_]m4_translit([$2],[a-z],[A-Z]), 1, [Define as 1 if building with $1 library]) + dnl # support optional + AC_MSG_VERBOSE([++ executing success action]) + m4_if([$8],, :, [$8]) + else + dnl # support optional + AC_MSG_VERBOSE([++ executing failure action]) + m4_if([$9],, [ + if test ".${RPM_CHECK_LIB_LOCATION}" != . && \ + test ".${RPM_CHECK_LIB_LOCATION}" != .none; then + AC_MSG_ERROR([unable to find usable $1 library]) + fi + ], [$9]) + fi + ${as_unset} RPM_CHECK_LIB_LOCATION +]) diff --git a/macros.in b/macros.in index 851822891d..1af2b91cf9 100644 --- a/macros.in +++ b/macros.in @@ -59,6 +59,7 @@ %__ssh @__SSH@ %__tar @__TAR@ %__unzip @__UNZIP@ +%__zstd @__ZSTD@ %__gem @__GEM@ %__git @__GIT@ %__hg @__HG@ diff --git a/rpmio/macro.c b/rpmio/macro.c index 0a7e02cf7d..50344c4300 100644 --- a/rpmio/macro.c +++ b/rpmio/macro.c @@ -912,6 +912,9 @@ doFoo(MacroBuf mb, int negate, const char * f, size_t fn, case COMPRESSED_7ZIP: sprintf(be, "%%__7zip x %s", b); break; + case COMPRESSED_ZSTD: + sprintf(be, "%%__zstd -dc %s", b); + break; } b = be; } else if (STREQ("getenv", f, fn)) { diff --git a/rpmio/rpmfileutil.c b/rpmio/rpmfileutil.c index 805729e19f..9e47ff8a5a 100644 --- a/rpmio/rpmfileutil.c +++ b/rpmio/rpmfileutil.c @@ -351,6 +351,9 @@ int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed) (magic[4] == 0x5a) && (magic[5] == 0x00)) { /* new style xz (lzma) with magic */ *compressed = COMPRESSED_XZ; + } else if ((magic[0] == 0x28) && (magic[1] == 0x85) && + (magic[2] == 0x2f) ) { + *compressed = COMPRESSED_ZSTD; } else if ((magic[0] == 'L') && (magic[1] == 'Z') && (magic[2] == 'I') && (magic[3] == 'P')) { *compressed = COMPRESSED_LZIP; diff --git a/rpmio/rpmfileutil.h b/rpmio/rpmfileutil.h index 3c0a9841a1..916f6b240c 100644 --- a/rpmio/rpmfileutil.h +++ b/rpmio/rpmfileutil.h @@ -27,7 +27,8 @@ typedef enum rpmCompressedMagic_e { COMPRESSED_LZIP = 6, /*!< lzip can handle */ COMPRESSED_LRZIP = 7, /*!< lrzip can handle */ COMPRESSED_7ZIP = 8, /*!< 7zip can handle */ - COMPRESSED_GEM = 9 /*!< gem can handle */ + COMPRESSED_GEM = 9, /*!< gem can handle */ + COMPRESSED_ZSTD = 10 /*!< zstd can handle */ } rpmCompressedMagic; /** \ingroup rpmfileutil diff --git a/rpmio/rpmio.c b/rpmio/rpmio.c index 945c87624f..c2e978de88 100644 --- a/rpmio/rpmio.c +++ b/rpmio/rpmio.c @@ -151,6 +151,7 @@ static const FDIO_t gzdio; static const FDIO_t bzdio; static const FDIO_t xzdio; static const FDIO_t lzdio; +static const FDIO_t zstdio; /** \ingroup rpmio * Update digest(s) attached to fd. @@ -1035,6 +1036,243 @@ static const FDIO_t lzdio = &lzdio_s; #endif /* HAVE_LZMA_H */ +/* =============================================================== */ +/* Support for ZSTD library. */ +#if HAVE_ZSTD_H + +#include + +typedef struct rpmzstd_s { + int flags; /*!< open flags. */ + int fdno; + int level; /*!< compression level */ + FILE * fp; + void * _stream; /*!< ZSTD_{C,D}Stream */ + size_t nb; + void * b; + ZSTD_inBuffer zib; /*!< ZSTD_inBuffer */ + ZSTD_outBuffer zob; /*!< ZSTD_outBuffer */ +} * rpmzstd; + +static rpmzstd rpmzstdNew(int fdno, const char *fmode) +{ + int flags = 0; + int level = 3; + const char * s = fmode; + char stdio[32]; + char *t = stdio; + char *te = t + sizeof(stdio) - 2; + int c; + + switch ((c = *s++)) { + case 'a': + *t++ = (char)c; + flags &= ~O_ACCMODE; + flags |= O_WRONLY | O_CREAT | O_APPEND; + break; + case 'w': + *t++ = (char)c; + flags &= ~O_ACCMODE; + flags |= O_WRONLY | O_CREAT | O_TRUNC; + break; + case 'r': + *t++ = (char)c; + flags &= ~O_ACCMODE; + flags |= O_RDONLY; + break; + } + + while ((c = *s++) != 0) { + switch (c) { + case '.': + break; + case '+': + if (t < te) *t++ = c; + flags &= ~O_ACCMODE; + flags |= O_RDWR; + continue; + break; + default: + if (c >= (int)'0' && c <= (int)'9') { + level = strtol(s-1, (char **)&s, 10); +assert(level >= 1 && level <= 19); + } + continue; + break; + } + break; + } + *t = '\0'; + + FILE * fp = fdopen(fdno, stdio); + if (fp == NULL) + return NULL; + + void * _stream = NULL; + size_t nb = 0; + + if ((flags & O_ACCMODE) == O_RDONLY) { /* decompressing */ + if ((_stream = (void *) ZSTD_createDStream()) == NULL + || ZSTD_isError(ZSTD_initDStream(_stream))) { + return NULL; + } + nb = ZSTD_DStreamInSize(); + } else { /* compressing */ + if ((_stream = (void *) ZSTD_createCStream()) == NULL + || ZSTD_isError(ZSTD_initCStream(_stream, level))) { + return NULL; + } + nb = ZSTD_CStreamOutSize(); + } + + rpmzstd zstd = (rpmzstd) xcalloc(1, sizeof(*zstd)); + zstd->flags = flags; + zstd->fdno = fdno; + zstd->level = level; + zstd->fp = fp; + zstd->_stream = _stream; + zstd->nb = nb; + zstd->b = xmalloc(nb); + + return zstd; +} + +static FD_t zstdFdopen(FD_t fd, int fdno, const char * fmode) +{ + rpmzstd zstd = rpmzstdNew(fdno, fmode); + + if (zstd == NULL) + return NULL; + + fdSetFdno(fd, -1); /* XXX skip the fdio close */ + fdPush(fd, zstdio, zstd, fdno); /* Push zstdio onto stack */ + return fd; +} + +static int zstdFlush(FDSTACK_t fps) +{ + rpmzstd zstd = (rpmzstd) fps->fp; +assert(zstd); + int rc = -1; + + if ((zstd->flags & O_ACCMODE) == O_RDONLY) { /* decompressing */ + rc = 0; + } else { /* compressing */ + /* close frame */ + zstd->zob.dst = zstd->b; + zstd->zob.size = zstd->nb; + zstd->zob.pos = 0; + int xx = ZSTD_flushStream(zstd->_stream, &zstd->zob); + if (ZSTD_isError(xx)) + fps->errcookie = ZSTD_getErrorName(xx); + else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp)) + fps->errcookie = "zstdFlush fwrite failed."; + else + rc = 0; + } + return rc; +} + +static ssize_t zstdRead(FDSTACK_t fps, void * buf, size_t count) +{ + rpmzstd zstd = (rpmzstd) fps->fp; +assert(zstd); + ZSTD_outBuffer zob = { buf, count, 0 }; + + while (zob.pos < zob.size) { + /* Re-fill compressed data buffer. */ + if (zstd->zib.pos >= zstd->zib.size) { + zstd->zib.size = fread(zstd->b, 1, zstd->nb, zstd->fp); + if (zstd->zib.size == 0) + break; /* EOF */ + zstd->zib.src = zstd->b; + zstd->zib.pos = 0; + } + + /* Decompress next chunk. */ + int xx = ZSTD_decompressStream(zstd->_stream, &zob, &zstd->zib); + if (ZSTD_isError(xx)) { + fps->errcookie = ZSTD_getErrorName(xx); + return -1; + } + } + return zob.pos; +} + +static ssize_t zstdWrite(FDSTACK_t fps, const void * buf, size_t count) +{ + rpmzstd zstd = (rpmzstd) fps->fp; +assert(zstd); + ZSTD_inBuffer zib = { buf, count, 0 }; + + while (zib.pos < zib.size) { + + /* Reset to beginning of compressed data buffer. */ + zstd->zob.dst = zstd->b; + zstd->zob.size = zstd->nb; + zstd->zob.pos = 0; + + /* Compress next chunk. */ + int xx = ZSTD_compressStream(zstd->_stream, &zstd->zob, &zib); + if (ZSTD_isError(xx)) { + fps->errcookie = ZSTD_getErrorName(xx); + return -1; + } + + /* Write compressed data buffer. */ + if (zstd->zob.pos > 0) { + size_t nw = fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp); + if (nw != zstd->zob.pos) { + fps->errcookie = "zstdWrite fwrite failed."; + return -1; + } + } + } + return zib.pos; +} + +static int zstdClose(FDSTACK_t fps) +{ + rpmzstd zstd = (rpmzstd) fps->fp; +assert(zstd); + int rc = -2; + + if ((zstd->flags & O_ACCMODE) == O_RDONLY) { /* decompressing */ + rc = 0; + ZSTD_freeDStream(zstd->_stream); + } else { /* compressing */ + /* close frame */ + zstd->zob.dst = zstd->b; + zstd->zob.size = zstd->nb; + zstd->zob.pos = 0; + int xx = ZSTD_endStream(zstd->_stream, &zstd->zob); + if (ZSTD_isError(xx)) + fps->errcookie = ZSTD_getErrorName(xx); + else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp)) + fps->errcookie = "zstdClose fwrite failed."; + else + rc = 0; + ZSTD_freeCStream(zstd->_stream); + } + + if (zstd->fp && fileno(zstd->fp) > 2) + (void) fclose(zstd->fp); + + if (zstd->b) free(zstd->b); + free(zstd); + + return rc; +} + +static const struct FDIO_s zstdio_s = { + "zstdio", "zstd", + zstdRead, zstdWrite, NULL, zstdClose, + NULL, zstdFdopen, zstdFlush, NULL, zfdError, zfdStrerr +}; +static const FDIO_t zstdio = &zstdio_s ; + +#endif /* HAVE_ZSTD_H */ + /* =============================================================== */ #define FDIOVEC(_fps, _vec) \ @@ -1178,14 +1416,17 @@ static void cvtfmode (const char *m, switch (*m) { case 'a': + flags &= ~O_ACCMODE; flags |= O_WRONLY | O_CREAT | O_APPEND; if (--nstdio > 0) *stdio++ = *m; break; case 'w': + flags &= ~O_ACCMODE; flags |= O_WRONLY | O_CREAT | O_TRUNC; if (--nstdio > 0) *stdio++ = *m; break; case 'r': + flags &= ~O_ACCMODE; flags |= O_RDONLY; if (--nstdio > 0) *stdio++ = *m; break; @@ -1201,7 +1442,7 @@ static void cvtfmode (const char *m, case '.': break; case '+': - flags &= ~(O_RDONLY|O_WRONLY); + flags &= ~O_ACCMODE; flags |= O_RDWR; if (--nstdio > 0) *stdio++ = c; continue; @@ -1215,6 +1456,11 @@ static void cvtfmode (const char *m, if (--nstdio > 0) *stdio++ = c; continue; break; + case '?': + flags |= RPMIO_DEBUG_IO; + if (--nother > 0) *other++ = c; + continue; + break; default: if (--nother > 0) *other++ = c; continue; @@ -1242,6 +1488,9 @@ static FDIO_t findIOT(const char *name) #if HAVE_LZMA_H &xzdio_s, &lzdio_s, +#endif +#if HAVE_ZSTD_H + &zstdio_s, #endif NULL }; diff --git a/scripts/rpm2cpio.sh b/scripts/rpm2cpio.sh index f7d50fd96c..a883562ac2 100755 --- a/scripts/rpm2cpio.sh +++ b/scripts/rpm2cpio.sh @@ -49,5 +49,6 @@ case "$(_dd $offset bs=3 count=1)" in "$(printf '\037\213')"*) _dd $offset | gunzip ;; # '\x1f\x8b' "$(printf '\375\067')"*) _dd $offset | xzcat ;; # '\xfd\x37' "$(printf '\135\000')"*) _dd $offset | unlzma ;; # '\x5d\x00' + "$(printf '\050\265')"*) _dd $offset | unzstd ;; # '\x28\xb5' *) fatal "Unrecognized rpm file: $pkg" ;; esac