aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ci/appveyor-install.bat4
-rw-r--r--docs/markdown/Generating-sources.md2
-rw-r--r--docs/markdown/Reference-manual.md14
-rw-r--r--docs/markdown/Release-notes-for-0.43.0/001-generator-capture.md4
-rw-r--r--docs/markdown/snippets/custom-target-index.md21
-rw-r--r--mesonbuild/backend/backends.py8
-rw-r--r--mesonbuild/backend/ninjabackend.py26
-rw-r--r--mesonbuild/backend/vs2010backend.py55
-rw-r--r--mesonbuild/build.py42
-rw-r--r--mesonbuild/compilers/c.py3
-rw-r--r--mesonbuild/dependencies/dev.py84
-rw-r--r--mesonbuild/interpreter.py46
-rw-r--r--mesonbuild/interpreterbase.py10
-rw-r--r--mesonbuild/modules/gnome.py9
-rw-r--r--mesonbuild/wrap/wrap.py88
-rw-r--r--test cases/common/114 multiple dir configure file/meson.build4
-rw-r--r--test cases/common/114 multiple dir configure file/subdir/foo.txt0
-rw-r--r--test cases/common/114 multiple dir configure file/subdir/meson.build7
-rw-r--r--test cases/common/145 whole archive/allofme/meson.build1
-rw-r--r--test cases/common/145 whole archive/exe/meson.build2
-rw-r--r--test cases/common/145 whole archive/exe2/meson.build1
-rw-r--r--test cases/common/145 whole archive/meson.build20
-rw-r--r--test cases/common/145 whole archive/shlib/meson.build4
-rw-r--r--test cases/common/145 whole archive/stlib/meson.build1
-rw-r--r--test cases/common/145 whole archive/wholeshlib/meson.build1
-rw-r--r--test cases/common/161 index customtarget/gen_sources.py49
-rw-r--r--test cases/common/161 index customtarget/lib.c20
-rw-r--r--test cases/common/161 index customtarget/meson.build32
-rw-r--r--test cases/common/161 index customtarget/subdir/foo.c22
-rw-r--r--test cases/common/161 index customtarget/subdir/meson.build19
-rw-r--r--test cases/common/98 gen extra/meson.build10
-rw-r--r--test cases/common/98 gen extra/srcgen3.py16
-rw-r--r--test cases/failing/60 assign custom target index/meson.build24
-rw-r--r--test cases/frameworks/15 llvm/meson.build5
34 files changed, 523 insertions, 131 deletions
diff --git a/ci/appveyor-install.bat b/ci/appveyor-install.bat
index 9eddeac..becc80a 100644
--- a/ci/appveyor-install.bat
+++ b/ci/appveyor-install.bat
@@ -1,7 +1,5 @@
set CACHE=C:\cache
set CYGWIN_MIRROR="http://cygwin.mirror.constant.com"
-set CYGWIN_ADDITIONAL_REPO="http://www.dronecode.org.uk/cygwin/"
-set CYGWIN_ADDITIONAL_REPO_KEY="http://www.dronecode.org.uk/cygwin/dronecode.gpg"
if _%arch%_ == _x64_ set SETUP=setup-x86_64.exe && set CYGWIN_ROOT=C:\cygwin64
if _%arch%_ == _x86_ set SETUP=setup-x86.exe && set CYGWIN_ROOT=C:\cygwin
@@ -9,5 +7,5 @@ if _%arch%_ == _x86_ set SETUP=setup-x86.exe && set CYGWIN_ROOT=C:\cygwin
if not exist %CACHE% mkdir %CACHE%
echo Updating Cygwin and installing ninja and test prerequisites
-%CYGWIN_ROOT%\%SETUP% -qnNdO -R "%CYGWIN_ROOT%" -s "%CYGWIN_MIRROR%" -s "%CYGWIN_ADDITIONAL_REPO%" -K "%CYGWIN_ADDITIONAL_REPO_KEY%" -l "%CACHE%" -g -P "ninja,gcc-objc,gcc-objc++,libglib2.0-devel,zlib-devel,python3-pip"
+%CYGWIN_ROOT%\%SETUP% -qnNdO -R "%CYGWIN_ROOT%" -s "%CYGWIN_MIRROR%" -l "%CACHE%" -g -P "ninja,gcc-objc,gcc-objc++,libglib2.0-devel,zlib-devel,python3-pip"
echo Install done
diff --git a/docs/markdown/Generating-sources.md b/docs/markdown/Generating-sources.md
index c251805..c5e338d 100644
--- a/docs/markdown/Generating-sources.md
+++ b/docs/markdown/Generating-sources.md
@@ -31,7 +31,7 @@ gen_src = custom_target('gen-output',
'--h-out', '@OUTPUT1@'])
```
-The `@INPUT@` there will be transformed to `'out.c' 'out.h'`. Just like the output, you can also refer to each input file individually by index.
+The `@INPUT@` there will be transformed to `'somefile1.c' 'file2.c'`. Just like the output, you can also refer to each input file individually by index.
Then you just put that in your program and you're done.
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index f3640f0..f37fb34 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -554,6 +554,9 @@ following:
- `output` a template string (or list of template strings) defining
how an output file name is (or multiple output names are) generated
from a single source file name
+- `capture` when this argument is set to true, Meson captures `stdout`
+ of the `executable` and writes it to the target file specified as
+ `output`. Available since v0.43.0.
The returned object also has methods that are documented in the
[object methods section](#generator-object) below.
@@ -1309,9 +1312,9 @@ the following methods:
- `get_id()` returns a string identifying the compiler. For example,
`gcc`, `msvc`, [and more](Compiler-properties.md#compiler-id).
-- `get_supported_arguments(list_of_string)` returns an array
- containing only the arguments supported by the compiler, as if
- `has_argument` were called on them individually.
+- `get_supported_arguments(list_of_string)` *(added 0.43.0)* returns
+ an array containing only the arguments supported by the compiler,
+ as if `has_argument` were called on them individually.
- `has_argument(argument_name)` returns true if the compiler accepts
the specified command line argument, that is, can compile code
@@ -1567,6 +1570,11 @@ contains a target with the following methods:
this and will also allow Meson to setup inter-target dependencies
correctly. Please file a bug if that doesn't work for you.
+- `[index]` returns an opaque object that references this target, and can be
+ used as a source in other targets. When it is used as such it will make that
+ target depend on this custom target, but the only source added will be the
+ one that corresponds to the index of the custom target's output argument.
+
### `dependency` object
This object is returned by [`dependency()`](#dependency) and contains
diff --git a/docs/markdown/Release-notes-for-0.43.0/001-generator-capture.md b/docs/markdown/Release-notes-for-0.43.0/001-generator-capture.md
new file mode 100644
index 0000000..4eb7fc0
--- /dev/null
+++ b/docs/markdown/Release-notes-for-0.43.0/001-generator-capture.md
@@ -0,0 +1,4 @@
+## Generator learned capture
+
+Generators can now be configured to capture the standard output. See
+`test cases/common/98 gen extra/meson.build` for an example.
diff --git a/docs/markdown/snippets/custom-target-index.md b/docs/markdown/snippets/custom-target-index.md
new file mode 100644
index 0000000..10d7cf1
--- /dev/null
+++ b/docs/markdown/snippets/custom-target-index.md
@@ -0,0 +1,21 @@
+# Can index CustomTaget objects
+
+The `CustomTarget` object can now be indexed like an array. The resulting
+object can be used as a source file for other Targets, this will create a
+dependency on the original `CustomTarget`, but will only insert the generated
+file corresponding to the index value of the `CustomTarget`'s `output` keyword.
+
+ c = CustomTarget(
+ ...
+ output : ['out.h', 'out.c'],
+ )
+ lib1 = static_library(
+ 'lib1',
+ [lib1_sources, c[0]],
+ ...
+ )
+ exec = executable(
+ 'executable',
+ c[1],
+ link_with : lib1,
+ )
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 97959b6..960f2e2 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -155,6 +155,12 @@ class Backend:
dirname = 'meson-out'
return dirname
+ def get_target_dir_relative_to(self, t, o):
+ '''Get a target dir relative to another target's directory'''
+ target_dir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(t))
+ othert_dir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(o))
+ return os.path.relpath(target_dir, othert_dir)
+
def get_target_source_dir(self, target):
dirname = os.path.join(self.build_to_src, self.get_target_dir(target))
return dirname
@@ -174,7 +180,7 @@ class Backend:
Returns the full path of the generated source relative to the build root
"""
# CustomTarget generators output to the build dir of the CustomTarget
- if isinstance(gensrc, build.CustomTarget):
+ if isinstance(gensrc, (build.CustomTarget, build.CustomTargetIndex)):
return os.path.join(self.get_target_dir(gensrc), src)
# GeneratedList generators output to the private build directory of the
# target that the GeneratedList is used in
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 41b93cb..2e6e351 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -245,7 +245,7 @@ int dummy;
header_deps = []
# XXX: Why don't we add deps to CustomTarget headers here?
for genlist in target.get_generated_sources():
- if isinstance(genlist, build.CustomTarget):
+ if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
continue
for src in genlist.get_outputs():
if self.environment.is_header(src):
@@ -1761,10 +1761,11 @@ rule FORTRAN_DEP_HACK
outfile.write('\n')
def generate_generator_list_rules(self, target, outfile):
- # CustomTargets have already written their rules,
- # so write rules for GeneratedLists here
+ # CustomTargets have already written their rules and
+ # CustomTargetIndexes don't actually get generated, so write rules for
+ # GeneratedLists here
for genlist in target.get_generated_sources():
- if isinstance(genlist, build.CustomTarget):
+ if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
continue
self.generate_genlist_for_target(genlist, target, outfile)
@@ -1813,6 +1814,19 @@ rule FORTRAN_DEP_HACK
relout = self.get_target_private_dir(target)
args = self.replace_paths(target, args)
cmdlist = exe_arr + self.replace_extra_args(args, genlist)
+ if generator.capture:
+ exe_data = self.serialize_executable(
+ cmdlist[0],
+ cmdlist[1:],
+ self.environment.get_build_dir(),
+ capture=outfiles[0]
+ )
+ cmd = self.environment.get_build_command() + ['--internal', 'exe', exe_data]
+ abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
+ os.makedirs(abs_pdir, exist_ok=True)
+ else:
+ cmd = cmdlist
+
elem = NinjaBuildElement(self.all_outputs, outfiles, rulename, infilename)
if generator.depfile is not None:
elem.add_item('DEPFILE', depfile)
@@ -1821,7 +1835,7 @@ rule FORTRAN_DEP_HACK
elem.add_item('DESC', 'Generating {!r}.'.format(sole_output))
if isinstance(exe, build.BuildTarget):
elem.add_dep(self.get_target_filename(exe))
- elem.add_item('COMMAND', cmdlist)
+ elem.add_item('COMMAND', cmd)
elem.write(outfile)
def scan_fortran_module_outputs(self, target):
@@ -2013,7 +2027,7 @@ rule FORTRAN_DEP_HACK
# Generator output goes into the target private dir which is
# already in the include paths list. Only custom targets have their
# own target build dir.
- if not isinstance(i, build.CustomTarget):
+ if not isinstance(i, (build.CustomTarget, build.CustomTargetIndex)):
continue
idir = self.get_target_dir(i)
if idir not in custom_target_include_dirs:
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 22c1779..e4e9696 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -91,7 +91,7 @@ class Vs2010Backend(backends.Backend):
source_target_dir = self.get_target_source_dir(target)
down = self.target_to_build_root(target)
for genlist in target.get_generated_sources():
- if isinstance(genlist, build.CustomTarget):
+ if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
for i in genlist.get_outputs():
# Path to the generated source from the current vcxproj dir via the build root
ipath = os.path.join(down, self.get_target_dir(genlist), i)
@@ -128,6 +128,16 @@ class Vs2010Backend(backends.Backend):
.replace("@BUILD_ROOT@", self.environment.get_build_dir())
for x in args]
cmd = exe_arr + self.replace_extra_args(args, genlist)
+ if generator.capture:
+ exe_data = self.serialize_executable(
+ cmd[0],
+ cmd[1:],
+ self.environment.get_build_dir(),
+ capture=outfiles[0]
+ )
+ cmd = self.environment.get_build_command() + ['--internal', 'exe', exe_data]
+ abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
+ os.makedirs(abs_pdir, exist_ok=True)
cbs = ET.SubElement(idgroup, 'CustomBuild', Include=infilename)
ET.SubElement(cbs, 'Command').text = ' '.join(self.quote_arguments(cmd))
ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles)
@@ -201,6 +211,8 @@ class Vs2010Backend(backends.Backend):
for gendep in target.get_generated_sources():
if isinstance(gendep, build.CustomTarget):
all_deps[gendep.get_id()] = gendep
+ elif isinstance(gendep, build.CustomTargetIndex):
+ all_deps[gendep.target.get_id()] = gendep.target
else:
gen_exe = gendep.generator.get_exe()
if isinstance(gen_exe, build.Executable):
@@ -332,6 +344,11 @@ class Vs2010Backend(backends.Backend):
def quote_arguments(self, arr):
return ['"%s"' % i for i in arr]
+ def add_project_reference(self, root, include, projid):
+ ig = ET.SubElement(root, 'ItemGroup')
+ pref = ET.SubElement(ig, 'ProjectReference', Include=include)
+ ET.SubElement(pref, 'Project').text = '{%s}' % projid
+
def create_basic_crap(self, target):
project_name = target.name
root = ET.Element('Project', {'DefaultTargets': "Build",
@@ -525,6 +542,8 @@ class Vs2010Backend(backends.Backend):
if lpath in lpaths:
lpaths.remove(lpath)
lpaths.append(lpath)
+ elif arg.startswith(('/', '-')):
+ other.append(arg)
# It's ok if we miss libraries with non-standard extensions here.
# They will go into the general link arguments.
elif arg.endswith('.lib') or arg.endswith('.a'):
@@ -895,20 +914,26 @@ class Vs2010Backend(backends.Backend):
# *_winlibs that we want to link to are static mingw64 libraries.
extra_link_args += compiler.get_option_link_args(self.environment.coredata.compiler_options)
(additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args.to_native())
- if len(extra_link_args) > 0:
- extra_link_args.append('%(AdditionalOptions)')
- ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args)
- if len(additional_libpaths) > 0:
- additional_libpaths.insert(0, '%(AdditionalLibraryDirectories)')
- ET.SubElement(link, 'AdditionalLibraryDirectories').text = ';'.join(additional_libpaths)
# Add more libraries to be linked if needed
for t in target.get_dependencies():
lobj = self.build.targets[t.get_id()]
linkname = os.path.join(down, self.get_target_filename_for_linking(lobj))
if t in target.link_whole_targets:
- linkname = compiler.get_link_whole_for(linkname)[0]
- additional_links.append(linkname)
+ # /WHOLEARCHIVE:foo must go into AdditionalOptions
+ extra_link_args += compiler.get_link_whole_for(linkname)
+ # To force Visual Studio to build this project even though it
+ # has no sources, we include a reference to the vcxproj file
+ # that builds this target. Technically we should add this only
+ # if the current target has no sources, but it doesn't hurt to
+ # have 'extra' references.
+ trelpath = self.get_target_dir_relative_to(t, target)
+ tvcxproj = os.path.join(trelpath, t.get_id() + '.vcxproj')
+ tid = self.environment.coredata.target_guids[t.get_id()]
+ self.add_project_reference(root, tvcxproj, tid)
+ else:
+ # Other libraries go into AdditionalDependencies
+ additional_links.append(linkname)
for lib in self.get_custom_target_provided_libraries(target):
additional_links.append(self.relpath(lib, self.get_target_dir(target)))
additional_objects = []
@@ -917,6 +942,13 @@ class Vs2010Backend(backends.Backend):
additional_objects.append(o)
for o in custom_objs:
additional_objects.append(o)
+
+ if len(extra_link_args) > 0:
+ extra_link_args.append('%(AdditionalOptions)')
+ ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args)
+ if len(additional_libpaths) > 0:
+ additional_libpaths.insert(0, '%(AdditionalLibraryDirectories)')
+ ET.SubElement(link, 'AdditionalLibraryDirectories').text = ';'.join(additional_libpaths)
if len(additional_links) > 0:
additional_links.append('%(AdditionalDependencies)')
ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links)
@@ -1006,9 +1038,8 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
# Reference the regen target.
- ig = ET.SubElement(root, 'ItemGroup')
- pref = ET.SubElement(ig, 'ProjectReference', Include=os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj'))
- ET.SubElement(pref, 'Project').text = self.environment.coredata.regen_guid
+ regen_vcxproj = os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj')
+ self.add_project_reference(root, regen_vcxproj, self.environment.coredata.regen_guid)
self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
def gen_regenproj(self, project_name, ofname):
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index c54abbd..5f5dd6b 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -357,7 +357,7 @@ class BuildTarget(Target):
self.process_compilers()
self.process_kwargs(kwargs, environment)
self.check_unknown_kwargs(kwargs)
- if not self.sources and not self.generated and not self.objects:
+ if not any([self.sources, self.generated, self.objects, self.link_whole]):
raise InvalidArguments('Build target %s has no sources.' % name)
self.process_compilers_late()
self.validate_sources()
@@ -425,7 +425,7 @@ class BuildTarget(Target):
if s not in added_sources:
self.sources.append(s)
added_sources[s] = True
- elif isinstance(s, (GeneratedList, CustomTarget)):
+ elif isinstance(s, (GeneratedList, CustomTarget, CustomTargetIndex)):
self.generated.append(s)
else:
msg = 'Bad source of type {!r} in target {!r}.'.format(type(s).__name__, self.name)
@@ -1019,6 +1019,7 @@ class Generator:
raise InvalidArguments('First generator argument must be an executable.')
self.exe = exe
self.depfile = None
+ self.capture = False
self.process_kwargs(kwargs)
def __repr__(self):
@@ -1062,6 +1063,11 @@ class Generator:
if os.path.split(depfile)[1] != depfile:
raise InvalidArguments('Depfile must be a plain filename without a subdirectory.')
self.depfile = depfile
+ if 'capture' in kwargs:
+ capture = kwargs['capture']
+ if not isinstance(capture, bool):
+ raise InvalidArguments('Capture must be boolean.')
+ self.capture = capture
def get_base_outnames(self, inname):
plainname = os.path.split(inname)[1]
@@ -1676,6 +1682,15 @@ class CustomTarget(Target):
def type_suffix(self):
return "@cus"
+ def __getitem__(self, index):
+ return CustomTargetIndex(self, self.outputs[index])
+
+ def __setitem__(self, index, value):
+ raise NotImplementedError
+
+ def __delitem__(self, index):
+ raise NotImplementedError
+
class RunTarget(Target):
def __init__(self, name, command, args, dependencies, subdir):
super().__init__(name, subdir, False)
@@ -1735,6 +1750,29 @@ class Jar(BuildTarget):
pass
+class CustomTargetIndex:
+
+ """A special opaque object returned by indexing a CustomTaget. This object
+ exists in meson, but acts as a proxy in the backends, making targets depend
+ on the CustomTarget it's derived from, but only adding one source file to
+ the sources.
+ """
+
+ def __init__(self, target, output):
+ self.target = target
+ self.output = output
+
+ def __repr__(self):
+ return '<CustomTargetIndex: {!r}[{}]>'.format(
+ self.target, self.target.output.index(self.output))
+
+ def get_outputs(self):
+ return [self.output]
+
+ def get_subdir(self):
+ return self.target.get_subdir()
+
+
class ConfigureFile:
def __init__(self, subdir, sourcename, targetname, configuration_data):
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index c17726a..82b1ef0 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -924,6 +924,9 @@ class VisualStudioCCompiler(CCompiler):
def get_linker_search_args(self, dirname):
return ['/LIBPATH:' + dirname]
+ def get_gui_app_args(self):
+ return ['/SUBSYSTEM:WINDOWS']
+
def get_pic_args(self):
return [] # PIC is handled by the loader on Windows
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index f991d3c..d2dd107 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -129,9 +129,6 @@ class LLVMDependency(ExternalDependency):
'llvm-config-3.5', 'llvm-config35',
'llvm-config-5.0', 'llvm-config-devel', # development snapshot
]
- llvmconfig = None
- _llvmconfig_found = False
- __best_found = None
__cpp_blacklist = {'-DNDEBUG'}
def __init__(self, environment, kwargs):
@@ -139,11 +136,12 @@ class LLVMDependency(ExternalDependency):
# the C linker works fine if only using the C API.
super().__init__('llvm-config', environment, 'cpp', kwargs)
self.modules = []
+ self.llvmconfig = None
+ self.__best_found = None
# FIXME: Support multiple version requirements ala PkgConfigDependency
req_version = kwargs.get('version', None)
+ self.check_llvmconfig(req_version)
if self.llvmconfig is None:
- self.check_llvmconfig(req_version)
- if not self._llvmconfig_found:
if self.__best_found is not None:
mlog.log('found {!r} but need:'.format(self.__best_found),
req_version)
@@ -159,31 +157,36 @@ class LLVMDependency(ExternalDependency):
mlog.debug('stdout: {}\nstderr: {}'.format(out, err))
if self.required:
raise DependencyException('Dependency LLVM not found')
+ mlog.log('Dependency LLVM found:', mlog.red('NO'))
return
- else:
- self.version = out.strip()
- mlog.log('Dependency LLVM found:', mlog.green('YES'))
- self.is_found = True
- p, out = Popen_safe(
- [self.llvmconfig, '--libs', '--ldflags', '--system-libs'])[:2]
- if p.returncode != 0:
- raise DependencyException('Could not generate libs for LLVM.')
- self.link_args = shlex.split(out)
-
- p, out = Popen_safe([self.llvmconfig, '--cppflags'])[:2]
- if p.returncode != 0:
- raise DependencyException('Could not generate includedir for LLVM.')
- cargs = mesonlib.OrderedSet(shlex.split(out))
- self.compile_args = list(cargs.difference(self.__cpp_blacklist))
-
- p, out = Popen_safe([self.llvmconfig, '--components'])[:2]
- if p.returncode != 0:
- raise DependencyException('Could not generate modules for LLVM.')
- self.modules = shlex.split(out)
-
- modules = mesonlib.stringlistify(kwargs.get('modules', []))
- for mod in modules:
+ mlog.log('Dependency LLVM found:', mlog.green('YES'))
+ self.is_found = True
+
+ # Currently meson doesn't really atempt to handle pre-release versions,
+ # so strip the 'svn' off the end, since it will probably cuase problems
+ # for users who want the patch version.
+ self.version = out.strip().rstrip('svn')
+
+ p, out = Popen_safe(
+ [self.llvmconfig, '--libs', '--ldflags', '--system-libs'])[:2]
+ if p.returncode != 0:
+ raise DependencyException('Could not generate libs for LLVM.')
+ self.link_args = shlex.split(out)
+
+ p, out = Popen_safe([self.llvmconfig, '--cppflags'])[:2]
+ if p.returncode != 0:
+ raise DependencyException('Could not generate includedir for LLVM.')
+ cargs = mesonlib.OrderedSet(shlex.split(out))
+ self.compile_args = list(cargs.difference(self.__cpp_blacklist))
+
+ p, out = Popen_safe([self.llvmconfig, '--components'])[:2]
+ if p.returncode != 0:
+ raise DependencyException('Could not generate modules for LLVM.')
+ self.modules = shlex.split(out)
+
+ modules = mesonlib.stringlistify(mesonlib.flatten(kwargs.get('modules', [])))
+ for mod in sorted(set(modules)):
if mod not in self.modules:
mlog.log('LLVM module', mod, 'found:', mlog.red('NO'))
self.is_found = False
@@ -193,38 +196,33 @@ class LLVMDependency(ExternalDependency):
else:
mlog.log('LLVM module', mod, 'found:', mlog.green('YES'))
- @classmethod
- def check_llvmconfig(cls, version_req):
+ def check_llvmconfig(self, version_req):
"""Try to find the highest version of llvm-config."""
- for llvmconfig in cls.llvm_config_bins:
+ for llvmconfig in self.llvm_config_bins:
try:
p, out = Popen_safe([llvmconfig, '--version'])[0:2]
out = out.strip()
if p.returncode != 0:
continue
- # FIXME: As soon as some llvm-config is found, version checks
- # in further dependnecy() calls will be ignored
if version_req:
if version_compare(out, version_req, strict=True):
- if cls.__best_found and version_compare(out, '<={}'.format(cls.__best_found), strict=True):
+ if self.__best_found and version_compare(
+ out, '<={}'.format(self.__best_found), strict=True):
continue
- cls.__best_found = out
- cls.llvmconfig = llvmconfig
+ self.__best_found = out
+ self.llvmconfig = llvmconfig
else:
# If no specific version is requested use the first version
# found, since that should be the best.
- cls.__best_found = out
- cls.llvmconfig = llvmconfig
+ self.__best_found = out
+ self.llvmconfig = llvmconfig
break
except (FileNotFoundError, PermissionError):
pass
- if cls.__best_found:
+ if self.__best_found:
mlog.log('Found llvm-config:',
- mlog.bold(shutil.which(cls.llvmconfig)),
+ mlog.bold(shutil.which(self.llvmconfig)),
'({})'.format(out.strip()))
- cls._llvmconfig_found = True
- else:
- cls.llvmconfig = False
def need_threads(self):
return True
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 2cc5a9f..8197b5e 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -566,6 +566,11 @@ class JarHolder(BuildTargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
+class CustomTargetIndexHolder(InterpreterObject):
+ def __init__(self, object_to_hold):
+ super().__init__()
+ self.held_object = object_to_hold
+
class CustomTargetHolder(TargetHolder):
def __init__(self, object_to_hold, interp):
super().__init__()
@@ -582,6 +587,15 @@ class CustomTargetHolder(TargetHolder):
def full_path_method(self, args, kwargs):
return self.interpreter.backend.get_target_filename_abs(self.held_object)
+ def __getitem__(self, index):
+ return CustomTargetIndexHolder(self.held_object[index])
+
+ def __setitem__(self, index, value):
+ raise InterpreterException('Cannot set a member of a CustomTarget')
+
+ def __delitem__(self, index):
+ raise InterpreterException('Cannot delete a member of a CustomTarget')
+
class RunTargetHolder(InterpreterObject):
def __init__(self, name, command, args, dependencies, subdir):
super().__init__()
@@ -1321,7 +1335,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'version'},
'executable': exe_kwargs,
'find_program': {'required', 'native'},
- 'generator': {'arguments', 'output', 'depfile'},
+ 'generator': {'arguments', 'output', 'depfile', 'capture'},
'include_directories': {'is_system'},
'install_data': {'install_dir', 'install_mode', 'sources'},
'install_headers': {'install_dir', 'subdir'},
@@ -2550,12 +2564,10 @@ class Interpreter(InterpreterBase):
if not isinstance(inputfile, (str, mesonlib.File)):
raise InterpreterException('Input must be a string or a file')
if isinstance(inputfile, str):
- inputfile = os.path.join(self.subdir, inputfile)
- ifile_abs = os.path.join(self.environment.source_dir, inputfile)
- else:
- ifile_abs = inputfile.absolute_path(self.environment.source_dir,
- self.environment.build_dir)
- inputfile = inputfile.relative_name()
+ inputfile = mesonlib.File.from_source_file(self.environment.source_dir,
+ self.subdir, inputfile)
+ ifile_abs = inputfile.absolute_path(self.environment.source_dir,
+ self.environment.build_dir)
elif 'command' in kwargs and '@INPUT@' in kwargs['command']:
raise InterpreterException('@INPUT@ used as command argument, but no input file specified.')
# Validate output
@@ -2576,18 +2588,13 @@ class Interpreter(InterpreterBase):
raise InterpreterException('Argument "configuration" is not of type configuration_data')
mlog.log('Configuring', mlog.bold(output), 'using configuration')
if inputfile is not None:
- # Normalize the path of the conffile to avoid duplicates
- # This is especially important to convert '/' to '\' on Windows
- conffile = os.path.normpath(inputfile)
- if conffile not in self.build_def_files:
- self.build_def_files.append(conffile)
os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
missing_variables = mesonlib.do_conf_file(ifile_abs, ofile_abs,
conf.held_object)
if missing_variables:
var_list = ", ".join(map(repr, sorted(missing_variables)))
mlog.warning(
- "The variable(s) %s in the input file %r are not "
+ "The variable(s) %s in the input file %s are not "
"present in the given configuration data" % (
var_list, inputfile))
else:
@@ -2617,6 +2624,17 @@ class Interpreter(InterpreterBase):
mesonlib.replace_if_different(ofile_abs, dst_tmp)
else:
raise InterpreterException('Configure_file must have either "configuration" or "command".')
+ # If the input is a source file, add it to the list of files that we
+ # need to reconfigure on when they change. FIXME: Do the same for
+ # files() objects in the command: kwarg.
+ if inputfile and not inputfile.is_built:
+ # Normalize the path of the conffile (relative to the
+ # source root) to avoid duplicates. This is especially
+ # important to convert '/' to '\' on Windows
+ conffile = os.path.normpath(inputfile.relative_name())
+ if conffile not in self.build_def_files:
+ self.build_def_files.append(conffile)
+ # Install file if requested
idir = kwargs.get('install_dir', None)
if isinstance(idir, str):
cfile = mesonlib.File.from_built_file(ofile_path, ofile_fname)
@@ -2770,7 +2788,7 @@ different subdirectory.
results = []
for s in sources:
if isinstance(s, (mesonlib.File, GeneratedListHolder,
- CustomTargetHolder)):
+ CustomTargetHolder, CustomTargetIndexHolder)):
pass
elif isinstance(s, str):
s = mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index 1dd2f02..cb82e56 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -369,14 +369,16 @@ class InterpreterBase:
def evaluate_indexing(self, node):
assert(isinstance(node, mparser.IndexNode))
iobject = self.evaluate_statement(node.iobject)
- if not isinstance(iobject, list):
- raise InterpreterException('Tried to index a non-array object.')
+ if not hasattr(iobject, '__getitem__'):
+ raise InterpreterException(
+ 'Tried to index an object that doesn\'t support indexing.')
index = self.evaluate_statement(node.index)
if not isinstance(index, int):
raise InterpreterException('Index value is not an integer.')
- if index < -len(iobject) or index >= len(iobject):
+ try:
+ return iobject[index]
+ except IndexError:
raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject)))
- return iobject[index]
def function_call(self, node):
func_name = node.func_name
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 1ab075b..d1d7013 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -107,12 +107,12 @@ class GnomeModule(ExtensionModule):
for (ii, dep) in enumerate(dependencies):
if hasattr(dep, 'held_object'):
dependencies[ii] = dep = dep.held_object
- if not isinstance(dep, (mesonlib.File, build.CustomTarget)):
+ if not isinstance(dep, (mesonlib.File, build.CustomTarget, build.CustomTargetIndex)):
m = 'Unexpected dependency type {!r} for gnome.compile_resources() ' \
'"dependencies" argument.\nPlease pass the return value of ' \
'custom_target() or configure_file()'
raise MesonException(m.format(dep))
- if isinstance(dep, build.CustomTarget):
+ if isinstance(dep, (build.CustomTarget, build.CustomTargetIndex)):
if not mesonlib.version_compare(glib_version, gresource_dep_needed_version):
m = 'The "dependencies" argument of gnome.compile_resources() can not\n' \
'be used with the current version of glib-compile-resources due to\n' \
@@ -131,6 +131,7 @@ class GnomeModule(ExtensionModule):
elif isinstance(ifile, str):
ifile = os.path.join(state.subdir, ifile)
elif isinstance(ifile, (interpreter.CustomTargetHolder,
+ interpreter.CustomTargetIndexHolder,
interpreter.GeneratedObjectsHolder)):
m = 'Resource xml files generated at build-time cannot be used ' \
'with gnome.compile_resources() because we need to scan ' \
@@ -261,7 +262,7 @@ class GnomeModule(ExtensionModule):
dep_files.append(dep)
subdirs.append(dep.subdir)
break
- elif isinstance(dep, build.CustomTarget):
+ elif isinstance(dep, (build.CustomTarget, build.CustomTargetIndex)):
fname = None
outputs = {(o, os.path.basename(o)) for o in dep.get_outputs()}
for o, baseo in outputs:
@@ -443,7 +444,7 @@ class GnomeModule(ExtensionModule):
for s in libsources:
if hasattr(s, 'held_object'):
s = s.held_object
- if isinstance(s, build.CustomTarget):
+ if isinstance(s, (build.CustomTarget, build.CustomTargetIndex)):
gir_filelist.write(os.path.join(state.environment.get_build_dir(),
state.backend.get_target_dir(s),
s.get_outputs()[0]) + '\n')
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index 1678e35..26a3489 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -14,7 +14,7 @@
from .. import mlog
import contextlib
-import urllib.request, os, hashlib, shutil
+import urllib.request, os, hashlib, shutil, tempfile, stat
import subprocess
import sys
from pathlib import Path
@@ -256,6 +256,8 @@ class Resolver:
def get_data(self, url):
blocksize = 10 * 1024
+ h = hashlib.sha256()
+ tmpfile = tempfile.NamedTemporaryFile(mode='wb', dir=self.cachedir, delete=False)
if url.startswith('https://wrapdb.mesonbuild.com'):
resp = open_wrapdburl(url)
else:
@@ -267,26 +269,34 @@ class Resolver:
dlsize = None
if dlsize is None:
print('Downloading file of unknown size.')
- return resp.read()
+ while True:
+ block = resp.read(blocksize)
+ if block == b'':
+ break
+ h.update(block)
+ tmpfile.write(block)
+ hashvalue = h.hexdigest()
+ return hashvalue, tmpfile.name
print('Download size:', dlsize)
print('Downloading: ', end='')
sys.stdout.flush()
printed_dots = 0
- blocks = []
downloaded = 0
while True:
block = resp.read(blocksize)
if block == b'':
break
downloaded += len(block)
- blocks.append(block)
+ h.update(block)
+ tmpfile.write(block)
ratio = int(downloaded / dlsize * 10)
while printed_dots < ratio:
print('.', end='')
sys.stdout.flush()
printed_dots += 1
print('')
- return b''.join(blocks)
+ hashvalue = h.hexdigest()
+ return hashvalue, tmpfile.name
def get_hash(self, data):
h = hashlib.sha256()
@@ -298,30 +308,51 @@ class Resolver:
ofname = os.path.join(self.cachedir, p.get('source_filename'))
if os.path.exists(ofname):
mlog.log('Using', mlog.bold(packagename), 'from cache.')
- return
- srcurl = p.get('source_url')
- mlog.log('Downloading', mlog.bold(packagename), 'from', mlog.bold(srcurl))
- srcdata = self.get_data(srcurl)
- dhash = self.get_hash(srcdata)
- expected = p.get('source_hash')
- if dhash != expected:
- raise RuntimeError('Incorrect hash for source %s:\n %s expected\n %s actual.' % (packagename, expected, dhash))
- with open(ofname, 'wb') as f:
- f.write(srcdata)
+ else:
+ srcurl = p.get('source_url')
+ mlog.log('Downloading', mlog.bold(packagename), 'from', mlog.bold(srcurl))
+ dhash, tmpfile = self.get_data(srcurl)
+ expected = p.get('source_hash')
+ if dhash != expected:
+ os.remove(tmpfile)
+ raise RuntimeError('Incorrect hash for source %s:\n %s expected\n %s actual.' % (packagename, expected, dhash))
+ os.rename(tmpfile, ofname)
if p.has_patch():
- purl = p.get('patch_url')
- mlog.log('Downloading patch from', mlog.bold(purl))
- pdata = self.get_data(purl)
- phash = self.get_hash(pdata)
- expected = p.get('patch_hash')
- if phash != expected:
- raise RuntimeError('Incorrect hash for patch %s:\n %s expected\n %s actual' % (packagename, expected, phash))
- filename = os.path.join(self.cachedir, p.get('patch_filename'))
- with open(filename, 'wb') as f:
- f.write(pdata)
+ patch_filename = p.get('patch_filename')
+ filename = os.path.join(self.cachedir, patch_filename)
+ if os.path.exists(filename):
+ mlog.log('Using', mlog.bold(patch_filename), 'from cache.')
+ else:
+ purl = p.get('patch_url')
+ mlog.log('Downloading patch from', mlog.bold(purl))
+ phash, tmpfile = self.get_data(purl)
+ expected = p.get('patch_hash')
+ if phash != expected:
+ os.remove(tmpfile)
+ raise RuntimeError('Incorrect hash for patch %s:\n %s expected\n %s actual' % (packagename, expected, phash))
+ os.rename(tmpfile, filename)
else:
mlog.log('Package does not require patch.')
+ def copy_tree(self, root_src_dir, root_dst_dir):
+ """
+ Copy directory tree. Overwrites also read only files.
+ """
+ for src_dir, dirs, files in os.walk(root_src_dir):
+ dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
+ if not os.path.exists(dst_dir):
+ os.makedirs(dst_dir)
+ for file_ in files:
+ src_file = os.path.join(src_dir, file_)
+ dst_file = os.path.join(dst_dir, file_)
+ if os.path.exists(dst_file):
+ try:
+ os.remove(dst_file)
+ except PermissionError as exc:
+ os.chmod(dst_file, stat.S_IWUSR)
+ os.remove(dst_file)
+ shutil.copy2(src_file, dst_dir)
+
def extract_package(self, package):
if sys.version_info < (3, 5):
try:
@@ -348,4 +379,9 @@ class Resolver:
pass
shutil.unpack_archive(os.path.join(self.cachedir, package.get('source_filename')), extract_dir)
if package.has_patch():
- shutil.unpack_archive(os.path.join(self.cachedir, package.get('patch_filename')), self.subdir_root)
+ try:
+ shutil.unpack_archive(os.path.join(self.cachedir, package.get('patch_filename')), self.subdir_root)
+ except Exception:
+ with tempfile.TemporaryDirectory() as workdir:
+ shutil.unpack_archive(os.path.join(self.cachedir, package.get('patch_filename')), workdir)
+ self.copy_tree(workdir, self.subdir_root)
diff --git a/test cases/common/114 multiple dir configure file/meson.build b/test cases/common/114 multiple dir configure file/meson.build
index 180227c..c76c6b4 100644
--- a/test cases/common/114 multiple dir configure file/meson.build
+++ b/test cases/common/114 multiple dir configure file/meson.build
@@ -5,3 +5,7 @@ subdir('subdir')
configure_file(input : 'subdir/someinput.in',
output : 'outputhere',
configuration : configuration_data())
+
+configure_file(input : cfile1,
+ output : '@BASENAME@',
+ configuration : configuration_data())
diff --git a/test cases/common/114 multiple dir configure file/subdir/foo.txt b/test cases/common/114 multiple dir configure file/subdir/foo.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/common/114 multiple dir configure file/subdir/foo.txt
diff --git a/test cases/common/114 multiple dir configure file/subdir/meson.build b/test cases/common/114 multiple dir configure file/subdir/meson.build
index a8f731d..9c72bf9 100644
--- a/test cases/common/114 multiple dir configure file/subdir/meson.build
+++ b/test cases/common/114 multiple dir configure file/subdir/meson.build
@@ -2,3 +2,10 @@ configure_file(input : 'someinput.in',
output : 'outputsubdir',
install : false,
configuration : configuration_data())
+
+py3 = import('python3').find_python()
+
+cfile1 = configure_file(input : 'foo.txt',
+ output : 'foo.h.in',
+ capture : true,
+ command : [py3, '-c', 'print("#mesondefine FOO_BAR")'])
diff --git a/test cases/common/145 whole archive/allofme/meson.build b/test cases/common/145 whole archive/allofme/meson.build
new file mode 100644
index 0000000..f5c2027
--- /dev/null
+++ b/test cases/common/145 whole archive/allofme/meson.build
@@ -0,0 +1 @@
+stlib = static_library('allofme', '../libfile.c')
diff --git a/test cases/common/145 whole archive/exe/meson.build b/test cases/common/145 whole archive/exe/meson.build
new file mode 100644
index 0000000..f47a246
--- /dev/null
+++ b/test cases/common/145 whole archive/exe/meson.build
@@ -0,0 +1,2 @@
+exe = executable('prog', '../prog.c',
+ link_with : dylib)
diff --git a/test cases/common/145 whole archive/exe2/meson.build b/test cases/common/145 whole archive/exe2/meson.build
new file mode 100644
index 0000000..5365f03
--- /dev/null
+++ b/test cases/common/145 whole archive/exe2/meson.build
@@ -0,0 +1 @@
+exe2 = executable('prog2', '../prog.c', link_with : dylib2)
diff --git a/test cases/common/145 whole archive/meson.build b/test cases/common/145 whole archive/meson.build
index eadebf8..617ae03 100644
--- a/test cases/common/145 whole archive/meson.build
+++ b/test cases/common/145 whole archive/meson.build
@@ -1,5 +1,7 @@
project('whole archive', 'c')
+add_project_arguments('-I' + meson.source_root(), language : 'c')
+
cc = meson.get_compiler('c')
if cc.get_id() == 'msvc'
@@ -8,15 +10,15 @@ if cc.get_id() == 'msvc'
endif
endif
-stlib = static_library('allofme', 'libfile.c')
-
-# Nothing in dylib.c uses func1, so the linker would throw it
-# away and thus linking the exe would fail.
-dylib = shared_library('shlib', 'dylib.c',
- link_whole : stlib)
-
-exe = executable('prog', 'prog.c',
- link_with : dylib)
+subdir('allofme')
+subdir('shlib')
+subdir('exe')
test('prog', exe)
+# link_whole only
+subdir('stlib')
+subdir('wholeshlib')
+subdir('exe2')
+
+test('prog2', exe2)
diff --git a/test cases/common/145 whole archive/shlib/meson.build b/test cases/common/145 whole archive/shlib/meson.build
new file mode 100644
index 0000000..34a1b78
--- /dev/null
+++ b/test cases/common/145 whole archive/shlib/meson.build
@@ -0,0 +1,4 @@
+# Nothing in dylib.c uses func1, so the linker would throw it
+# away and thus linking the exe would fail.
+dylib = shared_library('shlib', '../dylib.c',
+ link_whole : stlib)
diff --git a/test cases/common/145 whole archive/stlib/meson.build b/test cases/common/145 whole archive/stlib/meson.build
new file mode 100644
index 0000000..07a434e
--- /dev/null
+++ b/test cases/common/145 whole archive/stlib/meson.build
@@ -0,0 +1 @@
+static = static_library('static', '../dylib.c')
diff --git a/test cases/common/145 whole archive/wholeshlib/meson.build b/test cases/common/145 whole archive/wholeshlib/meson.build
new file mode 100644
index 0000000..69a1995
--- /dev/null
+++ b/test cases/common/145 whole archive/wholeshlib/meson.build
@@ -0,0 +1 @@
+dylib2 = shared_library('link_whole', link_whole : [stlib, static])
diff --git a/test cases/common/161 index customtarget/gen_sources.py b/test cases/common/161 index customtarget/gen_sources.py
new file mode 100644
index 0000000..0bdb529
--- /dev/null
+++ b/test cases/common/161 index customtarget/gen_sources.py
@@ -0,0 +1,49 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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 argparse
+import textwrap
+
+HEADER = textwrap.dedent('''\
+ void stringify(int foo, char * buffer);
+ ''')
+
+CODE = textwrap.dedent('''\
+ #include <stdio.h>
+
+ #ifndef WORKS
+ # error "This shouldn't have been included"
+ #endif
+
+ void stringify(int foo, char * buffer) {
+ sprintf(buffer, "%i", foo);
+ }
+ ''')
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--header')
+ parser.add_argument('--code')
+ args = parser.parse_args()
+
+ with open(args.header, 'w') as f:
+ f.write(HEADER)
+
+ with open(args.code, 'w') as f:
+ f.write(CODE)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test cases/common/161 index customtarget/lib.c b/test cases/common/161 index customtarget/lib.c
new file mode 100644
index 0000000..17117d5
--- /dev/null
+++ b/test cases/common/161 index customtarget/lib.c
@@ -0,0 +1,20 @@
+/* Copyright © 2017 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "gen.h"
+
+void func(char * buffer) {
+ stringify(1, buffer);
+}
diff --git a/test cases/common/161 index customtarget/meson.build b/test cases/common/161 index customtarget/meson.build
new file mode 100644
index 0000000..11cb214
--- /dev/null
+++ b/test cases/common/161 index customtarget/meson.build
@@ -0,0 +1,32 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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.
+
+project('custom_target_index', 'c', default_options : 'c_std=c89')
+
+py_mod = import('python3')
+prog_python = py_mod.find_python()
+
+gen = custom_target(
+ 'gen.[ch]',
+ input : 'gen_sources.py',
+ output : ['gen.c', 'gen.h'],
+ command : [prog_python, '@INPUT@', '--header', '@OUTPUT1@', '--code', '@OUTPUT0@'],
+)
+
+lib = static_library(
+ 'libfoo',
+ ['lib.c', gen[1]],
+)
+
+subdir('subdir')
diff --git a/test cases/common/161 index customtarget/subdir/foo.c b/test cases/common/161 index customtarget/subdir/foo.c
new file mode 100644
index 0000000..c620a11
--- /dev/null
+++ b/test cases/common/161 index customtarget/subdir/foo.c
@@ -0,0 +1,22 @@
+/* Copyright © 2017 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "gen.h"
+
+int main(void) {
+ char buf[50];
+ stringify(10, buf);
+ return 0;
+}
diff --git a/test cases/common/161 index customtarget/subdir/meson.build b/test cases/common/161 index customtarget/subdir/meson.build
new file mode 100644
index 0000000..47bcd32
--- /dev/null
+++ b/test cases/common/161 index customtarget/subdir/meson.build
@@ -0,0 +1,19 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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.
+
+foo = executable(
+ 'foo',
+ ['foo.c', gen[0], gen[1]],
+ c_args : '-DWORKS',
+)
diff --git a/test cases/common/98 gen extra/meson.build b/test cases/common/98 gen extra/meson.build
index 772f52e..cbbdceb 100644
--- a/test cases/common/98 gen extra/meson.build
+++ b/test cases/common/98 gen extra/meson.build
@@ -28,3 +28,13 @@ plainname_gen = generator(prog2,
plainname_src = plainname_gen.process('name.l')
test('plainname', executable('plainname', plainname_src))
+
+prog3 = find_program('srcgen3.py')
+capture_gen = generator(prog3,
+ output : ['@BASENAME@.yy.c'],
+ arguments : ['@INPUT@'],
+ capture : true)
+
+capture_src = capture_gen.process('name.l')
+
+test('capture', executable('capture', capture_src))
diff --git a/test cases/common/98 gen extra/srcgen3.py b/test cases/common/98 gen extra/srcgen3.py
new file mode 100644
index 0000000..ad0a5cb
--- /dev/null
+++ b/test cases/common/98 gen extra/srcgen3.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import argparse
+
+parser = argparse.ArgumentParser()
+parser.add_argument('input',
+ help='the input file')
+
+options = parser.parse_args(sys.argv[1:])
+
+with open(options.input) as f:
+ content = f.read().strip()
+
+print(content)
diff --git a/test cases/failing/60 assign custom target index/meson.build b/test cases/failing/60 assign custom target index/meson.build
new file mode 100644
index 0000000..7f2a820
--- /dev/null
+++ b/test cases/failing/60 assign custom target index/meson.build
@@ -0,0 +1,24 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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.
+
+prog_python = import('python3').find_python()
+
+target = custom_target(
+ 'target',
+ output : ['1', '2'],
+ command : [prog_python, '-c',
+ 'with open("1", "w") as f: f.write("foo"); with open("2", "w") as f: f.write("foo")'],
+)
+
+target[0] = 'foo'
diff --git a/test cases/frameworks/15 llvm/meson.build b/test cases/frameworks/15 llvm/meson.build
index af7f8c6..e1d97cb 100644
--- a/test cases/frameworks/15 llvm/meson.build
+++ b/test cases/frameworks/15 llvm/meson.build
@@ -10,8 +10,7 @@ llvm_dep = dependency(
d = dependency('llvm', modules : 'not-found', required : false)
assert(d.found() == false, 'not-found llvm module found')
-# XXX: Version checks are broken, see FIXME in LLVMDependency
-#d = dependency('llvm', version : '<0.1', required : false)
-#assert(d.found() == false, 'ancient llvm module found')
+d = dependency('llvm', version : '<0.1', required : false)
+assert(d.found() == false, 'ancient llvm module found')
executable('sum', 'sum.c', dependencies : llvm_dep)