aboutsummaryrefslogtreecommitdiff
path: root/PACKAGING.md
diff options
context:
space:
mode:
authormilancurcic <caomaco@gmail.com>2020-04-28 16:24:26 -0400
committermilancurcic <caomaco@gmail.com>2020-04-28 16:24:26 -0400
commit9367aea89bace7946980ae21a831768aee878cdc (patch)
treeac46103795a8b79d39084fb91cb001cc4e42fe75 /PACKAGING.md
parentbb58c8b9adae6b9f6a7b41d5bbd3059b6ff90a21 (diff)
downloadfpm-9367aea89bace7946980ae21a831768aee878cdc.tar.gz
fpm-9367aea89bace7946980ae21a831768aee878cdc.zip
add guide for preparing packages for FPM
Diffstat (limited to 'PACKAGING.md')
-rw-r--r--PACKAGING.md527
1 files changed, 527 insertions, 0 deletions
diff --git a/PACKAGING.md b/PACKAGING.md
new file mode 100644
index 0000000..4269aa8
--- /dev/null
+++ b/PACKAGING.md
@@ -0,0 +1,527 @@
+# Preparing your package for FPM
+
+This document describes how you need to organize your application or library
+for it to successfully build with the Fortran Package Manager (FPM).
+
+* [What kind of package can FPM build?](#what-kind-of-package-can-fpm-build)
+* [Example package layouts](#example-package-layouts)
+ - [Single program](#single-program)
+ - [Single-module library](#single-module-library)
+ - [Multi-module library](#multi-module-library)
+ - [Application and library](#application-and-library)
+ - [Multi-level library](#multi-level-library)
+
+## 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.
+
+## Example package layouts
+
+This section describes some example package layouts that you can build with FPM.
+You can use them to model the layout of your own package.
+
+### Single program
+
+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 hello
+ print *, 'Hello, World!'
+end program hello
+```
+
+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"
+dependencies = []
+compiler = "gfortran"
+devel-options = ["-g", "-Wall", "-Wextra", "-pedantic"]
+release-options = ["-O3"]
+
+[executables.executable-name]
+main = "main.f90"
+source-dirs = "app"
+linker-options = ["-O3"]
+dependencies = []
+```
+
+The preamble includes some metadata, such as `license`, `author`, and similar,
+that you may have seen in other package manager configuration files.
+It also includes build instructions such as `dependencies`, `compiler` and
+development and release options.
+We'll get back to these later.
+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`.
+
+Next, let's look at the parameters in the `[executables.executable-name]`
+section:
+
+```
+main = "main.f90"
+source-dirs = "app"
+linker-options = ["-O3"]
+dependencies = []
+```
+
+The first line specifies the source file name, and the second line specifies
+the directory in which the program source file is located.
+We'll revisit `linker-options` and `dependencies` later--ignore them for now.
+
+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!
+```
+
+> **Issue candidate**: fpm.toml setting `main = "main.f90"` is currently hardcoded.
+
+> **Question**: How to specify multiple executable programs?
+
+If your application needs to use a module internally, but you don't intent
+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 hello
+ use math_constants, only: pi
+ print *, 'Hello, World!'
+ print *, 'pi = ', pi
+end program hello
+```
+
+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.
+
+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.
+
+### Single-module library
+
+The package layout for this example looks like this:
+
+```
+.
+├── fpm.toml
+└── src
+ └── math_constants.f90
+```
+
+In this example we'll build a simple math constants library that exports
+the number pi as a parameter:
+
+```
+$ cat src/math_constants.f90
+module math_constants
+ real, parameter :: pi = 4 * atan(1.)
+end module math_constants
+```
+
+and our `fpm.toml` looks like this:
+
+```
+name = "math_constants"
+version = "0.1.0"
+license = "MIT"
+author = "Jane Programmer"
+maintainer = "jane@example.com"
+copyright = "2020 Jane Programmer"
+dependencies = []
+compiler = "gfortran"
+devel-options = ["-g", "-Wall", "-Wextra", "-pedantic"]
+release-options = ["-O3"]
+
+[library]
+source-dirs = "src"
+```
+
+The key difference here relative to our single-program example is the
+`[library]` section:
+
+```
+[library]
+source-dirs = "src"
+```
+
+This tells FPM where your library source files are located.
+In this case, it's the `src` directory.
+
+Now use `fpm build` to build it:
+
+```
+$ fpm build
+# 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
+```
+
+Based on the output of `fpm build`, FPM first ran `gfortran` to emit the
+binary object (`math_constants.o`) and module (`math_constants.mod`) files.
+Then it ran `ar` to create a static library archive `math_constants.a`.
+`build/debug/library` is thus both your include and library path, should you
+want to compile and link an exteranl program with this library.
+
+For modules in the top-level (`src`) directory, FPM requires that:
+
+* The module has the same name as the source file.
+* There is only one module per file.
+
+These two requirements simplify the build process for FPM.
+As Fortran compilers emit module files (`.mod`) with the same name as the
+module itself (but not the source file, `.f90`), naming the module the same as
+the source file allows FPM to:
+
+* Uniquely and exactly map a source file (`.f90`) to its object (`.o`) and
+module (`.mod`) files.
+* Avoid conflicts with modules of the same name that could appear in dependency
+packages (more on this in a bit).
+
+Since this is a library without executable programs, `fpm run` here does
+nothing.
+
+> **Issue candidate**: `fpm run` should trigger an error if run in a project
+> without executable programs. For example:
+>
+> ```
+> fpm error: This package has no executable programs.
+> ```
+
+In this example, our library is made of only one module.
+However, most real-world libraries are likely to use multiple modules.
+Let's see how you can package your multi-module library.
+
+### 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 the single-module
+example.
+
+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
+```
+
+and the static library includes all the object files:
+
+```
+$ nm build/debug/library/math_constants.a
+
+math_constants.o:
+
+type_kinds.o:
+```
+
+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.
+
+### Application and 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 new `fpm.toml` is now:
+
+```
+name = "math_constants"
+version = "0.1.0"
+license = "MIT"
+author = "Jane Programmer"
+maintainer = "jane@example.com"
+copyright = "2020 Jane Programmer"
+dependencies = []
+compiler = "gfortran"
+devel-options = ["-g", "-Wall", "-Wextra", "-pedantic"]
+release-options = ["-O3"]
+
+[executables.executable-name]
+main = "main.f90"
+source-dirs = "app"
+linker-options = ["-O3"]
+dependencies = []
+
+[library]
+source-dirs = "src"
+```
+
+and our executable program source file is:
+
+```
+$ cat app/main.f90
+program demo
+ use math_constants, only: e, pi
+ print *, 'math_constants library demo'
+ print *, 'pi = ', pi
+ print *, 'e = ', e
+end program demo
+```
+
+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
+```
+
+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.
+
+### 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 demo
+ 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 demo
+```
+
+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`.