> For the complete documentation index, see [llms.txt](https://chenxiaowei.gitbook.io/cmake-cookbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chenxiaowei.gitbook.io/cmake-cookbook/9.0-chinese/9.1-chinese.md).

# 9.1 使用C/C++库构建Fortran项目

**NOTE**:*此示例代码可以在* <https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-9/recipe-01> *中找到，其中有两个示例：一个是Fortran与C的混例，另一个是Fortran和C++的混例。该示例在CMake 3.5版(或更高版本)中是有效的，并且已经在GNU/Linux、macOS和Windows上进行过测试。*

Fortran作为高性能计算语言有着悠久的历史。目前，许多线性代数库仍然使用Fortran语言编写，许多大型的数字处理包也保持与过去几十年的代码兼容。而Fortran提出了一个很自然的语法处理数值数组，它缺乏与操作系统交互，所以为了编程的通用性，需要一个互操作性层(使用C实现)，才发布了Fortran 2003标准。本示例将展示如何用C系统库和自定义C代码来对接Fortran代码。

## 准备工作

第7章中，我们把项目结构列为一个树。每个子目录都有一个`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`，会将源码编译成可执行文件。另外两个子目录`interface`和`utils`包含更多的源代码，这些源代码将被编译成库。

`interfaces`子目录中的源代码展示了如何包装向后追踪的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：它将C函数封装在Fortran可执行文件中使用。

## 具体实施

我们有4个`CMakeLists.txt`实例要查看——根目录下1个，子目录下3个。让我们从根目录的`CMakeLists.txt`开始:

1. 声明一个Fortran和C的混合语言项目:

   ```
   cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
   project(recipe-01 LANGUAGES Fortran C)
   ```
2. CMake将静态库和动态库保存在`build`目录下的`lib`目录中。可执行文件保存在`bin`目录下，Fortran编译模块文件保存在`modules`目录下:

   ```
   set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
   set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
   set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
   set(CMAKE_Fortran_MODULE_DIRECTORY
   ${CMAKE_CURRENT_BINARY_DIR}/modules)
   ```
3. 接下来，我们进入第一个子`CMakeLists.txt`，添加`src`子目录:

   ```
   add_subdirectory(src)
   ```
4. `src/CMakeLists.txt`文件添加了两个子目录:

   ```
   add_subdirectory(interfaces)
   add_subdirectory(utils)
   ```

在`interfaces`子目录中，我们将执行以下操作:

1. 包括`FortranCInterface.cmak`模块，并验证C和Fortran编译器可以正确地交互:

   ```
   include(FortranCInterface)
   FortranCInterface_VERIFY()
   ```
2. 接下来，我们找到Backtrace系统库，因为我们想在Fortran代码中使用它:

   ```
   find_package(Backtrace REQUIRED)
   ```
3. 然后，创建一个共享库目标，其中包含Backtrace包装器、随机数生成器，以及Fortran包装器的源文件:

   ```
   add_library(bt-randomgen-wrap SHARED "")

   target_sources(bt-randomgen-wrap
     PRIVATE
       interface_backtrace.f90
       interface_randomgen.f90
       randomgen.c
     )
   ```
4. 我们还为新生成的库目标设置了链接库。使用`PUBLIC`属性，以便连接到其他目标时，能正确地看到依赖关系:

   ```
   target_link_libraries(bt-randomgen-wrap
     PUBLIC
         ${Backtrace_LIBRARIES}
     )
   ```

`utils`子目录中，还有一个`CMakeLists.txt`，其只有一单行程序：我们创建一个新的库目标，子目录中的源文件将被编译到这个目标库中。并与这个目标没有依赖关系:

```
add_library(utils SHARED util_strings.f90)
```

回到`src/CMakeLists.txt`:

1. 使用`bt-randomgen-example.f90`添加一个可执行目标:

   ```
   add_executable(bt-randomgen-example bt-randomgen-example.f90)
   ```
2. 最后，将在子`CMakeLists.txt`中生成的库目标，并链接到可执行目标:

   ```
   target_link_libraries(bt-randomgen-example
     PRIVATE
         bt-randomgen-wrap
         utils
     )
   ```

## 工作原理

确定链接了正确库之后，需要保证程序能够正确调用函数。每个编译器在生成机器码时都会执行命名检查。不过，这种操作的约定不是通用的，而是与编译器相关的。`FortranCInterface`，我们已经在第3章第4节时，检查所选C编译器与Fortran编译器的兼容性。对于当前的目的，命名检查并不是一个真正的问题。Fortran 2003标准提供了可选`name`参数的函数和子例程定义了`bind`属性。如果提供了这个参数，编译器将使用程序员指定的名称为这些子例程和函数生成符号。例如，backtrace函数可以从C语言中暴露给Fortran，并保留其命名:

```
function backtrace(buffer, size) result(bt) bind(C, name="backtrace")
```

## 更多信息

`interface/CMakeLists.txt`中的CMake代码还表明，可以使用不同语言的源文件创建库。CMake能够做到以下几点:

* 列出的源文件中获取目标文件，并识别要使用哪个编译器。
* 选择适当的链接器，以便构建库(或可执行文件)。

CMake如何决定使用哪个编译器？在`project`命令时使用参数`LANGUAGES`指定，这样CMake会检查系统上给定语言编译器。当使用源文件列表添加目标时，CMake将根据文件扩展名选择适当地编译器。因此，以`.c`结尾的文件使用C编译器编译，而以`.f90`结尾的文件(如果需要预处理，可以使用`.F90`)将使用Fortran编译器编译。类似地，对于C++， `.cpp`或`.cxx`扩展将触发`C++`编译器。我们只列出了C/C++和Fortran语言的一些可能的、有效的文件扩展名，但是CMake可以识别更多的扩展名。如果您的项目中的文件扩展名，由于某种原因不在可识别的扩展名之列，该怎么办？源文件属性可以用来告诉CMake在特定的源文件上使用哪个编译器，就像这样：

```
set_source_files_properties(my_source_file.axx
  PROPERTIES
      LANGUAGE CXX
  )
```

那链接器呢？CMake如何确定目标的链接器语言？对于不混合编程语言的目标很简单：通过生成目标文件的编译器命令调用链接器即可。如果目标混合了多个语言，就像示例中一样，则根据在语言混合中，优先级最高的语言来选择链接器语言。比如，我们的示例中混合了Fortran和C，因此Fortran语言比C语言具有更高的优先级，因此使用Fortran用作链接器语言。当混合使用Fortran和C++时，后者具有更高的优先级，因此C++被用作链接器语言。就像编译器语言一样，我们可以通过目标相应的`LINKER_LANGUAGE`属性，强制CMake为我们的目标使用特定的链接器语言:

```
set_target_properties(my_target
  PROPERTIES
      LINKER_LANGUAGE Fortran
  )
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://chenxiaowei.gitbook.io/cmake-cookbook/9.0-chinese/9.1-chinese.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
