aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--authors.txt3
-rw-r--r--mesonbuild/backend/backends.py89
-rw-r--r--mesonbuild/backend/ninjabackend.py110
-rw-r--r--mesonbuild/build.py111
-rw-r--r--mesonbuild/compilers.py210
-rw-r--r--mesonbuild/dependencies.py4
-rw-r--r--mesonbuild/environment.py12
-rw-r--r--mesonbuild/interpreter.py50
-rw-r--r--mesonbuild/mesonlib.py53
-rw-r--r--mesonbuild/mesonmain.py3
-rw-r--r--mesonbuild/mintro.py27
-rw-r--r--mesonbuild/modules/gnome.py74
-rw-r--r--mesonbuild/modules/i18n.py8
-rw-r--r--mesonbuild/modules/pkgconfig.py8
-rwxr-xr-xmesonbuild/scripts/depfixer.py6
-rw-r--r--mesonbuild/scripts/gettext.py16
-rw-r--r--mesonbuild/scripts/yelphelper.py118
-rwxr-xr-xrun_tests.py12
-rwxr-xr-xrun_unittests.py25
-rw-r--r--test cases/common/110 extract same name/meson.build3
-rw-r--r--test cases/common/5 linkstatic/libfile2.c3
-rw-r--r--test cases/common/5 linkstatic/libfile3.c3
-rw-r--r--test cases/common/5 linkstatic/libfile4.c3
-rw-r--r--test cases/common/5 linkstatic/meson.build2
-rw-r--r--test cases/common/84 extract from nested subdir/meson.build8
-rw-r--r--test cases/frameworks/13 yelp/help/C/index.page8
-rw-r--r--test cases/frameworks/13 yelp/help/C/media/test.txt1
-rw-r--r--test cases/frameworks/13 yelp/help/de/de.po13
-rw-r--r--test cases/frameworks/13 yelp/help/es/es.po13
-rw-r--r--test cases/frameworks/13 yelp/help/es/media/test.txt1
-rw-r--r--test cases/frameworks/13 yelp/help/meson.build7
-rw-r--r--test cases/frameworks/13 yelp/installed_files.txt5
-rw-r--r--test cases/frameworks/13 yelp/meson.build2
-rw-r--r--test cases/frameworks/7 gnome/mkenums/meson-sample.h2
-rw-r--r--test cases/frameworks/7 gnome/resources-data/meson.build2
-rw-r--r--test cases/osx/3 has function xcode8/meson.build26
-rw-r--r--test cases/vala/12 custom output/foo.vala0
-rw-r--r--test cases/vala/12 custom output/meson.build9
-rw-r--r--test cases/vala/5 target glib/meson.build6
-rw-r--r--test cases/vala/8 generated sources/installed_files.txt1
-rw-r--r--test cases/vala/8 generated sources/meson.build1
-rw-r--r--test cases/vala/8 generated sources/onlygen/maingen.in3
-rw-r--r--test cases/vala/8 generated sources/onlygen/meson.build7
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')])