03-FAQ:寻找关于fortran的项目管理方法(FPM)


<center>FAQ:寻找关于fortran的项目管理方法(FPM)</center>

SSSSSSSSSSSSSSSSSSS

umake/make,2020

https://github.com/umake/make

  • The Ultimate Makefile to compile all your C, C++, Assembly and Fortran projects.

    Tired of installing aditional tools to compile your projects? (Automake, CMake and Ant). Missing the old days, when all you needed to do was typing make? Here is the solution! A single generalized Makefile which aims to compile and mix almost everything related to C, C++, Assembly and Fortran projects - with support to many executables, static and shared libraries, lexer and parser generators, dependency management,file creation and much more! Everything just 4 letters away from you.

starting

Good tools are simple. That’s why make is so successful. And that’s the goal of Ultimate Makefile.

In order to start, just download the Makefile:

curl -O https://raw.githubusercontent.com/umake/make/master/Makefile

To begin a new project, just type:

make init

A group of directories and a git repository are now ready for you!

运行fortran单个文件

umake例子:https://github.com/umake/make/blob/master/test/fortran/test_single-file.sh

运行时间及其缓慢,7000+行代码,可读性…;

This is a simple CMake tutorial project which contains some different scenarios.

  • hello-world: Demo a simplest CMake project.
  • hello-world-clear: Separate the output files and src files.
  • hello-world-lib: Demo how to make a static/shared library.
  • hello-world-shared: Demo how to utilize external static/shared library.
  • curl: Demo how to use cmake with curl.
  • hello-module: Demo how to use cmake find module.
  • config-file: Demo how to work with config.h.
  • hunter-simple: Demo how to use hunter and gtest.
  • boost: Demo how to use boost library.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Akagi201/learning-cmake

SSSSSSSSSSSSSSSSSSSSSSSS FPM

【国外老哥对fpm介绍】

https://www.bilibili.com/video/BV145411w7Pr?p=26

00:35 Outline

00:53 what problems does a package manager solve?

01:57 How does fpm solve these problems?

01:57 managing dependecies

02:50 building

03:25 testing

04:11 new projects

04:58 finding libraries

05:32 demo

05:32 new projects

07:50 testing

08:10 managing dependencies

12:11 future development

13:25 fortran/fpm

14:08 question (听不懂…)

SSSSSSSSSSSSSSSSSSSSSSSSSSS

【fortran-lang/fpm】

https://github.com/fortran-lang/fpm

Setting up fpm

MSYS2

Fpm is available as MinGW package in the MSYS2 package manager. To install fpm with pacman use

pacman -S mingw-w64-x86_64-fpm

Afterwards fpm will be available for usage. Currently i686, x86_64 and ucrt-x86_64 are supported MinGW architectures for fpm. For more details check the package information here.

Creating a new project

Creating a new fpm project is as simple as running the command fpm new project_name. This will create a new folder in your current directory with the following contents and initialized as a git repository.

  • fpm.toml – with your project’s name and some default standard meta-data
  • README.md – with your project’s name
  • .gitignore
  • src/project_name.f90 – with a simple hello world subroutine
  • app/main.f90 (if --app flag used) – a program that calls the subroutine
  • test/main.f90 (if --test flag used) – an empty test program

Building your Fortran project with fpm

Enter your Fortran project cd $project_name.

fpm understands the basic commands:

  • fpm build – build your library, executables and tests

  • fpm run – run executables

    进入到project,直接就 fpm run,会出错:

    删掉 src文件夹,fpm run,还是会出错;

  • fpm test – run tests

  • fpm install - installs the executables locally

The command fpm run can optionally accept the name of the specific executable to run, as can fpm test; like fpm run specific_executable. Command line arguments can also be passed to the executable(s) or test(s) with the option -- some arguments.

See additional instructions in the Packaging guide or the manifest reference.

SSSSSSSSSSSSSSSSSSSSSSSSS

【fpm 手册doc(有中文cn)】

https://fpm.fortran-lang.org/en/

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

First steps with fpm

This tutorial covers the basic usage of the Fortran Package Manager (fpm) command line. It will cover the generation of a new project and the possibility to compile a project into an executable as well as the possibility to run the resulting program.

To start a new project with fpm use the fpm new command

❯ fpm new first_steps

By default fpm creates a git repository with a dummy project in the fpm standard layout

❯ cd first_steps
❯ tree .
.
├── README.md
├── app
│   └── main.f90
├── fpm.toml
├── src
│   └── first_steps.f90
└── test
    └── check.f90

3 directories, 5 files

This is everything we need to start our new project. First, we inspect the package manifest, fpm.toml, which is populated with stub entries for us:

fpm.toml

name = "first_steps"
version = "0.1.0"
license = "license"
author = "Jane Doe"
maintainer = "jane.doe@example.com"
copyright = "Copyright 2021, Jane Doe"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false

The package manifest contains all the required meta data for the new project. Next we checkout the main executable, app/main.f90, fpm has generated for us:

app/main.f90

program main
  use first_steps, only: say_hello
  implicit none

  call say_hello()
end program main

The program already uses a module from our library, which we can find in src/first_steps.f90:

src/first_steps.f90

module first_steps
  implicit none
  private

  public :: say_hello
contains
  subroutine say_hello
    print *, "Hello, first_steps!"
  end subroutine say_hello
end module first_steps

We can run the executable directly with the command fpm run:

❯ fpm run
...
 Hello, first_steps!

Similarly, fpm has already created a stub for testing, which can be invoked with fpm test:

❯ fpm test
...
 Put some tests in here!

Fpm will automatically track changes in your project when running your project using the run and test commands.

Summary

In this tutorial you learned how to

  • create a new project from the fpm command line
  • build and run your project executables with fpm
  • run tests with fpm

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Adding dependency

This tutorial covers the usage of dependencies with fpm and how to reuse existing fpm projects.

In this tutorial you learned how to

  • depend on another fpm project in the package manifest
  • add development dependencies for testing
  • use dependencies for executables

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Using the standard library(失败->成功)

We start with a new project with fpm, we want to build a command line application to read a file, find a certain pattern and replace it. Since we do not want to write the replace function ourselves, we will use the Fortran standard library (stdlib) as dependency. In the package manifest we define stdlib in the dependencies table:

fpm.toml

name = "demo"
version = "0.1.0"

[dependencies]
stdlib.git = "https://github.com/fortran-lang/stdlib"
stdlib.branch = "stdlib-fpm"

Now we create a module with a procedure to perform the substitution. It requires three steps:

  1. reading a whole line from one unit
  2. replace the pattern in the string
  3. write the new string to an output

We will use the replace_all function from the stdlib_strings module for this purpose. The implementation is shown here

src/demo.f90

module demo
  use stdlib_io, only : getline
  use stdlib_strings, only : replace_all
  implicit none
  private

  public :: substitute

contains

  !> Read all lines from input, replace pattern and print it to output
  subroutine substitute(input, output, pattern, replacement)
    !> Formatted input unit
    integer, intent(in) :: input
    !> Formatted output unit
    integer, intent(in) :: output
    !> Pattern to replace in input
    character(len=*), intent(in) :: pattern
    !> Replacement for pattern in output
    character(len=*), intent(in) :: replacement

    character(len=:), allocatable :: line
    integer :: stat

    do
      call getline(input, line, stat)
      if (stat /= 0) exit
      write(output, '(a)') replace_all(line, pattern, replacement)
    end do
  end subroutine substitute

end module demo

Finally, we need a command line driver to make use of our new function.

app/main.f90

program main
  use, intrinsic :: iso_fortran_env, only : output_unit
  use demo, only : substitute
  implicit none
  character(len=256) :: pattern, replacement, input_file
  integer :: input

  call get_command_argument(1, pattern)
  call get_command_argument(2, replacement)
  call get_command_argument(3, input_file)

  open(newunit=input, file=input_file, status='old')
  call substitute(input, output_unit, trim(pattern), trim(replacement))
  close(input)
end program main

We can check our command line driver by running it with fpm:

❯ fpm run -- demo substitute fpm.toml
name = "substitute"
version = "0.1.0"

[dependencies]
stdlib.git = "https://github.com/fortran-lang/stdlib"
stdlib.branch = "stdlib-fpm"

运行失败:

刘锦灿@LAPTOP-085N0595 /d/zetero/VScode/Fortran/fpm/demo
$ gfortran -c build/dependencies/stdlib/src/stdlib_hash_64bit.f90 -g -I build/gfortran_2A42023B310FA28D -o build/gfortran_2A42023B310FA28D/demo/build_dependencies_stdlib_src_stdlib_io_npy.f90.o

错误:f951.exe: Fatal Error: Reading module ‘build/gfortran_2A42023B310FA28D/stdlib_kinds.mod’ at line 18 column 1: Unexpected EOF compilation terminated.

使用 MSYS2-MinGW 64 解决

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Adding a testing framework

Before we continue implementing new features, we want to add some tests to verify that our implementation keeps worked as we modify it. A minimalist testing framework is available with test-drive. Since the testing framework is only required when developing the package itself, but not for other packages which might in the future make use of our modules, we add a local dependency. The test-drive package is added in the dev-dependencies table as shown below

fpm.toml

name = "demo"
version = "0.1.0"

[dependencies]
stdlib.git = "https://github.com/fortran-lang/stdlib"
stdlib.branch = "stdlib-fpm"

[dev-dependencies]
test-drive.git = "https://github.com/fortran-lang/test-drive"
test-drive.tag = "v0.4.0"

Note

For a development dependency like a testing framework we choose a strict version pin by specifying the tag we want to use.


Now we can write a simple unit test, since our function works with units, we will create scratch units to create the input and capture the output. For now we will add a simple one line substitution as single test case

test/main.f90

module test_demo
  use demo, only : substitute
  use stdlib_io, only : getline
  use testdrive, only : error_type, unittest_type, new_unittest, check
  implicit none
  private

  public :: collect_demo

contains

  !> Collect all exported unit tests
  subroutine collect_demo(testsuite)
    !> Collection of tests
    type(unittest_type), allocatable, intent(out) :: testsuite(:)

    testsuite = [new_unittest("substitute", test_substitute)]
  end subroutine collect_demo

  !> Check substitution of a single line
  subroutine test_substitute(error)
    !> Error handling
    type(error_type), allocatable, intent(out) :: error
    integer :: input, output, stat
    character(len=:), allocatable :: line
    open(newunit=input, status="scratch")
    write(input, '(a)') "This is a valid test"
    rewind(input)

    open(newunit=output, status="scratch")
    call substitute(input, output, "test", "example")
    close(input)

    rewind(output)
    call getline(output, line, stat)
    close(output)

    call check(error, line, "This is a valid example")
  end subroutine test_substitute
end module test_demo

program tester
  use, intrinsic :: iso_fortran_env, only : error_unit
  use testdrive, only : run_testsuite
  use test_demo, only : collect_demo
  implicit none
  integer :: stat

  stat = 0
  call run_testsuite(collect_demo, error_unit, stat)

  if (stat > 0) then
    write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!"
    error stop
  end if

end program tester

We run our new test using fpm

❯ fpm test
  Starting substitute ... (1/1)
       ... substitute [PASSED]

Creating the scratch units for multiple unit tests will be repetitive, this kind of tasks can usually be done in a separate procedure and reused in several tests.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Target-specific dependencies

Dependencies can also be used for specific targets only. This can be used for adding a command line interface package, which is only used for the executable but not part of the library dependencies.

fpm.toml

name = "demo"
version = "0.1.0"

[dependencies]
stdlib.git = "https://github.com/fortran-lang/stdlib"
stdlib.branch = "stdlib-fpm"

[dev-dependencies]
test-drive.git = "https://github.com/fortran-lang/test-drive"
test-drive.tag = "v0.4.0"

[[executable]]
name = "demo"
[executable.dependencies]
M_CLI2.git = "https://github.com/urbanjost/M_CLI2"

We restructure our main program a bit for using M_CLI2 to handle the command line input. The unnamed array contains all positional command line arguments, we still use the first two as pattern and replacement, and use all remaining arguments as input. We also add an option to redirect the output. Our final main program looks like

app/main.f90

program main
  use, intrinsic :: iso_fortran_env, only : output_unit
  use demo, only : substitute
  use m_cli2, only : set_args, unnamed, sget
  implicit none
  character(len=:), allocatable :: input_file, output_file, pattern, replacement
  integer :: input, output, i

  call set_args("--output:o ''")

  output_file = trim(sget("output"))
  if (len(output_file) > 0) then
    open(file=output_file, newunit=output)
  else
    output = output_unit
  end if

  pattern = trim(unnamed(1))
  replacement = trim(unnamed(2))

  do i = 3, size(unnamed)
    input_file = trim(unnamed(i))
    open(file=input_file, newunit=input, status='old')
    call substitute(input, output_unit, trim(pattern), trim(replacement))
    close(input)
  end do

  if (output /= output_unit) close(output)
end program main

Again we run a quick check using fpm

❯ fpm run -- demo substitute fpm.toml
name = "substitute"
version = "0.1.0"

[dependencies]
stdlib.git = "https://github.com/fortran-lang/stdlib"
stdlib.branch = "stdlib-fpm"

[dev-dependencies]
test-drive.git = "https://github.com/fortran-lang/test-drive"
test-drive.tag = "v0.4.0"

[[executable]]
name = "substitute"
[executable.dependencies]
M_CLI2.git = "https://github.com/urbanjost/M_CLI2"

The output looks as expected with two substitutions.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Extending fpm with plugins

The Fortran package manager has a plugin system which allows to easily extend its functionality. This tutorial will show how to install a plugin with fpm and use it.

In this tutorial you learned how to

  • installing an fpm plugin
  • use the fpm-search plugin to query the registry
  • generate a dependency entry from a query result

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

注册表搜索工具(失败)

The fpm-search project is a plugin to query the package registry. Since it is built with fpm we can easily install it on our system with

git clone https://github.com/urbanjost/fpm-search
cd fpm-search
fpm install --profile release

这会将二进制文件 fpm-search安装到 ~/.local/bin(或在Windows上的 %APPDATA%\local\bin)。

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Manifest reference

The fpm.toml file for each project is called its manifest. It is written using the TOML format.

项目目标

每个fpm项目都可以定义函数库、可执行程序和测试目标。库目标被导出并可用于其他项目。

库配置

定义项目的导出库目标。如果在项目中找到源目录或包含目录,则会生成库。默认的源目录和包含目录是 srcinclude;这些可以在库(library)部分中使用源目录(source-dir)和包含目录(include-dir)条目进行修改。源目录和包含目录的目录相对于项目根目录给出,并在所有平台上使用 /用作目录分隔符。

例如:

[library]
source-dir = "lib"
include-dir = "inc"

包含目录

备注

仅在Fortran fpm中受支持。

使用Fortran语句或C预处理器语句的项目可以使用包含目录(include-dir)键为包含的文件指定搜索目录。包含目录(include-dir)可以包含一个或多个目录,其中使用字符串列表指定多个目录。所有项目依赖项中的包含目录都使用相应的编译器标志传递给编译器。

例如:

[library]
include-dir = ["include", "third_party/include"]

备注

包含目录(include-dir)当前不允许使用预构建的 .mod模块文件。

可执行目标

可执行目标是定义为可执行部分的Fortran程序。如果未指定可执行文件(executable)部分,则会在 app目录中搜索程序定义。对于显式指定的可执行文件,必须始终指定名称(name)条目。每个可执行文件的源目录都可以在源目录(source-dir)条目中进行调整。源目录的路径是相对于项目根目录给出的,并在所有平台上使用 /用作路径分隔符。可以在主(main)条目中指定包含程序正文的源文件。

可执行文件可以有自己的依赖项。有关更多详细信息,请参阅指定依赖项。

可执行文件还可以指定自己的外部库依赖项。有关更多详细信息,请参阅外部库。

备注

仅在Fortran fpm中支持针对库的链接。

例如:

[[executable]]
name = "app-name"
source-dir = "prog"
main = "program.f90"

[[executable]]
name = "app-tool"
link = "z"
[executable.dependencies]
helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git" }

指定许多单独的可执行文件可以通过使用内联表来完成,以便简洁。

executable = [
  { name = "a-prog" },
  { name = "app-tool", source-dir = "tool" },
]
示例目标

项目的示例应用程序被定义为示例部分。如果未指定示例(example)部分,则会在 example目录中搜索程序定义。对于显式指定的示例,必须始终指定名称(name)条目。每个示例的源目录都可以在源目录(source-dir)条目中进行调整。源目录的路径是相对于项目根目录给出的,并在所有平台上使用 /用作路径分隔符。可以在主(main)条目中指定包含程序正文的源文件。

可执行文件可以有自己的依赖项。有关更多详细信息,请参阅指定依赖项。

可执行文件还可以指定自己的外部库依赖项。有关更多详细信息,请参阅外部库。

备注

仅在Fortran fpm中支持针对库的链接。

例如:

[[example]]
name = "demo-app"
source-dir = "demo"
main = "program.f90"

[[example]]
name = "example-tool"
link = "z"
[example.dependencies]
helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git" }
测试目标

测试目标是定义为测试部分的Fortran程序。它们遵循与可执行目标类似的规则。如果未指定测试(test)部分,则会在 test目录中搜索程序定义。对于显式指定的测试,必须始终指定名称(name)条目。每个测试的源目录都可以在源目录(source-dir)条目中进行调整。源目录的路径是相对于项目根目录给出的,并在所有平台上使用 /用作路径分隔符。可以在主(main)条目中指定包含程序正文的源文件。

可执行文件可以有自己的依赖项。有关更多详细信息,请参阅指定依赖项。

可执行文件还可以指定自己的外部库依赖项。有关更多详细信息,请参阅外部库。

备注

仅在Fortran fpm中支持针对库的链接。

例如:

[[test]]
name = "test-name"
source-dir = "testing"
main = "tester.F90"

[[test]]
name = "tester"
link = ["blas", "lapack"]
[test.dependencies]
helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git" }

链接外部库

备注

仅在Fortran fpm中受支持。

要声明外部库的链接时间依赖关系,可以在链接(link)条目中指定本机库的列表。将一个库指定为字符串,或指定一个字符串列表,以防应链接多个库。如果可能,项目应仅链接一个本机库。库依赖项列表将导出到从属包。

例如:

使用zlib链接库:

[build]
link = "z"

要依赖LAPACK,BLAS也应该被链接起来。在这种情况下,库的顺序很重要:

[build]
link = ["blas", "lapack"]

使用系统安装的模块

若要使用未在fpm包或其依赖项中定义的模块,请使用生成(build)表中的外部模块(external-modules)键指定模块名称。

重要

fpm无法自动定位外部模块文件;用户有责任使用编译器标志指定必要的包含目录,以便编译器可以在编译期间找到外部模块文件。

例如:

[build]
external-modules = "netcdf"

可以将多个外部模块指定为一个列表。

例如:

[build]
external-modules = ["netcdf", "h5lt"]

目标自动发现

备注

仅在Fortran fpm中受支持。

可执行文件和测试可以在其默认目录中自动发现。自动发现以递归方式在 appexampletest目录中搜索定义,并分别将它们声明为可执行目标、示例目标和测试目标。默认情况下,自动发现处于启用状态。

要禁用目标的自动发现,请将自动可执行文件(auto-executables)、自动示例(auto-examples)和自动测试(auto-tests)条目设置为false

[build]
auto-executables = false
auto-examples = false
auto-tests = false

指定依赖项

SSSSSSSSSSSSSSSSSSSSSSS

方流返,Fortran编程 – FPM包管理器介绍(建议感兴趣的跳着看)

00:01 bilibili直播配置

05:30 xmake官网

06:40 xmake演示,主要c,c++,

11:39 xmake作者,知乎,国人软件,支持很多语言,fortran,但可能没其他语言官方的包管理器好

16:03 xmake缺陷

  • mod,贡献,

23:10 fpm文档,方流返在写,

  • cargo,
  • 环境路径,

32:42 fpm缺点,issue,

35:45 fpm演示,运行c,交互c,

39:27 fpm演示,图形界面

  • 编码问题
  • flag的管理,

47:26 中文学习文档,https://fortran-fans.github.io/Fortran-in-Action/,

SSSSSSSSSSSSSSSS xmake?

xmake作者B站账号

https://space.bilibili.com/2058574066

SSSSSSSSSSSSSSSSSSSSSSS

【我创建的对应FPM的github项目(nice)】

https://github.com/Liu-Jincan/study-fpm

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

【Preparing your package for FPM,重点】

https://github.com/fortran-lang/fpm/blob/main/PACKAGING.md

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

What kind of package can fpm build?

You can use fpm to build:

  • Applications (program only)
  • Libraries (modules only)
  • Combination of the two (programs and modules combined)

Let’s look at some examples of different kinds of package layouts that you can use with fpm.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Single program (失败,简单的例子,体现的也是前面的失败,WHY?已解决)

Let’s start with the simplest package imaginable—a single program without dependencies or modules. Here’s what the layout of the top-level directory looks like:

.
├── app
│   └── main.f90
└── fpm.toml

We have one source file (main.f90) in one directory (app). Its contents are:

program main
    print *, 'Hello, World!'
end program main

This program prints the usual greeting to the standard output, and nothing more.

There’s another important file in the top-level directory, fpm.toml. This is fpm’s configuration file specific to your package. It includes all the data that fpm needs to build your app. In our simple case, it looks like this:

name = "hello"
version = "0.1.0"
license = "MIT"
author = "Jane Programmer"
maintainer = "jane@example.com"
copyright = "2020 Jane Programmer"

The preamble includes some metadata, such as license, author, and similar, that you may have seen in other package manager configuration files. The one option that matters here right now is:

name = "hello"

This line specifies the name of your package, which determines the name of the executable file of your program. In this example, our program executable, once built, will be called hello.

Let’s now build this program using fpm:

$ fpm build
# gfortran (for build/debug/app/main.o)
# gfortran (for build/debug/app/hello)

On the first line, we ran fpm build to compile and link the application. The latter two lines are emitted by fpm, and indicate which command was executed at each build step (gfortran), and which files have been output by it: object file main.o, and executable hello.

We can now run the app with fpm run:

$ fpm run
 Hello, World!

If your application needs to use a module internally, but you don’t intend to build it as a library to be used in other projects, you can include the module in your program source file as well. For example:

$ cat app/main.f90
module math_constants
    real, parameter :: pi = 4 * atan(1.)
end module math_constants

program main
    use math_constants, only: pi
    print *, 'Hello, World!'
    print *, 'pi = ', pi
end program main

Now, run this using fpm run:

$ fpm run
# gfortran (for build/debug/app/main.o)
# gfortran (for build/debug/app/hello)
 Hello, World!
 pi =    3.14159274
  • Notice that you can run fpm run, and if the package hasn’t been built yet, fpm build will run automatically for you. This is true if the source files have been updated since the last build. Thus, if you want to run your application, you can skip the fpm build step, and go straight to fpm run.
  • Although we have named our program hello, which is the same name as the package name in fpm.toml, you can name it anything you want as long as it’s permitted by the language.

==》

FAQ已解决

==》

In this last example, our source file defined a math_constants module inside the same source file as the main program. Let’s see how we can define an fpm package that makes this module available as a library.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Multi-module library

In this example, we’ll use another module to define a 64-bit real kind parameter and make it available in math_constants to define pi with higher precision. To make this exercise worthwhile, we’ll define another math constant, Euler’s number.

Our package layout looks like this:

.
├── fpm.toml
└── src
    ├── math_constants.f90
    └── type_kinds.f90

And our source file contents are:

$ cat src/math_constants.f90
module math_constants
    use type_kinds, only: rk
    real(rk), parameter :: pi = 4 * atan(1._rk)
    real(rk), parameter :: e = exp(1._rk)
end module math_constants

$ cat src/type_kinds.f90
module type_kinds
    use iso_fortran_env, only: real64
    integer, parameter :: rk = real64
end module type_kinds

and there are no changes to our fpm.toml relative to previous examples.

  • Like before, notice that the module type_kinds is name exactly as the source file that contains it. This is important.

    模块名称和其文件名称一致

By now you know how to build the package:

$ fpm build
# gfortran (for build/debug/library/type_kinds.o build/debug/library/type_kinds.mod)
# gfortran (for build/debug/library/math_constants.o build/debug/library/math_constants.mod)
# ar (for build/debug/library/math_constants.a)
ar: creating build/debug/library/math_constants.a

Our build path now contains:

$ ls build/debug/library/
math_constants.a  math_constants.mod  math_constants.o  type_kinds.mod  type_kinds.o
  • 没看到 build/debug/library,

And the static library includes all the object files:

$ nm build/debug/library/math_constants.a

math_constants.o:

type_kinds.o:
  • 没看到 math_constants.a
  • nm命令是什么,

The takeaways from this example are that:

  • fpm automatically scanned the src directory for any source files.

    自动浏览资源文件,

  • It also resolved the dependency order between different modules.

    解决了不同modules的依赖问题(👍),

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Single program+Multi-module library

Let’s now combine the two previous examples into one: We’ll build the math constants library and an executable program that uses it. We’ll use this program as a demo, and to verify that defining higher-precision constants from the previous example actually worked.

Here’s the package layout for your application + library package:

.
├── app
│   └── main.f90
├── fpm.toml
└── src
    ├── math_constants.f90
    └── type_kinds.f90

Our fpm.toml remains unchanged and our executable program source file is:

$ cat app/main.f90
program main
    use math_constants, only: e, pi
    print *, 'math_constants library demo'
    print *, 'pi = ', pi
    print *, 'e = ', e
end program main

Let’s go straight to running the demo program:

$ fpm run
# gfortran (for build/debug/library/type_kinds.o build/debug/library/type_kinds.mod)
# gfortran (for build/debug/library/math_constants.o build/debug/library/math_constants.mod)
# ar (for build/debug/library/math_constants.a)
ar: creating build/debug/library/math_constants.a
# gfortran (for build/debug/app/main.o)
# gfortran (for build/debug/app/math_constants)
 math_constants library demo
 pi =    3.1415926535897931
 e =    2.7182818284590451
  • 直接运行 fpm run会出错,

    刘锦灿@LAPTOP-085N0595 MINGW64 /d/zetero/VScode/Fortran/fpm/multi-module
    $ fpm run
    <INFO> No executables to run
    STOP 0
    
    刘锦灿@LAPTOP-085N0595 MINGW64 /d/zetero/VScode/Fortran/fpm/multi-module
    $ fpm build
    
    刘锦灿@LAPTOP-085N0595 MINGW64 /d/zetero/VScode/Fortran/fpm/multi-module
    $ fpm run
    <INFO> No executables to run
    STOP 0
  • 解决方法:删除mod文件和 build文件夹,再 fpm run,

    RM -f ./src/*.mod && RM -f -R build && RM -f ./app/*.mod && fpm run

The fpm build + run process works as expected, and our program correctly outputs higher-precision constants.


So far we covered how fpm builds:

  • A single program
  • A single-module library
  • A multi-module library
  • A program and a library

However, all our modules so far have been organized in the top level source directory. More complex libraries may organize their modules in subdirectories. Let’s see how we can build this with fpm.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Single program+Multi-level library

In this example, we’ll define our library as a collection of modules, two of which are defined in a subdirectory:

.
├── app
│   └── main.f90
├── fpm.toml
└── src
    ├── math_constants
    │   ├── derived.f90
    │   └── fundamental.f90
    ├── math_constants.f90
    └── type_kinds.f90

First, fpm.toml and src/type_kinds.f90 remain unchanged relative to the previous example.

The rest of the source files are:

$ cat src/math_constants.f90
module math_constants
    use math_constants_fundamental, only: e, pi
    use math_constants_derived, only: half_pi, two_pi
end module math_constants

$ cat src/math_constants/fundamental.f90
module math_constants_fundamental
    use type_kinds, only: rk
    real(rk), parameter :: pi = 4 * atan(1._rk)
    real(rk), parameter :: e = exp(1._rk)
end module math_constants_fundamental

$ cat src/math_constants/derived.f90
module math_constants_derived
    use math_constants_fundamental, only: pi
    use type_kinds, only: rk
    real(rk), parameter :: two_pi = 2 * pi
    real(rk), parameter :: half_pi = pi / 2
end module math_constants_derived

$ cat app/main.f90
program main
    use math_constants, only: e, pi, half_pi, two_pi
    print *, 'math_constants library demo'
    print *, 'pi = ', pi
    print *, '2*pi = ', two_pi
    print *, 'pi/2 = ', half_pi
    print *, 'e = ', e
end program main

Our top-level math_constants module now doesn’t define the constants, but imports them from the two modules in the subdirectory. Constants e and pi we define in the math_constants_fundamental module, and two_pi and half_pi in the math_constants_derived module. From the main program, we access all the constants from the top-level module math_constants.

Let’s build and run this package:

$ fpm run
# gfortran (for build/debug/library/type_kinds.o build/debug/library/type_kinds.mod)
# gfortran (for build/debug/library/math_constants_fundamental.o build/debug/library/math_constants_fundamental.mod)
# gfortran (for build/debug/library/math_constants_derived.o build/debug/library/math_constants_derived.mod)
# gfortran (for build/debug/library/math_constants.o build/debug/library/math_constants.mod)
# ar (for build/debug/library/math_constants.a)
ar: creating build/debug/library/math_constants.a
# gfortran (for build/debug/app/main.o)
# gfortran (for build/debug/app/math_constants)
 math_constants library demo
 pi =    3.1415926535897931
 2*pi =    6.2831853071795862
 pi/2 =    1.5707963267948966
 e =    2.7182818284590451

Again, fpm built and run the package as expected.

  • Recall from an earlier example that fpm required the modules in the top-level src directory to be named the same as their source file. This is why src/math_constants.f90 defines module math_constants.

  • For modules defined in subdirectories, there’s an additional requirement: module name must contain the path components of the directory that its source file is in. In our case, src/math_constants/fundamental.f90 defines the math_constants_fundamental module. Likewise, src/math_constants/derived.f90 defines the math_constants_derived module.

    This rule applies generally to any number of nested directories and modules. For example, src/a/b/c/d.f90 must define a module called a_b_c_d.

    很重要的一点;


Takeaways from this example are that:

  • You can place your module source files in any levels of subdirectories inside src.
  • The module name must include the path components and the source file name–for example, src/a/b/c/d.f90 must define a module called a_b_c_d.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

fpm.toml: [library],[[executable]]

So far we’ve let fpm use its defaults to determine the layout of our package. It determined where our library sources would live, what the name of the executable will be, and some other things. But we can be more explicit about it, and make some changes to those things.

Let’s look at what the fpm.toml file from our last example would look like if we specified everything.

name = "math_constants"
version = "0.1.0"
license = "MIT"
author = "Jane Programmer"
maintainer = "jane@example.com"
copyright = "2020 Jane Programmer"

[library]
source-dir="src"

[[ executable ]]
name="math_constants"
source-dir="app"
main="main.f90"

You can see that by making these explicit in the fpm.toml we are able to change many of the settings that fpm used by default. We can change the folders where our sources are stored, we can change the name of our executable, and we can change the name of the file our program is defined in.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

FAQ:如何实现每一个application文件与一个executable对应?

  1. 为了明确每一个 executable与每一个application文件的对应关系,将 auto-executables设置为false,

    可以通过 fpm run --list查询 executable的个数,

    fpm run --list

    如果 auto-executables不设置为false,其默认为true,那么按照上面的操作会出错,因为 auto=executables会对应一个application文件,加上 executable对应的三个application文件,而实际上只有三个application文件,肯定存在某一个application文件有重复的 executable文件,这在 fpm中是不允许的。

  2. 想运行某一个文件可以,例如 fpn run "app1_exe2"

    像运行所有的target文件,即executable文件,可以 fpm run --all

exampletestexecutable类似;

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

FAQ:每次 fpm run都要删除 modbuild文件夹?

不是的,

在每次 fpm run前可以删除 mod,(因为 mod文件时自动生成的,fpm实际运行是不需要 mod

但是对于 build文件夹不应该删除,否则信息会丢失,

建议:find . -name *.mod | xargs rm -f && fpm run

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

fpm.toml: [test]

fpm also provides support for unit testing. By default, fpm looks for a program in test/main.f90 which it will compile and execute with the command fpm test. The tests are treated pretty much exactly like the executables.

[test]和[executable]不是相似,现在看来,是基本一摸一样;

Let’s define one explicitly in our fpm.toml file. We’ll make sure that our definition of pi satisfies the property sin(pi) == 0.0. Here’s the fpm.toml file:

name = "math_constants"
version = "0.1.0"
license = "MIT"
author = "Jane Programmer"
maintainer = "jane@example.com"
copyright = "2020 Jane Programmer"

[library]
source-dir="src"

[[ executable ]]
name="math_constants"
source-dir="app"
main="main.f90"

[[ test ]]
name="runTests"
source-dir="test"
main="main.f90"

where the contents of the main.f90 file are

program main
    use math_constants, only: pi
    print *, "sin(pi) = ", sin(pi)
end program main

With this setup, we can run our tests.

$ fpm test
# gfortran (for build/debug/library/type_kinds.o build/debug/library/type_kinds.mod)
# gfortran (for build/debug/library/math_constants_fundamental.o build/debug/library/math_constants_fundamental.mod)
# gfortran (for build/debug/library/math_constants_derived.o build/debug/library/math_constants_derived.mod)
# gfortran (for build/debug/library/math_constants.o build/debug/library/math_constants.mod)
# ar (for build/debug/library/math_constants.a)
ar: creating build/debug/library/math_constants.a
# gfortran (for build/debug/app/main.o)
# gfortran (for build/debug/app/math_constants)
# gfortran (for build/debug/test/main.o)
# gfortran (for build/debug/test/runTests)
 sin(pi) =    1.2246467991473532E-016

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

fpm.toml: [example]

Here’s the fpm.toml file:

[build]
auto-examples=false

[[ example ]]
name="math_constants"
source-dir="app"
main="main.f90"

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Adding dependencies

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

fpm.toml:[dependencies]

Inevitably, you’ll want to be able to include other libraries in your project. fpm makes this incredibly simple, by taking care of fetching and compiling your dependencies for you. You just tell it what your dependencies are, and where to find them. Let’s add a dependency to our library. Now our fpm.toml file looks like this:

name = "math_constants"
version = "0.1.0"
license = "MIT"
author = "Jane Programmer"
maintainer = "jane@example.com"
copyright = "2020 Jane Programmer"

[library]
source-dir="src"

[dependencies]
helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git" }

[[ executable ]]
name="math_constants"
source-dir="app"
main="main.f90"

[[ test ]]
name="runTests"
source-dir="test"
main="main.f90"

Now you can use any modules from this library anywhere in your code. Just like this:

program main
    use helloff, only: create_greeting
    use math_constants, only: e, pi, half_pi, two_pi
    print *, 'math_constants library demo'
    print *, 'pi = ', pi
    print *, '2*pi = ', two_pi
    print *, 'pi/2 = ', half_pi
    print *, 'e = ', e
    print *, create_greeting("fpm")
end program main
  • 这里 use helloff中,helloff是一个module

And now, fpm run will output the following:

math_constants library demo
pi =    3.1415926535897931
2*pi =    6.2831853071795862
pi/2 =    1.5707963267948966
e =    2.7182818284590451
Hello, fpm!

You can also be specific about which version of a dependency you’d like.

  • You can specify a branch to use like helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git", branch = "master" },
  • or a tag like helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git", tag = "v1.2.3" },
  • or even a specific commit like helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git", rev = "a1b2c3" }.

You can even specify the path to another folder, if for example you’ve got another fpm package in the same repository. Like this: helloff = { path = "helloff" }. Note that you should not specify paths outside of your repository, or things won’t work for your users.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

fpm.toml: [executable.dependencies]

Additionally, any users of your library will now automatically depend on your dependencies too. So if you don’t need that dependency for the library, like in the above example, then you can specify it for the specific executable like below. Then fpm will still fetch and compile it when building your executable, but users of your library won’t have to.

name = "math_constants"
version = "0.1.0"
license = "MIT"
author = "Jane Programmer"
maintainer = "jane@example.com"
copyright = "2020 Jane Programmer"

[library]
source-dir="src"

[[ executable ]]
name="math_constants"
source-dir="app"
main="main.f90"
[executable.dependencies]
helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git" }

[[ test ]]
name="runTests"
source-dir="test"
main="main.f90"

You can also specify dependencies for your tests in a similar way, with [test.dependencies] instead of [executable.dependencies]. There’s also another option for test dependencies.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

fpm.toml: [dev-dependencies]

The below example makes the dependencies available for all the tests, but again your users won’t depend on these.

name = "math_constants"
version = "0.1.0"
license = "MIT"
author = "Jane Programmer"
maintainer = "jane@example.com"
copyright = "2020 Jane Programmer"

[library]
source-dir="src"

[dev-dependencies]
helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git" }

[[ executable ]]
name="math_constants"
source-dir="app"
main="main.f90"

[[ test ]]
name="runTests"
source-dir="test"
main="main.f90"

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

FAQ:这几类的dependencies有什么区别吗?

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

Custom build scripts (FAQ:不知道在干什么)

fpm.toml: build-script

If there is something special about your library that makes fpm unable to build it, you can provide your own build script. fpm will then simply call your build script to build the library.

To specify a build script to be used, put it in the library section of your fpm.toml file, like:

[library]
source-dir="src"
build-script="my_build_script"

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

flag

fpm will set the following environment variables to specify some parameters to the build script:

  • FC – The Fortran compiler to be used.
  • FFLAGS – The flags that should be passed to the Fortran compiler.
  • BUILD_DIR – Where the compiled files should be placed.
  • INCLUDE_DIRS – The folders where any dependencies can be found, space separated. It is then the responsibility of the build script to generate the appropriate include flags.

Additionally, script will be called with the name of the archive (*.a file) that should be produced as the command line argument.

Note: If the name of the build script is Makefile or ends with .mk, then the make program will be used to run it. Not the the archive file is explicitly specified as the target to be built

Note: All file and directory names are specified with their full canonical path.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

FAQ:求助,ReadingModule问题(code:block大哥解决)

@LAPTOP-085N0595  /d/zetero/VScode/Fortran/fpm/demo
$ gfortran -c build/dependencies/stdlib/src/stdlib_hash_64bit.f90 -g -Ibuild/gfortran_2A42023B310FA28D -o build/gfortran_2A42023B310FA28D/demo/build_dependencies_stdlib_src_stdlib_io_npy.f90.o

错误:f951.exe: Fatal Error: Reading module ‘build/gfortran_2A42023B310FA28D/stdlib_kinds.mod’ at line 18 column 1: Unexpected EOF compilation terminated.

==》

【管理员】code::blocks(1943133009) 21:37:58
https://github.com/msys2/MINGW-packages/issues/6908

【新进】Win10+VScode+Gfortran(3079779149) 21:38:59
用的是msys2的bash

【新进】Win10+VScode+Gfortran(3079779149) 22:03:36
code::blocks https://github.com/msys2/MINGW-packages/issues/6908
@code::blocks 十分感谢,我遇到的情况和这个issue一样。在VScode中即使使用msys2的bash还是会发生这种问题,将module名称换成更短的情况可能编译成功,保险起见,还是建议在WSYS2 MinGW中使用gfortran。再次感谢~~

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

FAQ:fpm中git下载依赖问题

刘锦灿@LAPTOP-085N0595  /d/zetero/VScode/Fortran/fpm/fpm-search
$ fpm install --profile release
Initialized empty Git repository in D:/zetero/VScode/Fortran/fpm/fpm-search/build/dependencies/M_CLI2/.git/
fatal: unable to access 'https://github.com/urbanjost/M_CLI2/': OpenSSL SSL_read: Connection was aborted, errno 10053
<ERROR>Error while fetching git repository for remote dependency
STOP 1

设计到git安装依赖库的命令,总是会发病,需要多试几次,

对应不是dev依赖库的安装,记住安装失败后,去build指定位置删除对应文件夹,否则:

刘锦灿@LAPTOP-085N0595  /d/zetero/VScode/Fortran/fpm/fpm-search
$ fpm install --profile release
fatal: your current branch 'main' does not have any commits yet
<ERROR>Error while retrieving commit information
STOP 1

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

FAQ:NotGNU问题

刘锦灿@LAPTOP-085N0595 MINGW64 /d/zetero/VScode/Fortran/fpm/multi-module
$ fpm build
 + mkdir build\dependencies
 + mkdir build\gfortran_2A42023B310FA28D
 + mkdir build\gfortran_2A42023B310FA28D\hello\
 + gfortran -c .\.\src\type_kinds.f90  -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\hello\src_type_kinds.f90.o
 + gfortran -c .\.\src\math_constants.f90  -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\hello\src_math_constants.f90.o
.\.\src\math_constants.f90:2:9:

    2 |     use type_kinds, only: rk
      |         1
Fatal Error: File '.\.\src/type_kinds.mod' opened at (1) is not a GNU Fortran module file
compilation terminated.
<ERROR> Compilation failed for object " src_math_constants.f90.o "
<ERROR>stopping due to failed compilation
STOP 1

解决方法:

RM -f ./src/*.mod && RM -f -R build && RM -f ./app/*.mod && fpm run

find --help
find ./app ./src ./lib -name *.mod | xargs rm -f && RM -f -R build && fpm run

find . -name *.mod | xargs rm -f && fpm run

FAQ:Fortran runtime error: File cannot be deleted

解决方法:多运行几次


Author: Jincan
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Jincan !
  TOC