Fpm: Automatically discover executables & tests

Created on 7 Sep 2020  ยท  15Comments  ยท  Source: fortran-lang/fpm

Currently you are required to explicitly specify each test and executable in the fpm manifest file.
This is a lot of manual work and can lead to overly-verbose manifests (see M_system for example).

I don't think this is necessary for implementation and I would rather have executables and tests in the default locations to be discovered automatically. Is this not how cargo does it?
The existing [[test]] and [[executable]] entries can be retained for specifying custom settings for individual executables & tests.

I believe this behaviour was mentioned in https://github.com/fortran-lang/fpm/issues/105#issuecomment-648333807 and is also related to https://github.com/fortran-lang/fpm/issues/91.

This is very easy to implement in the fortran implementation from #155; the implementation already discovers all programs, but currently filters out those not specified in fpm.toml in order to match existing behaviour.

Most helpful comment

Unlike Rust, Fortran has the advantage that we can easily figure out which modules are used by what program, so @LKedward's proposal will work great. I suggest we do that.

Furthermore, I would suggest that for this layout:

โ”œโ”€โ”€ apps
โ”‚   โ”œโ”€โ”€ app_mod.f90
โ”‚   โ”œโ”€โ”€ say_goodbye.f90
โ”‚   โ””โ”€โ”€ say_hello
โ”‚       โ”œโ”€โ”€ app_hello_mod.f90
โ”‚       โ””โ”€โ”€ say_Hello.f90
โ”œโ”€โ”€ fpm.toml
โ”œโ”€โ”€ src
โ”‚   โ”œโ”€โ”€ farewell_m.f90
โ”‚   โ””โ”€โ”€ greet_m.f90
โ””โ”€โ”€ tests
    โ”œโ”€โ”€ farewell_test.f90
    โ”œโ”€โ”€ greet_test.f90
    โ””โ”€โ”€ test_mod.f90

It should just work with this fpm.toml:

name = "hello_complex"

Since fpm can figure out everything.

We can discuss whether the apps dir should be called app or bin or src/bin or src/app or src/apps, but the idea stays the same.

All 15 comments

I'd be okay with making some changes to the "default" functionality, so long as the following are satisfied:

  • It can be easily explained to a user without many (any?) caveats and exceptions
  • It is possible to achieve exactly the same functionality when explicitly declared in fpm.toml
  • It doesn't encourage bad practices (granted that may be fairly subjective)

For example, implicit none requires the programmer to be more explicit, and involves more work, but is at this point pretty universally considered to be best practice (i.e. would be considered bad practice not to include it).

For me, having multiple programs and modules in one directory would be "bad practice", because for someone trying to understand your project, it is no longer clear which files are programs and which are modules, or which modules are used by which programs, at least not without opening and reading each file.

I would be ok with having multiple programs in a single directory provided there are no modules in that directory. But I do struggle a bit with what it would look like to specify that in fpm.toml.

I would also be ok with having multiple programs in separate folders being detected automatically, but I struggle a bit with what folders it should look in? Maybe just subdirectories of the app folder?

So with those ideas, we effectively end up with 3 possible "default" fpm.toml specifications.

  1. A single [[executable]] entry with app as the directory and main.f90 as the file
  2. Somehow specifying app as a directory containing multiple programs
  3. Multiple [[executable]] entries (1 for each subdirectory of app)

There are then multiple "error" conditions that must be checked, with some being unclear as to what the user might have expected to happen.

  • What if multiple programs and 1 or more modules are found in the app folder?
  • What if there is a file app/main.f90 and subdirectories of app?

I'm open to ideas, these are just some of my concerns.

I think we should do it like Cargo does it, which is to agree on a standard layout and as long as you follow it, you don't have to specify anything in fpm.toml. Here is the Cargo's layout: https://doc.rust-lang.org/cargo/guide/project-layout.html

So we will have app/program1.f90 and app/program2.f90 to be two single file executables, and for multi file executables we will have subdirectories of app. The same layout that Cargo is doing, only instead of src/bin, we have app.

I think the Cargo way does make for a decently easy to understand and describe usage, although it does make explicit specification in fpm.toml more complicated, and complicates (for me at least) the most common use case, where I've got one (test) program with multiple modules. This would require me to put them in a subdirectory of app (or test) even though I've only got one. Not a big deal, just worth mentioning.

I'd be curious to see what it would look like to specify additional details (compiler flags, link flags, etc.) for specific tests or executables as well. I'm not opposed to doing it this way, I'd just like to see some examples before we make a decision.

@everythingfunctional can you show an example project where you have one test program with multiple modules that you only want to be part of this test program and not the library (in src)?

That's how my testing framework works. All of the tests are written in modules, and all of the tests are run via a single executable. And so all of my libraries' tests are written that way.

I disagree with placing unnecessary constraints on package layout based on arguments of "bad practice" since there is always a package or use-case that will justifiably need to go against such constraints. The analogy with implicit none is stretching (IMO) since implicit typing easily leads to undesirable program behaviour whereas package layout is more aesthetic in nature.

I'm proposing automatic discovery based on a default layout like Cargo, but with a __simpler (and looser) set of rules__:

  • By default, fpm looks in app, and any subdirectories therein, for executable targets;
  • Executable targets are 'module-associated' and linked with any other (non-executable) sources __in the same directory__ as the executable source.

For me, this is simple to understand conceptually and flexible enough for all use cases.
This allows Brad's use-case of a single executable + multiple modules and my common use-case of multiple executables with one or two supporting (non-library) modules.

The corresponding 'default' fpm.toml entry would be something akin to:

app-dir = "app"
test-dir = "test"

with [[executable]] and [[test]] available for individual program customisation.

I can mock-up some working cases later as examples.

There's a mostly working demo on my program_discovery branch, where I've changed the hello_complex example to:

โ”œโ”€โ”€ apps
โ”‚ย ย  โ”œโ”€โ”€ app_mod.f90
โ”‚ย ย  โ”œโ”€โ”€ say_goodbye.f90
โ”‚ย ย  โ””โ”€โ”€ say_hello
โ”‚ย ย      โ”œโ”€โ”€ app_hello_mod.f90
โ”‚ย ย      โ””โ”€โ”€ say_Hello.f90
โ”œโ”€โ”€ fpm.toml
โ”œโ”€โ”€ source
โ”‚ย ย  โ”œโ”€โ”€ farewell_m.f90
โ”‚ย ย  โ””โ”€โ”€ greet_m.f90
โ””โ”€โ”€ tests
    โ”œโ”€โ”€ farewell_test.f90
    โ”œโ”€โ”€ greet_test.f90
    โ””โ”€โ”€ test_mod.f90

with manifest:

name = "hello_complex"

app-dir="apps"
test-dir = "tests"

[library]
source-dir="source"

[[executable]]
name="say_hello_world"
source-dir="apps/say_hello"
main="say_Hello.f90"

which demonstrates:

  • automatic app/test discovery in app-dir/test-dir
  • 'app-private' modules (in same directory)
  • app customisation in fpm.toml

That's how my testing framework works. All of the tests are written in modules, and all of the tests are run via a single executable. And so all of my libraries' tests are written that way.

@everythingfunctional Would this issue go away if you put your test modules in the same source file as the program that calls them? In that case the default fpm/Cargo approach would work for you out of the box without specifying anything.

Unlike Rust, Fortran has the advantage that we can easily figure out which modules are used by what program, so @LKedward's proposal will work great. I suggest we do that.

Furthermore, I would suggest that for this layout:

โ”œโ”€โ”€ apps
โ”‚   โ”œโ”€โ”€ app_mod.f90
โ”‚   โ”œโ”€โ”€ say_goodbye.f90
โ”‚   โ””โ”€โ”€ say_hello
โ”‚       โ”œโ”€โ”€ app_hello_mod.f90
โ”‚       โ””โ”€โ”€ say_Hello.f90
โ”œโ”€โ”€ fpm.toml
โ”œโ”€โ”€ src
โ”‚   โ”œโ”€โ”€ farewell_m.f90
โ”‚   โ””โ”€โ”€ greet_m.f90
โ””โ”€โ”€ tests
    โ”œโ”€โ”€ farewell_test.f90
    โ”œโ”€โ”€ greet_test.f90
    โ””โ”€โ”€ test_mod.f90

It should just work with this fpm.toml:

name = "hello_complex"

Since fpm can figure out everything.

We can discuss whether the apps dir should be called app or bin or src/bin or src/app or src/apps, but the idea stays the same.

@milancurcic , no, because the program is generated by a program I wrote to scan your tests. And I don't believe we support having modules in with a program.

I think @LKedward 's approach is probably headed in the right direction. I'll take a look at the implementation.

And I don't believe we support having modules in with a program.

Both Haskell and Fortran fpm support this now and we should continue to support it. I will contribute an example to the existing suite of test packages. But it's irrelevant for your use case as you say.

My most common case for tests is that all the tests call three modules. One has things like routines to compare reals and tables of numbers and statistics routines; another is a logging utility and the other is a debug utility that mostly concentrates on fail/pass procedures and allowing scripts to be called with the results (which then pass data to databases, web pages, log files, ...) So if I have multiple programs (perhaps dozens) that all use those modules and I wanted one directory for the programs and one for the modules would I put the modules in app/ and the programs in app/programs/ or the programs in app/ and the modules in app/modules/ or have app/programs and appl/modules? I am thinking any of those are better than having 20 subdirectories right now; but wondering if you are envisioning all three of those to work?

I branched my own fpm so I could use POSIX-ony libraries like M_systems and fortran-libcurl and parse everything with M_CLI2 so I could get a better feel for how this will work some day; and I have found
1) It is much nicer to use fpm if it looks for the fpm.toml file in directories above it which I found can be complicated by softlinks a bit but is pretty basic but requires testing if files exist and changing directories
2) that it is really useful to have a --list option on run and test in particular to show you what programs are going to be built and their pathnames. I also think it would be nice if that listing was written out in fpm.toml format so you could quickly build/rebuild fpm.toml; but perhaps I would not need that as much with the autoscan described above
3) I personally found that moving past a basic build with just a few modules that having something like .gitignore becomes essential. Even as it is now I found the original fpm failing because it "found" some working files I had in directories or reference files. The proposed changed would have it finding even more. I often want files in a directory for reference or during development that I have to keep renaming so they are not "discovered". Even just being able to specify a directory NOT be searched like A .fmpignore with FODDER/ in it (ie. do not search directory FODDER) is handly. Not sure how common an issue that is for others, but having looked at many packages over the years they often have unused or platform-specific files sitting with the production ones.

So an upward search for fpm.toml; an ability to do a dry run with a listing of builds (perhaps with output in fpm.toml format; and something that lets files be ignored (the git(1) .gitignore model works for me) are on my wishlist.

So if I have multiple programs (perhaps dozens) that all use those modules and I wanted one directory for the programs and one for the modules would I put the modules in app/ and the programs in app/programs/ or the programs in app/ and the modules in app/modules/ or have app/programs and appl/modules?

Unfortunately my proposed changes won't allow the configurations you've mentioned; it will avoid the need to have 20 subdirectories, but will require both modules and programs to be all in the same directory.

A possible solution for your logging/debug modules etc. is to store them in a separate package and include them as [dev-dependencies] which are only required for the test programs.

I would support a file ignore list as an entry in fpm.toml or as a .fpmignore file. Also dry run is a good idea that should be implemented.

@urbanjost fpm should have an option to allow users like you to use it. Experience with Cargo and Rust shows that having a default layout and autodiscovery works excellent for over 90% of packages, and the rest just needs some way to be able to use it, perhaps adding options to fpm.toml to disable autodiscovery as you suggested.

Closing as implemented in #190

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lauvergn picture lauvergn  ยท  7Comments

scivision picture scivision  ยท  10Comments

awvwgk picture awvwgk  ยท  9Comments

certik picture certik  ยท  8Comments

awvwgk picture awvwgk  ยท  6Comments