>From 60fd60cd6556e315a3ce286be8c074ce23c5fbdb Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Mon, 13 Mar 2017 12:41:59 +0100 Subject: [PATCH] automake: Shorter object file names under subdir-objects Combining the 'subdir-objects' option with target-specific flags, had the consequence of producing long object file names. This was done to preventively ensure the uniqueness of object file names. We are now using shorter names by default, and handle long names when an actual conflict is detected. This will hopefully reduce the necessity of using the 'prog_SHORTNAME' facility. Example Makefile.am: AUTOMAKE_OPTIONS = subdir-objects bin_PROGRAMS += path/to/foo path_to_foo_CFLAGS = $(AM_CFLAGS) -g previously resulted in object: $(top_builddir)/path/to/path_to_foo-foo.o now object file name is: $(top_builddir)/path/to/foo-foo.o * bin/automake.in (longest_common_prefix, may_truncate) (find_duplicate_basenames): New subroutines. (handle_programs, handle_libraries): Keep track of duplicate names. (handle_single_transform): Truncate object file names when possible. * t/subobj-objname-clash.sh: New test. * t/list-of-tests.mk (handwritten_TESTS): Add it. * NEWS: Update. --- NEWS | 7 +++ bin/automake.in | 117 +++++++++++++++++++++++++++++++++++++++++++--- t/list-of-tests.mk | 1 + t/subobj-objname-clash.sh | 94 +++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 t/subobj-objname-clash.sh diff --git a/NEWS b/NEWS index af904d442..f93d76dd9 100644 --- a/NEWS +++ b/NEWS @@ -64,6 +64,13 @@ New in 1.16: +* Miscellaneous changes + + - When subdir-objects is in effect, Automake will now construct + shorter object file names when no programs and libraries name + clashes are encountered. This should make the discouraged use of + 'foo_SHORTNAME' unnecessary in many cases. + * Bugs fixed: - Automatic dependency tracking has been fixed to work also when the diff --git a/bin/automake.in b/bin/automake.in index b0bde8675..5d854baab 100644 --- a/bin/automake.in +++ b/bin/automake.in @@ -1562,6 +1562,45 @@ sub check_libobjs_sources } } +# $PREFIX +# longest_common_prefix (@WORDS) +# ------------------------------ +# Return the longest common prefix of @WORDS. If there is no common prefix +# return the empty string. +# +# Implementation taken from +# http://linux.seindal.dk/2005/09/09/longest-common-prefix-in-perl/ +sub longest_common_prefix (@) +{ + my $prefix = shift; + for (@_) + { + chop $prefix + while (! /^$prefix/); + } + + return $prefix; +} + +# $BOOLEAN +# may_truncate ($SHORTNAME, %TRANSFORM) +# ------------------------------------- +# Check if $SHORTNAME can be truncated based on the +# 'TRUNCATE_OBJNAME_BLACKLIST' property of %TRANSFORM. +sub may_truncate ($%) +{ + my ($shortname, %transform) = @_; + my $blacklist = $transform{"TRUNCATE_OBJNAME_BLACKLIST"}; + + # Disallow truncation if $derived ends with any of the blacklisted targets + # the blacklist still contains the non-canonicalize basename ($name.$ext). + foreach my $basename (@$blacklist) + { + return 0 + if (canonicalize ($basename) eq $shortname); + } + return 1; +} # @OBJECTS # handle_single_transform ($VAR, $TOPPARENT, $DERIVED, $OBJ, $FILE, %TRANSFORM) @@ -1711,7 +1750,34 @@ sub handle_single_transform # some situations. So we provide _SHORTNAME to # override. - my $dname = $derived; + # If subdir-object is in effect, it's not necessary to use the + # complete 'DERIVED_OBJECT' since objects are placed next to + # their source file. Therefore it is already unique (within + # that directory). Thus, we try to avoid unnecessarily long + # file names by stripping the directory components of + # 'DERIVED_OBJECT'. + # + # NOTE: In some setups, the stripping may lead to duplicated + # objects, even in presence of per-executable flags, for + # example if there are identically-named programs in different + # directories, so these targets must be exempted using a + # blacklist + my $dname = $derived; + if ($directory ne '' && option 'subdir-objects') + { + # At this point, we don't clear information about what parts + # of $derived are truly path components. We can determine + # by comparing against the canonicalization of $directory. + # And if $directory is empty there is nothing to strip anyway. + my $canon_dirname = canonicalize ($directory) . "_"; + my @names = ($derived, $canon_dirname); + my $prefix = longest_common_prefix (@names); + # After canonicalization, "_" separates directories, thus use + # everything after the the last separator. + $dname = substr ($derived, rindex ($prefix, "_") + 1); + # check if this dname is on the blacklist + $dname = $derived unless may_truncate ($dname, %transform); + } my $var = var ($derived . '_SHORTNAME'); if ($var) { @@ -2431,6 +2497,23 @@ sub handle_libtool () LTRMS => join ("\n", @libtool_rms)); } +# @DUPLICATES +# find_duplicate_basenames (@VARIABLES) +# ------------------------------------- +# Expects @VARIABLES to be a list as returned via '&am_install_var'. +sub find_duplicate_basenames (@) +{ + my %seen = (); + my @dups = (); + foreach my $pair (@_) + { + my $base = basename (@$pair[1]); + push (@dups, $base) + if ($seen{$base}); + $seen{$base} = $base; + } + return @dups; +} sub handle_programs () { @@ -2443,6 +2526,12 @@ sub handle_programs () my $seen_global_libobjs = var ('LDADD') && handle_lib_objects ('', 'LDADD'); + # Check if we have programs with identical basename. In this case, the object + # file name truncation is disabled for this program (and other programs with + # the same basename, because the object file names may clash (this only + # applies to subdir-object setups). + my @blacklist = find_duplicate_basenames (@proglist); + foreach my $pair (@proglist) { my ($where, $one_file) = @$pair; @@ -2460,8 +2549,10 @@ sub handle_programs () $where->push_context ("while processing program '$one_file'"); $where->set (INTERNAL->get); - my $linker = handle_source_transform ($xname, $one_file, $obj, $where, - NONLIBTOOL => 1, LIBTOOL => 0); + my $linker = + handle_source_transform ($xname, $one_file, $obj, $where, + NONLIBTOOL => 1, LIBTOOL => 0, + TRUNCATE_OBJNAME_BLACKLIST => address@hidden); if (var ($xname . "_LDADD")) { @@ -2541,6 +2632,12 @@ sub handle_libraries () define_variable ('ARFLAGS', 'cru', INTERNAL); define_verbose_tagvar ('AR'); + # Check if we have libraries with identical basename. In this case, the + # object file name truncation is disabled for this library (and other + # libraries with the same basename, because the object file names may clash + # (this only applies to subdir-object setups). + my @blacklist = find_duplicate_basenames (@liblist); + foreach my $pair (@liblist) { my ($where, $onelib) = @$pair; @@ -2597,7 +2694,8 @@ sub handle_libraries () set_seen ('EXTRA_' . $xlib . '_DEPENDENCIES'); handle_source_transform ($xlib, $onelib, $obj, $where, - NONLIBTOOL => 1, LIBTOOL => 0); + NONLIBTOOL => 1, LIBTOOL => 0, + TRUNCATE_OBJNAME_BLACKLIST => address@hidden); # If the resulting library lies in a subdirectory, # make sure this directory will exist. @@ -2728,6 +2826,11 @@ sub handle_ltlibraries () skip_ac_subst => 1); } + # Check if we have libraries with identical basename. In this case, the + # automatic object file name truncation is disabled because the object file + # names may clash (this only applies to subdir-object setups) + my @blacklist = find_duplicate_basenames (@liblist); + foreach my $pair (@liblist) { my ($where, $onelib) = @$pair; @@ -2799,8 +2902,10 @@ sub handle_ltlibraries () "use '${xlib}_LIBADD', not '${xlib}_LDADD'"); - my $linker = handle_source_transform ($xlib, $onelib, $obj, $where, - NONLIBTOOL => 0, LIBTOOL => 1); + my $linker = + handle_source_transform ($xlib, $onelib, $obj, $where, + NONLIBTOOL => 0, LIBTOOL => 1, + TRUNCATE_OBJNAME_BLACKLIST => address@hidden); # Determine program to use for link. my($xlink, $vlink) = define_per_target_linker_variable ($linker, $xlib); diff --git a/t/list-of-tests.mk b/t/list-of-tests.mk index defca1361..a5b253504 100644 --- a/t/list-of-tests.mk +++ b/t/list-of-tests.mk @@ -1062,6 +1062,7 @@ t/subobjname.sh \ t/subobj-clean-pr10697.sh \ t/subobj-clean-lt-pr10697.sh \ t/subobj-indir-pr13928.sh \ +t/subobj-objname-clash.sh \ t/subobj-vpath-pr13928.sh \ t/subobj-pr13928-more-langs.sh \ t/subpkg.sh \ diff --git a/t/subobj-objname-clash.sh b/t/subobj-objname-clash.sh new file mode 100644 index 000000000..48408cb23 --- /dev/null +++ b/t/subobj-objname-clash.sh @@ -0,0 +1,94 @@ +#! /bin/sh +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Make sure that object names don't clash when using subdir-objects. + +. test-init.sh + +mkdir -p src + +cat >> configure.ac << 'END' +AC_PROG_CC +AC_PROG_RANLIB +AC_OUTPUT +END + +cat > Makefile.am << 'END' +AUTOMAKE_OPTIONS = subdir-objects foreign + +include prog.mk +include lib.mk +END + +# Check that programs with same basename don't end up sharing the same +# object files. +cat > prog.mk << 'END' +noinst_PROGRAMS = foo src/foo +src_foo_CPPFLAGS = -DVAL=1 +src_foo_SOURCES = src/foo.c src/main.c + +foo_CPPFLAGS = -DVAL=0 +foo_SOURCES = src/foo.c src/main.c +END + +# Check the same thing for libraries. +cat > lib.mk << 'END' +noinst_LIBRARIES = libbar.a src/libbar.a + +libbar_a_SOURCES = src/foo.c +libbar_a_CPPFLAGS = -DVAL=0 + +src_libbar_a_SOURCES = src/foo.c +src_libbar_a_CPPFLAGS = -DVAL=1 + +noinst_PROGRAMS += bar src/bar + +bar_SOURCES = src/main.c +bar_LDADD = libbar.a + +src_bar_SOURCES = src/main.c +src_bar_LDADD = src/libbar.a +END + +cat > src/foo.c << 'END' +int +foo () +{ + return VAL; +} +END + +cat > src/main.c << 'END' +int foo (void); + +int +main () +{ + return foo (); +} +END + +$ACLOCAL +$AUTOCONF +$AUTOMAKE --add-missing + +./configure +$MAKE +./foo || fail_ "./foo should return 0" +./src/foo && fail_ "./src/foo should return 1" +./bar || fail_ "./bar should return 0" +./src/bar && fail_ "./src/bar should return 1" +$MAKE clean -- 2.11.0