Use CMake to propagate versions and hashes to gitlab jobs
[alexxy/gromacs.git] / cmake / FindLibStdCpp.cmake
1 #
2 # This file is part of the GROMACS molecular simulation package.
3 #
4 # Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
5 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 # and including many others, as listed in the AUTHORS file in the
7 # top-level source directory and at http://www.gromacs.org.
8 #
9 # GROMACS is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public License
11 # as published by the Free Software Foundation; either version 2.1
12 # of the License, or (at your option) any later version.
13 #
14 # GROMACS is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with GROMACS; if not, see
21 # http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23 #
24 # If you want to redistribute modifications to GROMACS, please
25 # consider that scientific software is very special. Version
26 # control is crucial - bugs must be traceable. We will be happy to
27 # consider code for inclusion in the official distribution, but
28 # derived work must not be called official GROMACS. Details are found
29 # in the README & COPYING files - if they are missing, get the
30 # official version at http://www.gromacs.org.
31 #
32 # To help us fund GROMACS development, we humbly ask that you cite
33 # the research papers on the package. Check out http://www.gromacs.org.
34
35 # For compilers which might require libstdc++ (Clang and Intel), and do
36 # not already work, find it and set CMAKE_CXX_FLAGS.
37 #
38 # Does nothing if compiler includes std-library (e.g. GCC), or already
39 # works, or compiler uses different std-library
40 # (either because of different defaults (e.g. on MacOS) or user flags (e.g. -stdlib=libc++)).
41 # The heuristic by the compiler of how to find libstdc++ is ignored. Any user-provided flags in
42 # e.g. CXXFLAGS for the location of libstdc++ are honored. The user can choose the libstdc++ by setting
43 # GMX_GPLUSPLUS_PATH, PATH or CMAKE_PREFIX_PATH to make sure the correct the g++ is found.
44 # Gives error if no g++ is found or the g++ found isn't new enough (5.1 is required).
45 # The location of g++ is cached as GMX_GPLUSPLUS_PATH making sure that the same libstdc++ is used
46 # for builds at different times using the same cache file (so that e.g. module loading is
47 # not required for a reproducible build). Note that GMX_GPLUSPLUS_PATH is ignored if it is
48 # not needed because the compiler already found a std library via some other mechanism.
49
50 if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Intel") # Compilers supported
51     return()
52 endif()
53
54 include(CheckCXXSourceCompiles)
55
56 # Test that required 2017 standard library features work.
57 # Note that this check also requires linking to succeed.
58 set (SAMPLE_CODE_TO_TEST_CXX17 "
59 #include <string>
60 #include <string_view>
61 #include <optional>
62 int main(int argc, char **argv) {
63   std::optional<std::string> input(argv[0]);
64   std::string_view view(input.value());
65   return int(view[0]);
66 }")
67 check_cxx_source_compiles("${SAMPLE_CODE_TO_TEST_CXX17}" CXX17_COMPILES)
68
69 if (CXX17_COMPILES)
70     # The compiler has been set up properly to find a standard
71     # library, and if so GROMACS should leave it alone.
72     return()
73 endif()
74
75 # The compiler couldn't use the standard libary for an unknown reason.
76 # See if the compiler is using libstdc++ (via libstc++ heuristics). If so,
77 # then we may be able to help the compiler find the standard library.
78 check_cxx_source_compiles("#include <new>
79 int main() { return __GLIBCXX__; }" USING_LIBSTDCXX)
80
81 if (NOT USING_LIBSTDCXX)
82     message(FATAL_ERROR "The C++ compiler cannot find a working standard library and it is "
83             "not libstdc++. The GROMACS build cannot handle this case. Please use a working "
84             "C++17 compiler and standard library.")
85 endif()
86
87 if (DEFINED GMX_GPLUSGPLUS_PATH)
88     set(EXTRA_MESSAGE ", ignoring the value of GMX_GPLUSPLUS_PATH")
89 endif()
90 string(TOUPPER "${CMAKE_BUILD_TYPE}" _cmake_build_type)
91 if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM")
92    if ("${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${_cmake_build_type}}" MATCHES "--gcc-toolchain")
93        message(STATUS "The --gcc-toolchain option is already present in the CMAKE_CXX_FLAGS "
94            "(or perhaps those specific to the CMAKE_BUILD_TYPE), and the GROMACS build "
95            "will use that one${EXTRA_MESSAGE}.")
96    else()
97        set(NEED_TO_FIND_GPLUSPLUS TRUE)
98    endif()
99 else() #Intel
100    if ("${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${_cmake_build_type}}" MATCHES "-gcc-name")
101        message(STATUS "The -gcc-name option is already present in the CMAKE_CXX_FLAGS "
102            "(or perhaps those specific to the CMAKE_BUILD_TYPE), and the GROMACS build "
103            "will use that one${EXTRA_MESSAGE}.")
104    else()
105        set(NEED_TO_FIND_GPLUSPLUS TRUE)
106    endif()
107 endif()
108
109 if(NEED_TO_FIND_GPLUSPLUS)
110     # Find a gcc (perhaps already specified by the user in
111     # GMX_GPLUSPLUS_PATH) and prepare to reproducibly use its libstdc++.
112     find_program(GMX_GPLUSPLUS_PATH g++)
113     if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM")
114         set(EXTRA_MESSAGE
115             " Clang supports using libc++ with -DCMAKE_CXX_FLAGS=--stdlib=libc++, and if so there will be no need to find g++.")
116     endif()
117     if (NOT EXISTS "${GMX_GPLUSPLUS_PATH}")
118         message(FATAL_ERROR "Couldn't find g++. Please set GMX_GPLUSPLUS_PATH, PATH or CMAKE_PREFIX_PATH "
119             "accordingly for cmake to find it.${EXTRA_MESSAGE}")
120     endif()
121
122     # Ensure that a suitable version of g++ was found, caching the
123     # result for future configuration stages.
124     if (NOT GMX_GPLUSPLUS_VERSION)
125         execute_process(COMMAND ${GMX_GPLUSPLUS_PATH} -dumpfullversion -dumpversion OUTPUT_VARIABLE GMX_GPLUSPLUS_VERSION
126             ERROR_VARIABLE GMX_GPLUSPLUS_VERSION_ERROR
127             OUTPUT_STRIP_TRAILING_WHITESPACE)
128         if (NOT "${GMX_GPLUSPLUS_VERSION}" MATCHES "^[0-9]+\\.[0-9]+\\.?[0-9]?$") #Should never happen
129             message(FATAL_ERROR "Couldn't detect g++ version for ${GMX_GPLUSPLUS_PATH}. Version output: ${GMX_GPLUSPLUS_VERSION} "
130                 ", error: ${GMX_GPLUSPLUS_VERSION_ERROR}. Please report to developers.${EXTRA_MESSAGE}")
131         endif()
132         # Cache this, so future configurations won't have to run g++ again.
133         set(GMX_GPLUSPLUS_VERSION ${GMX_GPLUSPLUS_VERSION} CACHE STRING "Version of g++ from which libstdc++ is obtained")
134     endif()
135     if (${GMX_GPLUSPLUS_VERSION} VERSION_LESS 5.1)
136         set(FATAL_ERROR_MESSAGE "Found g++ at ${GMX_GPLUSPLUS_PATH}. Its version is ${GMX_GPLUSPLUS_VERSION}. "
137             "GROMACS requires at least version 5.1. "
138             "Please specify a different g++ using GMX_GPLUSPLUS_PATH, PATH or CMAKE_PREFIX_PATH.${EXTRA_MESSAGE}")
139         # Be helpful and don't require the user to unset these manually.
140         unset(GMX_GPLUSPLUS_PATH CACHE)
141         unset(GMX_GPLUSPLUS_VERSION CACHE)
142         message(FATAL_ERROR ${FATAL_ERROR_MESSAGE})
143     endif()
144
145     # Now make some sanity checks on the compiler using libstdc++.
146     if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM")
147         get_filename_component(GMX_GPLUSPLUS_PATH "${GMX_GPLUSPLUS_PATH}" REALPATH)
148         get_filename_component(GMX_GPLUSPLUS_PATH "${GMX_GPLUSPLUS_PATH}" DIRECTORY) #strip g++
149         get_filename_component(GMX_GPLUSPLUS_PATH "${GMX_GPLUSPLUS_PATH}" DIRECTORY) #strip bin
150         if (NOT EXISTS "${GMX_GPLUSPLUS_PATH}/include/c++")
151             message(FATAL_ERROR "${GMX_GPLUSPLUS_PATH}/include/c++ doesn't exist even though it should. "
152                 "Please report to developers.")
153         endif()
154     endif()
155
156     # Set up to use the libstdc++ from that g++. Note that we checked
157     # the existing contents of CMAKE_CXX_FLAGS* variables earlier, so
158     # we will not override any user settings here.
159     if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM")
160         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${GMX_GPLUSPLUS_PATH}")
161     endif()
162 endif()
163
164 # Now run a sanity check on the compiler using libstdc++, regardless
165 # of how it was specified or found.
166
167 # Test required 2017 standard library features again.
168 unset(CXX17_COMPILES CACHE)
169 check_cxx_source_compiles("${SAMPLE_CODE_TO_TEST_CXX17}" CXX17_COMPILES)
170
171 if (NOT CXX17_COMPILES)
172     if (NEED_TO_FIND_GPLUSPLUS)
173         set (EXTRA_MESSAGE " The g++ found at ${GMX_GPLUSPLUS_PATH} had a suitable version, so "
174             "something else must be the problem")
175     else()
176         set (EXTRA_MESSAGE " Check your toolchain documentation or environment flags so that "
177             "they will find a suitable C++17 standard library")
178     endif()
179     message(FATAL_ERROR "GROMACS requires C++17, but a test of such functionality in the C++ standard "
180         "library failed to compile.${EXTRA_MESSAGE}")
181 endif()