diff options
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) |