summaryrefslogtreecommitdiff
path: root/mesonbuild/backend/ninjabackend.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/backend/ninjabackend.py')
-rw-r--r--mesonbuild/backend/ninjabackend.py110
1 files changed, 76 insertions, 34 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index debb4fbcf..afef9a997 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+from typing import List
import os
import re
import shlex
@@ -29,9 +29,9 @@ from .. import build
from .. import mlog
from .. import dependencies
from .. import compilers
-from ..compilers import CompilerArgs, CCompiler, VisualStudioCCompiler
+from ..compilers import CompilerArgs, CCompiler, VisualStudioCCompiler, FortranCompiler
from ..linkers import ArLinker
-from ..mesonlib import File, MesonException, OrderedSet
+from ..mesonlib import File, MachineChoice, MesonException, OrderedSet
from ..mesonlib import get_compiler_for_source, has_path_sep
from .backends import CleanTrees
from ..build import InvalidArguments
@@ -428,12 +428,7 @@ int dummy;
# Generate rules for building the remaining source files in this target
outname = self.get_target_filename(target)
obj_list = []
- use_pch = self.environment.coredata.base_options.get('b_pch', False)
is_unity = self.is_unity(target)
- if use_pch and target.has_pch():
- pch_objects = self.generate_pch(target, outfile)
- else:
- pch_objects = []
header_deps = []
unity_src = []
unity_deps = [] # Generated sources that must be built before compiling a Unity target.
@@ -486,6 +481,12 @@ int dummy;
header_deps=header_deps)
obj_list.append(o)
+ use_pch = self.environment.coredata.base_options.get('b_pch', False)
+ if use_pch and target.has_pch():
+ pch_objects = self.generate_pch(target, outfile, header_deps=header_deps)
+ else:
+ pch_objects = []
+
# Generate compilation targets for C sources generated from Vala
# sources. This can be extended to other $LANG->C compilers later if
# necessary. This needs to be separate for at least Vala
@@ -1460,7 +1461,7 @@ int dummy;
or langname == 'cs':
continue
crstr = ''
- cross_args = self.environment.properties.host.get_external_link_args(langname)
+ cross_args = self.environment.coredata.get_external_link_args(MachineChoice.HOST, langname)
if is_cross:
crstr = '_CROSS'
rule = 'rule %s%s_LINKER\n' % (langname, crstr)
@@ -1826,7 +1827,8 @@ rule FORTRAN_DEP_HACK%s
if compiler is None:
self.fortran_deps[target.get_basename()] = {}
return
- modre = re.compile(r"\s*module\s+(\w+)", re.IGNORECASE)
+ modre = re.compile(r"\s*\bmodule\b\s+(\w+)\s*$", re.IGNORECASE)
+ submodre = re.compile(r"\s*\bsubmodule\b\s+\((\w+:?\w+)\)\s+(\w+)\s*$", re.IGNORECASE)
module_files = {}
for s in target.get_sources():
# FIXME, does not work for Fortran sources generated by
@@ -1836,34 +1838,44 @@ rule FORTRAN_DEP_HACK%s
continue
filename = s.absolute_path(self.environment.get_source_dir(),
self.environment.get_build_dir())
- # Some Fortran editors save in weird encodings,
- # but all the parts we care about are in ASCII.
- with open(filename, errors='ignore') as f:
+ # Fortran keywords must be ASCII.
+ with open(filename, encoding='ascii', errors='ignore') as f:
for line in f:
modmatch = modre.match(line)
if modmatch is not None:
modname = modmatch.group(1).lower()
- if modname == 'procedure':
- # MODULE PROCEDURE construct
- continue
if modname in module_files:
raise InvalidArguments(
'Namespace collision: module %s defined in '
'two files %s and %s.' %
(modname, module_files[modname], s))
module_files[modname] = s
+ else:
+ submodmatch = submodre.match(line)
+ if submodmatch is not None:
+ submodname = submodmatch.group(2).lower()
+ if submodname in module_files:
+ raise InvalidArguments(
+ 'Namespace collision: submodule %s defined in '
+ 'two files %s and %s.' %
+ (submodname, module_files[submodname], s))
+ module_files[submodname] = s
+
self.fortran_deps[target.get_basename()] = module_files
- def get_fortran_deps(self, compiler, src, target):
+ def get_fortran_deps(self, compiler: FortranCompiler, src: str, target) -> List[str]:
mod_files = []
- usere = re.compile(r"\s*use\s+(\w+)", re.IGNORECASE)
+ usere = re.compile(r"\s*use,?\s*(?:non_intrinsic)?\s*(?:::)?\s*(\w+)", re.IGNORECASE)
+ submodre = re.compile(r"\s*\bsubmodule\b\s+\((\w+:?\w+)\)\s+(\w+)\s*$", re.IGNORECASE)
dirname = self.get_target_private_dir(target)
tdeps = self.fortran_deps[target.get_basename()]
- with open(src) as f:
+ with open(src, encoding='ascii', errors='ignore') as f:
for line in f:
usematch = usere.match(line)
if usematch is not None:
usename = usematch.group(1).lower()
+ if usename == 'intrinsic': # this keeps the regex simpler
+ continue
if usename not in tdeps:
# The module is not provided by any source file. This
# is due to:
@@ -1882,9 +1894,23 @@ rule FORTRAN_DEP_HACK%s
# the same name.
if mod_source_file.fname == os.path.basename(src):
continue
- mod_name = compiler.module_name_to_filename(
- usematch.group(1))
+ mod_name = compiler.module_name_to_filename(usename)
mod_files.append(os.path.join(dirname, mod_name))
+ else:
+ submodmatch = submodre.match(line)
+ if submodmatch is not None:
+ parents = submodmatch.group(1).lower().split(':')
+ assert len(parents) in (1, 2), (
+ 'submodule ancestry must be specified as'
+ ' ancestor:parent but Meson found {}'.parents)
+ for parent in parents:
+ if parent not in tdeps:
+ raise MesonException("submodule {} relies on parent module {} that was not found.".format(submodmatch.group(2).lower(), parent))
+ if tdeps[parent].fname == os.path.basename(src): # same file
+ continue
+ mod_name = compiler.module_name_to_filename(parent)
+ mod_files.append(os.path.join(dirname, mod_name))
+
return mod_files
def get_cross_stdlib_args(self, target, compiler):
@@ -2184,12 +2210,7 @@ rule FORTRAN_DEP_HACK%s
commands += compiler.get_module_outdir_args(self.get_target_private_dir(target))
element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src)
- for d in header_deps:
- if isinstance(d, File):
- d = d.rel_to_builddir(self.build_to_src)
- elif not self.has_dir_part(d):
- d = os.path.join(self.get_target_private_dir(target), d)
- element.add_dep(d)
+ self.add_header_deps(target, element, header_deps)
for d in extra_deps:
element.add_dep(d)
for d in order_deps:
@@ -2198,7 +2219,14 @@ rule FORTRAN_DEP_HACK%s
elif not self.has_dir_part(d):
d = os.path.join(self.get_target_private_dir(target), d)
element.add_orderdep(d)
- element.add_orderdep(pch_dep)
+ if compiler.id == 'msvc':
+ # MSVC does not show includes coming from the PCH with '/showIncludes',
+ # thus we must add an implicit dependency to the generated PCH.
+ element.add_dep(pch_dep)
+ else:
+ # All other compilers properly handle includes through the PCH, so only an
+ # orderdep is needed to make the initial build without depfile work.
+ element.add_orderdep(pch_dep)
# Convert from GCC-style link argument naming to the naming used by the
# current compiler.
commands = commands.to_native()
@@ -2209,6 +2237,14 @@ rule FORTRAN_DEP_HACK%s
element.write(outfile)
return rel_obj
+ def add_header_deps(self, target, ninja_element, header_deps):
+ for d in header_deps:
+ if isinstance(d, File):
+ d = d.rel_to_builddir(self.build_to_src)
+ elif not self.has_dir_part(d):
+ d = os.path.join(self.get_target_private_dir(target), d)
+ ninja_element.add_dep(d)
+
def has_dir_part(self, fname):
# FIXME FIXME: The usage of this is a terrible and unreliable hack
if isinstance(fname, File):
@@ -2239,6 +2275,7 @@ rule FORTRAN_DEP_HACK%s
just_name = os.path.basename(header)
(objname, pch_args) = compiler.gen_pch_args(just_name, source, dst)
commands += pch_args
+ commands += self._generate_single_compile(target, compiler)
commands += self.get_compile_debugfile_args(compiler, target, objname)
dep = dst + '.' + compiler.get_depfile_suffix()
return commands, dep, dst, [objname]
@@ -2254,7 +2291,7 @@ rule FORTRAN_DEP_HACK%s
dep = dst + '.' + compiler.get_depfile_suffix()
return commands, dep, dst, [] # Gcc does not create an object file during pch generation.
- def generate_pch(self, target, outfile):
+ def generate_pch(self, target, outfile, header_deps=[]):
cstr = ''
pch_objects = []
if target.is_cross:
@@ -2285,6 +2322,7 @@ rule FORTRAN_DEP_HACK%s
elem = NinjaBuildElement(self.all_outputs, dst, rulename, src)
if extradep is not None:
elem.add_dep(extradep)
+ self.add_header_deps(target, elem, header_deps)
elem.add_item('ARGS', commands)
elem.add_item('DEPFILE', dep)
elem.write(outfile)
@@ -2350,15 +2388,14 @@ rule FORTRAN_DEP_HACK%s
target_args = self.build_target_link_arguments(linker, target.link_whole_targets)
return linker.get_link_whole_for(target_args) if len(target_args) else []
- @staticmethod
@lru_cache(maxsize=None)
- def guess_library_absolute_path(linker, libname, search_dirs, patterns):
+ def guess_library_absolute_path(self, linker, libname, search_dirs, patterns):
for d in search_dirs:
for p in patterns:
trial = CCompiler._get_trials_from_pattern(p, d, libname)
if not trial:
continue
- trial = CCompiler._get_file_from_list(trial)
+ trial = CCompiler._get_file_from_list(self.environment, trial)
if not trial:
continue
# Return the first result
@@ -2471,6 +2508,11 @@ rule FORTRAN_DEP_HACK%s
if not isinstance(target, build.StaticLibrary):
commands += self.get_link_whole_args(linker, target)
+ if self.environment.is_cross_build() and not target.is_cross:
+ for_machine = MachineChoice.BUILD
+ else:
+ for_machine = MachineChoice.HOST
+
if not isinstance(target, build.StaticLibrary):
# Add link args added using add_project_link_arguments()
commands += self.build.get_project_link_args(linker, target.subproject, target.is_cross)
@@ -2480,7 +2522,7 @@ rule FORTRAN_DEP_HACK%s
if not target.is_cross:
# Link args added from the env: LDFLAGS. We want these to
# override all the defaults but not the per-target link args.
- commands += self.environment.coredata.get_external_link_args(linker.get_language())
+ commands += self.environment.coredata.get_external_link_args(for_machine, linker.get_language())
# Now we will add libraries and library paths from various sources
@@ -2526,7 +2568,7 @@ rule FORTRAN_DEP_HACK%s
# to be after all internal and external libraries so that unresolved
# symbols from those can be found here. This is needed when the
# *_winlibs that we want to link to are static mingw64 libraries.
- commands += linker.get_option_link_args(self.environment.coredata.compiler_options)
+ commands += linker.get_option_link_args(self.environment.coredata.compiler_options[for_machine])
dep_targets = []
dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal))