fpm
fpm copied to clipboard
Fetch Version Number From fpm.toml
Is there a way to fetch the version number defined in the fpm.toml in the compile process of fpm to remove the redundancy to the version number defined in this line?
Currently not. There are several options here:
- fpm could export the version number as a preprocessor macro (different formats are possible, like
PROJECT_VERSION
as string orPROJECT_VERSION_{MAJOR,MINOR,PATCH}
with the individual integers. - fpm could automatically generate a module
<name>_version
which contains the version number as parameter (for compile time evaluation) and a getter function as well (for runtime evaluation, dynamic linking). We possibly have to support a user-defined template for this purpose. - fpm can read the version number from a file, this could be used to somehow transfer the version number to Fortran source code.
I like the idea of option 2. But maybe this could be more general. E.g. project_metadata_m.f90
containing not only the version number but also the projects name, license, author etc.
Problem with option 1 is that there is still no standard preprocessor for Fortran (as far as I know). Problem with option 3 is that the executable can be copied anywhere where it cannot find this file anymore. IMO the version number should be compiled into the executable program.
Potential syntax for the manifest might be:
[[generate]] # table name open for discussion
output = "src/version.f90"
input = "config/version.f90.in"
scheme = "configure_file" # allows to support different formats later
To generate files for option 2 the configure file syntax supported by several build systems could be used, a template could look like this:
module @PROJECT_NAME@_version
implicit none
private
public :: @PROJECT_NAME@_version_string, @PROJECT_NAME@_version_compact
public :: get_@PROJECT_NAME@_version
!> String representation of the @PROJECT_NAME@ version
character(len=*), parameter :: @PROJECT_NAME@_version_string = @PROJECT_VERSION@
!> Numeric representation of the @PROJECT_NAME@ version
integer, parameter :: @PROJECT_NAME@_version_compact(3) = [@PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@]
contains
!> Getter function to retrieve @PROJECT_NAME@ version
subroutine get_@PROJECT_NAME@_version(major, minor, patch, string)
!> Major version number of the @PROJECT_NAME@ version
integer, intent(out), optional :: major
!> Minor version number of the @PROJECT_NAME@ version
integer, intent(out), optional :: minor
!> Patch version number of the @PROJECT_NAME@ version
integer, intent(out), optional :: patch
!> String representation of the @PROJECT_NAME@ version
character(len=:), allocatable, intent(out), optional :: string
if (present(major)) then
major = @PROJECT_NAME@_version_compact(1)
end if
if (present(minor)) then
minor = @PROJECT_NAME@_version_compact(2)
end if
if (present(patch)) then
patch = @PROJECT_NAME@_version_compact(3)
end if
if (present(string)) then
string = @PROJECT_NAME@_version_string
end if
end subroutine get_@PROJECT_NAME@_version
end module @PROJECT_NAME@_version
This would allow to use the same strategy for including meta data with fpm, meson, cmake, etc., which is something I personally find very important, because it would be quite tedious to adapt an fpm-specialized format to other build systems.
This sounds very complicated, but if this is necessary to keep this compatible to meson etc. so be it.
My approach (without any knowledge of meson etc.) would look like this:
fpm build
would put the information parsed from fpm.toml
into a module, which would not be copied into src
but it would be saved/compiled directly into the build directory, next to the other compiled modules.
module project_metadata_m
character(len=*), parameter :: name = "example"
integer, parameter :: version(*) = [0, 1, 2]
! author, licence, etc.
end module project_metadata_m
This module could then be use
ed by the routine which prints the version information.
I don't know how important compatibility to other build systems is and it's definitely another discussion. But as we see with this example, compatibility comes with a very high cost. IMHO things are getting too complicated, both for the users and for the implementation of fpm, if we would try to be too open for everything else.
I think the approaches outlined in the last two comments can be merged. Edit: in fact, this is precisely option 2, just including all the useful information, and not only the version number.
The approach suggested by @Carltoffel can be realized by shipping fpm together with a standard template for the metadata/version, according to the syntax proposed by @awvwgk. Users could override this default template by providing their own ".in" file within the src
directory.
The template file would need to be either compiled into fpm or located in a folder where fpm can find it (potentially trouble).
Generating source files should require a similar infrastructure like a custom preprocessor, i.e. generated files have to be updated in case their source changes, get scanned for modules and added to the build targets.
I wonder if their is clash between the meson/CMake configuration preprocessor syntax and fypp, due to the use of the '@' symbol?
Preprocessing a generated file sounds risky, better get it right on the first try. But I have seen multiple passes with fypp and cpp in the past (forpy) and are guilty of building some myself in meson (cpp, ffi-builder, cc, cc).
I was bringing up preprocessing since the generation of files would require the same infrastructure as supporting an external preprocessor in fpm. Of course this could lead to cases we have to pass a file through multiple possible incompatible generator steps. Not sure whether fypp is actually compatible with configure_file
templates, but I guess the @:
macro declaration is distinct enough to not match an identifier that can be expanded by a configuration.
#252 discusses similiar issues, and there was another I thought was in the Discussion session I cannot find discussing a module versus INCLUDE file, whether it should generate the file in the build/ directory or in /src or whether a new directory at the top level or within src/, such as src/meta and whether it should be a fixed name or something configurable ( I personally like the idea of a default for everything to keep it simple. I started a plug-in that is basically independent of fpm and it just reads the fpm.toml file and queries the date and time and such and builds a module (after experimenting with pre-processing, include files, and a module I like this the best but there are uses for each and they are not necessarily mutually exclusive). The biggest trouble with the plug-in is that it is not automatically triggered by a build, but you have to run "fpm meta" or "fpm-meta", which could easily be resolved by a check for and execution of it by the build but is a real drawback if you forget to run it, but it is primarily for testing the other ideas for it, is simple and can progress independent of the core utilities. Again resolvable but it does not pick up the compiler version for the same resaon (not incorporated into the build automatically) but it is still useful. You get the TOML info, the date and time it was run (which if integrated would be the last build time, most useful for applications. I did not finish an option that used a --list output to get all the filenames and build a "manifest" dictionary with the last change data of all the files used in the build; it was an interesting idea but I was not using it but a configuration zealot might find it appealing. Using a preprocessor has advantages, especially if the macros are passed to the build but I use my own preprocessor and it would be a huge issue to switch to another and I suspect others have some issue with anything other than perhaps using fypp, and many want their code to be preprocessor free so a preprocessor might be complementary but not a single-method solution in my opinion. some of it runs into issues with incremental builds, the module solves that the best; if doing any kind of pre-processing being able to have an OS TYPE and COMPILER TYPE in particular are valuable. Even now, passing -DOS_TYPE and -DCOMPILER would be really handy as most compilers support the -D switch for use with fpp or cpp in a standard way for simple stuff (although each one seems to be a little different in the dusty corners at a minimum). I like something just writing a module; as fpm grows in power I have quit using CMake and except that to become common unless we make fpm just as complicated to use :>
So I think a good first cut is a routine that writes a module that is just a Fortran version of the metadata in the TOML file is the best first cut. It is easy to do a lot of it completely outside of fpm, but since fpm already calls the TOML library and knows when to trigger a rewrite it seems some time of integration is required, not just a plug-in but for more rapid development this could still take the form of a plug-in that is just called during a build. That would let people customize in such features as local configuration information and so on in a reasonable fashion without requiring templates and pre-processors; just a custom plug-in. One thing I was playing with in mine was a custom call to making an sqlite3 log of the builds and the results of the last QA run, as an example.
Another thing that came into my mind is git:
You can tag commits with version numbers which will create another redundancy. I cannot imagine any solution to this "problem" but maybe someone else knows something?
At least it could be very handy if one could request a dependency via version number and not only the revision hash. But for this to work the tags have to be correct. Otherwise fpm would have to search for a commit where the fpm.toml
contains the specified version.