diff options
44 files changed, 798 insertions, 274 deletions
diff --git a/.travis.yml b/.travis.yml index fdf82ef..cf3a5a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,13 +13,13 @@ services: before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install ninja python3; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull jpakkane/mesonci:xenial; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull jpakkane/mesonci:yakkety; fi # We need to copy the current checkout inside the Docker container, # because it has the MR id to be tested checked out. script: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo FROM jpakkane/mesonci:xenial > Dockerfile; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo FROM jpakkane/mesonci:yakkety > Dockerfile; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo ADD . /root >> Dockerfile; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker build -t withgit .; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker run withgit /bin/sh -c "cd /root && TRAVIS=true ./run_tests.py"; fi diff --git a/authors.txt b/authors.txt index bdad5bd..f591d13 100644 --- a/authors.txt +++ b/authors.txt @@ -51,3 +51,6 @@ Guillaume Poirier-Morency Scott D Phillips Gautier Pelloux-Prayer Alexandre Foley +Jouni Kosonen +Aurelien Jarno +Mark Schulte diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 0eb4c6e..e91b44b 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 @@ -220,11 +200,15 @@ class Backend(): with open(exe_data, 'wb') as f: if isinstance(exe, dependencies.ExternalProgram): exe_fullpath = exe.fullpath + exe_needs_wrapper = False elif isinstance(exe, (build.BuildTarget, build.CustomTarget)): exe_fullpath = [self.get_target_filename_abs(exe)] + exe_needs_wrapper = exe.is_cross else: exe_fullpath = [exe] - is_cross = self.environment.is_cross_build() and \ + exe_needs_wrapper = False + is_cross = exe_needs_wrapper and \ + self.environment.is_cross_build() and \ self.environment.cross_info.need_cross_compiler() and \ self.environment.cross_info.need_exe_wrapper() if is_cross: @@ -278,24 +262,37 @@ 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() + 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 fa537ad..dd0143c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -18,11 +18,12 @@ 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 import subprocess, shutil +from collections import OrderedDict if mesonlib.is_windows(): quote_char = '"' @@ -239,7 +240,7 @@ int dummy; (relative to the build directory) of that type and the value being the GeneratorList or CustomTarget that generated it. """ - srcs = {} + srcs = OrderedDict() for gensrc in target.get_generated_sources(): for s in gensrc.get_outputs(): f = self.get_target_generated_dir(target, gensrc, s) @@ -247,7 +248,7 @@ int dummy; return srcs def get_target_sources(self, target): - srcs = {} + srcs = OrderedDict() for s in target.get_sources(): # BuildTarget sources are always mesonlib.File files which are # either in the source root, or generated with configure_file and @@ -258,6 +259,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) @@ -286,10 +301,10 @@ int dummy; # Pre-existing target C/C++ sources to be built; dict of full path to # source relative to build root and the original File object. - target_sources = {} + target_sources = OrderedDict() # GeneratedList and CustomTarget sources to be built; dict of the full # path to source relative to build root and the generating target/list - generated_sources = {} + generated_sources = OrderedDict() # Array of sources generated by valac that have to be compiled vala_generated_sources = [] if 'vala' in target.compilers: @@ -319,6 +334,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 = [] @@ -329,13 +356,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) + 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) 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 +372,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. @@ -352,14 +380,17 @@ 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) 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,17 +398,22 @@ 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)) + # 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): 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) @@ -386,7 +422,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)) @@ -904,10 +940,10 @@ int dummy; the keys being the path to the file (relative to the build directory) and the value being the object that generated or represents the file. """ - vala = {} - vapi = {} - others = {} - othersgen = {} + vala = OrderedDict() + vapi = OrderedDict() + others = OrderedDict() + othersgen = OrderedDict() # Split pre-existing sources for s in t.get_sources(): # BuildTarget sources are always mesonlib.File files which are @@ -992,15 +1028,14 @@ int dummy; # Library name args += ['--library=' + target.name] # Outputted header - hname = os.path.join(self.get_target_dir(target), target.name + '.h') + hname = os.path.join(self.get_target_dir(target), target.vala_header) args += ['-H', hname] valac_outputs.append(hname) # Outputted vapi file - base_vapi = target.name + '.vapi' - vapiname = os.path.join(self.get_target_dir(target), base_vapi) + vapiname = os.path.join(self.get_target_dir(target), target.vala_vapi) # Force valac to write the vapi file in the target build dir. # Without this, it will write it inside c_out_dir - args += ['--vapi=../' + base_vapi] + args += ['--vapi', os.path.join('..', target.vala_vapi)] valac_outputs.append(vapiname) if self.environment.coredata.get_builtin_option('werror'): args += valac.get_werror_args() @@ -1053,7 +1088,7 @@ int dummy; args += ['--out-dir', target.subdir] args += ['--emit', 'dep-info', '--emit', 'link'] orderdeps = [os.path.join(t.subdir, t.get_filename()) for t in target.link_targets] - linkdirs = {} + linkdirs = OrderedDict() for d in target.link_targets: linkdirs[d.subdir] = True for d in linkdirs.keys(): @@ -1668,14 +1703,14 @@ 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 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) + 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) @@ -1716,12 +1751,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 +1840,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 @@ -1857,7 +1893,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) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index e16f118..c3867e0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -17,7 +17,7 @@ from . import environment from . import dependencies from . import mlog import copy, os, re -from .mesonlib import File, flatten, MesonException, stringlistify +from .mesonlib import File, flatten, MesonException, stringlistify, classify_unity_sources from .environment import for_windows, for_darwin known_basic_kwargs = {'install' : True, @@ -55,6 +55,8 @@ known_lib_kwargs.update({'version' : True, # Only for shared libs 'name_prefix' : True, 'name_suffix' : True, 'vs_module_defs' : True, # Only for shared libs + 'vala_header': True, + 'vala_vapi': True, 'pic' : True, # Only for static libs }) @@ -176,9 +178,42 @@ class IncludeDirs(): return self.extra_build_dirs class ExtractedObjects(): + ''' + Holds a list of sources for which the objects must be extracted + ''' def __init__(self, target, srclist): self.target = target self.srclist = srclist + self.check_unity_compatible() + + def check_unity_compatible(self): + # Figure out if the extracted object list is compatible with a Unity + # build. When we're doing a Unified build, we go through the sources, + # and create a single source file from each subset of the sources that + # can be compiled with a specific compiler. Then we create one object + # from each unified source file. + # If the list of sources for which we want objects is the same as the + # list of sources that go into each unified build, we're good. + self.unity_compatible = False + srclist_set = set(self.srclist) + # Objects for all the sources are required, so we're compatible + if srclist_set == set(self.target.sources): + self.unity_compatible = True + return + # Check if the srclist is a subset (of the target's sources) that is + # going to form a unified source file and a single object + compsrcs = classify_unity_sources(self.target.compilers.values(), + self.target.sources) + for srcs in compsrcs.values(): + if srclist_set == set(srcs): + self.unity_compatible = True + return + msg = 'Single object files can not be extracted in Unity builds. ' \ + 'You can only extract all the object files at once.' + raise MesonException(msg) + + def get_want_all_objects(self): + return self.want_all_objects class EnvironmentVariables(): def __init__(self): @@ -313,6 +348,13 @@ class BuildTarget(): raise InvalidArguments(msg) @staticmethod + def can_compile_sources(compiler, sources): + for s in sources: + if compiler.can_compile(s): + return True + return False + + @staticmethod def can_compile_remove_sources(compiler, sources): removed = False for s in sources[:]: @@ -322,16 +364,23 @@ class BuildTarget(): return removed def process_compilers(self): - if len(self.sources) == 0: + if len(self.sources) + len(self.generated) == 0: return sources = list(self.sources) + for gensrc in self.generated: + sources += gensrc.get_outputs() + # Populate list of compilers if self.is_cross: compilers = self.environment.coredata.cross_compilers else: compilers = self.environment.coredata.compilers for lang, compiler in compilers.items(): - if self.can_compile_remove_sources(compiler, sources): + if self.can_compile_sources(compiler, sources): self.compilers[lang] = compiler + # If all our sources are Vala, our target also needs the C compiler but + # it won't get added above. + if 'vala' in self.compilers and 'c' not in self.compilers: + self.compilers['c'] = compilers['c'] def validate_sources(self): if len(self.sources) == 0: @@ -383,18 +432,15 @@ class BuildTarget(): if 'link_with' in self.kwargs: self.kwargs['link_with'] = self.unpack_holder(self.kwargs['link_with']) - def extract_objects(self, srcargs): + def extract_objects(self, srclist): obj_src = [] - for srclist in srcargs: - if not isinstance(srclist, list): - srclist = [srclist] - for src in srclist: - if not isinstance(src, str): - raise MesonException('Extraction arguments must be strings.') - src = File(False, self.subdir, src) - if src not in self.sources: - raise MesonException('Tried to extract unknown source %s.' % src) - obj_src.append(src) + for src in srclist: + if not isinstance(src, str): + raise MesonException('Object extraction arguments must be strings.') + src = File(False, self.subdir, src) + if src not in self.sources: + raise MesonException('Tried to extract unknown source %s.' % src) + obj_src.append(src) return ExtractedObjects(self, obj_src) def extract_all_objects(self): @@ -449,6 +495,9 @@ class BuildTarget(): if not isinstance(valalist, list): valalist = [valalist] self.add_compiler_args('vala', valalist) + if not isinstance(self, Executable): + self.vala_header = kwargs.get('vala_header', self.name + '.h') + self.vala_vapi = kwargs.get('vala_vapi', self.name + '.vapi') dlist = stringlistify(kwargs.get('d_args', [])) self.add_compiler_args('d', dlist) self.link_args = kwargs.get('link_args', []) @@ -470,7 +519,7 @@ class BuildTarget(): deplist = kwargs.get('dependencies', []) if not isinstance(deplist, list): deplist = [deplist] - self.add_external_deps(deplist) + self.add_deps(deplist) self.custom_install_dir = kwargs.get('install_dir', None) if self.custom_install_dir is not None: if not isinstance(self.custom_install_dir, str): @@ -600,7 +649,7 @@ class BuildTarget(): def get_include_dirs(self): return self.include_dirs - def add_external_deps(self, deps): + def add_deps(self, deps): if not isinstance(deps, list): deps = [deps] for dep in deps: @@ -620,7 +669,7 @@ class BuildTarget(): [], [], []) self.external_deps.append(extpart) # Deps of deps. - self.add_external_deps(dep.ext_deps) + self.add_deps(dep.ext_deps) elif isinstance(dep, dependencies.Dependency): self.external_deps.append(dep) self.process_sourcelist(dep.get_sources()) @@ -629,10 +678,13 @@ class BuildTarget(): # about the interpreter so we can't import it and use isinstance. # This should be reliable enough. if hasattr(dep, 'subproject'): - raise InvalidArguments('''Tried to use subproject object as a dependency. -You probably wanted to use a dependency declared in it instead. Access it -by calling get_variable() on the subproject object.''') - raise InvalidArguments('Argument is not an external dependency.') + raise InvalidArguments('Tried to use subproject object as a dependency.\n' + 'You probably wanted to use a dependency declared in it instead.\n' + 'Access it by calling get_variable() on the subproject object.') + raise InvalidArguments('Argument is of an unacceptable type {!r}.\nMust be ' + 'either an external dependency (returned by find_library() or ' + 'dependency()) or an internal dependency (returned by ' + 'declare_dependency()).'.format(type(dep).__name__)) def get_external_deps(self): return self.external_deps @@ -658,7 +710,7 @@ by calling get_variable() on the subproject object.''') return elif len(pchlist) == 1: if not environment.is_header(pchlist[0]): - raise InvalidArguments('Pch argument %s is not a header.' % pchlist[0]) + raise InvalidArguments('PCH argument %s is not a header.' % pchlist[0]) elif len(pchlist) == 2: if environment.is_header(pchlist[0]): if not environment.is_source(pchlist[1]): @@ -701,8 +753,7 @@ by calling get_variable() on the subproject object.''') class Generator(): def __init__(self, args, kwargs): if len(args) != 1: - raise InvalidArguments('Generator requires one and only one positional argument') - + raise InvalidArguments('Generator requires exactly one positional argument: the executable') exe = args[0] if hasattr(exe, 'held_object'): exe = exe.held_object @@ -1124,8 +1175,7 @@ class CustomTarget: raise InvalidArguments('Output must not contain a path segment.') self.capture = kwargs.get('capture', False) if self.capture and len(self.output) != 1: - raise InvalidArguments( - 'Capturing can only output to a single file.') + raise InvalidArguments('Capturing can only output to a single file.') if 'command' not in kwargs: raise InvalidArguments('Missing keyword argument "command".') if 'depfile' in kwargs: @@ -1146,7 +1196,7 @@ class CustomTarget: final_cmd.append(c) elif isinstance(c, dependencies.ExternalProgram): if not c.found(): - raise InvalidArguments('Tried to use not found external program in a build rule.') + raise InvalidArguments('Tried to use not found external program {!r} in a build rule.'.format(c.name)) final_cmd += c.get_command() elif isinstance(c, (BuildTarget, CustomTarget)): self.dependencies.append(c) @@ -1187,7 +1237,7 @@ class CustomTarget: while hasattr(ed, 'held_object'): ed = ed.held_object if not isinstance(ed, (CustomTarget, BuildTarget)): - raise InvalidArguments('Can only depend on toplevel targets.') + raise InvalidArguments('Can only depend on toplevel targets: custom_target or build_target (executable or a library)') self.extra_depends.append(ed) depend_files = kwargs.get('depend_files', []) if not isinstance(depend_files, list): @@ -1197,7 +1247,7 @@ class CustomTarget: self.depend_files.append(i) else: mlog.debug(i) - raise InvalidArguments('Unknown type in depend_files.') + raise InvalidArguments('Unknown type {!r} in depend_files.'.format(type(i).__name__)) def get_basename(self): return self.name @@ -1217,6 +1267,9 @@ class CustomTarget: def get_outputs(self): return self.output + def get_filename(self): + return self.output[0] + def get_sources(self): return self.sources diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 6ad2f1e..94e8a54 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -17,7 +17,7 @@ import subprocess, os.path import tempfile from .import mesonlib from . import mlog -from .mesonlib import MesonException +from .mesonlib import MesonException, version_compare from . import coredata """This file contains the data files of all compilers Meson knows @@ -342,6 +342,9 @@ class Compiler(): def get_language(self): return self.language + def get_default_suffix(self): + return self.default_suffix + def get_exelist(self): return self.exelist[:] @@ -497,9 +500,6 @@ class CCompiler(Compiler): def get_depfile_suffix(self): return 'd' - def get_default_suffix(self): - return self.default_suffix - def get_exelist(self): return self.exelist[:] @@ -707,12 +707,16 @@ int main () {{ {1}; }}''' args = self.unix_link_flags_to_native(cargs + extra_args) # Read c_args/cpp_args/etc from the cross-info file (if needed) args += self.get_cross_extra_flags(env, compile=True, link=False) + # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env + # We assume that the user has ensured these are compiler-specific + args += env.coredata.external_args[self.language] # We only want to compile; not link args += self.get_compile_only_args() with self.compile(code, args) as p: return p.returncode == 0 - def links(self, code, env, extra_args=None, dependencies=None): + def _links_wrapper(self, code, env, extra_args, dependencies): + "Shares common code between self.links and self.run" if extra_args is None: extra_args = [] elif isinstance(extra_args, str): @@ -729,27 +733,19 @@ int main () {{ {1}; }}''' args += self.get_linker_debug_crt_args() # Read c_args/c_link_args/cpp_args/cpp_link_args/etc from the cross-info file (if needed) args += self.get_cross_extra_flags(env, compile=True, link=True) - with self.compile(code, args) as p: + # Add LDFLAGS from the env. We assume that the user has ensured these + # are compiler-specific + args += env.coredata.external_link_args[self.language] + return self.compile(code, args) + + def links(self, code, env, extra_args=None, dependencies=None): + with self._links_wrapper(code, env, extra_args, dependencies) as p: return p.returncode == 0 def run(self, code, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - if dependencies is None: - dependencies = [] - elif not isinstance(dependencies, list): - dependencies = [dependencies] if self.is_cross and self.exe_wrapper is None: raise CrossNoRunException('Can not run test applications in this cross environment.') - cargs = [a for d in dependencies for a in d.get_compile_args()] - link_args = [a for d in dependencies for a in d.get_link_args()] - # Convert flags to the native type of the selected compiler - args = self.unix_link_flags_to_native(cargs + link_args + extra_args) - # Select a CRT if needed since we're linking - args += self.get_linker_debug_crt_args() - # Read c_link_args/cpp_link_args/etc from the cross-info file - args += self.get_cross_extra_flags(env, compile=True, link=True) - with self.compile(code, args) as p: + with self._links_wrapper(code, env, extra_args, dependencies) as p: if p.returncode != 0: mlog.debug('Could not compile test file %s: %d\n' % ( p.input_name, @@ -878,55 +874,65 @@ int main(int argc, char **argv) { raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename) return align - def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + @staticmethod + def _no_prototype_templ(): """ - First, this function looks for the symbol in the default libraries - provided by the compiler (stdlib + a few others usually). If that - fails, it checks if any of the headers specified in the prefix provide - an implementation of the function, and if that fails, it checks if it's - implemented as a compiler-builtin. + Try to find the function without a prototype from a header by defining + our own dummy prototype and trying to link with the C library (and + whatever else the compiler links in by default). This is very similar + to the check performed by Autoconf for AC_CHECK_FUNCS. """ - if extra_args is None: - extra_args = [] - # Define the symbol to something else in case it is defined by the - # includes or defines listed by the user `{0}` or by the compiler. - # Then, undef the symbol to get rid of it completely. - templ = ''' + # Define the symbol to something else since it is defined by the + # includes or defines listed by the user (prefix -> {0}) or by the + # compiler. Then, undef the symbol to get rid of it completely. + head = ''' #define {1} meson_disable_define_of_{1} #include <limits.h> {0} #undef {1} ''' - # Override any GCC internal prototype and declare our own definition for # the symbol. Use char because that's unlikely to be an actual return # value for a function which ensures that we override the definition. - templ += ''' + head += ''' #ifdef __cplusplus extern "C" #endif char {1} (); ''' - - # glibc defines functions that are not available on Linux as stubs that - # fail with ENOSYS (such as e.g. lchmod). In this case we want to fail - # instead of detecting the stub as a valid symbol. - # We always include limits.h above to ensure that these are defined for - # stub functions. - stubs_fail = ''' - #if defined __stub_{1} || defined __stub___{1} - fail fail fail this function is not going to work - #endif - ''' - templ += stubs_fail - - # And finally the actual function call - templ += ''' - int - main () + # The actual function call + main = ''' + int main () {{ return {1} (); }}''' + return head, main + + @staticmethod + def _have_prototype_templ(): + """ + Returns a head-er and main() call that uses the headers listed by the + user for the function prototype while checking if a function exists. + """ + # Add the 'prefix', aka defines, includes, etc that the user provides + head = '#include <limits.h>\n{0}\n' + # We don't know what the function takes or returns, so try to use it as + # a function pointer + main = '\nint main() {{ int a = (int) &{1}; }}' + return head, main + + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + """ + First, this function looks for the symbol in the default libraries + provided by the compiler (stdlib + a few others usually). If that + fails, it checks if any of the headers specified in the prefix provide + an implementation of the function, and if that fails, it checks if it's + implemented as a compiler-builtin. + """ + if extra_args is None: + extra_args = [] + + # Short-circuit if the check is already provided by the cross-info file varname = 'has function ' + funcname varname = varname.replace(' ', '_') if self.is_cross: @@ -935,16 +941,35 @@ int main(int argc, char **argv) { if isinstance(val, bool): return val raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname)) - if self.links(templ.format(prefix, funcname), env, extra_args, dependencies): - return True + + # glibc defines functions that are not available on Linux as stubs that + # fail with ENOSYS (such as e.g. lchmod). In this case we want to fail + # instead of detecting the stub as a valid symbol. + # We already included limits.h earlier to ensure that these are defined + # for stub functions. + stubs_fail = ''' + #if defined __stub_{1} || defined __stub___{1} + fail fail fail this function is not going to work + #endif + ''' + + # If we have any includes in the prefix supplied by the user, assume + # that the user wants us to use the symbol prototype defined in those + # includes. If not, then try to do the Autoconf-style check with + # a dummy prototype definition of our own. + # This is needed when the linker determines symbol availability from an + # SDK based on the prototype in the header provided by the SDK. + # Ignoring this prototype would result in the symbol always being + # marked as available. + if '#include' in prefix: + head, main = self._have_prototype_templ() + else: + head, main = self._no_prototype_templ() + templ = head + stubs_fail + main + # Add -O0 to ensure that the symbol isn't optimized away by the compiler args = extra_args + self.get_no_optimization_args() - # Sometimes the implementation is provided by the header, or the header - # redefines the symbol to be something else. In that case, we want to - # still detect the function. We still want to fail if __stub_foo or - # _stub_foo are defined, of course. - header_templ = '#include <limits.h>\n{0}\n' + stubs_fail + '\nint main() {{ {1}; }}' - if self.links(header_templ.format(prefix, funcname), env, args, dependencies): + if self.links(templ.format(prefix, funcname), env, extra_args, dependencies): return True # Some functions like alloca() are defined as compiler built-ins which # are inlined by the compiler, so test for that instead. Built-ins are @@ -1106,9 +1131,6 @@ class MonoCompiler(Compiler): def get_dependency_gen_args(self, outtarget, outfile): return [] - def get_default_suffix(self): - return self.default_suffix - def get_linker_exelist(self): return self.exelist[:] @@ -1193,9 +1215,6 @@ class JavaCompiler(Compiler): def get_dependency_gen_args(self, outtarget, outfile): return [] - def get_default_suffix(self): - return self.default_suffix - def get_linker_exelist(self): return self.exelist[:] @@ -1781,7 +1800,7 @@ class VisualStudioCCompiler(CCompiler): # Translate GNU-style -lfoo library name to the import library elif i.startswith('-l'): name = i[2:] - if name in ('m', 'c'): + if name in ('m', 'c', 'pthread'): # With MSVC, these are provided by the C runtime which is # linked in by default continue @@ -1794,7 +1813,8 @@ class VisualStudioCCompiler(CCompiler): result = [] for i in args: # -mms-bitfields is specific to MinGW-GCC - if i == '-mms-bitfields': + # -pthread is only valid for GCC + if i in ('-mms-bitfields', '-pthread'): continue result.append(i) return result @@ -1842,7 +1862,6 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler): def __init__(self, exelist, version, is_cross, exe_wrap): self.language = 'cpp' VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - self.default_suffix = 'cpp' self.base_options = ['b_pch'] # FIXME add lto, pgo and the like def get_options(self): @@ -1956,7 +1975,8 @@ class GnuCCompiler(GnuCompiler, CCompiler): def get_options(self): opts = {'c_std' : coredata.UserComboOption('c_std', 'C language standard to use', - ['none', 'c89', 'c99', 'c11', 'gnu89', 'gnu99', 'gnu11'], + ['none', 'c89', 'c99', 'c11', + 'gnu89', 'gnu99', 'gnu11'], 'none')} if self.gcc_type == GCC_MINGW: opts.update({ @@ -2078,6 +2098,20 @@ class ClangCompiler(): raise MesonException('Unreachable code when converting clang type to gcc type.') return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion) + def has_argument(self, arg, env): + return super().has_argument(['-Werror=unknown-warning-option', arg], env) + + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + # Starting with XCode 8, we need to pass this to force linker + # visibility to obey OS X and iOS minimum version targets with + # -mmacosx-version-min, -miphoneos-version-min, etc. + # https://github.com/Homebrew/homebrew-core/issues/3727 + if self.clang_type == CLANG_OSX and version_compare(self.version, '>=8.0'): + extra_args.append('-Wl,-no_weak_imports') + return super().has_function(funcname, prefix, env, extra_args, dependencies) + class ClangCCompiler(ClangCompiler, CCompiler): def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None): CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) @@ -2090,7 +2124,8 @@ class ClangCCompiler(ClangCompiler, CCompiler): def get_options(self): return {'c_std' : coredata.UserComboOption('c_std', 'C language standard to use', - ['none', 'c89', 'c99', 'c11'], + ['none', 'c89', 'c99', 'c11', + 'gnu89', 'gnu99', 'gnu11',], 'none')} def get_option_compile_args(self, options): @@ -2103,11 +2138,8 @@ class ClangCCompiler(ClangCompiler, CCompiler): def get_option_link_args(self, options): return [] - def has_argument(self, arg, env): - return super().has_argument(['-Werror=unknown-warning-option', arg], env) - -class ClangCPPCompiler(ClangCompiler, CPPCompiler): +class ClangCPPCompiler(ClangCompiler, CPPCompiler): def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) ClangCompiler.__init__(self, cltype) @@ -2117,8 +2149,9 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): def get_options(self): return {'cpp_std' : coredata.UserComboOption('cpp_std', 'C++ language standard to use', - ['none', 'c++03', 'c++11', 'c++14', 'c++1z'], - 'none')} + ['none', 'c++03', 'c++11', 'c++14', 'c++1z', + 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++1z'], + 'none')} def get_option_compile_args(self, options): args = [] @@ -2130,28 +2163,17 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): def get_option_link_args(self, options): return [] - def has_argument(self, arg, env): - return super().has_argument(['-Werror=unknown-warning-option', arg], env) - -class ClangObjCCompiler(GnuObjCCompiler): +class ClangObjCCompiler(ClangCompiler, GnuObjCCompiler): def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): - super().__init__(exelist, version, is_cross, exe_wrapper) - self.id = 'clang' + GnuObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + ClangCompiler.__init__(self, cltype) self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] - self.clang_type = cltype - if self.clang_type != CLANG_OSX: - self.base_options.append('b_lundef') - self.base_options.append('b_asneeded') -class ClangObjCPPCompiler(GnuObjCPPCompiler): +class ClangObjCPPCompiler(ClangCompiler, GnuObjCPPCompiler): def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): - super().__init__(exelist, version, is_cross, exe_wrapper) - self.id = 'clang' - self.clang_type = cltype + GnuObjCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + ClangCompiler.__init__(self, cltype) self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] - if self.clang_type != CLANG_OSX: - self.base_options.append('b_lundef') - self.base_options.append('b_asneeded') class FortranCompiler(Compiler): def __init__(self, exelist, version, is_cross, exe_wrapper=None): diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index df6dcff..74738ae 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -37,6 +37,10 @@ class Dependency(): self.is_found = False self.type_name = type_name + def __repr__(self): + s = '<{0} {1}: {2}>' + return s.format(self.__class__.__name__, self.name, self.is_found) + def get_compile_args(self): return [] diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 86c23ae..b810e20 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -833,9 +833,9 @@ class Environment(): return self.coredata.get_builtin_option('datadir') -def get_args_from_envvars(lang, compiler_is_linker): +def get_args_from_envvars(compiler): """ - @lang: Language to fetch environment flags for + @compiler: Compiler to fetch environment flags for Returns a tuple of (compile_flags, link_flags) for the specified language from the inherited environment @@ -844,14 +844,18 @@ def get_args_from_envvars(lang, compiler_is_linker): if val: mlog.log('Appending {} from environment: {!r}'.format(var, val)) + lang = compiler.get_language() + compiler_is_linker = False + if hasattr(compiler, 'get_linker_exelist'): + compiler_is_linker = (compiler.get_exelist() == compiler.get_linker_exelist()) + if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'): return ([], []) # Compile flags cflags_mapping = {'c': 'CFLAGS', 'cpp': 'CXXFLAGS', 'objc': 'OBJCFLAGS', 'objcpp': 'OBJCXXFLAGS', - 'fortran': 'FFLAGS', - 'd': 'DFLAGS'} + 'fortran': 'FFLAGS', 'd': 'DFLAGS'} compile_flags = os.environ.get(cflags_mapping[lang], '') log_var(cflags_mapping[lang], compile_flags) compile_flags = compile_flags.split() diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 44551fc..5203528 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1565,7 +1565,7 @@ class Interpreter(): r = wrap.Resolver(os.path.join(self.build.environment.get_source_dir(), self.subproject_dir)) resolved = r.resolve(dirname) if resolved is None: - msg = 'Subproject directory {!r} does not exist and can not be downloaded.' + msg = 'Subproject directory {!r} does not exist and cannot be downloaded.' raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname))) subdir = os.path.join(self.subproject_dir, resolved) os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) @@ -1798,11 +1798,8 @@ class Interpreter(): else: raise mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='') - compiler_is_linker = False - if hasattr(comp, 'get_linker_exelist'): - compiler_is_linker = (comp.get_exelist() == comp.get_linker_exelist()) if not comp.get_language() in self.coredata.external_args: - (ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp.get_language(), compiler_is_linker) + (ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp) self.coredata.external_args[comp.get_language()] = ext_compile_args self.coredata.external_link_args[comp.get_language()] = ext_link_args self.build.add_compiler(comp) @@ -2272,9 +2269,18 @@ requirements use the version keyword argument instead.''') @stringArgs def func_add_global_arguments(self, node, args, kwargs): if self.subproject != '': - raise InvalidCode('Global arguments can not be set in subprojects because there is no way to make that reliable.') + msg = 'Global arguments can not be set in subprojects because ' \ + 'there is no way to make that reliable.\nPlease only call ' \ + 'this if is_subproject() returns false. Alternatively, ' \ + 'define a variable that\ncontains your language-specific ' \ + 'arguments and add it to the appropriate *_args kwarg ' \ + 'in each target.' + raise InvalidCode(msg) if self.global_args_frozen: - raise InvalidCode('Tried to set global arguments after a build target has been declared.\nThis is not permitted. Please declare all global arguments before your targets.') + msg = 'Tried to set global arguments after a build target has ' \ + 'been declared.\nThis is not permitted. Please declare all ' \ + 'global arguments before your targets.' + raise InvalidCode(msg) if not 'language' in kwargs: raise InvalidCode('Missing language definition in add_global_arguments') lang = kwargs['language'].lower() @@ -2286,11 +2292,20 @@ requirements use the version keyword argument instead.''') @stringArgs def func_add_global_link_arguments(self, node, args, kwargs): if self.subproject != '': - raise InvalidCode('Global arguments can not be set in subprojects because there is no way to make that reliable.') + msg = 'Global link arguments can not be set in subprojects because ' \ + 'there is no way to make that reliable.\nPlease only call ' \ + 'this if is_subproject() returns false. Alternatively, ' \ + 'define a variable that\ncontains your language-specific ' \ + 'arguments and add it to the appropriate *_args kwarg ' \ + 'in each target.' + raise InvalidCode(msg) if self.global_args_frozen: - raise InvalidCode('Tried to set global arguments after a build target has been declared.\nThis is not permitted. Please declare all global arguments before your targets.') + msg = 'Tried to set global link arguments after a build target has ' \ + 'been declared.\nThis is not permitted. Please declare all ' \ + 'global arguments before your targets.' + raise InvalidCode(msg) if not 'language' in kwargs: - raise InvalidCode('Missing language definition in add_global_arguments') + raise InvalidCode('Missing language definition in add_global_link_arguments') lang = kwargs['language'].lower() if lang in self.build.global_link_args: self.build.global_link_args[lang] += args @@ -2411,7 +2426,7 @@ requirements use the version keyword argument instead.''') for l in self.get_used_languages(target): if self.environment.cross_info.has_stdlib(l) and \ self.subproject != self.environment.cross_info.get_stdlib(l)[0]: - target.add_external_deps(self.build.cross_stdlibs[l]) + target.add_deps(self.build.cross_stdlibs[l]) def check_sources_exist(self, subdir, sources): for s in sources: @@ -2419,7 +2434,7 @@ requirements use the version keyword argument instead.''') continue # This means a generated source and they always exist. fname = os.path.join(subdir, s) if not os.path.isfile(fname): - raise InterpreterException('Tried to add non-existing source %s.' % s) + raise InterpreterException('Tried to add non-existing source file %s.' % s) def function_call(self, node): func_name = node.func_name @@ -2475,7 +2490,7 @@ requirements use the version keyword argument instead.''') else: return posargs[1] else: - raise InterpreterException('bool.to_string() must have either no arguments or exactly two string arguments.') + raise InterpreterException('bool.to_string() must have either no arguments or exactly two string arguments that signify what values to return for true and false.') elif method_name == 'to_int': if obj == True: return 1 @@ -2515,7 +2530,7 @@ requirements use the version keyword argument instead.''') return re.sub(r'[^a-zA-Z0-9]', '_', obj) elif method_name == 'split': if len(posargs) > 1: - raise InterpreterException('Split() must have at most one argument.') + raise InterpreterException('Split() must have at most one argument.') elif len(posargs) == 1: s = posargs[0] if not isinstance(s, str): @@ -2536,7 +2551,7 @@ requirements use the version keyword argument instead.''') try: return int(obj) except Exception: - raise InterpreterException('String can not be converted to int: ' + obj) + raise InterpreterException('String {!r} cannot be converted to int'.format(obj)) elif method_name == 'join': if len(posargs) != 1: raise InterpreterException('Join() takes exactly one argument.') @@ -2577,8 +2592,6 @@ requirements use the version keyword argument instead.''') else: obj = self.evaluate_statement(invokable) method_name = node.name - if method_name == 'extract_objects' and self.environment.coredata.get_builtin_option('unity'): - raise InterpreterException('Single object files can not be extracted in Unity builds.') args = node.args if isinstance(obj, mparser.StringNode): obj = obj.get_value() @@ -2643,8 +2656,7 @@ requirements use the version keyword argument instead.''') for i in node.ifs: result = self.evaluate_statement(i.condition) if not(isinstance(result, bool)): - print(result) - raise InvalidCode('If clause does not evaluate to true or false.') + raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result)) if result: self.evaluate_codeblock(i.block) return diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index abb5641..943a23e 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -70,6 +70,22 @@ class File: def __hash__(self): return hash((self.fname, self.subdir, self.is_built)) +def get_compiler_for_source(compilers, src): + for comp in compilers: + if comp.can_compile(src): + return comp + raise RuntimeError('No specified compiler can handle file {!s}'.format(src)) + +def classify_unity_sources(compilers, sources): + compsrclist = {} + for src in sources: + comp = get_compiler_for_source(compilers, src) + if comp not in compsrclist: + compsrclist[comp] = [src] + else: + compsrclist[comp].append(src) + return compsrclist + def flatten(item): if not isinstance(item, list): return item @@ -165,16 +181,18 @@ def version_compare(vstr1, vstr2): return cmpop(varr1, varr2) def default_libdir(): - try: - pc = subprocess.Popen(['dpkg-architecture', '-qDEB_HOST_MULTIARCH'], - stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) - (stdo, _) = pc.communicate() - if pc.returncode == 0: - archpath = stdo.decode().strip() - return 'lib/' + archpath - except Exception: - pass - if os.path.isdir('/usr/lib64'): + if is_debianlike(): + try: + pc = subprocess.Popen(['dpkg-architecture', '-qDEB_HOST_MULTIARCH'], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL) + (stdo, _) = pc.communicate() + if pc.returncode == 0: + archpath = stdo.decode().strip() + return 'lib/' + archpath + except Exception: + pass + if os.path.isdir('/usr/lib64') and not os.path.islink('/usr/lib64'): return 'lib64' return 'lib' @@ -255,10 +273,10 @@ def do_mesondefine(line, confdata): def do_conf_file(src, dst, confdata): try: - with open(src) as f: + with open(src, encoding='utf-8') as f: data = f.readlines() - except Exception: - raise MesonException('Could not read input file %s.' % src) + except Exception as e: + raise MesonException('Could not read input file %s: %s' % (src, str(e))) # Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define # Also allow escaping '@' with '\@' regex = re.compile(r'[^\\]?@([-a-zA-Z0-9_]+)@') @@ -302,14 +320,17 @@ def dump_conf_header(ofilename, cdata): def replace_if_different(dst, dst_tmp): # If contents are identical, don't touch the file to prevent # unnecessary rebuilds. + different = True try: with open(dst, 'r') as f1, open(dst_tmp, 'r') as f2: if f1.read() == f2.read(): - os.unlink(dst_tmp) - return + different = False except FileNotFoundError: pass - os.replace(dst_tmp, dst) + if different: + os.replace(dst_tmp, dst) + else: + os.unlink(dst_tmp) def stringlistify(item): if isinstance(item, str): diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 7f7ab43..943c087 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -227,6 +227,9 @@ def run_script_command(args): elif cmdname == 'gettext': import mesonbuild.scripts.gettext as abc cmdfunc = abc.run + elif cmdname == 'yelphelper': + import mesonbuild.scripts.yelphelper as abc + cmdfunc = abc.run else: raise MesonException('Unknown internal command {}.'.format(cmdname)) return cmdfunc(cmdargs) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index a18912e..492bf3f 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -20,7 +20,7 @@ Currently only works for the Ninja backend. Others use generated project files and don't need this info.""" import json, pickle -from . import coredata, build, mesonlib +from . import coredata, build import argparse import sys, os @@ -41,7 +41,20 @@ parser.add_argument('--dependencies', action='store_true', dest='dependencies', help='list external dependencies.') parser.add_argument('args', nargs='+') -def list_targets(coredata, builddata): +def determine_installed_path(target, installdata): + install_target = None + for i in installdata.targets: + if os.path.split(i[0])[1] == target.get_filename(): # FIXME, might clash due to subprojects. + install_target = i + break + if install_target is None: + raise RuntimeError('Something weird happened. File a bug.') + fname = i[0] + outdir = i[1] + outname = os.path.join(installdata.prefix, outdir, os.path.split(fname)[-1]) + return outname + +def list_targets(coredata, builddata, installdata): tlist = [] for (idname, target) in builddata.get_targets().items(): t = {} @@ -68,6 +81,7 @@ def list_targets(coredata, builddata): t['type'] = typename if target.should_install(): t['installed'] = True + t['install_filename'] = determine_installed_path(target, installdata) else: t['installed'] = False tlist.append(t) @@ -173,6 +187,7 @@ def run(args): bdir = '' corefile = os.path.join(bdir, 'meson-private/coredata.dat') buildfile = os.path.join(bdir, 'meson-private/build.dat') + installfile = os.path.join(bdir, 'meson-private/install.dat') testfile = os.path.join(bdir, 'meson-private/meson_test_setup.dat') benchmarkfile = os.path.join(bdir, 'meson-private/meson_benchmark_setup.dat') with open(corefile, 'rb') as f: @@ -180,11 +195,13 @@ def run(args): with open(buildfile, 'rb') as f: builddata = pickle.load(f) with open(testfile, 'rb') as f: - testdata = pickle.load(f) + testdata = pickle.load(f) with open(benchmarkfile, 'rb') as f: - benchmarkdata = pickle.load(f) + benchmarkdata = pickle.load(f) + with open(installfile, 'rb') as f: + installdata = pickle.load(f) if options.list_targets: - list_targets(coredata, builddata) + list_targets(coredata, builddata, installdata) elif options.target_files is not None: list_target_files(options.target_files, coredata, builddata) elif options.buildsystem_files: diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 6b44af1..ebfc335 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -17,6 +17,7 @@ functionality such as gobject-introspection and gresources.''' from .. import build import os +import sys import subprocess from ..mesonlib import MesonException from .. import dependencies @@ -343,9 +344,18 @@ class GnomeModule: else: raise MesonException( 'Gir includes must be str, GirTarget, or list of them') + + cflags = [] if state.global_args.get('c'): + cflags += state.global_args['c'] + for compiler in state.compilers: + if compiler.get_language() == 'c': + sanitize = compiler.get_options().get('b_sanitize') + if sanitize: + cflags += compilers.sanitizer_compile_args(sanitize) + if cflags: scan_command += ['--cflags-begin'] - scan_command += state.global_args['c'] + scan_command += cflags scan_command += ['--cflags-end'] if kwargs.get('symbol_prefix'): sym_prefix = kwargs.pop('symbol_prefix') @@ -371,6 +381,9 @@ class GnomeModule: deps = [deps] deps = (girtarget.get_all_link_deps() + girtarget.get_external_deps() + deps) + # ldflags will be misinterpreted by gir scanner (showing + # spurious dependencies) but building GStreamer fails if they + # are not used here. cflags, ldflags, gi_includes = self.get_dependencies_flags(deps, state, depends) scan_command += list(cflags) + list(ldflags) for i in gi_includes: @@ -452,6 +465,65 @@ class GnomeModule: target_g = build.CustomTarget(targetname, state.subdir, kwargs) return target_g + def yelp(self, state, args, kwargs): + if len(args) < 1: + raise MesonException('Yelp requires a project id') + + project_id = args[0] + sources = mesonlib.stringlistify(kwargs.pop('sources', [])) + if not sources: + if len(args) > 1: + sources = mesonlib.stringlistify(args[1:]) + if not sources: + raise MesonException('Yelp requires a list of sources') + source_str = '@@'.join(sources) + + langs = mesonlib.stringlistify(kwargs.pop('languages', [])) + media = mesonlib.stringlistify(kwargs.pop('media', [])) + symlinks = kwargs.pop('symlink_media', False) + + if not isinstance(symlinks, bool): + raise MesonException('symlink_media must be a boolean') + + if kwargs: + raise MesonException('Unknown arguments passed: {}'.format(', '.join(kwargs.keys()))) + + install_cmd = [ + sys.executable, + state.environment.get_build_command(), + '--internal', + 'yelphelper', + 'install', + '--subdir=' + state.subdir, + '--id=' + project_id, + '--installdir=' + os.path.join(state.environment.get_datadir(), 'help'), + '--sources=' + source_str, + ] + if symlinks: + install_cmd.append('--symlinks=true') + if media: + install_cmd.append('--media=' + '@@'.join(media)) + if langs: + install_cmd.append('--langs=' + '@@'.join(langs)) + inscript = build.InstallScript(install_cmd) + + potargs = [state.environment.get_build_command(), '--internal', 'yelphelper', 'pot', + '--subdir=' + state.subdir, + '--id=' + project_id, + '--sources=' + source_str] + pottarget = build.RunTarget('help-' + project_id + '-pot', sys.executable, + potargs, [], state.subdir) + + poargs = [state.environment.get_build_command(), '--internal', 'yelphelper', 'update-po', + '--subdir=' + state.subdir, + '--id=' + project_id, + '--sources=' + source_str, + '--langs=' + '@@'.join(langs)] + potarget = build.RunTarget('help-' + project_id + '-update-po', sys.executable, + poargs, [], state.subdir) + + return [inscript, pottarget, potarget] + def gtkdoc(self, state, args, kwargs): if len(args) != 1: raise MesonException('Gtkdoc must have one positional argument.') diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 00787f8..28e04cb 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -33,6 +33,12 @@ class I18nModule: pottarget = build.RunTarget(packagename + '-pot', sys.executable, potargs, [], state.subdir) gmoargs = [state.environment.get_build_command(), '--internal', 'gettext', 'gen_gmo'] + languages gmotarget = build.RunTarget(packagename + '-gmo', sys.executable, gmoargs, [], state.subdir) + updatepoargs = [state.environment.get_build_command(), '--internal', 'gettext', 'update_po', packagename] + updatepoargs.append('@@'.join(languages)) + if datadirs: + updatepoargs.append('--datadirs=' + ':'.join(datadirs)) + updatepoargs += extra_args + updatepotarget = build.RunTarget(packagename + '-update-po', sys.executable, updatepoargs, [], state.subdir) installcmd = [sys.executable, state.environment.get_build_command(), '--internal', @@ -43,7 +49,7 @@ class I18nModule: state.environment.coredata.get_builtin_option('localedir'), ] + languages iscript = build.InstallScript(installcmd) - return [pottarget, gmotarget, iscript] + return [pottarget, gmotarget, iscript, updatepotarget] def initialize(): return I18nModule() diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 0cfd309..7556375 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -45,10 +45,10 @@ class PkgConfigModule: fname = os.path.join(outdir, pcfile) with open(fname, 'w') as ofile: ofile.write('prefix=%s\n' % coredata.get_builtin_option('prefix')) - ofile.write('libdir=${prefix}/%s\n' % - coredata.get_builtin_option('libdir')) - ofile.write('includedir=${prefix}/%s\n\n' % - coredata.get_builtin_option('includedir')) + # '${prefix}' is ignored if the second path is absolute (see + # 'os.path.join' for details) + ofile.write('libdir=%s\n' % os.path.join('${prefix}', coredata.get_builtin_option('libdir'))) + ofile.write('includedir=%s\n' % os.path.join('${prefix}', coredata.get_builtin_option('includedir'))) ofile.write('Name: %s\n' % name) if len(description) > 0: ofile.write('Description: %s\n' % description) diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index 7124c6f..34fb2f7 100755 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -23,6 +23,7 @@ DT_RPATH = 15 DT_RUNPATH = 29 DT_STRTAB = 5 DT_SONAME = 14 +DT_MIPS_RLD_MAP_REL = 1879048245 class DataSizes(): def __init__(self, ptrsize, is_le): @@ -307,6 +308,11 @@ class Elf(DataSizes): rpentry.d_tag = 0 self.dynamic = self.dynamic[:i] + self.dynamic[i+1:] + [rpentry] break; + # DT_MIPS_RLD_MAP_REL is relative to the offset of the tag. Adjust it consequently. + for entry in self.dynamic[i:]: + if entry.d_tag == DT_MIPS_RLD_MAP_REL: + entry.val += 2 * (self.ptrsize // 8) + break self.bf.seek(sec.sh_offset) for entry in self.dynamic: entry.write(self.bf) diff --git a/mesonbuild/scripts/gettext.py b/mesonbuild/scripts/gettext.py index 95fd45a..44dfd50 100644 --- a/mesonbuild/scripts/gettext.py +++ b/mesonbuild/scripts/gettext.py @@ -38,6 +38,13 @@ def gen_gmo(src_sub, bld_sub, langs): '-o', os.path.join(bld_sub, l + '.gmo')]) return 0 +def update_po(src_sub, pkgname, langs): + potfile = os.path.join(src_sub, pkgname + '.pot') + for l in langs: + pofile = os.path.join(src_sub, l + '.po') + subprocess.check_call(['msgmerge', '-q', '-o', pofile, pofile, potfile]) + return 0 + def do_install(src_sub, bld_sub, dest, pkgname, langs): for l in langs: srcfile = os.path.join(bld_sub, l + '.gmo') @@ -62,6 +69,15 @@ def run(args): src_sub = os.path.join(os.environ['MESON_SOURCE_ROOT'], os.environ['MESON_SUBDIR']) bld_sub = os.path.join(os.environ['MESON_BUILD_ROOT'], os.environ['MESON_SUBDIR']) return gen_gmo(src_sub, bld_sub, args[1:]) + elif subcmd == 'update_po': + pkgname = args[1] + langs = args[2].split('@@') + datadirs = args[3][11:] if args[3].startswith('--datadirs=') else None + extra_args = args[4:] if datadirs is not None else args[3:] + src_sub = os.path.join(os.environ['MESON_SOURCE_ROOT'], os.environ['MESON_SUBDIR']) + if run_potgen(src_sub, pkgname, datadirs, extra_args) != 0: + return 1 + return update_po(src_sub, pkgname, langs) elif subcmd == 'install': subdir = args[1] pkgname = args[2] diff --git a/mesonbuild/scripts/yelphelper.py b/mesonbuild/scripts/yelphelper.py new file mode 100644 index 0000000..00d713a --- /dev/null +++ b/mesonbuild/scripts/yelphelper.py @@ -0,0 +1,118 @@ +# Copyright 2016 Patrick Griffis <tingping@tingping.se> + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +import sys, os +import subprocess +import shutil +import argparse +from mesonbuild import mlog +from mesonbuild.mesonlib import MesonException +from mesonbuild.scripts import destdir_join + +parser = argparse.ArgumentParser() +parser.add_argument('command') +parser.add_argument('--id', dest='project_id') +parser.add_argument('--subdir', dest='subdir') +parser.add_argument('--installdir', dest='install_dir') +parser.add_argument('--sources', dest='sources') +parser.add_argument('--media', dest='media', default='') +parser.add_argument('--langs', dest='langs', default='') +parser.add_argument('--symlinks', type=bool, dest='symlinks', default=False) + +def build_pot(srcdir, project_id, sources): + # Must be relative paths + sources = [os.path.join('C', source) for source in sources] + outfile = os.path.join(srcdir, project_id + '.pot') + subprocess.call(['itstool', '-o', outfile, *sources]) + +def update_po(srcdir, project_id, langs): + potfile = os.path.join(srcdir, project_id + '.pot') + for lang in langs: + pofile = os.path.join(srcdir, lang, lang + '.po') + subprocess.call(['msgmerge', '-q', '-o', pofile, pofile, potfile]) + +def build_translations(srcdir, blddir, langs): + for lang in langs: + outdir = os.path.join(blddir, lang) + os.makedirs(outdir, exist_ok=True) + subprocess.call([ + 'msgfmt', os.path.join(srcdir, lang, lang + '.po'), + '-o', os.path.join(outdir, lang + '.gmo') + ]) + +def merge_translations(blddir, sources, langs): + for lang in langs: + subprocess.call([ + 'itstool', '-m', os.path.join(blddir, lang, lang + '.gmo'), + '-o', os.path.join(blddir, lang), + *sources, + ]) + +def install_help(srcdir, blddir, sources, media, langs, install_dir, destdir, project_id, symlinks): + c_install_dir = os.path.join(install_dir, 'C', project_id) + for lang in langs + ['C']: + indir = destdir_join(destdir, os.path.join(install_dir, lang, project_id)) + os.makedirs(indir, exist_ok=True) + for source in sources: + infile = os.path.join(srcdir if lang == 'C' else blddir, lang, source) + outfile = os.path.join(indir, source) + mlog.log('Installing %s to %s.' %(infile, outfile)) + shutil.copyfile(infile, outfile) + shutil.copystat(infile, outfile) + for m in media: + infile = os.path.join(srcdir, lang, m) + outfile = os.path.join(indir, m) + if not os.path.exists(infile): + if lang == 'C': + mlog.log(mlog.bold('Warning:'), 'Media file "%s" did not exist in C directory' %m) + elif symlinks: + srcfile = os.path.join(c_install_dir, m) + mlog.log('Symlinking %s to %s.' %(outfile, srcfile)) + if '/' in m or '\\' in m: + os.makedirs(os.path.dirname(outfile), exist_ok=True) + os.symlink(srcfile, outfile) + continue + symfile = os.path.join(install_dir, m) + mlog.log('Installing %s to %s.' %(infile, outfile)) + if '/' in m or '\\' in m: + os.makedirs(os.path.dirname(outfile), exist_ok=True) + shutil.copyfile(infile, outfile) + shutil.copystat(infile, outfile) + +def run(args): + options = parser.parse_args(args) + langs = options.langs.split('@@') if options.langs else [] + media = options.media.split('@@') if options.media else [] + sources = options.sources.split('@@') + destdir = os.environ.get('DESTDIR', '') + src_subdir = os.path.join(os.environ['MESON_SOURCE_ROOT'], options.subdir) + build_subdir = os.path.join(os.environ['MESON_BUILD_ROOT'], options.subdir) + abs_sources = [os.path.join(src_subdir, 'C', source) for source in sources] + + if options.command == 'pot': + build_pot(src_subdir, options.project_id, sources) + elif options.command == 'update-po': + build_pot(src_subdir, options.project_id, sources) + update_po(src_subdir, options.project_id, langs) + elif options.command == 'build': + if langs: + build_translations(src_subdir, build_subdir, langs) + elif options.command == 'install': + install_dir = os.path.join(os.environ['MESON_INSTALL_PREFIX'], options.install_dir) + if langs: + build_translations(src_subdir, build_subdir, langs) + merge_translations(build_subdir, abs_sources, langs) + install_help(src_subdir, build_subdir, sources, media, langs, install_dir, + destdir, options.project_id, options.symlinks) + diff --git a/run_tests.py b/run_tests.py index ec8970a..752354e 100755 --- a/run_tests.py +++ b/run_tests.py @@ -21,15 +21,7 @@ from mesonbuild import mesonlib if __name__ == '__main__': returncode = 0 if mesonlib.is_linux(): - myenv = os.environ.copy() - myenv['CC'] = 'gcc' - myenv['CXX'] = 'g++' - print('Running unittests with GCC.\n') - returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'], env=myenv) - if shutil.which('clang'): - myenv['CC'] = 'clang' - myenv['CXX'] = 'clang++' - print('\nRunning unittests with clang.\n') - returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'], env=myenv) + print('Running unittests.\n') + returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v']) returncode += subprocess.call([sys.executable, 'run_project_tests.py'] + sys.argv[1:]) sys.exit(returncode) diff --git a/run_unittests.py b/run_unittests.py index b9c1397..0e3b7d5 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -43,6 +43,7 @@ class LinuxlikeTests(unittest.TestCase): self.builddir = tempfile.mkdtemp() self.meson_command = [sys.executable, os.path.join(src_root, 'meson.py')] self.mconf_command = [sys.executable, os.path.join(src_root, 'mesonconf.py')] + self.mintro_command = [sys.executable, os.path.join(src_root, 'mesonintrospect.py')] self.ninja_command = [detect_ninja(), '-C', self.builddir] self.common_test_dir = os.path.join(src_root, 'test cases/common') self.vala_test_dir = os.path.join(src_root, 'test cases/vala') @@ -67,6 +68,10 @@ class LinuxlikeTests(unittest.TestCase): with open(os.path.join(self.builddir, 'compile_commands.json')) as ifile: return json.load(ifile) + def introspect(self, arg): + out = subprocess.check_output(self.mintro_command + [arg, self.builddir]) + return json.loads(out.decode('utf-8')) + def test_basic_soname(self): testdir = os.path.join(self.common_test_dir, '4 shared') self.init(testdir) @@ -136,5 +141,25 @@ class LinuxlikeTests(unittest.TestCase): self.assertTrue('-Werror' in vala_command) self.assertTrue('-Werror' in c_command) + def test_static_compile_order(self): + testdir = os.path.join(self.common_test_dir, '5 linkstatic') + self.init(testdir) + compdb = self.get_compdb() + # Rules will get written out in this order + self.assertTrue(compdb[0]['file'].endswith("libfile.c")) + self.assertTrue(compdb[1]['file'].endswith("libfile2.c")) + self.assertTrue(compdb[2]['file'].endswith("libfile3.c")) + self.assertTrue(compdb[3]['file'].endswith("libfile4.c")) + # FIXME: We don't have access to the linker command + + def test_install_introspection(self): + testdir = os.path.join(self.common_test_dir, '8 install') + self.init(testdir) + intro = self.introspect('--targets') + if intro[0]['type'] == 'executable': + intro = intro[::-1] + self.assertEqual(intro[0]['install_filename'], '/usr/local/libtest/libstat.a') + self.assertEqual(intro[1]['install_filename'], '/usr/local/bin/prog') + if __name__ == '__main__': unittest.main() diff --git a/test cases/common/110 extract same name/meson.build b/test cases/common/110 extract same name/meson.build index 9384c47..0bfbb4d 100644 --- a/test cases/common/110 extract same name/meson.build +++ b/test cases/common/110 extract same name/meson.build @@ -1,6 +1,7 @@ project('object extraction', 'c') lib = shared_library('somelib', ['lib.c', 'src/lib.c']) -obj = lib.extract_objects(['lib.c', 'src/lib.c']) +# Also tests that the object list is flattened properly +obj = lib.extract_objects(['lib.c', ['src/lib.c']]) exe = executable('main', 'main.c', objects: obj) test('extraction', exe) diff --git a/test cases/common/5 linkstatic/libfile2.c b/test cases/common/5 linkstatic/libfile2.c new file mode 100644 index 0000000..89780f5 --- /dev/null +++ b/test cases/common/5 linkstatic/libfile2.c @@ -0,0 +1,3 @@ +int func2() { + return 2; +} diff --git a/test cases/common/5 linkstatic/libfile3.c b/test cases/common/5 linkstatic/libfile3.c new file mode 100644 index 0000000..5e0d08b --- /dev/null +++ b/test cases/common/5 linkstatic/libfile3.c @@ -0,0 +1,3 @@ +int func3() { + return 3; +} diff --git a/test cases/common/5 linkstatic/libfile4.c b/test cases/common/5 linkstatic/libfile4.c new file mode 100644 index 0000000..3645c31 --- /dev/null +++ b/test cases/common/5 linkstatic/libfile4.c @@ -0,0 +1,3 @@ +int func4() { + return 4; +} diff --git a/test cases/common/5 linkstatic/meson.build b/test cases/common/5 linkstatic/meson.build index c1cb8b6..1f02a5c 100644 --- a/test cases/common/5 linkstatic/meson.build +++ b/test cases/common/5 linkstatic/meson.build @@ -1,6 +1,6 @@ project('static library linking test', 'c') -lib = build_target('mylib', 'libfile.c', target_type : 'static_library') +lib = build_target('mylib', 'libfile.c', 'libfile2.c', 'libfile3.c', 'libfile4.c', target_type : 'static_library') exe = executable('prog', 'main.c', link_with : lib) test('runtest', exe) diff --git a/test cases/common/84 extract from nested subdir/meson.build b/test cases/common/84 extract from nested subdir/meson.build index 6db4290..3a42806 100644 --- a/test cases/common/84 extract from nested subdir/meson.build +++ b/test cases/common/84 extract from nested subdir/meson.build @@ -1,4 +1,8 @@ project('Extract objects from subdirs.', 'c') -subdir('src') -subdir('tst') +if meson.is_unity() + message('Unity build: skipping incompatible test') +else + subdir('src') + subdir('tst') +endif diff --git a/test cases/frameworks/13 yelp/help/C/index.page b/test cases/frameworks/13 yelp/help/C/index.page new file mode 100644 index 0000000..1b367e6 --- /dev/null +++ b/test cases/frameworks/13 yelp/help/C/index.page @@ -0,0 +1,8 @@ +<page xmlns="http://projectmallard.org/1.0/" + xmlns:its="http://www.w3.org/2005/11/its" + type="guide" + id="index"> + <title> + Hello! + </title> +</page> diff --git a/test cases/frameworks/13 yelp/help/C/media/test.txt b/test cases/frameworks/13 yelp/help/C/media/test.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/test cases/frameworks/13 yelp/help/C/media/test.txt @@ -0,0 +1 @@ +hello diff --git a/test cases/frameworks/13 yelp/help/de/de.po b/test cases/frameworks/13 yelp/help/de/de.po new file mode 100644 index 0000000..a54ce7f --- /dev/null +++ b/test cases/frameworks/13 yelp/help/de/de.po @@ -0,0 +1,13 @@ +msgid "" +msgstr "" +"Project-Id-Version: meson master\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. (itstool) path: page/title +#: C/index.page:5 +msgid "Hello!" +msgstr "Hallo!" diff --git a/test cases/frameworks/13 yelp/help/es/es.po b/test cases/frameworks/13 yelp/help/es/es.po new file mode 100644 index 0000000..b69ce7f --- /dev/null +++ b/test cases/frameworks/13 yelp/help/es/es.po @@ -0,0 +1,13 @@ +msgid "" +msgstr "" +"Project-Id-Version: meson master\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. (itstool) path: page/title +#: C/index.page:5 +msgid "Hello!" +msgstr "¡Hola!" diff --git a/test cases/frameworks/13 yelp/help/es/media/test.txt b/test cases/frameworks/13 yelp/help/es/media/test.txt new file mode 100644 index 0000000..3453b00 --- /dev/null +++ b/test cases/frameworks/13 yelp/help/es/media/test.txt @@ -0,0 +1 @@ +Hola. diff --git a/test cases/frameworks/13 yelp/help/meson.build b/test cases/frameworks/13 yelp/help/meson.build new file mode 100644 index 0000000..7c6f01d --- /dev/null +++ b/test cases/frameworks/13 yelp/help/meson.build @@ -0,0 +1,7 @@ +gnome = import('gnome') + +gnome.yelp('meson', + sources: 'index.page', + media: 'media/test.txt', + languages: ['de', 'es'], +) diff --git a/test cases/frameworks/13 yelp/installed_files.txt b/test cases/frameworks/13 yelp/installed_files.txt new file mode 100644 index 0000000..59d2158 --- /dev/null +++ b/test cases/frameworks/13 yelp/installed_files.txt @@ -0,0 +1,5 @@ +usr/share/help/C/meson/index.page +usr/share/help/C/meson/media/test.txt +usr/share/help/es/meson/index.page +usr/share/help/es/meson/media/test.txt +usr/share/help/de/meson/index.page diff --git a/test cases/frameworks/13 yelp/meson.build b/test cases/frameworks/13 yelp/meson.build new file mode 100644 index 0000000..725ff7b --- /dev/null +++ b/test cases/frameworks/13 yelp/meson.build @@ -0,0 +1,2 @@ +project('yelp', 'c') +subdir('help') diff --git a/test cases/frameworks/7 gnome/mkenums/meson-sample.h b/test cases/frameworks/7 gnome/mkenums/meson-sample.h index 51e5421..ee7b5cb 100644 --- a/test cases/frameworks/7 gnome/mkenums/meson-sample.h +++ b/test cases/frameworks/7 gnome/mkenums/meson-sample.h @@ -1,3 +1,5 @@ +#pragma once + typedef enum { MESON_THE_XVALUE, diff --git a/test cases/frameworks/7 gnome/resources-data/meson.build b/test cases/frameworks/7 gnome/resources-data/meson.build index 6343c0e..fd8cb0a 100644 --- a/test cases/frameworks/7 gnome/resources-data/meson.build +++ b/test cases/frameworks/7 gnome/resources-data/meson.build @@ -3,7 +3,7 @@ subdir('subdir') fake_generator_script = ''' import os, sys assert os.path.exists(sys.argv[1]), "File %s not found" % sys.argv[1] -print("This is a generated resource") +print("This is a generated resource.") ''' # Generate file res3.txt from file res3.txt.in. This is then included diff --git a/test cases/osx/3 has function xcode8/meson.build b/test cases/osx/3 has function xcode8/meson.build new file mode 100644 index 0000000..300d352 --- /dev/null +++ b/test cases/osx/3 has function xcode8/meson.build @@ -0,0 +1,26 @@ +project('has function xcode8', 'c') + +cc = meson.get_compiler('c') + +# XCode 8 location for the macOS 10.12 SDK +sdk_args = ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk'] +args_10_11 = ['-mmacosx-version-min=10.11'] + sdk_args +args_10_12 = ['-mmacosx-version-min=10.12'] + sdk_args + +# Test requires XCode 8 which has the MacOSX 10.12 SDK +if cc.version().version_compare('>=8.0') + if cc.has_function('clock_gettime', args : args_10_11, prefix : '#include <time.h>') + error('Should not have found clock_gettime via <time.h> when targetting Mac OS X 10.11') + endif + if not cc.has_function('clock_gettime', args : args_10_12, prefix : '#include <time.h>') + error('Did NOT find clock_gettime via <time.h> when targetting Mac OS X 10.12') + endif + if not cc.has_function('clock_gettime', args : args_10_11) + error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.11') + endif + if not cc.has_function('clock_gettime', args : args_10_12) + error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.12') + endif +else + message('Test needs XCode 8, skipping...') +endif diff --git a/test cases/vala/12 custom output/foo.vala b/test cases/vala/12 custom output/foo.vala new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/vala/12 custom output/foo.vala diff --git a/test cases/vala/12 custom output/meson.build b/test cases/vala/12 custom output/meson.build new file mode 100644 index 0000000..ef6dbb5 --- /dev/null +++ b/test cases/vala/12 custom output/meson.build @@ -0,0 +1,9 @@ +project('valatest', 'c', 'vala') + +glib = dependency('glib-2.0') +gobject = dependency('gobject-2.0') + +library('foo-1.0', 'foo.vala', + vala_header: 'foo.h', + vala_vapi: 'foo.vapi', + dependencies: [glib, gobject]) diff --git a/test cases/vala/5 target glib/meson.build b/test cases/vala/5 target glib/meson.build index 3f0d01e..f285d9f 100644 --- a/test cases/vala/5 target glib/meson.build +++ b/test cases/vala/5 target glib/meson.build @@ -1,4 +1,8 @@ -project('valatest', 'vala', 'c', default_options : ['werror=true']) +project('valatest', 'vala', 'c') + +if not meson.is_unity() + add_global_arguments('-Werror', language : 'c') +endif valadeps = [dependency('glib-2.0', version : '>=2.32'), dependency('gobject-2.0')] diff --git a/test cases/vala/8 generated sources/installed_files.txt b/test cases/vala/8 generated sources/installed_files.txt index a4c37f6..e1e9432 100644 --- a/test cases/vala/8 generated sources/installed_files.txt +++ b/test cases/vala/8 generated sources/installed_files.txt @@ -1 +1,2 @@ usr/bin/generatedtest +usr/bin/onlygentest diff --git a/test cases/vala/8 generated sources/meson.build b/test cases/vala/8 generated sources/meson.build index 7271821..6e03404 100644 --- a/test cases/vala/8 generated sources/meson.build +++ b/test cases/vala/8 generated sources/meson.build @@ -5,3 +5,4 @@ cd.set('x', 'y') subdir('src') subdir('tools') +subdir('onlygen') diff --git a/test cases/vala/8 generated sources/onlygen/maingen.in b/test cases/vala/8 generated sources/onlygen/maingen.in new file mode 100644 index 0000000..33c14ce --- /dev/null +++ b/test cases/vala/8 generated sources/onlygen/maingen.in @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/test cases/vala/8 generated sources/onlygen/meson.build b/test cases/vala/8 generated sources/onlygen/meson.build new file mode 100644 index 0000000..f48e0b8 --- /dev/null +++ b/test cases/vala/8 generated sources/onlygen/meson.build @@ -0,0 +1,7 @@ +onlygen = generator(copy, + output : '@BASENAME@.vala', + arguments : ['@INPUT@', '@OUTPUT@']) + +executable('onlygentest', onlygen.process('maingen.in'), + install : true, + dependencies: [dependency('glib-2.0'), dependency('gobject-2.0')]) |