CMake 秘籍(五)(2)

简介: CMake 秘籍(五)

CMake 秘籍(五)(1)https://developer.aliyun.com/article/1524578

工作原理

本示例展示了如何下载、构建和安装由 CMake 管理的构建系统的外部项目。与之前的示例不同,那里必须使用自定义构建系统,这种超级构建设置相对简洁。值得注意的是,find_package命令使用了CONFIG选项;这告诉 CMake 首先查找FFTW3Config.cmake文件以定位 FFTW3 库。这样的文件将库作为目标导出,供第三方项目使用。目标包含版本、配置和库的位置,即有关目标如何配置和构建的完整信息。如果系统上未安装该库,我们需要告诉 CMakeFFTW3Config.cmake文件的位置。这可以通过设置FFTW3_DIR变量来完成。这是在external/upstream/fftw3/CMakeLists.txt文件的最后一步,通过使用GNUInstallDirs.cmake模块,我们将FFTW3_DIR设置为缓存变量,以便稍后在超级构建中被拾取。

在配置项目时将CMAKE_DISABLE_FIND_PACKAGE_FFTW3设置为ON,将跳过 FFTW 库的检测并始终执行超级构建。请参阅文档:cmake.org/cmake/help/v3.5/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.html

使用超级构建管理依赖项:III. Google Test 框架

本示例的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-08/recipe-04找到,并包含一个 C++示例。该示例适用于 CMake 版本 3.11(及以上),并在 GNU/Linux、macOS 和 Windows 上进行了测试。代码仓库还包含一个与 CMake 3.5 兼容的示例。

在第四章,创建和运行测试,第 3 个菜谱,定义单元测试并链接到 Google Test,我们使用 Google Test 框架实现了单元测试,并在配置时使用相对较新的FetchContent模块(自 CMake 3.11 起可用)获取了 Google Test 源码。在本章中,我们将重温这个菜谱,减少对测试方面的关注,并深入探讨FetchContent,它提供了一个紧凑且多功能的模块,用于在配置时组装项目依赖。为了获得更多见解,以及对于 CMake 3.11 以下的版本,我们还将讨论如何使用ExternalProject_Add 在配置时模拟FetchContent

准备工作

在本菜谱中,我们将构建并测试与第四章,创建和运行测试,第 3 个菜谱,定义单元测试并链接到 Google Test中相同的源文件,main.cppsum_integers.cppsum_integers.hpptest.cpp。我们将使用FetchContentExternalProject_Add在配置时下载所有必需的 Google Test 源码,并且在本菜谱中只关注在配置时获取依赖,而不是实际的源码及其单元测试。

如何操作

在本菜谱中,我们将只关注如何获取 Google Test 源码以构建gtest_main目标。关于如何使用该目标测试示例源码的讨论,我们请读者参考第四章,创建和运行测试,第 3 个菜谱,定义单元测试并链接到 Google Test

  1. 我们首先包含FetchContent模块,它将提供我们所需的函数来声明、查询和填充依赖:
include(FetchContent)
  1. 接着,我们声明内容——其名称、仓库位置以及要获取的确切版本:
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG release-1.8.0
)
  1. 然后我们查询内容是否已经被获取/填充:
FetchContent_GetProperties(googletest)
  1. 之前的函数调用定义了googletest_POPULATED。如果内容尚未填充,我们将获取内容并配置子项目:
if(NOT googletest_POPULATED)
  FetchContent_Populate(googletest)
  # ...
  # adds the targets: gtest, gtest_main, gmock, gmock_main
  add_subdirectory(
    ${googletest_SOURCE_DIR}
    ${googletest_BINARY_DIR}
    )
  # ...
endif()
  1. 注意内容是在配置时获取的:
$ mkdir -p build
$ cd build
$ cmake ..
  1. 这将生成以下构建目录树。Google Test 源码现在已就位,可以由 CMake 处理并提供所需的目标:
build/
├── ...
├── _deps
│   ├── googletest-build
│   │   ├── ...
│   │   └── ...
│   ├── googletest-src
│   │   ├── ...
│   │   └── ...
│   └── googletest-subbuild
│       ├── ...
│       └── ...
└── ...

它是如何工作的

FetchContent模块允许在配置时填充内容。在我们的例子中,我们获取了一个带有明确 Git 标签的 Git 仓库:

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG release-1.8.0
)

FetchContent模块支持通过ExternalProject模块支持的任何方法获取内容 - 换句话说,通过Subversion、Mercurial、CVS 或 HTTP(S)。内容名称“googletest”是我们的选择,有了这个,我们将能够在查询其属性、填充目录以及稍后配置子项目时引用内容。在填充项目之前,我们检查内容是否已经获取,否则如果FetchContent_Populate()被调用超过一次,它将抛出错误:

if(NOT googletest_POPULATED)
  FetchContent_Populate(googletest)
  # ...
endif()

只有在那时我们才配置了子目录,我们可以通过googletest_SOURCE_DIRgoogletest_BINARY_DIR变量来引用它。这些变量是由FetchContent_Populate(googletest)设置的,并根据我们在声明内容时给出的项目名称构建的。

add_subdirectory(
  ${googletest_SOURCE_DIR}
  ${googletest_BINARY_DIR}
  )

FetchContent模块有许多选项(参见cmake.org/cmake/help/v3.11/module/FetchContent.html),这里我们可以展示一个:如何更改外部项目将被放置的默认路径。之前,我们看到默认情况下内容被保存到${CMAKE_BINARY_DIR}/_deps。我们可以通过设置FETCHCONTENT_BASE_DIR来更改此位置:

set(FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/custom)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG release-1.8.0
)

FetchContent已成为 CMake 3.11 版本中的标准部分。在下面的代码中,我们将尝试在配置时间使用ExternalProject_Add来模拟FetchContent。这不仅对旧版本的 CMake 实用,而且有望让我们更深入地了解FetchContent层下面发生的事情,并提供一个有趣的替代方案,以替代使用ExternalProject_Add在构建时间获取项目的典型方式。我们的目标是编写一个fetch_git_repo宏,并将其放置在fetch_git_repo.cmake中,以便我们可以这样获取内容:

include(fetch_git_repo.cmake)
fetch_git_repo(
  googletest
  ${CMAKE_BINARY_DIR}/_deps
  https://github.com/google/googletest.git
  release-1.8.0
)
# ...
# adds the targets: gtest, gtest_main, gmock, gmock_main
add_subdirectory(
  ${googletest_SOURCE_DIR}
  ${googletest_BINARY_DIR}
  )
# ...

这感觉类似于使用FetchContent。在幕后,我们将使用ExternalProject_Add。现在让我们揭开盖子,检查fetch_git_repofetch_git_repo.cmake中的定义:

macro(fetch_git_repo _project_name _download_root _git_url _git_tag)
  set(${_project_name}_SOURCE_DIR ${_download_root}/${_project_name}-src)
  set(${_project_name}_BINARY_DIR ${_download_root}/${_project_name}-build)
  # variables used configuring fetch_git_repo_sub.cmake
  set(FETCH_PROJECT_NAME ${_project_name})
  set(FETCH_SOURCE_DIR ${${_project_name}_SOURCE_DIR})
  set(FETCH_BINARY_DIR ${${_project_name}_BINARY_DIR})
  set(FETCH_GIT_REPOSITORY ${_git_url})
  set(FETCH_GIT_TAG ${_git_tag})
  configure_file(
    ${CMAKE_CURRENT_LIST_DIR}/fetch_at_configure_step.in
    ${_download_root}/CMakeLists.txt
    @ONLY
    )
  # undefine them again
  unset(FETCH_PROJECT_NAME)
  unset(FETCH_SOURCE_DIR)
  unset(FETCH_BINARY_DIR)
  unset(FETCH_GIT_REPOSITORY)
  unset(FETCH_GIT_TAG)
  # configure sub-project
  execute_process(
    COMMAND
      "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
    WORKING_DIRECTORY
      ${_download_root}
    )
  # build sub-project which triggers ExternalProject_Add
  execute_process(
    COMMAND
      "${CMAKE_COMMAND}" --build .
    WORKING_DIRECTORY
      ${_download_root}
    )
endmacro()

宏接收项目名称、下载根目录、Git 仓库 URL 和 Git 标签。宏定义了${_project_name}_SOURCE_DIR${_project_name}_BINARY_DIR,我们使用宏而不是函数,因为${_project_name}_SOURCE_DIR${_project_name}_BINARY_DIR需要在fetch_git_repo的作用域之外存活,因为我们稍后在主作用域中使用它们来配置子目录:

add_subdirectory(
  ${googletest_SOURCE_DIR}
  ${googletest_BINARY_DIR}
  )

fetch_git_repo宏内部,我们希望使用ExternalProject_Add配置时间获取外部项目,我们通过一个三步的技巧来实现这一点:

  1. 首先,我们配置fetch_at_configure_step.in
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(fetch_git_repo_sub LANGUAGES NONE)
include(ExternalProject)
ExternalProject_Add(
  @FETCH_PROJECT_NAME@
  SOURCE_DIR "@FETCH_SOURCE_DIR@"
  BINARY_DIR "@FETCH_BINARY_DIR@"
  GIT_REPOSITORY
    @FETCH_GIT_REPOSITORY@
  GIT_TAG
    @FETCH_GIT_TAG@
  CONFIGURE_COMMAND ""
  BUILD_COMMAND ""
  INSTALL_COMMAND ""
  TEST_COMMAND ""
  )

使用configure_file,我们生成一个CMakeLists.txt文件,其中之前的占位符被替换为在fetch_git_repo.cmake中定义的值。注意,之前的ExternalProject_Add命令被构造为仅获取,而不进行配置、构建、安装或测试。

  1. 其次,我们在配置时间(从根项目的角度)使用配置步骤触发ExternalProject_Add
# configure sub-project
execute_process(
  COMMAND
    "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . 
  WORKING_DIRECTORY
    ${_download_root}
  ) 
  1. 第三个也是最后一个技巧在fetch_git_repo.cmake中触发配置时间构建步骤:
# build sub-project which triggers ExternalProject_Add
execute_process(
  COMMAND
    "${CMAKE_COMMAND}" --build . 
  WORKING_DIRECTORY
    ${_download_root}
  )

这个解决方案的一个很好的方面是,由于外部依赖项不是由ExternalProject_Add配置的,我们不需要通过ExternalProject_Add调用将任何配置设置传递给项目。我们可以使用add_subdirectory配置和构建模块,就好像外部依赖项是我们项目源代码树的一部分一样。巧妙的伪装!

另请参阅

有关可用的FetchContent选项的详细讨论,请咨询cmake.org/cmake/help/v3.11/module/FetchContent.html

配置时间ExternalProject_Add解决方案的灵感来自 Craig Scott 的工作和博客文章:crascit.com/2015/07/25/cmake-gtest/

将您的项目作为超级构建进行管理

本示例的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-08/recipe-05获取,并且有一个 C++示例。本示例适用于 CMake 版本 3.6(及更高版本),并在 GNU/Linux、macOS 和 Windows 上进行了测试。

ExternalProjectFetchContent是 CMake 工具箱中的两个非常强大的工具。之前的示例应该已经说服了您超级构建方法在管理具有复杂依赖关系的项目方面的多功能性。到目前为止,我们已经展示了如何使用ExternalProject来处理以下内容:

  • 存储在您的源代码树中的源代码
  • 从在线服务器上的档案中检索来源

之前的示例展示了如何使用FetchContent来处理来自开源 Git 存储库的依赖项。本示例将展示如何使用ExternalProject达到相同的效果。最后一个示例将介绍一个将在第 4 个示例中重复使用的示例,即安装超级构建,在第十章,编写安装程序

准备工作

这个超级构建的源代码树现在应该感觉很熟悉:

.
├── CMakeLists.txt
├── external
│   └── upstream
│       ├── CMakeLists.txt
│       └── message
│           └── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    └── use_message.cpp

根目录有一个CMakeLists.txt,我们已经知道它将协调超级构建。叶目录srcexternal托管我们自己的源代码和满足对message库的依赖所需的 CMake 指令,我们将在本示例中构建该库。

如何操作

到目前为止,设置超级构建的过程应该感觉很熟悉。让我们再次看一下必要的步骤,从根CMakeLists.txt开始:

  1. 我们声明了一个具有相同默认构建类型的 C++11 项目:
cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
project(recipe-05 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT DEFINED CMAKE_BUILD_TYPE OR "${CMAKE_BUILD_TYPE}" STREQUAL "")
  set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
message(STATUS "Build type set to ${CMAKE_BUILD_TYPE}")
  1. 设置了EP_BASE目录属性。这将固定由ExternalProject管理的所有子项目的布局:
set_property(DIRECTORY PROPERTY EP_BASE ${CMAKE_BINARY_DIR}/subprojects)
  1. 我们设置了STAGED_INSTALL_PREFIX。与之前一样,此位置将用作构建树中依赖项的安装前缀:
set(STAGED_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/stage)
message(STATUS "${PROJECT_NAME} staged install: ${STAGED_INSTALL_PREFIX}")
  1. 我们添加external/upstream子目录:
add_subdirectory(external/upstream)
  1. 我们自己的项目也将由超级构建管理,因此使用ExternalProject_Add添加:
include(ExternalProject)
ExternalProject_Add(${PROJECT_NAME}_core
  DEPENDS
    message_external
  SOURCE_DIR
    ${CMAKE_CURRENT_SOURCE_DIR}/src
  CMAKE_ARGS
    -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
    -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
    -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
    -DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
    -DCMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}
    -Dmessage_DIR=${message_DIR}
  CMAKE_CACHE_ARGS
    -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
    -DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH}
  BUILD_ALWAYS
    1
  INSTALL_COMMAND
    ""
  )

external/upstream中的CMakeLists.txt只包含一个命令:

add_subdirectory(message)

跳转到message文件夹,我们再次看到管理我们对message库依赖的常用命令:

  1. 首先,我们调用find_package来找到一个合适的库版本:
find_package(message 1 CONFIG QUIET)
  1. 如果找到,我们通知用户并添加一个虚拟的INTERFACE库:
get_property(_loc TARGET message::message-shared PROPERTY LOCATION)
message(STATUS "Found message: ${_loc} (found version ${message_VERSION})")
add_library(message_external INTERFACE) # dummy
  1. 如果未找到,我们再次通知用户并继续使用ExternalProject_Add
message(STATUS "Suitable message could not be located, Building message instead.")
  1. 该项目托管在一个公共 Git 仓库中,我们使用GIT_TAG选项来指定下载哪个分支。像之前一样,我们让UPDATE_COMMAND选项保持空白:
include(ExternalProject)
ExternalProject_Add(message_external
  GIT_REPOSITORY
    https://github.com/dev-cafe/message.git
  GIT_TAG
    master
  UPDATE_COMMAND
    ""
  1. 外部项目使用 CMake 进行配置和构建。我们传递所有必要的构建选项:
CMAKE_ARGS
   -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}
   -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
   -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
   -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
   -DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
   -DCMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}
 CMAKE_CACHE_ARGS
   -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
  1. 我们决定在项目安装后进行测试:
TEST_AFTER_INSTALL
    1
  1. 我们不希望看到下载进度,也不希望屏幕上显示配置、构建和安装的信息,我们关闭ExternalProject_Add命令:
DOWNLOAD_NO_PROGRESS
    1
  LOG_CONFIGURE
    1
  LOG_BUILD
    1
  LOG_INSTALL
    1
  )
  1. 为了确保子项目在超级构建的其余部分中可被发现,我们设置message_DIR目录:
if(WIN32 AND NOT CYGWIN)
  set(DEF_message_DIR ${STAGED_INSTALL_PREFIX}/CMake)
else()
  set(DEF_message_DIR ${STAGED_INSTALL_PREFIX}/share/cmake/message)
endif()
file(TO_NATIVE_PATH "${DEF_message_DIR}" DEF_message_DIR)
set(message_DIR ${DEF_message_DIR}
    CACHE PATH "Path to internally built messageConfig.cmake" FORCE)

最后,让我们看看src文件夹中的CMakeLists.txt

  1. 再次,我们声明一个 C++11 项目:
cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
project(recipe-05_core
  LANGUAGES CXX
  )
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
  1. 这个项目需要message库:
find_package(message 1 CONFIG REQUIRED)
get_property(_loc TARGET message::message-shared PROPERTY LOCATION)
message(STATUS "Found message: ${_loc} (found version ${message_VERSION})")
  1. 我们声明一个可执行目标,并将其链接到我们依赖项提供的message-shared库:
add_executable(use_message use_message.cpp)
target_link_libraries(use_message
  PUBLIC
    message::message-shared
  )

它是如何工作的

这个配方突出了ExternalProject_Add命令的一些新选项:

  1. GIT_REPOSITORY:这可以用来指定包含我们依赖源代码的仓库的 URL。CMake 还可以使用其他版本控制系统,如 CVS(CVS_REPOSITORY)、SVN(SVN_REPOSITORY)或 Mercurial(HG_REPOSITORY)。
  2. GIT_TAG:默认情况下,CMake 将检出给定仓库的默认分支。然而,依赖于一个已知稳定的定义良好的版本是更可取的。这可以通过这个选项来指定,它可以接受 Git 识别为“版本”信息的任何标识符,如 Git 提交 SHA、Git 标签,或者仅仅是一个分支名称。对于 CMake 理解的其他版本控制系统,也有类似的选项。
  3. TEST_AFTER_INSTALL:很可能,你的依赖项有自己的测试套件,你可能想要运行测试套件以确保超级构建过程中一切顺利。这个选项将在安装步骤之后立即运行测试。

下面是ExternalProject_Add理解的额外测试选项:

  • TEST_BEFORE_INSTALL,它将在安装步骤之前运行测试套件
  • TEST_EXCLUDE_FROM_MAIN,我们可以使用它从测试套件中移除对外部项目主要目标的依赖

这些选项假设外部项目使用 CTest 管理测试。如果外部项目不使用 CTest 管理测试,我们可以设置TEST_COMMAND选项来执行测试。

引入超级构建模式,即使对于项目中包含的模块,也会带来额外的层次,重新声明小型 CMake 项目,并通过ExternalProject_Add显式传递配置设置。引入这一额外层次的好处是变量和目标作用域的清晰分离,这有助于管理复杂性、依赖关系和由多个组件组成的项目的命名空间,这些组件可以是内部的或外部的,并通过 CMake 组合在一起。

第十章:混合语言项目

在本章中,我们将涵盖以下示例:

  • 构建使用 C/C++库的 Fortran 项目
  • 构建使用 Fortran 库的 C/C++项目
  • 使用 Cython 构建 C++和 Python 项目
  • 使用 Boost.Python 构建 C++和 Python 项目
  • 使用 pybind11 构建 C++和 Python 项目
  • 使用 Python CFFI 混合 C、C++、Fortran 和 Python

引言

有许多现有的库在特定任务上表现出色。通常,在我们的代码库中重用这些库是一个非常好的主意,因为我们可以依赖其他专家团队多年的经验。随着计算机架构和编译器的演变,编程语言也在发展。过去,大多数科学软件都是用 Fortran 编写的,而现在,C、C++和解释型语言——尤其是 Python——正占据主导地位。将编译型语言编写的代码与解释型语言的绑定相结合变得越来越普遍,因为它提供了以下好处:

  • 终端用户可以自定义和扩展代码本身提供的能力,以完全满足他们的需求。
  • 人们可以将 Python 等语言的表达力与编译型语言的性能相结合,这种编译型语言在内存寻址方面更接近“硬件层面”,从而获得两者的最佳效果。

正如我们在之前的各个示例中一直展示的那样,project命令可以通过LANGUAGES关键字来设置项目中使用的语言。CMake 支持多种编译型编程语言,但并非全部。截至 CMake 3.5 版本,各种汇编语言(如 ASM-ATT、ASM、ASM-MASM 和 ASM-NASM)、C、C++、Fortran、Java、RC(Windows 资源编译器)和 Swift 都是有效选项。CMake 3.8 版本增加了对两种新语言的支持:C#和 CUDA(详见此处发布说明:cmake.org/cmake/help/v3.8/release/3.8.html#languages)。

在本章中,我们将展示如何将用不同编译型(C、C++和 Fortran)和解释型(Python)语言编写的代码集成到一个可移植和跨平台的解决方案中。我们将展示如何利用 CMake 和不同编程语言固有的工具来实现集成。

构建使用 C/C++库的 Fortran 项目

本示例的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-09/recipe-01找到,并包含两个示例:一个是混合 Fortran 和 C,另一个是混合 Fortran 和 C++。该示例适用于 CMake 3.5 版本(及以上)。两个版本的示例都已在 GNU/Linux 和 macOS 上进行了测试。

Fortran 作为高性能计算语言有着悠久的历史。许多数值线性代数库仍然主要用 Fortran 编写,许多需要与过去几十年积累的遗留代码保持兼容的大型数字处理软件包也是如此。虽然 Fortran 在处理数值数组时提供了非常自然的语法,但在与操作系统交互时却显得不足,主要是因为直到 Fortran 2003 标准发布时,才强制要求与 C 语言(计算机编程的事实上的通用语言)的互操作层。本食谱将展示如何将 Fortran 代码与 C 系统库和自定义 C 代码接口。

准备工作

如第七章,项目结构化所示,我们将把项目结构化为树状。每个子目录都有一个CMakeLists.txt文件,其中包含与该目录相关的指令。这使我们能够尽可能地将信息限制在叶目录中,如下例所示:

.
├── CMakeLists.txt
└── src
    ├── bt-randomgen-example.f90
    ├── CMakeLists.txt
    ├── interfaces
    │   ├── CMakeLists.txt
    │   ├── interface_backtrace.f90
    │   ├── interface_randomgen.f90
    │   └── randomgen.c
    └── utils
        ├── CMakeLists.txt
        └── util_strings.f90

在我们的例子中,我们有一个包含源代码的src子目录,包括我们的可执行文件bt-randomgen-example.f90。另外两个子目录,interfacesutils,包含将被编译成库的更多源代码。

interfaces子目录中的源代码展示了如何封装 backtrace C 系统库。例如,interface_backtrace.f90包含:

module interface_backtrace
  implicit none
  interface
    function backtrace(buffer, size) result(bt) bind(C, name="backtrace")
      use, intrinsic :: iso_c_binding, only: c_int, c_ptr
      type(c_ptr) :: buffer
      integer(c_int), value :: size
      integer(c_int) :: bt
    end function
    subroutine backtrace_symbols_fd(buffer, size, fd) bind(C, name="backtrace_symbols_fd")
      use, intrinsic :: iso_c_binding, only: c_int, c_ptr
      type(c_ptr) :: buffer
      integer(c_int), value :: size, fd
    end subroutine
  end interface
end module

上述示例展示了以下用法:

  • 内置的iso_c_binding模块,确保了 Fortran 和 C 类型及函数的互操作性。
  • interface声明,它将函数绑定到单独库中的符号。
  • bind(C)属性,它固定了声明函数的名称混淆。

这个子目录包含另外两个源文件:

  • randomgen.c,这是一个 C 源文件,它使用 C 标准的rand函数公开一个函数,用于在区间内生成随机整数。
  • interface_randomgen.f90,它封装了用于 Fortran 可执行文件中的 C 函数。

CMake 秘籍(五)(3)https://developer.aliyun.com/article/1524580

相关文章
|
7月前
|
编译器 Shell 开发工具
CMake 秘籍(八)(5)
CMake 秘籍(八)
36 2
|
7月前
|
编译器 Linux C语言
CMake 秘籍(二)(2)
CMake 秘籍(二)
57 2
|
7月前
|
Shell Linux C++
CMake 秘籍(六)(4)
CMake 秘籍(六)
50 1
|
7月前
|
Linux 编译器 C++
CMake 秘籍(七)(2)
CMake 秘籍(七)
44 1
|
7月前
|
消息中间件 Unix C语言
CMake 秘籍(二)(5)
CMake 秘籍(二)
153 1
|
7月前
|
Linux C++ iOS开发
CMake 秘籍(四)(1)
CMake 秘籍(四)
29 0
|
7月前
|
编译器 开发工具
CMake 秘籍(八)(2)
CMake 秘籍(八)
34 0
|
7月前
|
并行计算 Unix 编译器
CMake 秘籍(七)(5)
CMake 秘籍(七)
94 0
|
7月前
|
编译器 Linux C++
CMake 秘籍(六)(2)
CMake 秘籍(六)
112 0
|
7月前
|
并行计算 编译器 Linux
CMake 秘籍(二)(3)
CMake 秘籍(二)
34 0