fpm icon indicating copy to clipboard operation
fpm copied to clipboard

Fetch Version Number From fpm.toml

Open Carltoffel opened this issue 3 years ago • 10 comments

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?

Carltoffel avatar Aug 10 '21 08:08 Carltoffel

Currently not. There are several options here:

  1. fpm could export the version number as a preprocessor macro (different formats are possible, like PROJECT_VERSION as string or PROJECT_VERSION_{MAJOR,MINOR,PATCH} with the individual integers.
  2. 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.
  3. fpm can read the version number from a file, this could be used to somehow transfer the version number to Fortran source code.

awvwgk avatar Aug 10 '21 08:08 awvwgk

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.

Carltoffel avatar Aug 10 '21 08:08 Carltoffel

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.

awvwgk avatar Aug 10 '21 08:08 awvwgk

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 useed 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.

Carltoffel avatar Aug 10 '21 10:08 Carltoffel

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).

ivan-pi avatar Aug 10 '21 12:08 ivan-pi

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.

awvwgk avatar Aug 10 '21 13:08 awvwgk

I wonder if their is clash between the meson/CMake configuration preprocessor syntax and fypp, due to the use of the '@' symbol?

ivan-pi avatar Aug 10 '21 17:08 ivan-pi

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.

awvwgk avatar Aug 10 '21 18:08 awvwgk

#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.

urbanjost avatar Aug 11 '21 02:08 urbanjost

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.

Carltoffel avatar Aug 11 '21 07:08 Carltoffel