Improve libstd++ handling
authorRoland Schulz <roland.schulz@intel.com>
Mon, 28 Jan 2019 04:49:02 +0000 (20:49 -0800)
committerPaul Bauer <paul.bauer.q@gmail.com>
Tue, 19 Feb 2019 07:38:10 +0000 (08:38 +0100)
clang and icc use libstdc++ under Linux by default.

Previously such dependencies were handled differently than other
dependencies, in that we relied on the compiler autodetection, and
required the user to use the compiler flags to specify a proper
version.

Now we discover libstdc++ using cmake by finding g++, unless the user
or the toolchain they chose have already managed which gcc toolchain
to use. Then the found version is provided to the compiler with the
proper flags.

This has the advantages:
- Makes it easy for us to check the required version is used.
  Version check for libstdc++ 5 for C++14 is added.
- Makes it easy for the user to provide the correct version
  because standard cmake variables work.
- Makes build reproducible because the instance of libstdc++ used
  is constant (e.g. cached via GMX_GPLUSPLUS_PATH, or controlled
  by the toolchain, but not if the user chose non-reproducibility
  by setting CXXFLAGS environment variables). Previously, the
  auto-detection depended on the PATH environment variable at
  the time of build (not the time of the cmake run). Thus
  rebuilding with the same cache file was not guranteed to
  give the same result. It could cause cause confusing link
  errors if parts were rebuilt differently.

Also managed libc++ for clang-analyzer builds a bit better.

Fixes #2842

Change-Id: I855e16a6d4bd670cfb7acd6ea5c740f3a1b226bf

CMakeLists.txt
admin/builds/clang-analyzer.py
admin/builds/gromacs.py
cmake/FindLibStdCpp.cmake [new file with mode: 0644]
docs/install-guide/index.rst

index 1ecdc6da9b3a1a83486d529db3e2e0dbff5404ab..182dd706c0c5755ca48c3b33d810b866285770c2 100644 (file)
@@ -53,6 +53,8 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 
+find_package(LibStdCpp)
+
 # Set up common version variables, as well as general information about
 # the build tree (whether the build is from a source package or from a git
 # repository).  Also declares a few functions that will be used for generating
index 58d67ebfffaf3a8ebbd0065d067aa1d555457e3c..345ebb4015c9f661ac1ea2a59c3877bb4f668b24 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2017,2018, by the GROMACS development team, led by
+# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -37,6 +37,9 @@
 # intended to run.
 build_options = ['clang-6', 'clang-static-analyzer-6']
 
+# Policy global variables
+use_stdlib_through_env_vars = False
+
 def do_build(context):
     cmake_opts = {
             'CMAKE_BUILD_TYPE': 'Debug',
@@ -49,6 +52,7 @@ def do_build(context):
             'GMX_FFT_LIBRARY': 'fftpack',
             'GMX_CLANG_ANALYZER' : 'ON'
         }
+    context.env.append_to_env_var('CXXFLAGS', '-stdlib=libc++')
 
     context.run_cmake(cmake_opts)
     context.build_target(target=None)
index ec6fa73a5cd74e0decd1ce2e6f9823ef91ec49ca..4e265d75eedfc35a8195cde955f2a18eb78ecb3c 100644 (file)
@@ -34,6 +34,9 @@
 
 import os.path
 
+# Policy global variables
+use_stdlib_through_env_vars = False
+
 # These are accessible later in the script, just like other
 # declared options, via e.g. context.opts.release.
 extra_options = {
@@ -167,6 +170,22 @@ def do_build(context):
         if context.opts.mdrun_only:
             cmake_opts['GMX_BUILD_MDRUN_ONLY'] = 'ON'
 
+    # The build configuration has constructed the environment of the
+    # context so that a particular c++ standard library can be used,
+    # which may come from a different installation of gcc. Here, we
+    # tell CMake how to react to this.
+    #
+    # TODO Once gerrit 9051 and 9053 are both submitted on master,
+    # remove the hasattr part of the predicate, which will then be
+    # redundant.
+    if hasattr(context.env, 'gcc_exe') and context.env.gcc_exe is not None:
+        cmake_opts['GMX_GPLUSPLUS_PATH'] = context.env.gcc_exe
+        # TODO are these needed?
+        # gcc_exe_dirname = os.path.dirname(self.gcc_exe)
+        # gcc_toolchain_path = os.path.join(gcc_exe_dirname, '..')
+        # format_for_linker_flags="-Wl,-rpath,{gcctoolchain}/lib64 -L{gcctoolchain}/lib64"
+        # cmake_opts['CMAKE_CXX_LINK_FLAGS'] = format_for_linker_flags.format(gcctoolchain=gcc_toolchain_path)
+
     context.env.set_env_var('GMX_NO_TERM', '1')
 
     context.run_cmake(cmake_opts)
diff --git a/cmake/FindLibStdCpp.cmake b/cmake/FindLibStdCpp.cmake
new file mode 100644 (file)
index 0000000..a218508
--- /dev/null
@@ -0,0 +1,164 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2019, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+# For compilers which might require libstdc++ (Clang and Intel), find it and set CMAKE_CXX_FLAGS.
+#
+# Does nothing if compiler includes std-library (e.g. GCC) or compiler uses different std-library
+# (either because of different defaults (e.g. on MacOS) or user flags (e.g. -stdlib=libc++)).
+# The heuristic by the compiler of how to find libstdc++ is ignored. Any user-provided flags in
+# e.g. CXXFLAGS for the location of libstdc++ are honored. The user can choose the libstdc++ by setting
+# GMX_GPLUSPLUS_PATH, PATH or CMAKE_PREFIX_PATH to make sure the correct the g++ is found.
+# Gives error if no g++ is found or the g++ found isn't new enough (5.1 is required).
+# The location of g++ is cached as GMX_GPLUSPLUS_PATH making sure that the same libstdc++ is used
+# for builds at different times using the same cache file (so that e.g. module loading is
+# not required for a reproducible build).
+
+if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Intel") # Compilers supported
+    return()
+endif()
+
+# If the compiler isn't using libstdc++ (via its heuristics), then the
+# compiler is set up to use some other standard library (e.g. libc++
+# or the MSVC library). If so, then there is nothing to manage. Note
+# that at this point we don't care *which* libstdc++ (or version) is
+# found.
+include(CheckCXXSourceCompiles)
+check_cxx_source_compiles("#include <new>
+int main() { return __GLIBCXX__; }" USING_LIBSTDCXX)
+
+if (NOT USING_LIBSTDCXX)
+    return()
+endif()
+
+if (DEFINED GMX_GPLUSGPLUS_PATH)
+    set(EXTRA_MESSAGE ", ignoring the value of GMX_GPLUSPLUS_PATH")
+endif()
+string(TOUPPER "${CMAKE_BUILD_TYPE}" _cmake_build_type)
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+   if ("${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${_cmake_build_type}}" MATCHES "--gcc-toolchain")
+       message(STATUS "The --gcc-toolchain option is already present in the CMAKE_CXX_FLAGS "
+           "(or perhaps those specific to the CMAKE_BUILD_TYPE), and the GROMACS build "
+           "will use that one${EXTRA_MESSAGE}.")
+   else()
+       set(NEED_TO_FIND_GPLUSPLUS TRUE)
+   endif()
+else() #Intel
+   if ("${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${_cmake_build_type}}" MATCHES "-gcc-name")
+       message(STATUS "The -gcc-name option is already present in the CMAKE_CXX_FLAGS "
+           "(or perhaps those specific to the CMAKE_BUILD_TYPE), and the GROMACS build "
+           "will use that one${EXTRA_MESSAGE}.")
+   else()
+       set(NEED_TO_FIND_GPLUSPLUS TRUE)
+   endif()
+endif()
+
+if(NEED_TO_FIND_GPLUSPLUS)
+    # Find a gcc (perhaps already specified by the user in
+    # GMX_GPLUSPLUS_PATH) and prepare to reproducibly use its libstdc++.
+    find_program(GMX_GPLUSPLUS_PATH g++)
+    if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+        set(EXTRA_MESSAGE
+            " Clang supports using libc++ (CXXFLAGS=--stdlib=libc++), and if so there will be no need to find g++.")
+    endif()
+    if (NOT EXISTS "${GMX_GPLUSPLUS_PATH}")
+        message(FATAL_ERROR "Couldn't find g++. Please set GMX_GPLUSPLUS_PATH, PATH or CMAKE_PREFIX_PATH "
+            "accordingly for cmake to find it.${EXTRA_MESSAGE}")
+    endif()
+
+    # Ensure that a suitable version of g++ was found, caching the
+    # result for future configuration stages.
+    if (NOT GMX_GPLUSPLUS_VERSION)
+        execute_process(COMMAND ${GMX_GPLUSPLUS_PATH} -dumpfullversion -dumpversion OUTPUT_VARIABLE GMX_GPLUSPLUS_VERSION
+            ERROR_VARIABLE GMX_GPLUSPLUS_VERSION_ERROR
+            OUTPUT_STRIP_TRAILING_WHITESPACE)
+        if (NOT "${GMX_GPLUSPLUS_VERSION}" MATCHES "^[0-9]+\\.[0-9]+\\.?[0-9]?$") #Should never happen
+            message(FATAL_ERROR "Couldn't detect g++ version for ${GMX_GPLUSPLUS_PATH}. Version output: ${GMX_GPLUSPLUS_VERSION} "
+                ", error: ${GMX_GPLUSPLUS_VERSION_ERROR}. Please report to developers.${EXTRA_MESSAGE}")
+        endif()
+        # Cache this, so future configurations won't have to run g++ again.
+        set(GMX_GPLUSPLUS_VERSION ${GMX_GPLUSPLUS_VERSION} CACHE STRING "Version of g++ from which libstdc++ is obtained")
+    endif()
+    if (${GMX_GPLUSPLUS_VERSION} VERSION_LESS 5.1)
+        set(FATAL_ERROR_MESSAGE "Found g++ at ${GMX_GPLUSPLUS_PATH}. Its version is ${GMX_GPLUSPLUS_VERSION}. "
+            "GROMACS requires at least version 5.1. "
+            "Please specify a different g++ using GMX_GPLUSPLUS_PATH, PATH or CMAKE_PREFIX_PATH.${EXTRA_MESSAGE}")
+        # Be helpful and don't require the user to unset these manually.
+        unset(GMX_GPLUSPLUS_PATH CACHE)
+        unset(GMX_GPLUSPLUS_VERSION CACHE)
+        message(FATAL_ERROR ${FATAL_ERROR_MESSAGE})
+    endif()
+
+    # Now make some sanity checks on the compiler using libstdc++.
+    if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+        get_filename_component(GMX_GPLUSPLUS_PATH "${GMX_GPLUSPLUS_PATH}" REALPATH)
+        get_filename_component(GMX_GPLUSPLUS_PATH "${GMX_GPLUSPLUS_PATH}" DIRECTORY) #strip g++
+        get_filename_component(GMX_GPLUSPLUS_PATH "${GMX_GPLUSPLUS_PATH}" DIRECTORY) #strip bin
+        if (NOT EXISTS "${GMX_GPLUSPLUS_PATH}/include/c++")
+            message(FATAL_ERROR "${GMX_GPLUSPLUS_PATH}/include/c++ doesn't exist even though it should. "
+                "Please report to developers.")
+        endif()
+    else() #Intel
+        if (${GMX_GPLUSPLUS_VERSION} VERSION_GREATER_EQUAL 7 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19)
+            message(FATAL_ERROR "ICC versions below 19 don't support GCC versions above 6.")
+        endif ()
+    endif()
+
+    # Set up to use the libstdc++ from that g++. Note that we checked
+    # the existing contents of CMAKE_CXX_FLAGS* variables earlier, so
+    # we will not override any user settings here.
+    if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${GMX_GPLUSPLUS_PATH}")
+    else() #Intel
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gcc-name=${GMX_GPLUSPLUS_PATH}")
+    endif()
+endif()
+
+# Now run a sanity check on the compiler using libstdc++, regardless
+# of how it was specified or found.
+
+# Test a feature which was added in libstdc++ 5
+check_cxx_source_compiles("#include <iterator>
+int main() { int a[2]; std::cbegin(a); }" CXX14_COMPILES)
+
+if (NOT CXX14_COMPILES)
+    if (NEED_TO_FIND_GPLUSPLUS)
+        set (EXTRA_MESSAGE " The g++ found at ${GMX_GPLUSPLUS_PATH} had a suitable version, so "
+            "something else must be the problem")
+    else()
+        set (EXTRA_MESSAGE " Check your toolchain documentation or environment flags so that "
+            "they will find a suitable C++14 standard library")
+    endif()
+    message(FATAL_ERROR "GROMACS requires C++14, but a test of such functionality in the C++ standard "
+        "library failed to compile.${EXTRA_MESSAGE}")
+endif()
index fc7e8c93be4c50e30162986e4fc7bcf445422463..20d3b60d44c9174a0997d2aea3a34f5bd0af3485 100644 (file)
@@ -133,22 +133,15 @@ these are often shipped by your OS distribution's binutils package.
 
 C++14 support requires adequate support in both the compiler and the
 C++ library. The gcc and MSVC compilers include their own standard
-libraries and require no further configuration. For configuration of
-other compilers, read on.
+libraries and require no further configuration. If your vendor's
+compiler also manages the standard library library via compiler flags,
+these will be honored. For configuration of other compilers, read on.
 
 On Linux, both the Intel and clang compiler use the libstdc++ which
 comes with gcc as the default C++ library. For |Gromacs|, we require
 the compiler to support libstc++ version 5.1 or higher. To select a
-particular libstdc++ library, use:
-
-* For Intel: ``-DCMAKE_CXX_FLAGS=-gcc-name=/path/to/gcc/binary``
-  or make sure that the correct gcc version is first in path (e.g. by
-  loading the gcc module). It can also be useful to add
-  ``-DCMAKE_CXX_LINK_FLAGS="-Wl,-rpath,/path/to/gcc/lib64
-  -L/path/to/gcc/lib64"`` to ensure linking works correctly.
-* For clang:
-  ``-DCMAKE_CXX_FLAGS=--gcc-toolchain=/path/to/gcc/folder``. This
-  folder should contain ``include/c++``.
+particular libstdc++ library, provide the path to g++ with
+``-DGMX_GPLUSPLUS_PATH=/path/to/g++``.
 
 On Windows with the Intel compiler, the MSVC standard library is used,
 and at least MSVC 2017 is required. Load the enviroment variables with