From 93035fd1121dc3701e89ce50477f4523e4e03560 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Oct 2016 07:09:39 +0530 Subject: Print a useful error when unable to determine linker --- mesonbuild/backend/backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'mesonbuild/backend') diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 0eb4c6e..ad05a60 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -278,7 +278,7 @@ class Backend(): for s in src: if c.can_compile(s): return c - raise RuntimeError('Unreachable code') + raise AssertionError("BUG: Couldn't determine linker for sources {!r}".format(src)) def object_filename_from_source(self, target, source): return source.fname.replace('/', '_').replace('\\', '_') + '.' + self.environment.get_object_suffix() -- cgit v1.1 From 8bee336e2a9bf0033d2fa852a48d6d17ce2a8869 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Oct 2016 07:23:15 +0530 Subject: ninja: Always use RawFilename for unity sources and deps The use of has_dir_part is a terrible back that we need to move away from. This will eventually be fixed by always using File() objects everywhere. For now, this is needed for unity builds to work. --- mesonbuild/backend/ninjabackend.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'mesonbuild/backend') diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index fa537ad..fe3be4c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -329,13 +329,14 @@ int dummy; generated_source_files = [] for rel_src, gensrc in generated_sources.items(): generated_output_sources.append(rel_src) + raw_src = RawFilename(rel_src) if self.environment.is_source(rel_src) and not self.environment.is_header(rel_src): if is_unity: - unity_deps.append(rel_src) + unity_deps.append(raw_src) abs_src = os.path.join(self.environment.get_build_dir(), rel_src) unity_src.append(abs_src) else: - generated_source_files.append(RawFilename(rel_src)) + generated_source_files.append(raw_src) elif self.environment.is_object(rel_src): obj_list.append(rel_src) elif self.environment.is_library(rel_src): @@ -344,7 +345,7 @@ int dummy; # Assume anything not specifically a source file is a header. This is because # people generate files with weird suffixes (.inc, .fh) that they then include # in their source files. - header_deps.append(RawFilename(rel_src)) + header_deps.append(raw_src) # These are the generated source files that need to be built for use by # this target. We create the Ninja build file elements for this here # because we need `header_deps` to be fully generated in the above loop. @@ -356,10 +357,11 @@ int dummy; # sources. This can be extended to other $LANG->C compilers later if # necessary. This needs to be separate for at least Vala for src in vala_generated_sources: + raw_src = RawFilename(src) src_list.append(src) if is_unity: unity_src.append(os.path.join(self.environment.get_build_dir(), src)) - header_deps.append(src) + header_deps.append(raw_src) else: # Generated targets are ordered deps because the must exist # before the sources compiling them are used. After the first @@ -367,12 +369,12 @@ int dummy; # This should work in all cases. If it does not, then just # move them from orderdeps to proper deps. if self.environment.is_header(src): - header_deps.append(src) + header_deps.append(raw_src) else: # Passing 'vala' here signifies that we want the compile # arguments to be specialized for C code generated by # valac. For instance, no warnings should be emitted. - obj_list.append(self.generate_single_compile(target, outfile, src, 'vala', [], header_deps)) + obj_list.append(self.generate_single_compile(target, outfile, raw_src, 'vala', [], header_deps)) # Generate compile targets for all the pre-existing sources for this target for f, src in target_sources.items(): if not self.environment.is_header(src): @@ -386,7 +388,7 @@ int dummy; obj_list += self.flatten_object_list(target) if is_unity: for src in self.generate_unity_files(target, unity_src): - obj_list.append(self.generate_single_compile(target, outfile, src, True, unity_deps + header_deps)) + obj_list.append(self.generate_single_compile(target, outfile, RawFilename(src), True, unity_deps + header_deps)) linker = self.determine_linker(target, src_list + generated_output_sources) elem = self.generate_link(target, outfile, outname, obj_list, linker, pch_objects) self.generate_shlib_aliases(target, self.get_target_dir(target)) @@ -1670,10 +1672,10 @@ rule FORTRAN_DEP_HACK """ Compiles only C/C++ and ObjC/ObjC++ sources """ - if(isinstance(src, str) and src.endswith('.h')): - raise RuntimeError('Fug') + if isinstance(src, str) and src.endswith('.h'): + raise AssertionError('BUG: sources should not contain headers') if isinstance(src, RawFilename) and src.fname.endswith('.h'): - raise RuntimeError('Fug') + raise AssertionError('BUG: sources should not contain headers') extra_orderdeps = [] compiler = self.get_compiler_for_source(src, target.is_cross) commands = [] @@ -1716,12 +1718,12 @@ rule FORTRAN_DEP_HACK break if isinstance(src, RawFilename): rel_src = src.fname - elif is_generated: - if self.has_dir_part(src): - rel_src = src + if os.path.isabs(src.fname): + abs_src = src.fname else: - rel_src = os.path.join(self.get_target_private_dir(target), src) - abs_src = os.path.join(self.environment.get_source_dir(), rel_src) + abs_src = os.path.join(self.environment.get_build_dir(), src.fname) + elif is_generated: + raise AssertionError('BUG: broken generated source file handling for {!r}'.format(src)) else: if isinstance(src, File): rel_src = src.rel_to_builddir(self.build_to_src) @@ -1805,6 +1807,7 @@ rule FORTRAN_DEP_HACK return rel_obj def has_dir_part(self, fname): + # FIXME FIXME: The usage of this is a terrible and unreliable hack return '/' in fname or '\\' in fname # Fortran is a bit weird (again). When you link against a library, just compiling a source file -- cgit v1.1 From b070f505d362ecb9047b8da09737a127abeed613 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Oct 2016 07:30:29 +0530 Subject: ninja: Generate Vala compile rules after all header_deps are found Same reason as generated C sources --- mesonbuild/backend/ninjabackend.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'mesonbuild/backend') diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index fe3be4c..1f9357b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -353,9 +353,11 @@ int dummy; src_list.append(src) obj_list.append(self.generate_single_compile(target, outfile, src, True, header_deps=header_deps)) + # 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 + vala_generated_source_files = [] for src in vala_generated_sources: raw_src = RawFilename(src) src_list.append(src) @@ -371,10 +373,15 @@ int dummy; if self.environment.is_header(src): header_deps.append(raw_src) else: - # Passing 'vala' here signifies that we want the compile - # arguments to be specialized for C code generated by - # valac. For instance, no warnings should be emitted. - obj_list.append(self.generate_single_compile(target, outfile, raw_src, 'vala', [], header_deps)) + # We gather all these and generate compile rules below + # after `header_deps` (above) is fully generated + vala_generated_source_files.append(raw_src) + for src in vala_generated_source_files: + # Passing 'vala' here signifies that we want the compile + # arguments to be specialized for C code generated by + # valac. For instance, no warnings should be emitted. + obj_list.append(self.generate_single_compile(target, outfile, src, 'vala', [], header_deps)) + # Generate compile targets for all the pre-existing sources for this target for f, src in target_sources.items(): if not self.environment.is_header(src): -- cgit v1.1 From 57ce7d461877b2ad8da4f4712879880148759bc6 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Oct 2016 07:38:32 +0530 Subject: Add support for extracting objects in unity builds Not only does extract_all_objects() now work properly again, extract_objects() also works if you specify a subset of sources all of which have been compiled into a single unified object. So, for instance, this allows you to extract all the objects corresponding to the C sources compiled into a target consisting of C and C++ sources. --- mesonbuild/backend/backends.py | 81 +++++++++++++++++--------------------- mesonbuild/backend/ninjabackend.py | 6 +-- 2 files changed, 40 insertions(+), 47 deletions(-) (limited to 'mesonbuild/backend') diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index ad05a60..fd71924 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -19,7 +19,7 @@ from .. import mesonlib from .. import compilers import json import subprocess -from ..mesonlib import MesonException +from ..mesonlib import MesonException, get_compiler_for_source, classify_unity_sources class InstallData(): def __init__(self, source_dir, build_dir, prefix): @@ -78,21 +78,6 @@ class Backend(): priv_dirname = self.get_target_private_dir_abs(t) os.makedirs(priv_dirname, exist_ok=True) - def get_compiler_for_lang(self, lang): - for i in self.build.compilers: - if i.language == lang: - return i - raise RuntimeError('No compiler for language ' + lang) - - def get_compiler_for_source(self, src, is_cross): - comp = self.build.cross_compilers if is_cross else self.build.compilers - for i in comp: - if i.can_compile(src): - return i - if isinstance(src, mesonlib.File): - src = src.fname - raise RuntimeError('No specified compiler can handle file ' + src) - def get_target_filename(self, t): if isinstance(t, build.CustomTarget): if len(t.get_outputs()) != 1: @@ -153,14 +138,17 @@ class Backend(): # target that the GeneratedList is used in return os.path.join(self.get_target_private_dir(target), src) + def get_unity_source_filename(self, target, suffix): + return target.name + '-unity.' + suffix + def generate_unity_files(self, target, unity_src): - langlist = {} abs_files = [] result = [] + compsrcs = classify_unity_sources(target.compilers.values(), unity_src) - def init_language_file(language, suffix): + def init_language_file(suffix): outfilename = os.path.join(self.get_target_private_dir_abs(target), - target.name + '-unity' + suffix) + self.get_unity_source_filename(target, suffix)) outfileabs = os.path.join(self.environment.get_build_dir(), outfilename) outfileabs_tmp = outfileabs + '.tmp' @@ -171,20 +159,12 @@ class Backend(): result.append(outfilename) return open(outfileabs_tmp, 'w') - try: - for src in unity_src: - comp = self.get_compiler_for_source(src, target.is_cross) - language = comp.get_language() - try: - ofile = langlist[language] - except KeyError: - suffix = '.' + comp.get_default_suffix() - ofile = langlist[language] = init_language_file(language, - suffix) - ofile.write('#include<%s>\n' % src) - finally: - for x in langlist.values(): - x.close() + # For each language, generate a unity source file and return the list + for comp, srcs in compsrcs.items(): + lang = comp.get_language() + with init_language_file(comp.get_default_suffix()) as ofile: + for src in srcs: + ofile.write('#include<%s>\n' % src) [mesonlib.replace_if_different(x, x + '.tmp') for x in abs_files] return result @@ -281,21 +261,34 @@ class Backend(): raise AssertionError("BUG: Couldn't determine linker for sources {!r}".format(src)) def object_filename_from_source(self, target, source): - return source.fname.replace('/', '_').replace('\\', '_') + '.' + self.environment.get_object_suffix() + if isinstance(source, mesonlib.File): + source = source.fname + return source.replace('/', '_').replace('\\', '_') + '.' + self.environment.get_object_suffix() - def determine_ext_objs(self, extobj, proj_dir_to_build_root=''): + def determine_ext_objs(self, extobj, proj_dir_to_build_root): result = [] targetdir = self.get_target_private_dir(extobj.target) + # With unity builds, there's just one object that contains all the + # sources, so if we want all the objects, just return that. + if self.environment.coredata.get_builtin_option('unity'): + if not extobj.unity_compatible: + # This should never happen + msg = 'BUG: Meson must not allow extracting single objects ' \ + 'in Unity builds' + raise AssertionError(msg) + comp = get_compiler_for_source(extobj.target.compilers.values(), + extobj.srclist[0]) + # The unity object name uses the full absolute path of the source file + osrc = os.path.join(self.get_target_private_dir_abs(extobj.target), + self.get_unity_source_filename(extobj.target, + comp.get_default_suffix())) + objname = self.object_filename_from_source(extobj.target, osrc) + objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) + return [objpath] for osrc in extobj.srclist: - # If extracting in a subproject, the subproject - # name gets duplicated in the file name. - pathsegs = osrc.subdir.split(os.sep) - if pathsegs[0] == 'subprojects': - pathsegs = pathsegs[2:] - fixedpath = os.sep.join(pathsegs) - objname = os.path.join(proj_dir_to_build_root, targetdir, - self.object_filename_from_source(extobj.target, osrc)) - result.append(objname) + objname = self.object_filename_from_source(extobj.target, osrc) + objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) + result.append(objpath) return result def get_pch_include_args(self, compiler, target): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 1f9357b..4ed062c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -18,7 +18,7 @@ from .. import build from .. import mlog from .. import dependencies from .. import compilers -from ..mesonlib import File, MesonException +from ..mesonlib import File, MesonException, get_compiler_for_source from .backends import InstallData from ..build import InvalidArguments import os, sys, pickle, re @@ -1684,7 +1684,7 @@ rule FORTRAN_DEP_HACK if isinstance(src, RawFilename) and src.fname.endswith('.h'): raise AssertionError('BUG: sources should not contain headers') extra_orderdeps = [] - compiler = self.get_compiler_for_source(src, target.is_cross) + compiler = get_compiler_for_source(target.compilers.values(), src) commands = [] # The first thing is implicit include directories: source, build and private. commands += compiler.get_include_args(self.get_target_private_dir(target), False) @@ -1867,7 +1867,7 @@ rule FORTRAN_DEP_HACK 'directory as source, please put it in a subdirectory.' \ ''.format(target.get_basename()) raise InvalidArguments(msg) - compiler = self.get_compiler_for_lang(lang) + compiler = target.compilers[lang] if compiler.id == 'msvc': src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[-1]) (commands, dep, dst, objs) = self.generate_msvc_pch_command(target, compiler, pch) -- cgit v1.1 From 7e9203fac9d35db0aede73c105db1baaccaaefef Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Oct 2016 08:16:26 +0530 Subject: Fix Unity builds for Fortran and D targets Also warn if we're trying to use a unity build on a target with fortran or D sources. --- mesonbuild/backend/ninjabackend.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'mesonbuild/backend') diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 4ed062c..f826f89 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -258,6 +258,20 @@ int dummy; srcs[f] = s return srcs + # Languages that can mix with C or C++ but don't support unity builds yet + # because the syntax we use for unity builds is specific to C/++/ObjC/++. + langs_cant_unity = ('d', 'fortran') + def get_target_source_can_unity(self, target, source): + if isinstance(source, File): + source = source.fname + suffix = os.path.splitext(source)[1][1:] + for lang in self.langs_cant_unity: + if not lang in target.compilers: + continue + if suffix in target.compilers[lang].file_suffixes: + return False + return True + def generate_target(self, target, outfile): if isinstance(target, build.CustomTarget): self.generate_custom_target(target, outfile) @@ -319,6 +333,18 @@ int dummy; header_deps += self.get_generated_headers(target) src_list = [] + if is_unity: + # Warn about incompatible sources if a unity build is enabled + langs = set(target.compilers.keys()) + langs_cant = langs.intersection(self.langs_cant_unity) + if langs_cant: + langs_are = langs = ', '.join(langs_cant).upper() + langs_are += ' are' if len(langs_cant) > 1 else ' is' + msg = '{} not supported in Unity builds yet, so {} ' \ + 'sources in the {!r} target will be compiled normally' \ + ''.format(langs_are, langs, target.name) + mlog.log(mlog.red('FIXME'), msg) + # Get a list of all generated *sources* (sources files, headers, # objects, etc). Needed to determine the linker. generated_output_sources = [] @@ -331,7 +357,7 @@ int dummy; generated_output_sources.append(rel_src) raw_src = RawFilename(rel_src) if self.environment.is_source(rel_src) and not self.environment.is_header(rel_src): - if is_unity: + if is_unity and self.get_target_source_can_unity(target, rel_src): unity_deps.append(raw_src) abs_src = os.path.join(self.environment.get_build_dir(), rel_src) unity_src.append(abs_src) @@ -386,7 +412,7 @@ int dummy; for f, src in target_sources.items(): if not self.environment.is_header(src): src_list.append(src) - if is_unity: + if is_unity and self.get_target_source_can_unity(target, src): abs_src = os.path.join(self.environment.get_build_dir(), src.rel_to_builddir(self.build_to_src)) unity_src.append(abs_src) @@ -1677,7 +1703,7 @@ rule FORTRAN_DEP_HACK def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]): """ - Compiles only C/C++ and ObjC/ObjC++ sources + Compiles C/C++, ObjC/ObjC++, and D sources """ if isinstance(src, str) and src.endswith('.h'): raise AssertionError('BUG: sources should not contain headers') -- cgit v1.1