From f060c4464605e4f016b0eae476cb4dd3f47a9b40 Mon Sep 17 00:00:00 2001
From: Giovanni Bussi <giovanni.bussi@gmail.com>
Date: Sun, 4 Feb 2018 11:37:03 +0100
Subject: [PATCH] Added static library libplumed.a

I found out that by using the pre-linked kernel.o file it is possible
to make a static library libplumed.a that is correctly linked,
including static constructors. When available, this library is used for
plumed patch --static
---
 Makefile.conf.in     |  1 +
 configure            | 76 ++++++++++++++++++++++++++++++++++++++++++++
 configure.ac         | 53 +++++++++++++++++++++++++++++-
 src/lib/Makefile     | 31 ++++++++++++++++--
 src/lib/pkgconfig.in |  1 +
 5 files changed, 158 insertions(+), 4 deletions(-)

diff --git a/Makefile.conf.in b/Makefile.conf.in
index 9d9e38dff..43fffaee0 100644
--- a/Makefile.conf.in
+++ b/Makefile.conf.in
@@ -34,3 +34,4 @@ docdir=@docdir@
 htmldir=@htmldir@
 python_bin=@PYTHON_BIN@
 mpiexec=@MPIEXEC@
+make_static_archive=@make_static_archive@
diff --git a/configure b/configure
index 9214a5ccf..60db89bf8 100755
--- a/configure
+++ b/configure
@@ -622,6 +622,7 @@ ac_subst_vars='LTLIBOBJS
 LIBOBJS
 build_dir
 program_name
+make_static_archive
 AR_CR
 LD_RO
 program_can_run_mpi
@@ -712,6 +713,7 @@ enable_dependency_tracking
 enable_rpath
 enable_ld_r
 enable_ar_cr
+enable_static_archive
 enable_mpi
 enable_external_lapack
 enable_external_blas
@@ -1381,6 +1383,8 @@ Optional Features:
   --enable-ld-r           enable group object files, default: yes
   --enable-ar-cr          enable use ar to build libplumedWrapper.a, default:
                           yes
+  --enable-static-archive enable try to build libplumed.a for static linking,
+                          default: yes
   --enable-mpi            enable search for mpi, default: yes
   --enable-external-lapack
                           enable search for external lapack, default: yes
@@ -2705,6 +2709,24 @@ fi
 
 
 
+static_archive=
+# Check whether --enable-static-archive was given.
+if test "${enable_static_archive+set}" = set; then :
+  enableval=$enable_static_archive; case "${enableval}" in
+             (yes) static_archive=true ;;
+             (no)  static_archive=false ;;
+             (*)   as_fn_error $? "wrong argument to --enable-static-archive" "$LINENO" 5 ;;
+  esac
+else
+  case "yes" in
+             (yes) static_archive=true ;;
+             (no)  static_archive=false ;;
+  esac
+
+fi
+
+
+
 mpi=
 # Check whether --enable-mpi was given.
 if test "${enable_mpi+set}" = set; then :
@@ -8794,6 +8816,60 @@ done
 
 fi
 
+make_static_archive=no
+
+
+if test "${static_archive}" == true ; then
+  if test -z "$LD_RO" || test -z "$AR_CR" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no way to create a static archive if ld -ro or ar cr do not work" >&5
+$as_echo "$as_me: WARNING: no way to create a static archive if ld -ro or ar cr do not work" >&2;}
+    static_archive=false
+  fi
+fi
+
+if test "${static_archive}" == true ; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether static-object constructors can be linked from a static archive" >&5
+$as_echo_n "checking whether static-object constructors can be linked from a static archive... " >&6; }
+
+  magic_token=c1bc476d093a3a5c67b4530e6c54c633593fe9aa
+  rm -f conftest-*
+
+  cat << EOF > conftest-main.cpp
+  void f(void);
+  int main(int argc,char**argv){ f(); return 0; }
+EOF
+  cat << EOF > conftest-f.cpp
+  void f(void){ return; }
+EOF
+  cat << EOF > conftest-g.cpp
+#include <iostream>
+  class g{
+    public:
+    g(){ std::cout<<"$magic_token\n"; }
+  } init;
+EOF
+
+  $CXX $CXXFLAGS -c conftest-main.cpp 1> /dev/null 2> /dev/null
+  $CXX $CXXFLAGS -c conftest-f.cpp 1> /dev/null 2> /dev/null
+  $CXX $CXXFLAGS -c conftest-g.cpp 1> /dev/null 2> /dev/null
+
+  $LD_RO conftest-both.o conftest-f.o conftest-g.o 1> /dev/null 2> /dev/null
+# linking the previously merged objects should work:
+  $AR_CR conftest-both.a conftest-both.o 1> /dev/null 2> /dev/null
+# something like the following, instead, should not work:
+#  $AR_CR conftest-both.a conftest-f.o conftest-g.o 1> /dev/null 2> /dev/null
+#
+  $CXX $CXXFLAGS -o conftest.exe conftest-main.o conftest-both.a 1> /dev/null 2> /dev/null
+  if grep -q $magic_token ./conftest.exe ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+    make_static_archive=yes
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+  fi
+fi
+
 if test "${static_patch}" == true ; then
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: I will now check if C++ objects can be linked by C/Fortran compilers" >&5
diff --git a/configure.ac b/configure.ac
index 6d37a79aa..81b794cdd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -239,6 +239,7 @@ PLUMED_CONFIG_ENABLE([dependency-tracking],[dependency tracking],[yes])
 PLUMED_CONFIG_ENABLE([rpath],[store rpath],[no])
 PLUMED_CONFIG_ENABLE([ld-r],[group object files],[yes])
 PLUMED_CONFIG_ENABLE([ar-cr],[use ar to build libplumedWrapper.a],[yes])
+PLUMED_CONFIG_ENABLE([static-archive],[try to build libplumed.a for static linking],[yes])
 PLUMED_CONFIG_ENABLE([mpi],[search for mpi],[yes])
 PLUMED_CONFIG_ENABLE([external-lapack],[search for external lapack],[yes])
 PLUMED_CONFIG_ENABLE([external-blas],[search for external blas],[yes])
@@ -923,7 +924,7 @@ EOF
     AC_MSG_RESULT([no])
   fi
 done
- 
+
 fi
 
 AC_SUBST(AR_CR)
@@ -967,6 +968,56 @@ done
  
 fi
 
+make_static_archive=no
+AC_SUBST(make_static_archive)
+
+if test "${static_archive}" == true ; then
+  if test -z "$LD_RO" || test -z "$AR_CR" ; then
+    AC_MSG_WARN([no way to create a static archive if ld -ro or ar cr do not work])
+    static_archive=false
+  fi
+fi
+
+if test "${static_archive}" == true ; then
+  AC_MSG_CHECKING([whether static-object constructors can be linked from a static archive])
+
+  magic_token=c1bc476d093a3a5c67b4530e6c54c633593fe9aa
+  rm -f conftest-*
+  
+  cat << EOF > conftest-main.cpp
+  void f(void);
+  int main(int argc,char**argv){ f(); return 0; }
+EOF
+  cat << EOF > conftest-f.cpp
+  void f(void){ return; }
+EOF
+  cat << EOF > conftest-g.cpp
+#include <iostream>
+  class g{
+    public:
+    g(){ std::cout<<"$magic_token\n"; }
+  } init;
+EOF
+
+  $CXX $CXXFLAGS -c conftest-main.cpp 1> /dev/null 2> /dev/null
+  $CXX $CXXFLAGS -c conftest-f.cpp 1> /dev/null 2> /dev/null
+  $CXX $CXXFLAGS -c conftest-g.cpp 1> /dev/null 2> /dev/null
+  
+  $LD_RO conftest-both.o conftest-f.o conftest-g.o 1> /dev/null 2> /dev/null
+# linking the previously merged objects should work:
+  $AR_CR conftest-both.a conftest-both.o 1> /dev/null 2> /dev/null
+# something like the following, instead, should not work:
+#  $AR_CR conftest-both.a conftest-f.o conftest-g.o 1> /dev/null 2> /dev/null
+#
+  $CXX $CXXFLAGS -o conftest.exe conftest-main.o conftest-both.a 1> /dev/null 2> /dev/null
+  if grep -q $magic_token ./conftest.exe ; then
+    AC_MSG_RESULT([yes])
+    make_static_archive=yes
+  else
+    AC_MSG_RESULT([no])
+  fi
+fi
+
 if test "${static_patch}" == true ; then
 
 AC_MSG_NOTICE([I will now check if C++ objects can be linked by C/Fortran compilers])
diff --git a/src/lib/Makefile b/src/lib/Makefile
index 340ef0b81..3f1b7c7be 100644
--- a/src/lib/Makefile
+++ b/src/lib/Makefile
@@ -96,6 +96,9 @@ install-build:
 ifdef LD_RO
 	$(MAKE) PLUMED_INSTALL=Install install/kernel.o
 endif
+ifeq ($(make_static_archive),yes)
+	$(MAKE) PLUMED_INSTALL=Install install/libplumed.a
+endif
 # modulefile
 	@cat modulefile.in | \
            sed "s|@_SOEXT_@|$(SOEXT)|" | \
@@ -108,7 +111,8 @@ endif
            sed "s|@_SOEXT_@|$(SOEXT)|" | \
            sed "s|@_program_name_@|$(program_name)|" | \
            sed "s|@_libdir_@|$(libdir)|" | \
-           sed "s|@_libs_@||" | \
+           sed "s|@_libs_@|$(LIBS) $(DYNAMIC_LIBS)|" | \
+           sed "s|@_private_libs_@||" | \
            sed "s|@_bindir_@|$(bindir)|" | \
            sed "s|@_includedir_@|$(includedir)|" | \
            sed "s|@_prefix_@|$(prefix)|" | \
@@ -123,6 +127,7 @@ endif
            sed "s|@_program_name_@|$(program_name)|" | \
            sed "s|@_libdir_@|$(libdir)|" | \
            sed "s|@_libs_@||" | \
+           sed "s|@_private_libs_@|$(LIBS) $(DYNAMIC_LIBS)|" | \
            sed "s|@_bindir_@|$(bindir)|" | \
            sed "s|@_includedir_@|$(includedir)|" | \
            sed "s|@_prefix_@|$(prefix)|" | \
@@ -137,6 +142,7 @@ endif
            sed "s|@_program_name_@|$(program_name)Wrapper|" | \
            sed "s|@_libdir_@|$(libdir)|" | \
            sed "s|@_libs_@|$(LIBS)|" | \
+           sed "s|@_private_libs_@||" | \
            sed "s|@_bindir_@|$(bindir)|" | \
            sed "s|@_includedir_@|$(includedir)|" | \
            sed "s|@_prefix_@|$(prefix)|" | \
@@ -152,6 +158,12 @@ install/kernel.o: $(OBJ_KERNEL)
 	$(LD_RO) install/kernel.o $(OBJ_KERNEL)
 endif
 
+ifeq ($(make_static_archive),yes)
+install/libplumed.a: install/kernel.o $(OBJ_WRAPPER)
+	rm -f $@
+	$(AR_CR) $@ $^
+endif
+
 # standard target (according to GNU doc)
 install-html:
 	if test -d ../../user-doc/html ; then mkdir -p "$(DESTDIR)$(htmldir)" && cd ../../ && tar cf - user-doc/html | tar xf - -C "$(DESTDIR)$(htmldir)/" ; fi
@@ -219,13 +231,16 @@ endif
 	cp ../config/compile_options.sh "$(DESTDIR)$(libdir)/$(program_name)/src/config/compile_options.sh"
 # copy config.txt file (we leave it in src/colvar/ for backward compatibility)
 	cp ../config/config.txt "$(DESTDIR)$(libdir)/$(program_name)/src/config/config.txt"
+ifeq ($(make_static_archive),yes)
+	cp install/libplumed.a "$(DESTDIR)$(libdir)/lib$(program_name).a"
+else
+	cp $(OBJ_WRAPPER) "$(DESTDIR)$(libdir)/$(program_name)/obj/PlumedStatic.o"
 ifdef LD_RO
 	cp install/kernel.o "$(DESTDIR)$(libdir)/$(program_name)/obj/kernel.o"
 else
 	../maketools/copyobjects "$(DESTDIR)$(libdir)/$(program_name)/obj/k" $(subst ../config/Config.o,../config/ConfigInstall.o,$(OBJ_KERNEL))
 endif
-	cp $(OBJ_WRAPPER) "$(DESTDIR)$(libdir)/$(program_name)/obj/PlumedStatic.o"
-	cp $(OBJ_DYNAMIC_WRAPPER) "$(DESTDIR)$(libdir)/$(program_name)/obj/Plumed.o"
+endif
 # also copy .h files into include/ dir
 	cd ../../src ; tar cf - */*.h */*/*.h | tar xf - -C "$(DESTDIR)$(includedir)/$(program_name)/"
 # install executable
@@ -251,6 +266,8 @@ ifdef SOEXT
 endif
 ifdef AR_CR
 	cp install/libplumedWrapper.a "$(DESTDIR)$(libdir)/lib$(program_name)Wrapper.a"
+else
+	cp $(OBJ_DYNAMIC_WRAPPER) "$(DESTDIR)$(libdir)/$(program_name)/obj/Plumed.o"
 endif
 # modulefile
 	cp install/modulefile "$(DESTDIR)$(libdir)/$(program_name)/modulefile"
@@ -463,22 +480,30 @@ ifdef AR_CR
 else
 	@echo "PLUMED_RUNTIME_LOAD= \"$(libdir)/$(program_name)/obj/Plumed.o\" $(LIBS) $(LDFLAGS)" > $@
 endif
+ifeq ($(make_static_archive),yes)
+	@echo "PLUMED_STATIC_LOAD= \"$(libdir)/lib$(program_name).a\" $(LIBS) $(DYNAMIC_LIBS) $(LDFLAGS)" >> $@
+else
 ifdef LD_RO
 	@echo "PLUMED_STATIC_LOAD= \"$(libdir)/$(program_name)/obj/kernel.o\" \"$(libdir)/$(program_name)/obj/PlumedStatic.o\" $(LIBS) $(DYNAMIC_LIBS) $(LDFLAGS)" >> $@
 else
 # single quote required to preserve double quote in resulting file
 	@echo "PLUMED_STATIC_LOAD= '$(shell ../maketools/listobjects "$(libdir)/$(program_name)/obj/k" $(OBJ_KERNEL))' \"$(libdir)/$(program_name)/obj/PlumedStatic.o\" $(LIBS) $(DYNAMIC_LIBS) $(LDFLAGS)" >> $@
+endif
 endif
 	@echo "PLUMED_SHARED_LOAD= \"$(libdir)/lib$(program_name).$(SOEXT)\" $(LIBS) $(LDFLAGS)" >> $@
 	@echo "PLUMED_RUNTIME_DEPENDENCIES="   >> $@
 # in principle all objects are replaced at the same time
 # however, to keep compatibility with the include files generated in plumed 2.0 and 2.1,
 # I put all the objects (or shared objects) here
+ifeq ($(make_static_archive),yes)
+	@echo "PLUMED_STATIC_DEPENDENCIES= \"$(libdir)/lib$(program_name).a\"" >> $@
+else
 ifdef LD_RO
 	@echo "PLUMED_STATIC_DEPENDENCIES= \"$(libdir)/$(program_name)/obj/kernel.o\" \"$(libdir)/$(program_name)/obj/PlumedStatic.o\"" >> $@
 else
 # single quote required to preserve double quote in resulting file
 	@echo "PLUMED_STATIC_DEPENDENCIES= '$(shell ../maketools/listobjects "$(libdir)/$(program_name)/obj/k" $(OBJ_KERNEL))' \"$(libdir)/$(program_name)/obj/PlumedStatic.o\"" >> $@
+endif
 endif
 	@echo "PLUMED_SHARED_DEPENDENCIES= \"$(libdir)/lib$(program_name).$(SOEXT)\""  >> $@
 else
diff --git a/src/lib/pkgconfig.in b/src/lib/pkgconfig.in
index 98f2c493b..d715c1a92 100644
--- a/src/lib/pkgconfig.in
+++ b/src/lib/pkgconfig.in
@@ -9,4 +9,5 @@ URL: http://www.plumed.org
 Version: @_VERSION_@
 Cflags: -I${includedir} @_cppflags_@
 Libs: -L${libdir} -l@_program_name_@ @_libs_@
+Libs.private: @_private_libs_@
 Conflicts: @_conflicts_@
-- 
GitLab