File: CMakeLists.txt

package info (click to toggle)
gromacs 2026~rc-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 274,216 kB
  • sloc: xml: 3,831,143; cpp: 686,111; ansic: 75,300; python: 21,171; sh: 3,553; perl: 2,246; yacc: 644; fortran: 397; lisp: 265; makefile: 174; lex: 125; awk: 68; csh: 39
file content (233 lines) | stat: -rw-r--r-- 11,952 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
cmake_minimum_required(VERSION 3.28)
cmake_policy(VERSION 3.28)
# If you are using this repository as a template, you should probably change the
# project name and adopt your own versioning scheme.
project(sample_restraint)

# This project requires a GROMACS supporting gmxapi 0.0.8 or higher. It should
# be sufficient to source the GMXRC, but you can also set the GROMACS_DIR
# environment variable or gmxapi_ROOT CMake variable to help CMake find the GROMACS installation.

# Note that the code will need to be built separately for different versions of Python and for substantially different
# versions of GROMACS. If building from the command line, you can specify a Python executable with the PYTHON_EXECUTABLE
# variable. For instance, to make sure you are building for your default Python, cmake -DPYTHON_EXECUTABLE=`which python`.

if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 17)
endif()
if(CMAKE_CXX_STANDARD LESS 17)
  message(FATAL_ERROR "C++17 or newer is required")
endif()
set(CMAKE_CXX_VISIBILITY_PRESET hidden)

# CMake modules are in a subdirectory to keep this file cleaner
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)

# Check if Python package is being built directly or via add_subdirectory.
# I.e. is this being built as a standalone project or as part of the GROMACS
# build tree (for testing)?
set(GMXAPI_EXTENSION_MAIN_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
    set(GMXAPI_EXTENSION_MAIN_PROJECT ON)
endif()

option(GMXAPI_EXTENSION_DOWNLOAD_PYBIND ON)
if(GMXAPI_EXTENSION_DOWNLOAD_PYBIND)
    configure_file(CMakeLists.pybind.in pybind-download/CMakeLists.txt)
    execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
                    RESULT_VARIABLE result
                    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/pybind-download)
    if(result)
        message(FATAL_ERROR "CMake step for pybind download failed: ${result}")
    endif()
    execute_process(COMMAND ${CMAKE_COMMAND} --build .
                    RESULT_VARIABLE result
                    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/pybind-download)
    if(result)
        message(FATAL_ERROR "Build step for pybind failed: ${result}")
    endif()
    add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/pybind-src)
else()
    find_package(pybind11 2.12 REQUIRED)
endif()

if(GMXAPI_EXTENSION_MAIN_PROJECT)
    ######################################################
    # The following is boiler-plate recommended by GROMACS
    ######################################################

    find_package(GROMACS REQUIRED
                 NAMES gromacs gromacs_mpi gromacs_d gromacs_mpi_d
                 HINTS "$ENV{GROMACS_DIR}"
                 )

    gromacs_check_compiler(CXX)
    include_directories(${GROMACS_INCLUDE_DIRS})
    add_definitions(${GROMACS_DEFINITIONS})

    # Use static linking on MSVC
    if (CMAKE_GENERATOR MATCHES "Visual Studio")
        string(REPLACE /MD /MT CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE})
        set(CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE} CACHE STRING "" FORCE)
        string(REPLACE /MD /MT CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG})
        set(CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG} CACHE STRING "" FORCE)
    endif()
    ######################################################

    # Note that we find GROMACS before gmxapi as a workaround for issue #4563
    # for GROMACS releases that won't be patched.

    # Assuming GROMACS is in our path or that we have set either gmxapi_ROOT or the
    # GROMACS_DIR environment variable, this will find the CMake configuration for
    # the GROMACS libraries we need and define the CMake library object Gromacs::gmxapi
    find_package(gmxapi
                 0.2.1 REQUIRED CONFIG
                 PATHS "$ENV{GROMACS_DIR}"
                 )

    message(STATUS "Found gmxapi version ${gmxapi_VERSION_MAJOR}.${gmxapi_VERSION_MINOR}.${gmxapi_VERSION_PATCH}")
endif()

########################################################

# Stuff for our plugin:
#
# If the user is not in a virtual environment and is not a privileged user and has not specified an install location
# for the Python module (GMXPLUGIN_INSTALL_PATH), this option causes the automatic install location to query the user
# site-packages directory instead of using the default site-packages directory for the interpreter.
option(GMXPLUGIN_USER_INSTALL
       "Override the default site-packages directory with the user-specific Python packages directory. \
       (Do not use with virtual environments.) \
       Has no effect if GMXPLUGIN_INSTALL_PATH is defined or cached. \
       Use -UGMXPLUGIN_INSTALL_PATH to force recalculation."
       OFF)

# Since a user may have multiple virtual environments with different Python interpreters, it is generally confusing to
# have a package for a virtual environment installed in the user's default user site-packages directory. If virtual
# environments are in use at all, we recommend you do _not_ perform a "user" install in or out of a virtual env. If you do
# not use any Python virtual environments, we recommend you _do_ perform "user" installs exclusively. Overall, we
# we recommend you use Python virtual environments and activate one before performing a regular (non-"user") install.
if (PYTHON_EXECUTABLE)
    message(STATUS "Found Python interpreter: ${PYTHON_EXECUTABLE}")
    if (PYTHON_LIBRARIES OR PythonLibs_FOUND OR pybind11_FOUND)
        if (GMXPLUGIN_USER_INSTALL)
            execute_process(COMMAND ${PYTHON_EXECUTABLE} "-m" "site" "--user-site"
                            OUTPUT_VARIABLE GMXPLUGIN_DEFAULT_SITE_PACKAGES
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
            message(STATUS "Python user site-packages directory is ${GMXPLUGIN_DEFAULT_SITE_PACKAGES}")
        else()
            execute_process(COMMAND ${PYTHON_EXECUTABLE} -c
                                "import sys; import os; \
                                print(os.path.abspath(os.path.join(sys.prefix, \
                                    'lib', \
                                    'python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}', \
                                    'site-packages')))"
                            OUTPUT_VARIABLE GMXPLUGIN_DEFAULT_SITE_PACKAGES
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
            message(STATUS "Python site-packages directory is ${GMXPLUGIN_DEFAULT_SITE_PACKAGES}")
        endif()
    else()
        message(FATAL_ERROR
            "Found Python interpreter ${PYTHON_EXECUTABLE} but this Python installation does not have developer tools."
            "Set PYTHON_EXECUTABLE to the Python interpreter that was installed with a working Python.h header file.")
    endif()
else()
    message(FATAL_ERROR "Could not find Python interpreter. Set CMake flag -DPYTHON_EXECUTABLE=/path/to/python to hint.")
endif()

# At some point this may be part of a CMake package with several components for which a single CMAKE_INSTALL_PREFIX does
# not make sense, so let's manage the install path separately.
set(GMXPLUGIN_INSTALL_PATH ${GMXPLUGIN_DEFAULT_SITE_PACKAGES} CACHE PATH
    "Path to Python module install location (site-packages). For an automatically determined install location based on \
    the Python installation, leave undefined or explicitly undefined with -UGMXPLUGIN_INSTALL_PATH and, optionally, set \
    GMXPLUGIN_USER_INSTALL on or off to specify the installation's site-packages directory or the 'user' site-packages \
    directory.")

if(GMXAPI_EXTENSION_MAIN_PROJECT)
    message(STATUS "Python module will be installed to GMXPLUGIN_INSTALL_PATH cache value ${GMXPLUGIN_INSTALL_PATH}")
endif()

# Now move on to building the custom code.
add_subdirectory(src)

# Set up documentation build targets (work in progress).
add_subdirectory(docs)

# Process CMake configuration for Python and C++ tests.
include(CTest)
if(BUILD_TESTING AND (NOT DEFINED GMX_BUILD_UNITTESTS OR GMX_BUILD_UNITTESTS))
    # Projects based on this subtree should bundle googletest sources.
    # The GROMACS project already bundles googletest sources for internal use, but
    # they will only be available to this project with some additional management by
    # the parent project CMake configuration.
    option(DOWNLOAD_GOOGLETEST "Download the version of googletest used for GROMACS testing." OFF)
    mark_as_advanced(DOWNLOAD_GOOGLETEST)


    if(DOWNLOAD_GOOGLETEST)
        # Download and unpack googletest at configure time. For simplicity, we pin to the same
        # googletest ref as in
        #    https://gitlab.com/gromacs/gromacs/-/blob/main/src/external/googletest/README.Gromacs
        # Reference https://google.github.io/googletest/quickstart-cmake.html#set-up-a-project
        include(FetchContent)
        FetchContent_Declare(
            googletest
            URL https://github.com/google/googletest/archive/v1.13.0.zip
        )
        # For Windows: Prevent overriding the parent project's compiler/linker settings
        set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
        # Note: as of CMake 3.16.3, setting policy CMP0077 to NEW does not seem to allow
        # `set(INSTALL_GTEST OFF)` to work as desired, so we have to override the option definition.
        option(INSTALL_GTEST "Override default behavior" OFF)
        FetchContent_MakeAvailable(googletest)

        add_library(GTest::GTest ALIAS gtest)
        add_library(GTest::Main ALIAS gtest_main)
    else()
        if(GMXAPI_EXTENSION_MAIN_PROJECT)
            # Allow project maintainers to bundle or provide googletest sources in
            # a subdirectory `external`.
            if (DEFINED ENV{CI})
                # In a CI environment, assume that the supporting software can be provided.
                if ("$ENV{CI_PROJECT_NAME}" STREQUAL "gromacs")
                    # We are running in a GitLab CI environment for a gromacs repository.
                    # See also admin/gitlab-ci/api-client.matrix.gitlab-ci.yml
                    file(COPY
                         $ENV{CI_PROJECT_DIR}/src/external/googletest
                         DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/external/
                         )
                endif ()
                # Note: as of CMake 3.16.3, setting policy CMP0077 to NEW does not seem to allow
                # `set(INSTALL_GTEST OFF)` to work as desired, so we have to override the option definition.
                option(INSTALL_GTEST "Override default behavior" OFF)
                add_subdirectory(external/googletest)
                add_library(GTest::GTest ALIAS gtest)
                add_library(GTest::Main ALIAS gtest_main)
            else()
                # Logic could be added here for other CI environments. Otherwise, the user is
                # responsible for helping to find GTest or copying sources to external/googletest
                find_package(GTest REQUIRED)
            endif ()
        else() # building as part of a GROMACS build...
            if(TARGET gtest)
                add_library(GTest::GTest ALIAS gtest)
                add_library(GTest::Main ALIAS gtest_main)
            else()
                find_package(GTest)
            endif()
            if(NOT TARGET GTest::GTest)
                message(FATAL_ERROR "GTest::GTest target should have been supplied by the parent project.")
            endif()
            if(NOT TARGET GTest::Main)
                message(FATAL_ERROR "GTest::Main target should have been supplied by the parent project.")
            endif()
        endif()
    endif() # DOWNLOAD_GOOGLETEST

    include(GoogleTest)
    add_subdirectory(tests)
    check_cxx_compiler_flag(-Wno-implicit-int-float-conversion HAVE_NO_IMPLICIT_INT_FLOAT_CONVERSION)
    if(HAVE_NO_IMPLICIT_FLOAT_SIZE_CONVERSION)
        target_compile_options(GTest::GTest PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wno-implicit-int-float-conversion)
    endif()
endif()