Skip to content

Use out-of-source directory for .mod files by default #231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
apthorpe opened this issue May 25, 2021 · 3 comments
Closed

Use out-of-source directory for .mod files by default #231

apthorpe opened this issue May 25, 2021 · 3 comments
Assignees

Comments

@apthorpe
Copy link

This is an enhancement request related to both #176 and #187

By default the linter runs gfortran against source code in the directory where it is found, generating .mod files in those directories. This works fine until you use an out-of-source build system like CMake. The .mod files written by the linter into the source tree interfere with compilation and linking under CMake and complicate version control.

The workaround is to manually tell the linter to write .mod files to a separate out-of-source directory and to include that directory for the linter's use.

An example: For a project with the base directory C:/Users/me/Documents/git_projects/my_project, I will create the directory .fmod under the project root (C:/Users/me/Documents/git_projects/my_project/.fmod). I then modify the workspace's settings.json to add:

    "fortran.linterExtraArgs": [
        "-JC:/Users/me/Documents/git_projects/my_project/.fmod"
    ],
    "fortran.includePaths": [
        "C:/Users/me/Documents/git_projects/my_project/.fmod"
    ],

This gives me the benefit of linting and syntax checking while keeping temporary files out of my source directory.

The big problem is that I have to manually configure every project like this if I want to use both CMake and Fortran linting.

It seems like this could be easily solved by setting the default configuration to something like:

    "fortran.linterExtraArgs": [
        "-J${workspaceFolder}/.fmod"
    ],
    "fortran.includePaths": [
        "${workspaceFolder}/.fmod"
    ],

The directory .fmod needs to be created. There may be issues using a single directory to store files generated from a source tree if the source tree contains multiple files with the same name. In that case, replicate the source directory structure under .fmod. That is, for the source directory ${workspaceFolder}/.fmod/src/tree, set the include path and -J path to ${workspaceFolder}/.fmod/src/tree. This is slightly more complex but would solve the problem for projects with multilevel source directories, the sort of complex projects CMake was designed to manage.

If #176 adds ${workspaceFolder} support, this change would be an invisible resolution of #187.

@bertrandcz
Copy link

+1, this workaround works perfectly, thanks.

@gnikit
Copy link
Member

gnikit commented Jun 2, 2021

Interesting idea. I am not sure though that this should be the default option. As you mentioned yourself, we rely on gfortran for the linting so it is generally a good idea to mirror gfortran's default behaviour. Making this option as a default I suspect could cause problems to many users. As mentioned you can use -J to change the location of the extension's .mod files.

Now when it comes to building a copy of the directory structure just for the .mod files things tend to become a bit more complicated.
The main problem that I can identify is getting gfortran to generate the files into their new directories and then including them back in the right order. From what I can tell, passing the -J${workspaceFolder}/.fmod/src/tree compiler flag would result into all your .mod files being placed into a single directory thus suffering from the same problem as using -J/any/dir.

However, as you said, this would only be an issue if you had multiple source files with identical names, which in general is not considered a good idea, for reasons such as this one, regardless the project's complexity.

The only "easy" solution that I can think off for your problem is to get the extension to generate the .mod files using the relative paths to the ${workspaceFolder} and add an explicit option for out-of-place directory prefix for .mod file output. In your example it would be ${workspaceFolder}/.fmod. A harder way of doing it would be to perform some sort of name mangling with the .mod file name and relative path to root.

Either way, using multiple out-of-place directories for your .mod files because of identical names would run into the same problems as not using -J at all.

For example:

Let a and b be directories containing a file foo.f90, with modules named fooa and foob, respectively.
A third directory c contains a file bar.f90 which uses fooa but not foob
If the extension generates .mod files out-of-source while preserving the underlying directory structure the following source tree will be created.

.
├── a
│   └── foo.f90
├── b
│   └── foo.f90
├── c
│   └── bar.f90
└── .fmod
    ├── a
    │   └── foo.mod
    ├── b
    │   └── foo.mod
    └── c
        └── bar.mod

When linting any one of the foo.f90 files, the .mod file that will be used will be entirely dependant on the order with which a and b are included. That is true regardless of whether or not we preserved the underlying directory structure.
The extension will simply use the first ${fileBasename}.mod it encounters while scanning through the includePaths in the order that they have been provided.

Truth be told, I don't see a way that you can robustly provide linting for files with the same name without doing a major overhaul of how the extension handles linting.

Of course I also don't think that's a problem that will dominate someone's workflow since you can create the correct .mod file by simply saving the appropriate .f90 file.

@gnikit gnikit linked a pull request Jun 8, 2021 that will close this issue
@gnikit gnikit self-assigned this Nov 12, 2021
@gnikit
Copy link
Member

gnikit commented Nov 17, 2021

The out-of-source directory option has been added to v2.6.1 available in the VS Marketplace. It is not the default behaviour for the reasons stated above but you can define a custom directory for the mod output by setting the variable fortran.linterModOutput in the settings. You will still have to include the fortran.linterModOutput directory into your fortran.includePaths. Luckily path interpolation and glob support are also available now so setting up projects should be noticeably easier.

@gnikit gnikit closed this as completed Nov 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants