Open james-thunes opened 2 years ago
I haven't been able to get a small test case set up yet. I will try to create one and post to this issue
I'll just add that when I was testing this locally it seems to only be reproducible when running a parallel build with -jN
. A serial build would do things in the right order and I never saw that fail, but for parallel it's failing when a dependent source file is compiled concurrently before the module is complete.
I'll just add that when I was testing this locally it seems to only be reproducible when running a parallel build with
-jN
. A serial build would do things in the right order and I never saw that fail, but for parallel it's failing when a dependent source file is compiled concurrently before the module is complete.
That's just luck... If parallel builds fail due to missing dependencies (for any build system), it means the build system doesn't have all the appropriate dependencies and so isn't building everything needed for the failing build step. I run into this often (though mainly with non-scons based build systems where it's harder to get this right)..
Yeah, fair enough. Even for the parallel builds I'll add that it would sometimes work, sometimes not.
I wonder since it seems to be a windows specific thing then is something else going on. eg: the .mod file is still locked by the OS so the dependent module cannot link against it. We have run into this kind of issue with .lib (import library) files on windows parallel builds before as well if an Install()
was used to move the .lib file to some other place before it was linked into dependent module despite the fact that we would add Depends()
on the install task to the dependent module. We removed the use of Install()
targets for this as a result and it got better.
Ugh.. the joys of NTFS..
How are you creating your Environment()? Specifically are you setting FORTRANSUFFIXES
(or any of the F*SUFFIXES
env vars)?
Try adding this to your Environment()
FORTRANFILESUFFIXES=['.f90'],
this particular library has all *.f files, but I can try setting FORTRANFILESUFFIXES=['.f'],
How are you creating your Environment()? Specifically are you setting
FORTRANSUFFIXES
(or any of theF*SUFFIXES
env vars)?
We definitely do not configure that particular variable currently. We do configure these variables related to this issue
env["FORTRANMODDIR"] = "${TARGET.dir}"
env["F90PATH"] = "${TARGET.dir}"
It's the same for ifort
and gfortran
.
Okay, I've located a simple example and don't see the problem with it. Here's the tree dump:
+-.
+-SConstruct
+-list-module.f90
+-list-module.o
| +-list-module.f90
| +-/bin/gfortran
+-listmodule.mod
| +-list-module.f90
| +-/bin/gfortran
+-main-list
| +-main-list.o
| | +-main-list.f90
| | +-[listmodule.mod]
| | +-/bin/gfortran
| +-[list-module.o]
| +-/bin/gfortran
+-main-list.f90
+-[main-list.o]
main-list.o
is shown as depending on its source file, on listmodule.mod
, and on the compiler. That looks right to me? What am I missing?
@mwichmann - I think this is only an intel fortran issue. And I think it's due to loading default tools, then ifort and having files named .f when ifort looks like it only wants files named .i or .i90?
this particular library has all *.f files, but I can try setting
FORTRANFILESUFFIXES=['.f'],
Did that work?
I still have to look at the .i/.i90 thing, I think it should work just find on ordinary suffixes and I don't know what those actually mean.
this particular library has all *.f files, but I can try setting
FORTRANFILESUFFIXES=['.f'],
Did that work?
Sorry, been busy with other tasks. Will try to get to this as soon as I can.
this particular library has all *.f files, but I can try setting
FORTRANFILESUFFIXES=['.f'],
Did that work?
Ok, I was able to spend a bit of time with this today. I'm a bit confused to be honest. Setting FORTRANFILESUFFIXES=['.f'],
did indeed appear to resolve the issue. I was able to see the module dependencies when looking at the output with --tree=prune
. However, removing the flag and doing a clean rebuild of the code I see that the module dependencies is still seen. It definitely wasn't last week...
Most of this setup bemuses me, so you have company :)
Out of curiosity, are all the files in your project of the same suffix?
The library in question has a combination of both .f90
and .f
suffixes since it includes F90 free format and F77 fixed format code.
Okay, then the muck with "dialects" isn't completely useless to you (or if it is - please feel free to let us know!).
Right, the library in question uses both dialects because it links in an older code base shared with other applications. So, definitely useful to us. One issue though is that it's still valid to have F90/95 etc... code in fixed form files with a .f
extension, so it muddies the water some more.
I tried the --tree=prune
thing with the latest SCons.
SCons by Steven Knight et al.: SCons: v4.3.0.559790274f66fa55251f5754de34820a29c7327a, Tue, 16 Nov 2021 19:09:21 +0000, by bdeegan on octodog
on the problematic code and I get the following
| +-<path-to-lib>\importLibrary.lib
| +-<path-obj>\module1.obj
| | +-<path-src>\module1.f
| | +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
| +-<path-obj>\module2.obj
| | +-<path-src>\module2.f
| | +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
| +-<path-obj>\fortranFile1.obj
| | +-<path-src>\fortranFile1.f
| | +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
In this instance, fortranFile1.f
has both use module1
and use module2
statements within the contained functions/subroutines, yet the dependency is not showing up in the tree. However, if I look further down in the tree I see this:
| +-<path-obj>\fortranFile2.obj
| | +-<path-src>\fortranFile2.f90
| | +-<path-obj>\moduleA.mod
| | | +-<path-src>\moduleA.f90
| | | +-<path-obj>\moduleB.mod
| | | | +-<path-src>\moduleB.f90
| | | | +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
| | | +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
| | +-[<path-obj>\moduleB.mod]
| | +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
So, it seems to be getting the dependency correct for .f90
extensions but not .f
extensions.
Could it be that the .mod
files are not added here properly when we are passing .f
files that use modules to the library target?
So, it seems to be getting the dependency correct for .f90 extensions but not .f extensions.
That's not impossible, since they use separate settings groups, but this stuff is convoluted enough it's hard to see. There are also slightly differing settings for lib and non-lib.
Did you say you were using a modified copy of ifort.py?
Is it possible to attach a dump of the environment variables (really only the set that start with F are interesting, I believe) - it might show the anomaly that's hitting your usage.
Yes, we have a modified version of ifort.py. Staring at the differences with your current code the diffs are:
Here's the variables:
FORTRANFLAGS = ['/nologo', '/MD', '/Qvc14', '/warn:unused', '/warn:uncalled', '/O3'] # We set this one
F90FLAGS = ['/nologo', '/MD', '/Qvc14', '/warn:unused', '/warn:uncalled', '/O3'] # We set this one
F95FLAGS = ['/nologo', '/MD', '/Qvc14', '/warn:unused', '/warn:uncalled', '/O3'] # We set this one
FORTRANPATH = ['#.']
FORTRANSUFFIXES = ['.f', '.for', '.ftn', '.F', '.FOR', '.FTN', '.fpp', '.FPP', '.f77', '.F77', '.f90', '.F90', '.f95', '.F95', '.f03', '.F03', '.f08', '.F08']
FORTRANCOM = $FORTRAN -object:$TARGET -c $FORTRANFLAGS $_FORTRANINCFLAGS $_FORTRANMODFLAG $SOURCES
FORTRANPPCOM = $FORTRAN -object:$TARGET -c $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS $_FORTRANMODFLAG $SOURCES
FORTRANMODPREFIX =
FORTRANMODSUFFIX = .mod
FORTRANMODDIR = ${TARGET.dir} # We set this one
FORTRANMODDIRPREFIX = /module:
FORTRANMODDIRSUFFIX =
F77FLAGS =
F77COM = $F77 -object:$TARGET -c $F77FLAGS $_F77INCFLAGS $SOURCES
F77PPCOM = $F77 -object:$TARGET -c $F77FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F77INCFLAGS $SOURCES
F90COM = $F90 -object:$TARGET -c $F90FLAGS $_F90INCFLAGS $_FORTRANMODFLAG $SOURCES
F90PPCOM = $F90 -object:$TARGET -c $F90FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F90INCFLAGS $_FORTRANMODFLAG $SOURCES
F95COM = $F95 -object:$TARGET -c $F95FLAGS $_F95INCFLAGS $_FORTRANMODFLAG $SOURCES
F95PPCOM = $F95 -object:$TARGET -c $F95FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F95INCFLAGS $_FORTRANMODFLAG $SOURCES
F03FLAGS =
F03COM = $F03 -o $TARGET -c $F03FLAGS $_F03INCFLAGS $_FORTRANMODFLAG $SOURCES
F03PPCOM = $F03 -o $TARGET -c $F03FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F03INCFLAGS $_FORTRANMODFLAG $SOURCES
F08FLAGS =
F08COM = $F08 -o $TARGET -c $F08FLAGS $_F08INCFLAGS $_FORTRANMODFLAG $SOURCES
F08PPCOM = $F08 -o $TARGET -c $F08FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F08INCFLAGS $_FORTRANMODFLAG $SOURCES
F77 = ifort
F90 = ifort
FORTRAN = ifort
F95 = ifort
F90PATH = ${TARGET.dir} # We set this one
Noted which ones we explicitly set.
Based on my hypothesis I tried to reproduce this with a simple example, could not do it yet....
@dnwillia-work - could you provide your modified version of ifort.py ?
Yeah sure. I’ll push it up later today and share a PR.
At least at my end, the effort of the ifort.py
tool to add those extra recognized suffixes (.i
and .i90
) messes things up - you get into a place where apparently the .f
files aren't scanned (the .f90
ones aren't either, so this doesn't explain your issue). Are those actually used for anything? As I said... somewhere... I can't find any hint of those in current Intel docs, which aren't particularly searchable.
Regarding the file extensions there is this:
So, Intel does seem to accept .i
and .i90
as acceptable file extensions where those files are passed to the compiler.
Sigh... I'd swear I had been looking at that very page, must be blind.
lol. Yeah, it's not the easiest to navigate or digest it.
Here's the ifort tool module we are using currently:
There are these comments in the wiki
Thanks for sharing that. In general, I agree with what is proposed. The stuff about the dialects is probably overly complex.
I tried again to reproduce this issue outside our regular build system using the ifort.py tool I shared and cannot. My SConstruct looks like this:
import os
localTools = os.path.join('.', 'site_scons', 'site_tools')
env = Environment(toolPath = localTools, tools = ['default', 'ifort'])
env['LINKFLAGS'] += ['/subsystem:console'] # Needed to link a Program()
env.Program('hello77', ['hello77.f', 'fixedModule.f', 'freeModule.f90'])
env.Program('hello90', ['hello90.f90', 'fixedModule.f', 'freeModule.f90'])
Source files are:
hello77.f
program hello
use fixedModule, only : sayHello
use freeModule, only : sayHello90
call sayHello
call sayHello90
end program hello
hello90.f90
program hello90
use fixedModule, only : sayHello
use freeModule, only : sayHello90
call sayHello
call sayHello90
end program hello90
freeModule.f90
module freeModule
public :: sayHello90
contains
subroutine sayHello90()
print *,"Hello from free form module!"
end subroutine sayHello90
end module freeModule
fixedModule.f
module fixedModule
public :: sayHello
contains
subroutine sayHello()
print *,"Hello from fixed form module!"
end subroutine sayHello
end module fixedModule
I get this tree:
+-.
+-fixedModule.f
+-fixedmodule.mod
| +-fixedModule.f
| +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
+-fixedModule.obj
| +-fixedModule.f
| +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
+-freeModule.f90
+-freemodule.mod
| +-freeModule.f90
| +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
+-freeModule.obj
| +-freeModule.f90
| +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
+-hello77.exe
| +-hello77.obj
| | +-hello77.f
| | +-[fixedmodule.mod]
| | +-[freemodule.mod]
| | +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
| +-[fixedModule.obj]
| +-[freeModule.obj]
| +-C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.20.27508\bin\HostX64\x64\link.EXE
+-hello77.f
+-[hello77.obj]
+-hello90.exe
| +-hello90.obj
| | +-hello90.f90
| | +-[fixedmodule.mod]
| | +-[freemodule.mod]
| | +-C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.5.281\windows\bin\intel64\ifort.EXE
| +-[fixedModule.obj]
| +-[freeModule.obj]
| +-C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.20.27508\bin\HostX64\x64\link.EXE
+-hello90.f90
+-[hello90.obj]
+-SConstruct
So, it all looks ok.
Interesting side point: I have to add /subsystem:console
there for env.Program()
targets to link otherwise the linker does not find the entry point.
@mwichmann I ran into this again today on a fairly simple project I'm messing with.
I did some tracing/debugging and what I found is that this warning gets generated if I pass -warn=all
on the command line:
when a module uses another module. So, if you do this in my prior example:
module fixedModule
use freeModule
public :: sayHello
contains
subroutine sayHello()
print *,"Hello from fixed form module!"
end subroutine sayHello
end module fixedModule
then that fails because fixedModule
is compiled without compiling freeModule
first.
I checked that the scanner perfectly figures out that freeModule.mod
is a dependency of fixedModule.f
but n
here is returned as none:
n, i = self.find_include(dep, source_dir, path)
and you get the warning. I find it hard to parse everything that goes on if I follow the rabbit hole down the function call to find_include
but if the general assumption is that the module files will be present in the source tree like .h, .c, .f, .f90 etc... then that assumption is incorrect. The module files are a compiler output file, i.e. generated by the compiler.
Interesting. Is this only with ifort?
@mwichmann I ran into this again today on a fairly simple project I'm messing with.
I did some tracing/debugging and what I found is that this warning gets generated if I pass
-warn=all
on the command line:when a module uses another module. So, if you do this in my prior example:
module fixedModule use freeModule public :: sayHello contains subroutine sayHello() print *,"Hello from fixed form module!" end subroutine sayHello end module fixedModule
then that fails because
fixedModule
is compiled without compilingfreeModule
first.I checked that the scanner perfectly figures out that
freeModule.mod
is a dependency offixedModule.f
butn
here is returned as none:n, i = self.find_include(dep, source_dir, path)
and you get the warning. I find it hard to parse everything that goes on if I follow the rabbit hole down the function call to
find_include
but if the general assumption is that the module files will be present in the source tree like .h, .c, .f, .f90 etc... then that assumption is incorrect. The module files are a compiler output file, i.e. generated by the compiler
Can you paste output when run with scons --tree=all
?
The module files are a compiler output file, i.e. generated by the compiler.
As to the last sentence - at least on some level, the code knows that, that's what emitters do, add in files that are generated by tools.
Just as a note (unrelated to the issue), this stanza:
localTools = os.path.join('.', 'site_scons', 'site_tools')
env = Environment(toolPath = localTools, tools = ['default', 'ifort'])
should be unneeded - SCons already looks in site_scons/site_tools
without you having to add that to the toolpath
.
and I can run this example, assuming I got the whole thing including problems, with gfortran (and taking out the Windows-specific flag), so I'm assuming we'll be able to spot what's different in the setup evenutally.
@mwichmann Sorry for cluttering up this thread. I did a bunch more investigation and have reproduced a few issues, including the one in this thread which looks like it is due to Fortran code with modules not working right with variant_dir
See this repo:
https://github.com/dnwillia/SConsTests
I think everything for this issue is reproduced there on a really basic example.
Okay. We admittedly have a lot of Fortran issues already, and not enough people using it to drive getting things fixed. Maybe this is a moment.
https://github.com/SCons/scons/labels/Fortran
lol, yes. James and I were speculating last night we are the only ones.
Are you running of the master development branch? Or the latest release (4.5.2)?
From the linked gh:
None of this code builds out of the box at all on Windows. The ifort tool does not configure properly even if you have it pre-configured in the environment.
This is no surprise. The current Intel opeAPI toolset depends heavily on a setup that fills in environment variables, which SCons proceeds to ignore in its search for purity and reproducibility. Things which are vital to actually running ifort, icc, and their next-gen equivalents need to bet set up in the tool. The msvc setup tool has the same problem, and it's grown substantially over the years are more and more stuff becomes needed. I know I've said this before, sorry if it's repeating: this kind of heavyweight (you count on a few thousand lines of shell script or batch file to have run, leaving your environment ready-to-go) isn't a great fit for the current SCons model of simple tool modules and isolation of the environment in which the builds actually run - it makes for a lot of work, and the ground keeps changing under you.
@bdbaddog The tests are all with 4.5.2... I've not quite figured out how to create a wheel yet from your repo. Happy to try that.
Matt, regarding Intel compilers I think the compromise would be to have the tool call the necessary configuration script for the Intel compiler to setup the Environment object so that code will build. I have a custom config tool that works, which I derived from a SCons provided tool years ago, but it's kind of a pain to maintain it.
@bdbaddog The tests are all with 4.5.2... I've not quite figured out how to create a wheel yet from your repo. Happy to try that.
If you have a checked out git repo, you can just run via <path to your checked out scons>/scripts/scons.py <your normal args here>
Okay, and this line:
Building code with fortran modules does not work right with variant_dir. You need to set the location where fortran modules (.mod) get generated otherwise they get stored into the same directory as the SConstruct.
Yes, this comes up repeatedly. SConscripts are evaluated in the context of the directory they're in, but then the builds happen in the context of the SConstruct dir (simplification, but will do for now). Just had a wrestling match with flex/bison "extra files" over this. I know the past Fortran tool maintainers knew this, but that doesn't mean every combination is right. Certainly when glancing back over some of the past bugs this theme comes up several times - if there's a subdirectory involved (which there always is if there's a varintdir in use), then things can go wrong.
@bdbaddog The tests are all with 4.5.2... I've not quite figured out how to create a wheel yet from your repo. Happy to try that.
If you have a checked out git repo, you can just run via
<path to your checked out scons>/scripts/scons.py <your normal args here>
Or, in the top directory of your git checkout of scons, and with your virtualenv activated:
pip install -e .
OK let me give it a shot. I did not think of that.
Alright, done, updated the README with some notes about the install into my environment.
All the issues persist, including this one.
Matt I think the behaviour you describe here:
Yes, this comes up repeatedly. SConscripts are evaluated in the context of the directory they're in, but then the builds happen in the context of the SConstruct dir (simplification, but will do for now). Just had a wrestling match with flex/bison "extra files" over this.
is fine. It's just that the object files produced by the compiler do not end up in that directory. The module files should really be going to the same place as the object files. There is a link in my notes to the change I made that gets them into the right spot.
Which, at first glance, sounds sane (if you mean env["FORTRANMODDIR"] = "${TARGET.dir}"
). Waiting on @bdbaddog though!
Which, at first glance, sounds sane (if you mean
env["FORTRANMODDIR"] = "${TARGET.dir}"
). Waiting on @bdbaddog though!
Yes, that's the line I mean.
Describe the bug scons does not appear to be correctly find dependencies on *.mod files in Fortran files.
My project includes a Fortran library that includes a number of source files and a module including some shared variables. When compiling on windows with ifort, I get the following error when doing a clean build:
error #7002: Error in opening the compiled module file. Check INCLUDE paths.
Subsequent builds complete successfully.Investigation shows that the above compiler error is seen because the .mod file required by the source files is not built before the source file compilation. Reordering the list of source files improves the situation, but it still fails periodically when building in parallel. Subsequent attempts to compile work as the .mod file is created during the first scons attempt and are thus already available.
Per suggestion from discord, I ran scons with
--tree=prune
to check if the consumers of the module correctly listed the *.mod file as a build dependency. The output below (anonymized) shows the output for one of the source files:Note that the source file does not list the *.mod file as a dependency.
The project is cross-platform so I also checked the result of
--tree=prune
on linux (compiled with gfortran). Scons also did not recognize the dependency on the mod file (output was the same as above with the exception of the compiler path). However, the code compiles without issue on linux.Since the above shows that the source files are not identifying the *.mod file as a build dependency, my assumption is that there is some issue in the way that scons is determining dependencies for Fortran files. I'm not sure why there's not an issue with gfortran on linux.
Required information