Repository: mesonbuild/meson

Issue:
Crash with CMake subproject containing fortran sources
**Describe the bug**
While configuring a Meson project with a CMake subproject I get this:
```
Build targets in project: 255

PHARE 0.0.1

  Subprojects
    SAMRAI: YES

Found ninja-1.10.1 at /usr/bin/ninja
Traceback (most recent call last):                                                                  
  File "/home/jeandet/Documents/prog/meson/mesonbuild/mesonmain.py", line 140, in run
    return options.run_func(options)
  File "/home/jeandet/Documents/prog/meson/mesonbuild/msetup.py", line 245, in run
    app.generate()
  File "/home/jeandet/Documents/prog/meson/mesonbuild/msetup.py", line 159, in generate
    self._generate(env)
  File "/home/jeandet/Documents/prog/meson/mesonbuild/msetup.py", line 215, in _generate
    intr.backend.generate()
  File "/home/jeandet/Documents/prog/meson/mesonbuild/backend/ninjabackend.py", line 514, in generate
    self.generate_target(t)
  File "/home/jeandet/Documents/prog/meson/mesonbuild/backend/ninjabackend.py", line 832, in generate_target
    obj_list.append(self.generate_single_compile(target, src, False, [], header_deps))
  File "/home/jeandet/Documents/prog/meson/mesonbuild/backend/ninjabackend.py", line 2390, in generate_single_compile
    extra_deps += self.get_fortran_deps(compiler, abs_src, target)
  File "/home/jeandet/Documents/prog/meson/mesonbuild/backend/ninjabackend.py", line 2092, in get_fortran_deps
    mod_files = _scan_fortran_file_deps(src, srcdir, dirname, tdeps, compiler)
  File "/home/jeandet/Documents/prog/meson/mesonbuild/backend/ninjabackend.py", line 3046, in _scan_fortran_file_deps
    mod_files.extend(_scan_fortran_file_deps(incfile, srcdir, dirname, tdeps, compiler))
  File "/home/jeandet/Documents/prog/meson/mesonbuild/backend/ninjabackend.py", line 3039, in _scan_fortran_file_deps
    with src.open(encoding='ascii', errors='ignore') as f:
  File "/usr/lib64/python3.8/pathlib.py", line 1218, in open
    return io.open(self, mode, buffering, encoding, errors, newline,
  File "/usr/lib64/python3.8/pathlib.py", line 1074, in _opener
    return self._accessor.open(self, flags, mode)
FileNotFoundError: [Errno 2] No such file or directory: '/home/jeandet/Documents/prog/PHARE/bc_common.f'
```
Where top project PHARE has no Fortran sources, so this might have leaked form SAMRAI subproject, maybe missing subproject path?

**To Reproduce**

```
git clone -b meson-is-cool https://github.com/jeandet/PHARE
meson PHARE build
```

**system parameters**
* Native build
* Fedora 32
* Python  3.8.5
* Meson 0.55.1 and master
* Ninja 1.10.1



Discussion:

Resolving patch:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -543,10 +543,10 @@ def generate_compdb(self):
         # rule store as being wanted in compdb
         for for_machine in MachineChoice:
             for lang in self.environment.coredata.compilers[for_machine]:
-                rules += [ "%s%s" % (rule, ext) for rule in [self.get_compiler_rule_name(lang, for_machine)]
-                                                for ext in ['', '_RSP']]
-                rules += [ "%s%s" % (rule, ext) for rule in [self.get_pch_rule_name(lang, for_machine)]
-                                                for ext in ['', '_RSP']]
+                rules += ["%s%s" % (rule, ext) for rule in [self.get_compiler_rule_name(lang, for_machine)]
+                          for ext in ['', '_RSP']]
+                rules += ["%s%s" % (rule, ext) for rule in [self.get_pch_rule_name(lang, for_machine)]
+                          for ext in ['', '_RSP']]
         compdb_options = ['-x'] if mesonlib.version_compare(self.ninja_version, '>=1.9') else []
         ninja_compdb = [self.ninja_command, '-t', 'compdb'] + compdb_options + rules
         builddir = self.environment.get_build_dir()
@@ -990,7 +990,7 @@ def generate_coverage_command(self, elem, outputs):
                                     self.build.get_subproject_dir()),
                        self.environment.get_build_dir(),
                        self.environment.get_log_dir()] +
-                       (['--use_llvm_cov'] if use_llvm_cov else []))
+                      (['--use_llvm_cov'] if use_llvm_cov else []))
 
     def generate_coverage_rules(self):
         e = NinjaBuildElement(self.all_outputs, 'meson-coverage', 'CUSTOM_COMMAND', 'PHONY')
@@ -1560,13 +1560,13 @@ def generate_rust_target(self, target):
                                                                self.get_target_dir(target))
             else:
                 target_slashname_workaround_dir = self.get_target_dir(target)
-            (rpath_args, target.rpath_dirs_to_remove) = \
-                         rustc.build_rpath_args(self.environment,
-                                                self.environment.get_build_dir(),
-                                                target_slashname_workaround_dir,
-                                                self.determine_rpath_dirs(target),
-                                                target.build_rpath,
-                                                target.install_rpath)
+            rpath_args, target.rpath_dirs_to_remove = (
+                rustc.build_rpath_args(self.environment,
+                                       self.environment.get_build_dir(),
+                                       target_slashname_workaround_dir,
+                                       self.determine_rpath_dirs(target),
+                                       target.build_rpath,
+                                       target.install_rpath))
             # ... but then add rustc's sysroot to account for rustup
             # installations
             for rpath_arg in rpath_args:
@@ -2777,13 +2777,13 @@ def generate_link(self, target, outname, obj_list, linker, extra_args=None, stdl
                 self.get_target_dir(target))
         else:
             target_slashname_workaround_dir = self.get_target_dir(target)
-        (rpath_args, target.rpath_dirs_to_remove) = \
-                    linker.build_rpath_args(self.environment,
-                                            self.environment.get_build_dir(),
-                                            target_slashname_workaround_dir,
-                                            self.determine_rpath_dirs(target),
-                                            target.build_rpath,
-                                            target.install_rpath)
+        (rpath_args, target.rpath_dirs_to_remove) = (
+            linker.build_rpath_args(self.environment,
+                                    self.environment.get_build_dir(),
+                                    target_slashname_workaround_dir,
+                                    self.determine_rpath_dirs(target),
+                                    target.build_rpath,
+                                    target.install_rpath))
         commands += rpath_args
         # Add libraries generated by custom targets
         custom_target_libraries = self.get_custom_target_provided_libraries(target)
@@ -3041,7 +3041,9 @@ def _scan_fortran_file_deps(src: Path, srcdir: Path, dirname: Path, tdeps, compi
             # included files
             incmatch = incre.match(line)
             if incmatch is not None:
-                incfile = srcdir / incmatch.group(1)
+                incfile = src.parent / incmatch.group(1)
+                # NOTE: src.parent is most general, in particular for CMake subproject with Fortran file
+                # having an `include 'foo.f'` statement.
                 if incfile.suffix.lower()[1:] in compiler.file_suffixes:
                     mod_files.extend(_scan_fortran_file_deps(incfile, srcdir, dirname, tdeps, compiler))
             # modules


Test patch:
diff --git a/test cases/fortran/15 include/include_hierarchy.f90 b/test cases/fortran/15 include/include_hierarchy.f90
--- a/test cases/fortran/15 include/include_hierarchy.f90	
+++ b/test cases/fortran/15 include/include_hierarchy.f90	
@@ -1,3 +1,4 @@
+program test_include_hier
 
 implicit none
 
diff --git a/test cases/fortran/15 include/include_syntax.f90 b/test cases/fortran/15 include/include_syntax.f90
--- a/test cases/fortran/15 include/include_syntax.f90	
+++ b/test cases/fortran/15 include/include_syntax.f90	
@@ -1,3 +1,5 @@
+program test_include_syntax
+
 implicit none
 
 integer :: x, y
@@ -20,4 +22,4 @@
 
 print *, 'OK: Fortran include tests: x=',x
 
-end program
\ No newline at end of file
+end program
diff --git a/test cases/fortran/15 include/include_tests.f90 b/test cases/fortran/15 include/include_tests.f90
deleted file mode 100644
--- a/test cases/fortran/15 include/include_tests.f90	
+++ /dev/null
@@ -1,24 +0,0 @@
-implicit none
-
-integer :: x, y
-
-x = 1
-y = 0
-
-! include "timestwo.f90"
-
-! double quote and inline comment check
-include "timestwo.f90"  ! inline comment check
-if (x/=2) error stop 'failed on first include'
-
-! leading space and single quote check
-  include 'timestwo.f90'
-if (x/=4) error stop 'failed on second include'
-
-! Most Fortran compilers can't handle the non-standard #include,
-! including (ha!) Flang, Gfortran, Ifort and PGI.
-! #include "timestwo.f90"
-
-print *, 'OK: Fortran include tests: x=',x
-
-end program
\ No newline at end of file
diff --git a/test cases/fortran/15 include/meson.build b/test cases/fortran/15 include/meson.build
--- a/test cases/fortran/15 include/meson.build	
+++ b/test cases/fortran/15 include/meson.build	
@@ -1,8 +1,19 @@
 project('Inclusive', 'fortran',
   meson_version: '>= 0.51.1')
 
+cm = import('cmake')
+
 hier_exe = executable('include_hierarchy', 'include_hierarchy.f90')
 test('Fortran include file hierarchy', hier_exe)
 
 syntax_exe = executable('include_syntax', 'include_syntax.f90')
-test('Fortran include file syntax', syntax_exe)
\ No newline at end of file
+test('Fortran include file syntax', syntax_exe)
+
+# older CI runs into problems with too-old Ninja and CMake and Fortran
+ninja_version = run_command('ninja', '--version').stdout().strip()
+cmake_version = run_command('cmake', '--version').stdout().split()[2]
+if ninja_version.version_compare('>=1.10.0') and cmake_version.version_compare('>=3.17.0')
+  cm.subproject('cmake_inc')
+else
+  message('SKIP: CMake Fortran subproject with include. Ninja >= 1.10 and CMake >= 3.17 needed. You have Ninja ' + ninja_version + ' and CMake ' + cmake_version)
+endif
diff --git a/test cases/fortran/15 include/subprojects/cmake_inc/CMakeLists.txt b/test cases/fortran/15 include/subprojects/cmake_inc/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/test cases/fortran/15 include/subprojects/cmake_inc/CMakeLists.txt	
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.17)
+project(cmake_inc LANGUAGES Fortran)
+
+add_executable(main main.f90)
diff --git a/test cases/fortran/15 include/subprojects/cmake_inc/main.f90 b/test cases/fortran/15 include/subprojects/cmake_inc/main.f90
new file mode 100644
--- /dev/null
+++ b/test cases/fortran/15 include/subprojects/cmake_inc/main.f90	
@@ -0,0 +1,9 @@
+program test_subproject_inc
+
+implicit none
+
+include 'thousand.f90'
+
+if (thousand /= 1000) error stop 'did not include properly'
+
+end program
diff --git a/test cases/fortran/15 include/subprojects/cmake_inc/thousand.f90 b/test cases/fortran/15 include/subprojects/cmake_inc/thousand.f90
new file mode 100644
--- /dev/null
+++ b/test cases/fortran/15 include/subprojects/cmake_inc/thousand.f90	
@@ -0,0 +1 @@
+integer, parameter :: thousand = 1000