>From b2bd0a9041ed7401fb69f59f7450abb8b3249ecb Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Wed, 15 Dec 2021 19:18:31 +0100 Subject: [PATCH 3/3] Accommodate non-recursive Automake in a less hacky way. * gnulib-tool: New option --automake-subdir. (automake_subdir): New variable. (func_emit_initmacro_end): Add a second argument. Use it to prefix each object file name in *_LIBOBJS and *_LTLIBOBJS. (func_emit_shellvars_init): New function. (func_import): Add support for --automake-subdir. Invoke prefix-gnulib-mk. Update calls to func_emit_initmacro_end. Call func_emit_shellvars_init. (func_create_testdir): Update calls to func_emit_initmacro_end. Call func_emit_shellvars_init. * m4/gnulib-tool.m4 (gl_AUTOMAKE_SUBDIR): New macro. * m4/gnulib-common.m4 (gl_CONDITIONAL_HEADER): Use the value of the gl_source_base_prefix variable. * build-aux/prefix-gnulib-mk: New options --from-gnulib-tool, --prefix. (contents_of_file): Renamed from contents. (contents_of_stdin): New function. (process): Inline and remove function. * doc/gnulib-tool.texi (Non-recursive make): New section. --- ChangeLog | 22 ++++++++ build-aux/prefix-gnulib-mk | 81 +++++++++++++++++----------- doc/gnulib-tool.texi | 45 ++++++++++++++++ gnulib-tool | 107 ++++++++++++++++++++++++++++++------- m4/gnulib-common.m4 | 10 +++- m4/gnulib-tool.m4 | 5 +- 6 files changed, 219 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index 193405c6a..00761d614 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2021-12-15 Bruno Haible + + Accommodate non-recursive Automake in a less hacky way. + * gnulib-tool: New option --automake-subdir. + (automake_subdir): New variable. + (func_emit_initmacro_end): Add a second argument. Use it to prefix each + object file name in *_LIBOBJS and *_LTLIBOBJS. + (func_emit_shellvars_init): New function. + (func_import): Add support for --automake-subdir. Invoke + prefix-gnulib-mk. Update calls to func_emit_initmacro_end. Call + func_emit_shellvars_init. + (func_create_testdir): Update calls to func_emit_initmacro_end. Call + func_emit_shellvars_init. + * m4/gnulib-tool.m4 (gl_AUTOMAKE_SUBDIR): New macro. + * m4/gnulib-common.m4 (gl_CONDITIONAL_HEADER): Use the value of the + gl_source_base_prefix variable. + * build-aux/prefix-gnulib-mk: New options --from-gnulib-tool, --prefix. + (contents_of_file): Renamed from contents. + (contents_of_stdin): New function. + (process): Inline and remove function. + * doc/gnulib-tool.texi (Non-recursive make): New section. + 2021-12-15 Bruno Haible Move .h file names out of the *.m4 files. diff --git a/build-aux/prefix-gnulib-mk b/build-aux/prefix-gnulib-mk index 7b43eb0db..36f7527fc 100755 --- a/build-aux/prefix-gnulib-mk +++ b/build-aux/prefix-gnulib-mk @@ -74,9 +74,9 @@ EOF exit $exit_code; } -# contents ($FILE_NAME) -# --------------------- -sub contents ($) +# contents_of_file ($FILE_NAME) +# ----------------------------- +sub contents_of_file ($) { my ($file) = @_; local $/; # Turn on slurp-mode. @@ -86,6 +86,15 @@ sub contents ($) return $contents; } +# contents_of_stdin +# ----------------- +sub contents_of_stdin () +{ + local $/; # Turn on slurp-mode. + my $contents = ; + return $contents; +} + # prefix_word ($WORD) # ------------------- # Do not prefix special words such as variable dereferences. Also, @@ -202,46 +211,58 @@ sub prefix ($) return $_; } -# process ($IN) -# ------------- -sub process ($) { - my ($file) = @_; - my ($bak) = "$file.bak"; - rename ($file, $bak) or die "$ME: rename $file $bak failed: $!\n"; - my $contents = contents ($bak); - $contents = prefix ($contents); - my $out = new IO::File(">$file") - or die "$ME: $file: failed to open for writing: $!\n"; - print $out $contents; -} + my $from_gnulib_tool = 0; -{ GetOptions ( 'lib-name=s' => \$lib_name, help => sub { usage 0 }, version => sub { print "$ME version $VERSION\n"; exit }, + # Undocumented options: + 'from-gnulib-tool' => \$from_gnulib_tool, + 'prefix=s' => \$prefix, ) or usage 1; my $fail = 0; defined $lib_name or (warn "$ME: no library name; use --lib-name=NAME\n"), $fail = 1; - # There must be exactly one argument. - @ARGV == 0 - and (warn "$ME: missing FILE argument\n"), $fail = 1; - 1 < @ARGV - and (warn "$ME: too many arguments:\n", join ("\n", @ARGV), "\n"), - $fail = 1; - $fail - and usage 1; - - my $file = $ARGV[0]; - $prefix = (dirname $file) . '/'; - warn "prefix=$prefix\n"; - - process $file; + if ($from_gnulib_tool != 0) + { + 0 < @ARGV + and (warn "$ME: too many arguments:\n", join ("\n", @ARGV), "\n"), + $fail = 1; + $fail + and usage 1; + + my $contents = contents_of_stdin (); + $contents = prefix ($contents); + print STDOUT $contents; + } + else + { + # There must be exactly one argument. + @ARGV == 0 + and (warn "$ME: missing FILE argument\n"), $fail = 1; + 1 < @ARGV + and (warn "$ME: too many arguments:\n", join ("\n", @ARGV), "\n"), + $fail = 1; + $fail + and usage 1; + + my $file = $ARGV[0]; + $prefix = (dirname $file) . '/'; + warn "prefix=$prefix\n"; + + my ($bak) = "$file.bak"; + rename ($file, $bak) or die "$ME: rename $file $bak failed: $!\n"; + my $contents = contents_of_file ($bak); + $contents = prefix ($contents); + my $out = new IO::File(">$file") + or die "$ME: $file: failed to open for writing: $!\n"; + print $out $contents; + } } ### Setup "GNU" style for perl-mode and cperl-mode. diff --git a/doc/gnulib-tool.texi b/doc/gnulib-tool.texi index 7f66c4248..7e662cbcf 100644 --- a/doc/gnulib-tool.texi +++ b/doc/gnulib-tool.texi @@ -46,6 +46,7 @@ a real run without changing anything. * Link-time requirements:: Which libraries to link against * Finding POSIX substitutes:: Determining additional suitable Gnulib modules * Modified build rules:: Modifying the build rules of a Gnulib import +* Non-recursive make:: Building directly from the top-level directory * Multiple instances:: Using Gnulib for both a library and a program * gettextize and autopoint:: Caveat: @code{gettextize} and @code{autopoint} users! * Localization:: Handling Gnulib's own message translations. @@ -601,6 +602,50 @@ The other approach, the kitchen-sink module, is more advanced. See chapter @ref{Extending Gnulib}. +@node Non-recursive make +@section Building directly from the top-level directory + +By default, the Gnulib import directory will contain a generated +@code{Makefile.am} file. After configuring, this produces a generated +@code{Makefile} in this directory. As a consequence, the build from the +top-level directory will use a recursive @code{make} invocation for this +directory. + +Some people prefer a build system where the @code{Makefile} in the +top-level directory directly builds the artifacts in the subdirectories, +without an intermediate @code{make} invocation. This is called +``non-recursive make'' and is supported by Automake. For more details, +see @url{https://autotools.io/automake/nonrecursive.html}. + +Gnulib supports this flavour of build system too. To use it, pass two +options to @code{gnulib-tool}: @samp{--makefile-name} and +@samp{--automake-subdir}. + +With the @code{gnulib-tool} option @samp{--makefile-name}, you are +telling @code{gnulib-tool} to generate an includable @code{Makefile.am} +portion in the Gnulib import directory, rather than a self-contained +@code{Makefile.am}. For example, when you use +@samp{--makefile-name=Makefile.gnulib}, @code{gnulib-tool} will generate +@code{Makefile.gnulib}. + +With the option @samp{--automake-subdir}, you are telling +@code{gnulib-tool} that you will include the generated file from the +@code{Makefile.am} in the top-level directory, rather than from a +@code{Makefile.am} in the same directory. For example, the top-level +@code{Makefile.am} might contain this directive: + +@smallexample +include lib/Makefile.gnulib +@end smallexample + +The option @samp{--automake-subdir} is also supported in combination +with @samp{--with-tests} (@pxref{Unit tests}). Note that in this case, +however, the generated unit tests directory will contains a +@code{Makefile.am} and thus use a recursive @code{make} invocation. +This is not a problem, since the built artifacts of your package have +no dependencies towards the Gnulib unit tests, nor vice versa. + + @node Multiple instances @section Using Gnulib for both a library and a program diff --git a/gnulib-tool b/gnulib-tool index 25df157dc..70ea17a4c 100755 --- a/gnulib-tool +++ b/gnulib-tool @@ -307,7 +307,10 @@ Options for --import, --add/remove-import: \"Makefile.in\" if --gnu-make). --tests-makefile-name=NAME Name of makefile in the tests-base directory - (default as specified through --makefile-name) + (default as specified through --makefile-name). + --automake-subdir Specify that the makefile in the source-base + directory be generated in such a way that it can + be 'include'd from the toplevel Makefile.am. --macro-prefix=PREFIX Specify the prefix of the macros 'gl_EARLY' and 'gl_INIT'. Default is 'gl'. --po-domain=NAME Specify the prefix of the i18n domain. Usually use @@ -1115,6 +1118,7 @@ func_determine_path_separator # - gnu_make true if --gnu-make was given, false otherwise # - makefile_name from --makefile-name # - tests_makefile_name from --tests-makefile-name +# - automake_subdir true if --automake-subdir was given, false otherwise # - libtool true if --libtool was given, false if --no-libtool was # given, blank otherwise # - macro_prefix from --macro-prefix @@ -1162,6 +1166,7 @@ func_determine_path_separator gnu_make=false makefile_name= tests_makefile_name= + automake_subdir=false libtool= macro_prefix= po_domain= @@ -1406,6 +1411,9 @@ func_determine_path_separator --tests-makefile-name=* ) tests_makefile_name=`echo "X$1" | sed -e 's/^X--tests-makefile-name=//'` shift ;; + --automake-subdir ) + automake_subdir=true + shift ;; --libtool ) libtool=true shift ;; @@ -1518,7 +1526,7 @@ func_determine_path_separator || test -n "$excl_cxx_tests" || test -n "$excl_longrunning_tests" \ || test -n "$excl_privileged_tests" || test -n "$excl_unportable_tests" \ || test -n "$avoidlist" || test -n "$lgpl" || test -n "$makefile_name" \ - || test -n "$tests_makefile_name" \ + || test -n "$tests_makefile_name" || test "$automake_subdir" != false \ || test -n "$macro_prefix" || test -n "$po_domain" \ || test -n "$witness_c_macro" || test -n "$vc_files"; then echo "gnulib-tool: invalid options for 'update' mode" 1>&2 @@ -1610,6 +1618,22 @@ func_determine_path_separator func_fatal_error "minimum supported autoconf version is 2.64. Try adding AC_PREREQ([$DEFAULT_AUTOCONF_MINVERSION]) to your configure.ac." ;; esac + # Determine whether --automake-subdir is supported. + if $automake_subdir; then + found_subdir_objects=false + if test -f "${destdir:-.}"/Makefile.am; then + automake_options=`sed -n -e 's/^AUTOMAKE_OPTIONS[ ]*=\(.*\)$/\1/p' "${destdir:-.}"/Makefile.am` + for arg in $automake_options; do + case "$arg" in + subdir-objects ) found_subdir_objects=true ;; + esac + done + fi + if ! $found_subdir_objects; then + func_fatal_error "Option --automake-subdir is only supported if the definition of AUTOMAKE_OPTIONS in Makefile.am contains 'subdir-objects'." + fi + fi + # Remove trailing slashes from the directory names. This is necessary for # m4base (to avoid an error in func_import) and optional for the others. sed_trimtrailingslashes='s,\([^/]\)//*$,\1,' @@ -4372,9 +4396,12 @@ func_emit_initmacro_start () fi } -# func_emit_initmacro_end macro_prefix +# func_emit_initmacro_end macro_prefix gentests # emits the last few statements of the gl_INIT macro to standard output. # - macro_prefix prefix of gl_EARLY, gl_INIT macros to use +# - gentests true if a tests Makefile.am is being generated, +# false otherwise +# - automake_subdir true if --automake-subdir was given, false otherwise func_emit_initmacro_end () { macro_prefix_arg="$1" @@ -4406,9 +4433,14 @@ func_emit_initmacro_end () echo " if test -n \"\$${macro_prefix_arg}_LIBOBJS\"; then" echo " # Remove the extension." echo " sed_drop_objext='s/\\.o\$//;s/\\.obj\$//'" + if $automake_subdir && ! "$2" && test -n "$sourcebase" && test "$sourcebase" != '.'; then + subdir="$sourcebase/" + else + subdir= + fi echo " for i in \`for i in \$${macro_prefix_arg}_LIBOBJS; do echo \"\$i\"; done | sed -e \"\$sed_drop_objext\" | sort | uniq\`; do" - echo " ${macro_prefix_arg}_libobjs=\"\$${macro_prefix_arg}_libobjs \$i.\$ac_objext\"" - echo " ${macro_prefix_arg}_ltlibobjs=\"\$${macro_prefix_arg}_ltlibobjs \$i.lo\"" + echo " ${macro_prefix_arg}_libobjs=\"\$${macro_prefix_arg}_libobjs ${subdir}\$i.\$ac_objext\"" + echo " ${macro_prefix_arg}_ltlibobjs=\"\$${macro_prefix_arg}_ltlibobjs ${subdir}\$i.lo\"" echo " done" echo " fi" echo " AC_SUBST([${macro_prefix_arg}_LIBOBJS], [\$${macro_prefix_arg}_libobjs])" @@ -4453,6 +4485,24 @@ func_emit_initmacro_done () echo "])" } +# func_emit_shellvars_init gentests base +# emits some shell variable assignments to standard output. +# - gentests true if a tests Makefile.am is being generated, +# false otherwise +# - base base directory, relative to the top-level directory +# - automake_subdir true if --automake-subdir was given, false otherwise +func_emit_shellvars_init () +{ + # Define the base directory, relative to the top-level directory. + echo " gl_source_base='$2'" + # Define the prefix for the file name of generated files. + if $automake_subdir && ! $1; then + echo " gl_source_base_prefix='\$(top_build_prefix)$2/'" + else + echo " gl_source_base_prefix=" + fi +} + # func_emit_autoconf_snippet indentation # emits the autoconf snippet of a module. # Input: @@ -4782,6 +4832,7 @@ func_import () cached_lgpl= cached_makefile_name= cached_tests_makefile_name= + cached_automake_subdir= cached_cond_dependencies= cached_libtool= cached_macro_prefix= @@ -4862,6 +4913,9 @@ func_import () /gl_TESTS_MAKEFILE_NAME(/ { s,^.*gl_TESTS_MAKEFILE_NAME([[ ]*\([^]"$`\\)]*\).*$,cached_tests_makefile_name="\1",p } + /gl_AUTOMAKE_SUBDIR/ { + s,^.*$,cached_automake_subdir=true,p + } /gl_CONDITIONAL_DEPENDENCIES/ { s,^.*$,cached_cond_dependencies=true,p } @@ -5037,6 +5091,13 @@ func_import () if test -z "$tests_makefile_name"; then tests_makefile_name="$cached_tests_makefile_name" fi + # Use automake-subdir mode if specified either way. + if ! $automake_subdir; then + automake_subdir="$cached_automake_subdir" + if test -z "$automake_subdir"; then + automake_subdir=false + fi + fi # Use conditional dependencies if specified either way. if test -z "$cond_dependencies"; then cond_dependencies="$cached_cond_dependencies" @@ -5429,6 +5490,9 @@ s,^\(.................................................[^ ]*\) *, if test -n "$tests_makefile_name"; then func_append_actionarg "--tests-makefile-name=$tests_makefile_name" fi + if $automake_subdir; then + func_append_actionarg "--automake-subdir" + fi if test "$cond_dependencies" = true; then func_append_actionarg "--conditional-dependencies" else @@ -5539,7 +5603,11 @@ s,//*$,/,' func_dest_tmpfilename $sourcebase/$source_makefile_am destfile="$sourcebase/$source_makefile_am" modules="$main_modules" - func_emit_lib_Makefile_am > "$tmpfile" + if $automake_subdir; then + func_emit_lib_Makefile_am | "$gnulib_dir"/build-aux/prefix-gnulib-mk --from-gnulib-tool --lib-name="$libname" --prefix="$sourcebase/" > "$tmpfile" + else + func_emit_lib_Makefile_am > "$tmpfile" + fi if test -f "$destdir"/$sourcebase/$source_makefile_am; then if cmp -s "$destdir"/$sourcebase/$source_makefile_am "$tmpfile"; then rm -f "$tmpfile" @@ -5767,6 +5835,9 @@ s,//*$,/,' if test -n "$tests_makefile_name"; then echo "gl_TESTS_MAKEFILE_NAME([$tests_makefile_name])" fi + if test "$automake_subdir" = true; then + echo "gl_AUTOMAKE_SUBDIR" + fi if test "$cond_dependencies" = true; then echo "gl_CONDITIONAL_DEPENDENCIES" fi @@ -5888,7 +5959,7 @@ s,//*$,/,' fi echo " gl_m4_base='$m4base'" func_emit_initmacro_start $macro_prefix false - echo " gl_source_base='$sourcebase'" + func_emit_shellvars_init false "$sourcebase" if test -n "$witness_c_macro"; then echo " m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [$witness_c_macro])" fi @@ -5897,11 +5968,11 @@ s,//*$,/,' echo " m4_popdef([gl_MODULE_INDICATOR_CONDITION])" fi echo " # End of code from modules" - func_emit_initmacro_end $macro_prefix + func_emit_initmacro_end $macro_prefix false echo " gltests_libdeps=" echo " gltests_ltlibdeps=" func_emit_initmacro_start ${macro_prefix}tests $gentests - echo " gl_source_base='$testsbase'" + func_emit_shellvars_init true "$testsbase" # Define a tests witness macro that depends on the package. # PACKAGE is defined by AM_INIT_AUTOMAKE, PACKAGE_TARNAME is defined by AC_INIT. # See . @@ -5913,7 +5984,7 @@ s,//*$,/,' echo " m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [\$gl_module_indicator_condition])" func_emit_autoconf_snippets "$testsrelated_modules" "$main_modules $testsrelated_modules" func_verify_module true true true echo " m4_popdef([gl_MODULE_INDICATOR_CONDITION])" - func_emit_initmacro_end ${macro_prefix}tests + func_emit_initmacro_end ${macro_prefix}tests $gentests # _LIBDEPS and _LTLIBDEPS variables are not needed if this library is # created using libtool, because libtool already handles the dependencies. if test "$libtool" != true; then @@ -6549,11 +6620,11 @@ func_create_testdir () # We don't have explicit ordering constraints between the various # autoconf snippets. It's cleanest to put those of the library before # those of the tests. - echo "gl_source_base='../$sourcebase'" + func_emit_shellvars_init true "../$sourcebase" func_emit_autoconf_snippets "$modules" "$modules" func_verify_nontests_module false false false - echo "gl_source_base='.'" + func_emit_shellvars_init true '.' func_emit_autoconf_snippets "$modules" "$modules" func_verify_tests_module false false false - func_emit_initmacro_end $macro_prefix + func_emit_initmacro_end $macro_prefix true # _LIBDEPS and _LTLIBDEPS variables are not needed if this library is # created using libtool, because libtool already handles the dependencies. if test "$libtool" != true; then @@ -6662,18 +6733,18 @@ func_create_testdir () fi echo "gl_m4_base='$m4base'" func_emit_initmacro_start $macro_prefix false - echo "gl_source_base='$sourcebase'" + func_emit_shellvars_init false "$sourcebase" if $single_configure; then func_emit_autoconf_snippets "$main_modules" "$main_modules" func_verify_module true false false else func_emit_autoconf_snippets "$modules" "$modules" func_verify_nontests_module true false false fi - func_emit_initmacro_end $macro_prefix + func_emit_initmacro_end $macro_prefix false if $single_configure; then echo " gltests_libdeps=" echo " gltests_ltlibdeps=" func_emit_initmacro_start ${macro_prefix}tests true - echo " gl_source_base='$testsbase'" + func_emit_shellvars_init true "$testsbase" # Define a tests witness macro. echo " ${macro_prefix}tests_WITNESS=IN_GNULIB_TESTS" echo " AC_SUBST([${macro_prefix}tests_WITNESS])" @@ -7123,8 +7194,8 @@ s/\([.*$]\)/[\1]/g' # inc_longrunning_tests, inc_privileged_tests, # inc_unportable_tests, inc_all_tests, avoidlist, sourcebase, # m4base, pobase, docbase, testsbase, inctests, libname, lgpl, - # makefile_name, tests_makefile_name, libtool, macro_prefix, - # po_domain, witness_c_macro, vc_files + # makefile_name, tests_makefile_name, automake_subdir, libtool, + # macro_prefix, po_domain, witness_c_macro, vc_files # don't propagate from one directory to another. (func_import) || func_exit 1 done diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4 index 9061efb51..f70ef4ea9 100644 --- a/m4/gnulib-common.m4 +++ b/m4/gnulib-common.m4 @@ -1,4 +1,4 @@ -# gnulib-common.m4 serial 68 +# gnulib-common.m4 serial 69 dnl Copyright (C) 2007-2021 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -893,7 +893,13 @@ AC_DEFUN([gl_CONDITIONAL_HEADER], m4_pushdef([gl_generate_cond], [GL_GENERATE_]AS_TR_SH(m4_toupper($1))) case "$gl_generate_var" in false) gl_header_name='' ;; - true) gl_header_name='$1' ;; + true) + dnl It is OK to use a .h file in lib/ from within tests/, but not vice + dnl versa. + if test -z "$gl_header_name"; then + gl_header_name="${gl_source_base_prefix}$1" + fi + ;; *) echo "*** gl_generate_var is not set correctly" 1>&2; exit 1 ;; esac AC_SUBST(gl_header_name) diff --git a/m4/gnulib-tool.m4 b/m4/gnulib-tool.m4 index bbf38d7d3..3e977759f 100644 --- a/m4/gnulib-tool.m4 +++ b/m4/gnulib-tool.m4 @@ -1,4 +1,4 @@ -# gnulib-tool.m4 serial 3 +# gnulib-tool.m4 serial 4 dnl Copyright (C) 2004-2005, 2009-2021 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -47,6 +47,9 @@ AC_DEFUN([gl_MAKEFILE_NAME], []) dnl Usage: gl_TESTS_MAKEFILE_NAME([FILENAME]) AC_DEFUN([gl_TESTS_MAKEFILE_NAME], []) +dnl Usage: gl_AUTOMAKE_SUBDIR +AC_DEFUN([gl_AUTOMAKE_SUBDIR], []) + dnl Usage: gl_LIBTOOL AC_DEFUN([gl_LIBTOOL], []) -- 2.25.1