fpm icon indicating copy to clipboard operation
fpm copied to clipboard

A Possible Solution for Compiler Flags

Open everythingfunctional opened this issue 4 years ago • 8 comments

I have a proposed solution to the issue of how we deal with compiler flags.

I propose adding a section to the fpm.toml file where compiler flags can be specified, with the following schema.

  1. The top level table is named "compiler-flags" (I'm open to suggestions for a better name)
  2. There is a table for each compiler. If a table is not specified for a compiler, then only the default/built-in profiles are available
  3. There may be multiple tables for each compiler, whose name specifies the name of an available "profile"
  4. A profile table specifies a list of flags, as an array of strings
  5. A profile table may optionally specify a list of pairs of file and flags to be used for that file
  6. A profile only applies to a dependency if it does not explicitly override that profile
  7. Specifying a profile with the same name as one of the default profiles overrides the default flags for that profile, including for dependencies, unless they also explicitly override that profile

For example:

[compiler-flags]
[compiler-flags.gfortran]
[compiler-flags.gfortran.only_debug_symbols]
flags = ["-g"]
files = { "src/special.f90" = ["-Wall", "-Werror"] }

One can then use a specified profile like fpm build --profile only_debug_symbols. The default/built-in profiles would correspond to our existing modes, and the existing modes would be equivalent to specifying the profile explicitly. I.e. fpm build is equivalent to/implies fpm build --profile debug and fpm build --release is equivalent to/implies fpm build --profile release.

This has the following benefits:

  • A library can override the default flags if they are not appropriate, and have its users respect that
  • A project can specify some special profile in a convenient place for frequently used sets of flags
  • A project/library need not override compiler flags for every file if only one file needs something special
  • We don't need to come up with and support a coherent set of "features" supported by every compiler

I admit that the table structure seems somewhat deeply nested, so I'm open to suggestions of other schemas, but I think this covers everything that needs to be included.

P.S. If this works out I'd like to suggest an additional built-in profile - strict - that includes all possible compile and run time checking, with all warnings treated as errors. I think this is a very useful option to have, even if not utilized very often.

everythingfunctional avatar Feb 04 '21 20:02 everythingfunctional

Too bad we can't steal a good idea from cargo here, since they never had to deal more than one compiler in their ecosystem. Requiring some deeper nesting in the package manifest seems inevitable for a good Fortran compiler support.

  1. The top level table is named "compiler-flags" (I'm open to suggestions for a better name)

I would prefer to put the profiles under the build table just to keep the root table clean.

  1. There is a table for each compiler. If a table is not specified for a compiler, then only the default/built-in profiles are available

Agreed. Using gfortran instead of gnu or gcc might be preferable for easy mapping between compiler executable name and profile, up to the moment someone is trying to compile with gfortran-9. Not sure what the best solution here would be.

  1. There may be multiple tables for each compiler, whose name specifies the name of an available "profile"

I made a similar proposal using a table of tables in https://github.com/fortran-lang/fpm/issues/112#issuecomment-754032658, after thinking through different format this one seems to be the only feasible choice to support multiple compilers.

  1. A profile table specifies a list of flags, as an array of strings

How do you distinguish between compile arguments, link arguments and global arguments here?

  1. A profile table may optionally specify a list of pairs of file and flags to be used for that file

This warrants matching rules for file names, like "src/fragile/**" = ["-O1"] at some point. Not sure if arguments on per file basis should be encouraged by fpm, it will make the package manifest nearly unreadable depending on the complexity of the project.

  1. A profile only applies to a dependency if it does not explicitly override that profile
  2. Specifying a profile with the same name as one of the default profiles overrides the default flags for that profile, including for dependencies, unless they also explicitly override that profile

awvwgk avatar Feb 04 '21 21:02 awvwgk

How do you distinguish between compile arguments, link arguments and global arguments here?

Good point. I'd suggest instead of having a single flags key, have compile, link, and global keys.

everythingfunctional avatar Feb 04 '21 21:02 everythingfunctional

This warrants matching rules for file names, like "src/fragile/**" = ["-O1"] at some point. Not sure if arguments on per file basis should be encouraged by fpm, it will make the package manifest nearly unreadable depending on the complexity of the project.

Agreed. I would prefer not to encourage the use of per file compiler flags, but at some point somebody's going to "need" it, so we should have a way to do it.

everythingfunctional avatar Feb 04 '21 21:02 everythingfunctional

Agreed. Using gfortran instead of gnu or gcc might be preferable for easy mapping between compiler executable name and profile, up to the moment someone is trying to compile with gfortran-9. Not sure what the best solution here would be.

Personally, the only time I use different versions of the same compiler is to find the oldest compiler version which works. In Linux I can do this easily with update-alternatives.

I can imagine problems when compiler-flags change meaning or are removed. But if a package required some specific compiler flags to make it work right, there is a high change the package was non-standard to begin with. Ideally the users would bring this up to the package maintainer who could find a workaround.

ivan-pi avatar Feb 09 '21 19:02 ivan-pi

Personally, the only time I use different versions of the same compiler is to find the oldest compiler version which works. In Linux I can do this easily with update-alternatives.

Personally, I have always at least the four last versions of gfortran installed: gfortran is the latest (10), then I have gfortran-9, gfortran-8, gfortran-7, etc. When I have long computations to run, I choose the fastest one! And the latest version is not necessarily the fastest. Sometimes there is some kind of regressions considering the speed when a new major version arrives, and it improves with the following minor versions... Not huge differences but it can be up to 5 to 10%... Not negligible.

vmagnin avatar Feb 09 '21 21:02 vmagnin

Same here, I have at least two versions of GCC available on the same system, usually gcc-7 and gcc-10, mainly for regression testing. Also, when working with conda-build the compiler name is usually something like powerpc64le-conda-linux-gnu-gfortran, which is still a gfortran compiler.

awvwgk avatar Feb 09 '21 21:02 awvwgk

I was trying to package the original netlib's QUADPACK as fpm package and was running into this issue: fpm is able to build everything with gfortran-7. But gfortran-10 requires the -fallow-argument-mismatch flag (I had to hardcode this flag into my local branch). All good, now the problem is that gfortran-7 doesn't recognize this flag and stops compiling. Also, I would like to write example programs without this additional flag. So, in this case I think per-compiler flags are welcome.

ghost avatar Mar 01 '21 11:03 ghost

It would be nice to be able to set flags for a specific operating system. For example, the linker flag -ldl may be required on Linux, and break the build if present on Windows.

ghost avatar Apr 27 '21 13:04 ghost