diff options
124 files changed, 2346 insertions, 777 deletions
@@ -11,7 +11,7 @@ build system. ####Dependencies - [Python](http://python.org) (version 3.4 or newer) - - [Ninja](http://martine.github.com/ninja/) + - [Ninja](https://ninja-build.org) ####Installing from source diff --git a/authors.txt b/authors.txt index 4244917..fbac571 100644 --- a/authors.txt +++ b/authors.txt @@ -40,3 +40,8 @@ Noam Meltzer Vincent Szolnoky Zhe Wang Wim Taymans +Matthias Klumpp +Elliott Sales de Andrade +Patrick Griffis +Iain Lane +Daniel Brendle diff --git a/data/macros.meson b/data/macros.meson index 8aa5555..c89854b 100644 --- a/data/macros.meson +++ b/data/macros.meson @@ -7,6 +7,15 @@ export FCFLAGS="%{optflags} -I%{_fmoddir}" ; \ export LDFLAGS="%{__global_ldflags}" ; \ %__meson %{?1} \\\ - --prefix=%{_prefix} \\\ + --prefix=%{_prefix} \\\ + --libdir=%{_libdir} \\\ + --libexecdir=%{_libexecdir} \\\ + --bindir=%{_bindir} \\\ + --includedir=%{_includedir} \\\ + --datadir=%{_datadir} \\\ + --mandir=%{_mandir} \\\ + --localedir=%{_datadir}/locale \\\ + --sysconfdir=%{_sysconfdir} \\\ --buildtype=plain \ + %{nil} \ } @@ -52,8 +52,8 @@ def unpack(sproj, branch, outdir): return 1 spdir = os.path.split(outdir)[0] ofilename = os.path.join(spdir, config['wrap-file']['source_filename']) - ofile = open(ofilename, 'wb') - ofile.write(us) + with open(ofilename, 'wb') as ofile: + ofile.write(us) if 'lead_directory_missing' in config['wrap-file']: os.mkdir(outdir) shutil.unpack_archive(ofilename, outdir) diff --git a/man/meson.1 b/man/meson.1 index 83ac397..f41a945 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "July 2016" "meson 0.33.0" "User Commands" +.TH MESON "1" "September 2016" "meson 0.34.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/man/mesonconf.1 b/man/mesonconf.1 index 35f8486..4009c58 100644 --- a/man/mesonconf.1 +++ b/man/mesonconf.1 @@ -1,4 +1,4 @@ -.TH MESONCONF "1" "July 2016" "mesonconf 0.33.0" "User Commands" +.TH MESONCONF "1" "September 2016" "mesonconf 0.34.0" "User Commands" .SH NAME mesonconf - a tool to configure Meson builds .SH DESCRIPTION diff --git a/man/mesonintrospect.1 b/man/mesonintrospect.1 index 307b99a..74a006c 100644 --- a/man/mesonintrospect.1 +++ b/man/mesonintrospect.1 @@ -1,4 +1,4 @@ -.TH MESONCONF "1" "July 2016" "mesonintrospect 0.33.0" "User Commands" +.TH MESONCONF "1" "September 2016" "mesonintrospect 0.34.0" "User Commands" .SH NAME mesonintrospect - a tool to extract information about a Meson build .SH DESCRIPTION diff --git a/man/wraptool.1 b/man/wraptool.1 index 26d942f..b2168c3 100644 --- a/man/wraptool.1 +++ b/man/wraptool.1 @@ -1,4 +1,4 @@ -.TH WRAPTOOL "1" "July 2016" "meson 0.33.0" "User Commands" +.TH WRAPTOOL "1" "September 2016" "meson 0.34.0" "User Commands" .SH NAME wraptool - source dependency downloader .SH DESCRIPTION diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 81283dc..6f4501c 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -16,6 +16,7 @@ import os, pickle, re from .. import build from .. import dependencies from .. import mesonlib +from .. import compilers import json import subprocess from ..mesonlib import MesonException @@ -36,7 +37,7 @@ class InstallData(): class ExecutableSerialisation(): def __init__(self, name, fname, cmd_args, env, is_cross, exe_wrapper, - workdir, extra_paths): + workdir, extra_paths, capture): self.name = name self.fname = fname self.cmd_args = cmd_args @@ -45,6 +46,7 @@ class ExecutableSerialisation(): self.exe_runner = exe_wrapper self.workdir = workdir self.extra_paths = extra_paths + self.capture = capture class TestSerialisation: def __init__(self, name, suite, fname, is_cross, exe_wrapper, is_parallel, cmd_args, env, @@ -143,24 +145,34 @@ class Backend(): langlist = {} abs_files = [] result = [] - for src in unity_src: - comp = self.get_compiler_for_source(src, target.is_cross) - language = comp.get_language() - suffix = '.' + comp.get_default_suffix() - if language not in langlist: - outfilename = os.path.join(self.get_target_private_dir_abs(target), target.name + '-unity' + suffix) - outfileabs = os.path.join(self.environment.get_build_dir(), outfilename) - outfileabs_tmp = outfileabs + '.tmp' - abs_files.append(outfileabs) - outfileabs_tmp_dir = os.path.dirname(outfileabs_tmp) - if not os.path.exists(outfileabs_tmp_dir): - os.makedirs(outfileabs_tmp_dir) - outfile = open(outfileabs_tmp, 'w') - langlist[language] = outfile - result.append(outfilename) - ofile = langlist[language] - ofile.write('#include<%s>\n' % src) - [x.close() for x in langlist.values()] + + def init_language_file(language, suffix): + outfilename = os.path.join(self.get_target_private_dir_abs(target), + target.name + '-unity' + suffix) + outfileabs = os.path.join(self.environment.get_build_dir(), + outfilename) + outfileabs_tmp = outfileabs + '.tmp' + abs_files.append(outfileabs) + outfileabs_tmp_dir = os.path.dirname(outfileabs_tmp) + if not os.path.exists(outfileabs_tmp_dir): + os.makedirs(outfileabs_tmp_dir) + result.append(outfilename) + return open(outfileabs_tmp, 'w') + + try: + for src in unity_src: + comp = self.get_compiler_for_source(src, target.is_cross) + language = comp.get_language() + try: + ofile = langlist[language] + except KeyError: + suffix = '.' + comp.get_default_suffix() + ofile = langlist[language] = init_language_file(language, + suffix) + ofile.write('#include<%s>\n' % src) + finally: + for x in langlist.values(): + x.close() [mesonlib.replace_if_different(x, x + '.tmp') for x in abs_files] return result @@ -181,18 +193,25 @@ class Backend(): raise MesonException('Unknown data type in object list.') return obj_list - def serialise_executable(self, exe, cmd_args, workdir, env={}): + def serialise_executable(self, exe, cmd_args, workdir, env={}, + capture=None): import uuid # Can't just use exe.name here; it will likely be run more than once - scratch_file = 'meson_exe_{0}_{1}.dat'.format(exe.name, + if isinstance(exe, (dependencies.ExternalProgram, + build.BuildTarget, build.CustomTarget)): + basename = exe.name + else: + basename = os.path.basename(exe) + scratch_file = 'meson_exe_{0}_{1}.dat'.format(basename, str(uuid.uuid4())[:8]) exe_data = os.path.join(self.environment.get_scratch_dir(), scratch_file) with open(exe_data, 'wb') as f: if isinstance(exe, dependencies.ExternalProgram): exe_fullpath = exe.fullpath + elif isinstance(exe, (build.BuildTarget, build.CustomTarget)): + exe_fullpath = [self.get_target_filename_abs(exe)] else: - exe_fullpath = [os.path.join(self.environment.get_build_dir(), - self.get_target_filename(exe))] + exe_fullpath = [exe] is_cross = self.environment.is_cross_build() and \ self.environment.cross_info.need_cross_compiler() and \ self.environment.cross_info.need_exe_wrapper() @@ -204,21 +223,19 @@ class Backend(): extra_paths = self.determine_windows_extra_paths(exe) else: extra_paths = [] - es = ExecutableSerialisation(exe.name, exe_fullpath, cmd_args, env, + es = ExecutableSerialisation(basename, exe_fullpath, cmd_args, env, is_cross, exe_wrapper, workdir, - extra_paths) + extra_paths, capture) pickle.dump(es, f) return exe_data def serialise_tests(self): test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat') - datafile = open(test_data, 'wb') - self.write_test_file(datafile) - datafile.close() + with open(test_data, 'wb') as datafile: + self.write_test_file(datafile) benchmark_data = os.path.join(self.environment.get_scratch_dir(), 'meson_benchmark_setup.dat') - datafile = open(benchmark_data, 'wb') - self.write_benchmark_file(datafile) - datafile.close() + with open(benchmark_data, 'wb') as datafile: + self.write_benchmark_file(datafile) return (test_data, benchmark_data) def has_source_suffix(self, target, suffix): @@ -239,6 +256,9 @@ class Backend(): def has_swift(self, target): return self.has_source_suffix(target, '.swift') + def has_d(self, target): + return self.has_source_suffix(target, '.d') + def determine_linker(self, target, src): if isinstance(target, build.StaticLibrary): if self.build.static_cross_linker is not None: @@ -361,10 +381,12 @@ class Backend(): def build_target_link_arguments(self, compiler, deps): args = [] for d in deps: - if not isinstance(d, build.StaticLibrary) and\ - not isinstance(d, build.SharedLibrary): + if not isinstance(d, (build.StaticLibrary, build.SharedLibrary)): raise RuntimeError('Tried to link with a non-library target "%s".' % d.get_basename()) - args.append(self.get_target_filename_for_linking(d)) + if isinstance(compiler, compilers.LLVMDCompiler): + args.extend(['-L', self.get_target_filename_for_linking(d)]) + else: + args.append(self.get_target_filename_for_linking(d)) # If you have executable e that links to shared lib s1 that links to shared library s2 # you have to specify s2 as well as s1 when linking e even if e does not directly use # s2. Gcc handles this case fine but Clang does not for some reason. Thus we need to @@ -434,7 +456,8 @@ class Backend(): mfobj = {'type': 'dependency manifest', 'version': '1.0'} mfobj['projects'] = self.build.dep_manifest - open(ifilename, 'w').write(json.dumps(mfobj)) + with open(ifilename, 'w') as f: + f.write(json.dumps(mfobj)) d.data.append([ifilename, ofilename]) def get_regen_filelist(self): @@ -519,7 +542,7 @@ class Backend(): i = i.held_object if isinstance(i, str): fname = [os.path.join(self.build_to_src, target.subdir, i)] - elif isinstance(i, build.BuildTarget): + elif isinstance(i, (build.BuildTarget, build.CustomTarget)): fname = [self.get_target_filename(i)] elif isinstance(i, build.GeneratedList): fname = [os.path.join(self.get_target_private_dir(target), p) for p in i.get_outfilelist()] @@ -539,7 +562,7 @@ class Backend(): tmp = i.get_filename()[0] i = os.path.join(self.get_target_dir(i), tmp) elif isinstance(i, mesonlib.File): - i = os.path.join(i.subdir, i.fname) + i = i.rel_to_builddir(self.build_to_src) if absolute_paths: i = os.path.join(self.environment.get_build_dir(), i) # FIXME: str types are blindly added and ignore the 'absolute_paths' argument @@ -557,6 +580,14 @@ class Backend(): else: if '@OUTDIR@' in i: i = i.replace('@OUTDIR@', outdir) + elif '@DEPFILE@' in i: + if target.depfile is None: + raise MesonException('Custom target %s has @DEPFILE@ but no depfile keyword argument.' % target.name) + if absolute_paths: + dfilename = os.path.join(self.get_target_private_dir_abs(target), target.depfile) + else: + dfilename = os.path.join(self.get_target_private_dir(target), target.depfile) + i = i.replace('@DEPFILE@', dfilename) elif '@PRIVATE_OUTDIR_' in i: match = re.search('@PRIVATE_OUTDIR_(ABS_)?([-a-zA-Z0-9.@:]*)@', i) source = match.group(0) @@ -564,12 +595,10 @@ class Backend(): lead_dir = '' else: lead_dir = self.environment.get_build_dir() - target_id = match.group(2) i = i.replace(source, os.path.join(lead_dir, outdir)) cmd.append(i) - cmd = [i.replace('\\', '/') for i in cmd] return (srcs, ofilenames, cmd) def run_postconf_scripts(self): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index e00fe2f..db9e1b0 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -133,17 +133,18 @@ class NinjaBackend(backends.Backend): self.all_outputs = {} self.valgrind = environment.find_valgrind() - def detect_vs_dep_prefix(self, outfile, tempfilename): + def detect_vs_dep_prefix(self, tempfilename): '''VS writes its dependency in a locale dependent format. Detect the search prefix to use.''' # Of course there is another program called 'cl' on # some platforms. Let's just require that on Windows # cl points to msvc. if not mesonlib.is_windows() or shutil.which('cl') is None: - return outfile - outfile.close() - open(os.path.join(self.environment.get_scratch_dir(), 'incdetect.c'), - 'w').write('''#include<stdio.h> + return open(tempfilename, 'a') + filename = os.path.join(self.environment.get_scratch_dir(), + 'incdetect.c') + with open(filename, 'w') as f: + f.write('''#include<stdio.h> int dummy; ''') @@ -157,9 +158,8 @@ int dummy; for line in stdo.split(b'\r\n'): if line.endswith(b'stdio.h'): matchstr = b':'.join(line.split(b':')[0:2]) + b':' - binfile = open(tempfilename, 'ab') - binfile.write(b'msvc_deps_prefix = ' + matchstr + b'\r\n') - binfile.close() + with open(tempfilename, 'ab') as binfile: + binfile.write(b'msvc_deps_prefix = ' + matchstr + b'\r\n') return open(tempfilename, 'a') raise MesonException('Could not determine vs dep dependency prefix string.') @@ -167,30 +167,31 @@ int dummy; self.interpreter = interp outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename) tempfilename = outfilename + '~' - outfile = open(tempfilename, 'w') - outfile.write('# This is the build file for project "%s"\n' % self.build.get_project()) - outfile.write('# It is autogenerated by the Meson build system.\n') - outfile.write('# Do not edit by hand.\n\n') - outfile.write('ninja_required_version = 1.5.1\n\n') - outfile = self.detect_vs_dep_prefix(outfile, tempfilename) - self.generate_rules(outfile) - self.generate_phony(outfile) - outfile.write('# Build rules for targets\n\n') - [self.generate_target(t, outfile) for t in self.build.get_targets().values()] - outfile.write('# Test rules\n\n') - self.generate_tests(outfile) - outfile.write('# Install rules\n\n') - self.generate_install(outfile) - if 'b_coverage' in self.environment.coredata.base_options and \ - self.environment.coredata.base_options['b_coverage'].value: - outfile.write('# Coverage rules\n\n') - self.generate_coverage_rules(outfile) - outfile.write('# Suffix\n\n') - self.generate_utils(outfile) - self.generate_ending(outfile) + with open(tempfilename, 'w') as outfile: + outfile.write('# This is the build file for project "%s"\n' % + self.build.get_project()) + outfile.write('# It is autogenerated by the Meson build system.\n') + outfile.write('# Do not edit by hand.\n\n') + outfile.write('ninja_required_version = 1.5.1\n\n') + with self.detect_vs_dep_prefix(tempfilename) as outfile: + self.generate_rules(outfile) + self.generate_phony(outfile) + outfile.write('# Build rules for targets\n\n') + for t in self.build.get_targets().values(): + self.generate_target(t, outfile) + outfile.write('# Test rules\n\n') + self.generate_tests(outfile) + outfile.write('# Install rules\n\n') + self.generate_install(outfile) + if 'b_coverage' in self.environment.coredata.base_options and \ + self.environment.coredata.base_options['b_coverage'].value: + outfile.write('# Coverage rules\n\n') + self.generate_coverage_rules(outfile) + outfile.write('# Suffix\n\n') + self.generate_utils(outfile) + self.generate_ending(outfile) # Only ovewrite the old build file after the new one has been # fully created. - outfile.close() os.replace(tempfilename, outfilename) self.generate_compdb() @@ -202,7 +203,8 @@ int dummy; jsondb = subprocess.check_output([ninja_exe, '-t', 'compdb', 'c_COMPILER', 'cpp_COMPILER'], cwd=builddir) except Exception: raise MesonException('Could not create compilation database.') - open(os.path.join(builddir, 'compile_commands.json'), 'wb').write(jsondb) + with open(os.path.join(builddir, 'compile_commands.json'), 'wb') as f: + f.write(jsondb) # Get all generated headers. Any source file might need them so # we need to add an order dependency to them. @@ -366,7 +368,11 @@ int dummy; desc = 'Generating {0} with a {1} command.' if target.build_always: deps.append('PHONY') - elem = NinjaBuildElement(self.all_outputs, ofilenames, 'CUSTOM_COMMAND', srcs) + if target.depfile is None: + rulename = 'CUSTOM_COMMAND' + else: + rulename = 'CUSTOM_COMMAND_DEP' + elem = NinjaBuildElement(self.all_outputs, ofilenames, rulename, srcs) for i in target.depend_files: if isinstance(i, mesonlib.File): deps.append(i.rel_to_builddir(self.build_to_src)) @@ -379,21 +385,30 @@ int dummy; tmp = [tmp] for fname in tmp: elem.add_dep(os.path.join(self.get_target_dir(d), fname)) + # If the target requires capturing stdout, then use the serialized + # executable wrapper to capture that output and save it to a file. + # # Windows doesn't have -rpath, so for EXEs that need DLLs built within # the project, we need to set PATH so the DLLs are found. We use # a serialized executable wrapper for that and check if the # CustomTarget command needs extra paths first. - if mesonlib.is_windows() and \ - self.determine_windows_extra_paths(target.command[0]): + if target.capture or (mesonlib.is_windows() and + self.determine_windows_extra_paths(target.command[0])): exe_data = self.serialise_executable(target.command[0], cmd[1:], # All targets are built from the build dir - self.environment.get_build_dir()) + self.environment.get_build_dir(), + capture=ofilenames[0] if target.capture else None) cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'exe', exe_data] cmd_type = 'meson_exe.py custom' else: cmd_type = 'custom' + if target.depfile is not None: + rel_dfile = os.path.join(self.get_target_private_dir(target), target.depfile) + abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target)) + os.makedirs(abs_pdir, exist_ok=True) + elem.add_item('DEPFILE', rel_dfile) elem.add_item('COMMAND', cmd) elem.add_item('description', desc.format(target.name, cmd_type)) elem.write(outfile) @@ -409,11 +424,13 @@ int dummy; elif isinstance(i, (build.BuildTarget, build.CustomTarget)): relfname = self.get_target_filename(i) arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname)) + deps.append(relfname) + elif isinstance(i, mesonlib.File): + arg_strings.append(i.rel_to_builddir(self.build_to_src)) else: mlog.debug(str(i)) raise MesonException('Unreachable code in generate_run_target.') - elem = NinjaBuildElement(self.all_outputs, target.name, 'CUSTOM_COMMAND', deps) - elem.add_dep(deps) + elem = NinjaBuildElement(self.all_outputs, target.name, 'CUSTOM_COMMAND', []) cmd = runnerscript + [self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir] texe = target.command try: @@ -431,9 +448,13 @@ int dummy; cmd.append(abs_exe) elif isinstance(texe, dependencies.ExternalProgram): cmd += texe.get_command() + elif isinstance(texe, build.CustomTarget): + deps.append(self.get_target_filename(texe)) + cmd += [os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe))] else: cmd.append(target.command) cmd += arg_strings + elem.add_dep(deps) elem.add_item('COMMAND', cmd) elem.add_item('description', 'Running external command %s.' % target.name) elem.add_item('pool', 'console') @@ -492,8 +513,8 @@ int dummy; self.generate_subdir_install(d) elem.write(outfile) - ofile = open(install_data_file, 'wb') - pickle.dump(d, ofile) + with open(install_data_file, 'wb') as ofile: + pickle.dump(d, ofile) def generate_target_install(self, d): should_strip = self.environment.coredata.get_builtin_option('strip') @@ -643,7 +664,6 @@ int dummy; velem.write(outfile) # And then benchmarks. - benchmark_script = os.path.join(script_root, 'meson_benchmark.py') cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'benchmark', benchmark_data] elem = NinjaBuildElement(self.all_outputs, 'benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) @@ -664,6 +684,14 @@ int dummy; outfile.write(' command = $COMMAND\n') outfile.write(' description = $DESC\n') outfile.write(' restat = 1\n\n') + # Ninja errors out if you have deps = gcc but no depfile, so we must + # have two rules for custom commands. + outfile.write('rule CUSTOM_COMMAND_DEP\n') + outfile.write(' command = $COMMAND\n') + outfile.write(' description = $DESC\n') + outfile.write(' deps = gcc\n') + outfile.write(' depfile = $DEPFILE\n') + outfile.write(' restat = 1\n\n') outfile.write('rule REGENERATE_BUILD\n') c = (quote_char + ninja_quote(sys.executable) + quote_char, quote_char + ninja_quote(self.environment.get_build_command()) + quote_char, @@ -1358,8 +1386,16 @@ rule FORTRAN_DEP_HACK infilename = os.path.join(self.build_to_src, curfile) outfiles = genlist.get_outputs_for(curfile) outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles] + if generator.depfile is None: + rulename = 'CUSTOM_COMMAND' + args = base_args + else: + rulename = 'CUSTOM_COMMAND_DEP' + depfilename = generator.get_dep_outname(infilename) + depfile = os.path.join(self.get_target_private_dir(target), depfilename) + args = [x.replace('@DEPFILE@', depfile) for x in base_args] args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\ - for x in base_args] + for x in args] args = self.replace_outputs(args, self.get_target_private_dir(target), outfilelist) # We have consumed output files, so drop them from the list of remaining outputs. if sole_output == '': @@ -1368,7 +1404,9 @@ rule FORTRAN_DEP_HACK args = [x.replace("@SOURCE_DIR@", self.build_to_src).replace("@BUILD_DIR@", relout) for x in args] cmdlist = exe_arr + self.replace_extra_args(args, genlist) - elem = NinjaBuildElement(self.all_outputs, outfiles, 'CUSTOM_COMMAND', infilename) + elem = NinjaBuildElement(self.all_outputs, outfiles, rulename, infilename) + if generator.depfile is not None: + elem.add_item('DEPFILE', depfile) if len(extra_dependencies) > 0: elem.add_dep(extra_dependencies) elem.add_item('DESC', 'Generating $out') @@ -1393,16 +1431,22 @@ rule FORTRAN_DEP_HACK # but those are really rare. I hope. if not compiler.can_compile(s): continue - for line in open(os.path.join(self.environment.get_source_dir(), s.subdir, s.fname)): - modmatch = modre.match(line) - if modmatch is not None: - modname = modmatch.group(1) - if modname.lower() == 'procedure': # MODULE PROCEDURE construct - continue - if modname in module_files: - raise InvalidArguments('Namespace collision: module %s defined in two files %s and %s.' % - (modname, module_files[modname], s)) - module_files[modname] = s + filename = os.path.join(self.environment.get_source_dir(), + s.subdir, s.fname) + with open(filename) as f: + for line in f: + modmatch = modre.match(line) + if modmatch is not None: + modname = modmatch.group(1) + if modname.lower() == 'procedure': + # MODULE PROCEDURE construct + continue + if modname in module_files: + raise InvalidArguments( + 'Namespace collision: module %s defined in ' + 'two files %s and %s.' % + (modname, module_files[modname], s)) + module_files[modname] = s self.fortran_deps[target.get_basename()] = module_files def get_fortran_deps(self, compiler, src, target): @@ -1410,27 +1454,32 @@ rule FORTRAN_DEP_HACK usere = re.compile(r"\s*use\s+(\w+)", re.IGNORECASE) dirname = self.get_target_private_dir(target) tdeps= self.fortran_deps[target.get_basename()] - for line in open(src): - usematch = usere.match(line) - if usematch is not None: - usename = usematch.group(1) - if usename not in tdeps: - # The module is not provided by any source file. This is due to - # a) missing file/typo/etc - # b) using a module provided by the compiler, such as OpenMP - # There's no easy way to tell which is which (that I know of) - # so just ignore this and go on. Ideally we would print a - # warning message to the user but this is a common occurrance, - # which would lead to lots of distracting noise. - continue - mod_source_file = tdeps[usename] - # Check if a source uses a module it exports itself. - # Potential bug if multiple targets have a file with - # the same name. - if mod_source_file.fname == os.path.split(src)[1]: - continue - mod_name = compiler.module_name_to_filename(usematch.group(1)) - mod_files.append(os.path.join(dirname, mod_name)) + with open(src) as f: + for line in f: + usematch = usere.match(line) + if usematch is not None: + usename = usematch.group(1) + if usename not in tdeps: + # The module is not provided by any source file. This + # is due to: + # a) missing file/typo/etc + # b) using a module provided by the compiler, such as + # OpenMP + # There's no easy way to tell which is which (that I + # know of) so just ignore this and go on. Ideally we + # would print a warning message to the user but this is + # a common occurrence, which would lead to lots of + # distracting noise. + continue + mod_source_file = tdeps[usename] + # Check if a source uses a module it exports itself. + # Potential bug if multiple targets have a file with + # the same name. + if mod_source_file.fname == os.path.split(src)[1]: + continue + mod_name = compiler.module_name_to_filename( + usematch.group(1)) + mod_files.append(os.path.join(dirname, mod_name)) return mod_files def get_cross_stdlib_args(self, target, compiler): @@ -1555,9 +1604,7 @@ rule FORTRAN_DEP_HACK else: raise build.InvalidArguments('Invalid source type.') abs_src = os.path.join(self.environment.get_build_dir(), rel_src) - if isinstance(src, RawFilename): - src_filename = src.fname - elif isinstance(src, File): + if isinstance(src, (RawFilename, File)): src_filename = src.fname elif os.path.isabs(src): src_filename = os.path.basename(src) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 669bcf8..eca7473 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -175,14 +175,18 @@ class Vs2010Backend(backends.Backend): @staticmethod def touch_regen_timestamp(build_dir): - open(Vs2010Backend.get_regen_stampfile(build_dir), 'w').close() + with open(Vs2010Backend.get_regen_stampfile(build_dir), 'w'): + pass def generate_regen_info(self): deps = self.get_regen_filelist() regeninfo = RegenInfo(self.environment.get_source_dir(), self.environment.get_build_dir(), deps) - pickle.dump(regeninfo, open(os.path.join(self.environment.get_scratch_dir(), 'regeninfo.dump'), 'wb')) + filename = os.path.join(self.environment.get_scratch_dir(), + 'regeninfo.dump') + with open(filename, 'wb') as f: + pickle.dump(regeninfo, f) def get_obj_target_deps(self, obj_list): result = {} @@ -217,57 +221,66 @@ class Vs2010Backend(backends.Backend): return all_deps def generate_solution(self, sln_filename, projlist): - ofile = open(sln_filename, 'w') - ofile.write('Microsoft Visual Studio Solution File, Format Version 11.00\n') - ofile.write('# Visual Studio ' + self.vs_version + '\n') - prj_templ = prj_line = 'Project("{%s}") = "%s", "%s", "{%s}"\n' - for p in projlist: - prj_line = prj_templ % (self.environment.coredata.guid, p[0], p[1], p[2]) - ofile.write(prj_line) - all_deps = self.determine_deps(p) - ofile.write('\tProjectSection(ProjectDependencies) = postProject\n') - regen_guid = self.environment.coredata.regen_guid - ofile.write('\t\t{%s} = {%s}\n' % (regen_guid, regen_guid)) - for dep in all_deps.keys(): - guid = self.environment.coredata.target_guids[dep] - ofile.write('\t\t{%s} = {%s}\n' % (guid, guid)) - ofile.write('EndProjectSection\n') + with open(sln_filename, 'w') as ofile: + ofile.write('Microsoft Visual Studio Solution File, Format ' + 'Version 11.00\n') + ofile.write('# Visual Studio ' + self.vs_version + '\n') + prj_templ = prj_line = 'Project("{%s}") = "%s", "%s", "{%s}"\n' + for p in projlist: + prj_line = prj_templ % (self.environment.coredata.guid, + p[0], p[1], p[2]) + ofile.write(prj_line) + all_deps = self.determine_deps(p) + ofile.write('\tProjectSection(ProjectDependencies) = ' + 'postProject\n') + regen_guid = self.environment.coredata.regen_guid + ofile.write('\t\t{%s} = {%s}\n' % (regen_guid, regen_guid)) + for dep in all_deps.keys(): + guid = self.environment.coredata.target_guids[dep] + ofile.write('\t\t{%s} = {%s}\n' % (guid, guid)) + ofile.write('EndProjectSection\n') + ofile.write('EndProject\n') + test_line = prj_templ % (self.environment.coredata.guid, + 'RUN_TESTS', 'RUN_TESTS.vcxproj', + self.environment.coredata.test_guid) + ofile.write(test_line) ofile.write('EndProject\n') - test_line = prj_templ % (self.environment.coredata.guid, - 'RUN_TESTS', 'RUN_TESTS.vcxproj', self.environment.coredata.test_guid) - ofile.write(test_line) - ofile.write('EndProject\n') - regen_line = prj_templ % (self.environment.coredata.guid, - 'REGEN', 'REGEN.vcxproj', self.environment.coredata.regen_guid) - ofile.write(regen_line) - ofile.write('EndProject\n') - ofile.write('Global\n') - ofile.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') - ofile.write('\t\t%s|%s = %s|%s\n' % (self.buildtype, self.platform, self.buildtype, self.platform)) - ofile.write('\tEndGlobalSection\n') - ofile.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') - ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % - (self.environment.coredata.regen_guid, self.buildtype, self.platform, - self.buildtype, self.platform)) - ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % - (self.environment.coredata.regen_guid, self.buildtype, self.platform, - self.buildtype, self.platform)) - for p in projlist: + regen_line = prj_templ % (self.environment.coredata.guid, + 'REGEN', 'REGEN.vcxproj', + self.environment.coredata.regen_guid) + ofile.write(regen_line) + ofile.write('EndProject\n') + ofile.write('Global\n') + ofile.write('\tGlobalSection(SolutionConfigurationPlatforms) = ' + 'preSolution\n') + ofile.write('\t\t%s|%s = %s|%s\n' % + (self.buildtype, self.platform, self.buildtype, + self.platform)) + ofile.write('\tEndGlobalSection\n') + ofile.write('\tGlobalSection(ProjectConfigurationPlatforms) = ' + 'postSolution\n') ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % - (p[2], self.buildtype, self.platform, - self.buildtype, self.platform)) - if not isinstance(self.build.targets[p[0]], build.RunTarget): - ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % + (self.environment.coredata.regen_guid, self.buildtype, + self.platform, self.buildtype, self.platform)) + ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % + (self.environment.coredata.regen_guid, self.buildtype, + self.platform, self.buildtype, self.platform)) + for p in projlist: + ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % (p[2], self.buildtype, self.platform, self.buildtype, self.platform)) - ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % - (self.environment.coredata.test_guid, self.buildtype, self.platform, - self.buildtype, self.platform)) - ofile.write('\tEndGlobalSection\n') - ofile.write('\tGlobalSection(SolutionProperties) = preSolution\n') - ofile.write('\t\tHideSolutionNode = FALSE\n') - ofile.write('\tEndGlobalSection\n') - ofile.write('EndGlobal\n') + if not isinstance(self.build.targets[p[0]], build.RunTarget): + ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % + (p[2], self.buildtype, self.platform, + self.buildtype, self.platform)) + ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % + (self.environment.coredata.test_guid, self.buildtype, + self.platform, self.buildtype, self.platform)) + ofile.write('\tEndGlobalSection\n') + ofile.write('\tGlobalSection(SolutionProperties) = preSolution\n') + ofile.write('\t\tHideSolutionNode = FALSE\n') + ofile.write('\tEndGlobalSection\n') + ofile.write('EndGlobal\n') def generate_projects(self): projlist = [] @@ -862,12 +875,15 @@ class Vs2010Backend(backends.Backend): tree.write(ofname, encoding='utf-8', xml_declaration=True) # ElementTree can not do prettyprinting so do it manually doc = xml.dom.minidom.parse(ofname) - open(ofname, 'w').write(doc.toprettyxml()) + with open(ofname, 'w') as of: + of.write(doc.toprettyxml()) # World of horror! Python insists on not quoting quotes and # fixing the escaped " into &quot; whereas MSVS # requires quoted but not fixed elements. Enter horrible hack. - txt = open(ofname, 'r').read() - open(ofname, 'w').write(txt.replace('&quot;', '"')) + with open(ofname, 'r') as of: + txt = of.read() + with open(ofname, 'w') as of: + of.write(txt.replace('&quot;', '"')) def gen_regenproj(self, project_name, ofname): root = ET.Element('Project', {'DefaultTargets': 'Build', diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index e64866d..b157741 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -82,26 +82,22 @@ class XCodeBackend(backends.Backend): self.proj_dir = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.xcodeproj') os.makedirs(self.proj_dir, exist_ok=True) self.proj_file = os.path.join(self.proj_dir, 'project.pbxproj') - self.ofile = open(self.proj_file, 'w') - self.generate_prefix() - self.generate_pbx_aggregate_target() - self.generate_pbx_build_file() - self.generate_pbx_build_style() - self.generate_pbx_container_item_proxy() - self.generate_pbx_file_reference() - self.generate_pbx_group() - self.generate_pbx_native_target() - self.generate_pbx_project() - self.generate_pbx_shell_build_phase(test_data) - self.generate_pbx_sources_build_phase() - self.generate_pbx_target_dependency() - self.generate_xc_build_configuration() - self.generate_xc_configurationList() - self.generate_suffix() - - # for some reason, the entire file was not being flushed to the disk. - # closing it explicitly forces a flush and fixes the issue - self.ofile.close() + with open(self.proj_file, 'w') as self.ofile: + self.generate_prefix() + self.generate_pbx_aggregate_target() + self.generate_pbx_build_file() + self.generate_pbx_build_style() + self.generate_pbx_container_item_proxy() + self.generate_pbx_file_reference() + self.generate_pbx_group() + self.generate_pbx_native_target() + self.generate_pbx_project() + self.generate_pbx_shell_build_phase(test_data) + self.generate_pbx_sources_build_phase() + self.generate_pbx_target_dependency() + self.generate_xc_build_configuration() + self.generate_xc_configurationList() + self.generate_suffix() def get_xcodetype(self, fname): return self.xcodetypemap[fname.split('.')[-1]] diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6dd2bde..f95922e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -17,7 +17,7 @@ from . import environment from . import dependencies from . import mlog import copy, os, re -from .mesonlib import File, flatten, MesonException +from .mesonlib import File, flatten, MesonException, stringlistify from .environment import for_windows, for_darwin known_basic_kwargs = {'install' : True, @@ -27,6 +27,7 @@ known_basic_kwargs = {'install' : True, 'cpp_args' : True, 'cs_args' : True, 'vala_args' : True, + 'd_args' : True, 'link_args' : True, 'link_depends': True, 'link_with' : True, @@ -254,11 +255,9 @@ class BuildTarget(): for s in objects: if hasattr(s, 'held_object'): s = s.held_object - if isinstance(s, str): + if isinstance(s, (str, ExtractedObjects)): self.objects.append(s) - elif isinstance(s, ExtractedObjects): - self.objects.append(s) - elif isinstance(s, GeneratedList) or isinstance(s, CustomTarget): + elif isinstance(s, (GeneratedList, CustomTarget)): msg = 'Generated files are not allowed in the \'objects\' kwarg ' + \ 'for target {!r}.\nIt is meant only for '.format(self.name) + \ 'pre-built object files that are shipped with the\nsource ' + \ @@ -280,7 +279,7 @@ class BuildTarget(): if not s in added_sources: self.sources.append(s) added_sources[s] = True - elif isinstance(s, GeneratedList) or isinstance(s, CustomTarget): + elif isinstance(s, (GeneratedList, CustomTarget)): self.generated.append(s) else: msg = 'Bad source of type {!r} in target {!r}.'.format(type(s).__name__, self.name) @@ -387,6 +386,8 @@ class BuildTarget(): if not isinstance(valalist, list): valalist = [valalist] self.add_compiler_args('vala', valalist) + dlist = stringlistify(kwargs.get('d_args', [])) + self.add_compiler_args('d', dlist) self.link_args = kwargs.get('link_args', []) if not isinstance(self.link_args, list): self.link_args = [self.link_args] @@ -543,7 +544,14 @@ class BuildTarget(): self.external_deps.append(dep) self.process_sourcelist(dep.get_sources()) else: - raise InvalidArguments('Argument is not an external dependency') + # This is a bit of a hack. We do not want Build to know anything + # about the interpreter so we can't import it and use isinstance. + # This should be reliable enough. + if hasattr(dep, 'subproject'): + raise InvalidArguments('''Tried to use subproject object as a dependency. +You probably wanted to use a dependency declared in it instead. Access it +by calling get_variable() on the subproject object.''') + raise InvalidArguments('Argument is not an external dependency.') def get_external_deps(self): return self.external_deps @@ -554,8 +562,7 @@ class BuildTarget(): for t in target: if hasattr(t, 'held_object'): t = t.held_object - if not isinstance(t, StaticLibrary) and \ - not isinstance(t, SharedLibrary): + if not isinstance(t, (StaticLibrary, SharedLibrary)): raise InvalidArguments('Link target is not library.') if self.is_cross != t.is_cross: raise InvalidArguments('Tried to mix cross built and native libraries in target %s.' % self.name) @@ -620,9 +627,10 @@ class Generator(): exe = args[0] if hasattr(exe, 'held_object'): exe = exe.held_object - if not isinstance(exe, Executable) and not isinstance(exe, dependencies.ExternalProgram): + if not isinstance(exe, (Executable, dependencies.ExternalProgram)): raise InvalidArguments('First generator argument must be an executable.') self.exe = exe + self.depfile = None self.process_kwargs(kwargs) def __repr__(self): @@ -644,7 +652,6 @@ class Generator(): if not isinstance(a, str): raise InvalidArguments('A non-string object in "arguments" keyword argument.') self.arglist = args - if 'output' not in kwargs: raise InvalidArguments('Generator must have "output" keyword argument.') outputs = kwargs['output'] @@ -662,12 +669,26 @@ class Generator(): if '@OUTPUT@' in o: raise InvalidArguments('Tried to use @OUTPUT@ in a rule with more than one output.') self.outputs = outputs + if 'depfile' in kwargs: + depfile = kwargs['depfile'] + if not isinstance(depfile, str): + raise InvalidArguments('Depfile must be a string.') + if os.path.split(depfile)[1] != depfile: + raise InvalidArguments('Depfile must be a plain filename without a subdirectory.') + self.depfile = depfile def get_base_outnames(self, inname): plainname = os.path.split(inname)[1] basename = plainname.split('.')[0] return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs] + def get_dep_outname(self, inname): + if self.depfile is None: + raise InvalidArguments('Tried to get dep name for rule that does not have dependency file defined.') + plainname = os.path.split(inname)[1] + basename = plainname.split('.')[0] + return self.depfile.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) + def get_arglist(self): return self.arglist @@ -844,12 +865,10 @@ class SharedLibrary(BuildTarget): elif for_darwin(is_cross, env): prefix = 'lib' suffix = 'dylib' - if self.soversion: - # libfoo.X.dylib - self.filename_tpl = '{0.prefix}{0.name}.{0.soversion}.{0.suffix}' - else: - # libfoo.dylib - self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}' + # libfoo.dylib + self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}' + # On OS X, the filename should never have the soversion + # See: https://github.com/mesonbuild/meson/pull/680 else: prefix = 'lib' suffix = 'so' @@ -913,6 +932,8 @@ class SharedLibrary(BuildTarget): self.vs_module_defs = File.from_absolute_file(path) else: self.vs_module_defs = File.from_source_file(environment.source_dir, self.subdir, path) + # link_depends can be an absolute path or relative to self.subdir + self.link_depends.append(path) def check_unknown_kwargs(self, kwargs): self.check_unknown_kwargs_int(kwargs, known_shlib_kwargs) @@ -961,11 +982,13 @@ class CustomTarget: known_kwargs = {'input' : True, 'output' : True, 'command' : True, + 'capture' : False, 'install' : True, 'install_dir' : True, 'build_always' : True, 'depends' : True, 'depend_files' : True, + 'depfile' : True, } def __init__(self, name, subdir, kwargs): @@ -974,6 +997,7 @@ class CustomTarget: self.dependencies = [] self.extra_depends = [] self.depend_files = [] # Files that this target depends on but are not on the command line. + self.depfile = None self.process_kwargs(kwargs) self.extra_files = [] self.install_rpath = '' @@ -998,7 +1022,7 @@ class CustomTarget: for c in self.sources: if hasattr(c, 'held_object'): c = c.held_object - if isinstance(c, BuildTarget) or isinstance(c, CustomTarget) or isinstance(c, GeneratedList): + if isinstance(c, (BuildTarget, CustomTarget, GeneratedList)): deps.append(c) return deps @@ -1016,8 +1040,19 @@ class CustomTarget: raise InvalidArguments('Output argument not a string.') if '/' in i: raise InvalidArguments('Output must not contain a path segment.') + self.capture = kwargs.get('capture', False) + if self.capture and len(self.output) != 1: + raise InvalidArguments( + 'Capturing can only output to a single file.') if 'command' not in kwargs: raise InvalidArguments('Missing keyword argument "command".') + if 'depfile' in kwargs: + depfile = kwargs['depfile'] + if not isinstance(depfile, str): + raise InvalidArguments('Depfile must be a string.') + if os.path.split(depfile)[1] != depfile: + raise InvalidArguments('Depfile must be a plain filename without a subdirectory.') + self.depfile = depfile cmd = kwargs['command'] if not(isinstance(cmd, list)): cmd = [cmd] @@ -1025,13 +1060,13 @@ class CustomTarget: for i, c in enumerate(cmd): if hasattr(c, 'held_object'): c = c.held_object - if isinstance(c, str) or isinstance(c, File): + if isinstance(c, (str, File)): final_cmd.append(c) elif isinstance(c, dependencies.ExternalProgram): if not c.found(): raise InvalidArguments('Tried to use not found external program in a build rule.') final_cmd += c.get_command() - elif isinstance(c, BuildTarget) or isinstance(c, CustomTarget): + elif isinstance(c, (BuildTarget, CustomTarget)): self.dependencies.append(c) final_cmd.append(c) elif isinstance(c, list): @@ -1044,6 +1079,10 @@ class CustomTarget: else: raise InvalidArguments('Argument %s in "command" is invalid.' % i) self.command = final_cmd + if self.capture: + for c in self.command: + if isinstance(c, str) and '@OUTPUT@' in c: + raise InvalidArguments('@OUTPUT@ is not allowed when capturing output.') if 'install' in kwargs: self.install = kwargs['install'] if not isinstance(self.install, bool): @@ -1065,7 +1104,7 @@ class CustomTarget: for ed in extra_deps: while hasattr(ed, 'held_object'): ed = ed.held_object - if not isinstance(ed, CustomTarget) and not isinstance(ed, BuildTarget): + if not isinstance(ed, (CustomTarget, BuildTarget)): raise InvalidArguments('Can only depend on toplevel targets.') self.extra_depends.append(ed) depend_files = kwargs.get('depend_files', []) diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 73f2416..d1f3896 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -23,7 +23,7 @@ from . import coredata about. To support a new compiler, add its information below. Also add corresponding autodetection code in environment.py.""" -header_suffixes = ['h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi'] +header_suffixes = ['h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di'] cpp_suffixes = ['cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'ipp', 'hxx', 'c++'] c_suffixes = ['c'] clike_suffixes = c_suffixes + cpp_suffixes @@ -108,6 +108,27 @@ rust_buildtype_args = {'plain' : [], 'minsize' : [], } +d_gdc_buildtype_args = {'plain' : [], + 'debug' : ['-g', '-O0'], + 'debugoptimized' : ['-g', '-O'], + 'release' : ['-O3', '-frelease'], + 'minsize' : [], + } + +d_ldc_buildtype_args = {'plain' : [], + 'debug' : ['-g', '-O0'], + 'debugoptimized' : ['-g', '-O'], + 'release' : ['-O3', '-release'], + 'minsize' : [], + } + +d_dmd_buildtype_args = {'plain' : [], + 'debug' : ['-g'], + 'debugoptimized' : ['-g', '-O'], + 'release' : ['-O', '-release'], + 'minsize' : [], + } + mono_buildtype_args = {'plain' : [], 'debug' : ['-debug'], 'debugoptimized': ['-debug', '-optimize+'], @@ -525,9 +546,8 @@ class CCompiler(Compiler): binname += '.exe' # Write binary check source binary_name = os.path.join(work_dir, binname) - ofile = open(source_name, 'w') - ofile.write(code) - ofile.close() + with open(source_name, 'w') as ofile: + ofile.write(code) # Compile sanity check cmdlist = self.exelist + extra_flags + [source_name] + self.get_output_args(binary_name) pc = subprocess.Popen(cmdlist, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=work_dir) @@ -604,9 +624,8 @@ int main () {{ {1}; }}''' suflen = len(self.default_suffix) (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix) os.close(fd) - ofile = open(srcname, 'w') - ofile.write(code) - ofile.close() + with open(srcname, 'w') as ofile: + ofile.write(code) # Convert flags to the native type of the selected compiler args = self.unix_link_flags_to_native(extra_args) # Read c_args/cpp_args/etc from the cross-info file (if needed) @@ -634,9 +653,8 @@ int main () {{ {1}; }}''' os.close(fd) (fd, dstname) = tempfile.mkstemp() os.close(fd) - ofile = open(srcname, 'w') - ofile.write(code) - ofile.close() + with open(srcname, 'w') as ofile: + ofile.write(code) # Convert flags to the native type of the selected compiler args = self.unix_link_flags_to_native(extra_args) # Select a CRT if needed since we're linking @@ -659,9 +677,8 @@ int main () {{ {1}; }}''' raise CrossNoRunException('Can not run test applications in this cross environment.') (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix) os.close(fd) - ofile = open(srcname, 'w') - ofile.write(code) - ofile.close() + with open(srcname, 'w') as ofile: + ofile.write(code) # Convert flags to the native type of the selected compiler args = self.unix_link_flags_to_native(extra_args) # Select a CRT if needed since we're linking @@ -889,16 +906,21 @@ int main(int argc, char **argv) { # directly try to link via main(). return self.links('int main() {{ {0}; }}'.format('__builtin_' + funcname), env, args) - def has_member(self, typename, membername, prefix, env, extra_args=None): + def has_members(self, typename, membernames, prefix, env, extra_args=None): if extra_args is None: extra_args = [] - templ = '''%s -void bar() { - %s foo; - foo.%s; -}; + templ = '''{0} +void bar() {{ + {1} {2}; + {3} +}}; ''' - return self.compiles(templ % (prefix, typename, membername), env, extra_args) + # Create code that accesses all members + members = '' + for m in membernames: + members += 'foo.{};\n'.format(m) + code = templ.format(prefix, typename, 'foo', members) + return self.compiles(code, env, extra_args) def has_type(self, typename, prefix, env, extra_args): templ = '''%s @@ -979,9 +1001,9 @@ class ObjCCompiler(CCompiler): extra_flags = self.get_cross_extra_flags(environment, compile=True, link=False) if self.is_cross: extra_flags += self.get_compile_only_args() - ofile = open(source_name, 'w') - ofile.write('#import<stdio.h>\nint main(int argc, char **argv) { return 0; }\n') - ofile.close() + with open(source_name, 'w') as ofile: + ofile.write('#import<stdio.h>\n' + 'int main(int argc, char **argv) { return 0; }\n') pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) pc.wait() if pc.returncode != 0: @@ -1013,9 +1035,10 @@ class ObjCPPCompiler(CPPCompiler): extra_flags = self.get_cross_extra_flags(environment, compile=True, link=False) if self.is_cross: extra_flags += self.get_compile_only_args() - ofile = open(source_name, 'w') - ofile.write('#import<stdio.h>\nclass MyClass;int main(int argc, char **argv) { return 0; }\n') - ofile.close() + with open(source_name, 'w') as ofile: + ofile.write('#import<stdio.h>\n' + 'class MyClass;' + 'int main(int argc, char **argv) { return 0; }\n') pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) pc.wait() if pc.returncode != 0: @@ -1115,13 +1138,12 @@ class MonoCompiler(Compiler): src = 'sanity.cs' obj = 'sanity.exe' source_name = os.path.join(work_dir, src) - ofile = open(source_name, 'w') - ofile.write('''public class Sanity { + with open(source_name, 'w') as ofile: + ofile.write('''public class Sanity { static public void Main () { } } ''') - ofile.close() pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) pc.wait() if pc.returncode != 0: @@ -1227,14 +1249,13 @@ class JavaCompiler(Compiler): src = 'SanityCheck.java' obj = 'SanityCheck' source_name = os.path.join(work_dir, src) - ofile = open(source_name, 'w') - ofile.write('''class SanityCheck { + with open(source_name, 'w') as ofile: + ofile.write('''class SanityCheck { public static void main(String[] args) { int i; } } ''') - ofile.close() pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) pc.wait() if pc.returncode != 0: @@ -1274,11 +1295,10 @@ class ValaCompiler(Compiler): def sanity_check(self, work_dir, environment): src = 'valatest.vala' source_name = os.path.join(work_dir, src) - ofile = open(source_name, 'w') - ofile.write('''class SanityCheck : Object { + with open(source_name, 'w') as ofile: + ofile.write('''class SanityCheck : Object { } ''') - ofile.close() extra_flags = self.get_cross_extra_flags(environment, compile=True, link=False) pc = subprocess.Popen(self.exelist + extra_flags + ['-C', '-c', src], cwd=work_dir) pc.wait() @@ -1318,11 +1338,10 @@ class RustCompiler(Compiler): def sanity_check(self, work_dir, environment): source_name = os.path.join(work_dir, 'sanity.rs') output_name = os.path.join(work_dir, 'rusttest') - ofile = open(source_name, 'w') - ofile.write('''fn main() { + with open(source_name, 'w') as ofile: + ofile.write('''fn main() { } ''') - ofile.close() pc = subprocess.Popen(self.exelist + ['-o', output_name, source_name], cwd=work_dir) pc.wait() if pc.returncode != 0: @@ -1417,10 +1436,9 @@ class SwiftCompiler(Compiler): src = 'swifttest.swift' source_name = os.path.join(work_dir, src) output_name = os.path.join(work_dir, 'swifttest') - ofile = open(source_name, 'w') - ofile.write('''1 + 2 + with open(source_name, 'w') as ofile: + ofile.write('''1 + 2 ''') - ofile.close() extra_flags = self.get_cross_extra_flags(environment, compile=True, link=True) pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir) pc.wait() @@ -1433,6 +1451,268 @@ class SwiftCompiler(Compiler): suffix = filename.split('.')[-1] return suffix in ('swift') +class DCompiler(Compiler): + def __init__(self, exelist, version, is_cross): + super().__init__(exelist, version) + self.id = 'unknown' + self.language = 'd' + self.is_cross = is_cross + + def sanity_check(self, work_dir, environment): + source_name = os.path.join(work_dir, 'sanity.d') + output_name = os.path.join(work_dir, 'dtest') + with open(source_name, 'w') as ofile: + ofile.write('''void main() { +} +''') + pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + [source_name], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string()) + if subprocess.call(output_name) != 0: + raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string()) + + def needs_static_linker(self): + return True + + def name_string(self): + return ' '.join(self.exelist) + + def get_exelist(self): + return self.exelist + + def get_id(self): + return self.id + + def get_language(self): + return self.language + + def can_compile(self, fname): + suffix = fname.split('.')[-1] + return suffix in ('d', 'di') + + def get_linker_exelist(self): + return self.exelist[:] + + def depfile_for_object(self, objfile): + return objfile + '.' + self.get_depfile_suffix() + + def get_depfile_suffix(self): + return 'dep' + + def get_pic_args(self): + return ['-fPIC'] + + def get_std_shared_lib_link_args(self): + return ['-shared'] + + def get_soname_args(self, shlib_name, path, soversion): + return [] + + def get_unittest_args(self): + return ['-unittest'] + + def get_buildtype_linker_args(self, buildtype): + return [] + + def get_std_exe_link_args(self): + return [] + + def build_rpath_args(self, build_dir, rpath_paths, install_rpath): + # This method is to be used by LDC and DMD. + # GDC can deal with the verbatim flags. + if len(rpath_paths) == 0 and len(install_rpath) == 0: + return [] + paths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths]) + if len(paths) < len(install_rpath): + padding = 'X'*(len(install_rpath) - len(paths)) + if len(paths) == 0: + paths = padding + else: + paths = paths + ':' + padding + return ['-L-rpath={}'.format(paths)] + + def translate_args_to_nongnu(self, args): + dcargs = [] + # Translate common arguments to flags the LDC/DMD compilers + # can understand. + # The flags might have been added by pkg-config files, + # and are therefore out of the user's control. + for arg in args: + if arg == '-pthread': + continue + if arg.startswith('-Wl,'): + linkargs = arg[arg.index(',')+1:].split(',') + for la in linkargs: + dcargs.append('-L' + la.strip()) + continue + elif arg.startswith('-l'): + # translate library link flag + dcargs.append('-L' + arg) + continue + dcargs.append(arg) + + return dcargs + +class GnuDCompiler(DCompiler): + def __init__(self, exelist, version, is_cross): + DCompiler.__init__(self, exelist, version, is_cross) + self.id = 'gcc' + self.warn_args = {'1': ['-Wall', '-Wdeprecated'], + '2': ['-Wall', '-Wextra', '-Wdeprecated'], + '3': ['-Wall', '-Wextra', '-Wdeprecated', '-Wpedantic']} + self.base_options = ['b_colorout', 'b_sanitize'] + + def get_colorout_args(self, colortype): + if mesonlib.version_compare(self.version, '>=4.9.0'): + return gnu_color_args[colortype][:] + return [] + + def get_dependency_gen_args(self, outtarget, outfile): + # FIXME: Passing -fmake-deps results in a file-not-found message. + # Investigate why. + return [] + + def get_output_args(self, target): + return ['-o', target] + + def get_compile_only_args(self): + return ['-c'] + + def get_linker_output_args(self, target): + return ['-o', target] + + def get_include_args(self, path, is_system): + return ['-I' + path] + + def get_warn_args(self, level): + return self.warn_args[level] + + def get_werror_args(self): + return ['-Werror'] + + def get_linker_search_args(self, dirname): + return ['-L'+dirname] + + def get_buildtype_args(self, buildtype): + return d_gdc_buildtype_args[buildtype] + + def build_rpath_args(self, build_dir, rpath_paths, install_rpath): + return build_unix_rpath_args(build_dir, rpath_paths, install_rpath) + + def get_unittest_args(self): + return ['-funittest'] + +class LLVMDCompiler(DCompiler): + def __init__(self, exelist, version, is_cross): + DCompiler.__init__(self, exelist, version, is_cross) + self.id = 'llvm' + self.base_options = ['b_coverage', 'b_colorout'] + + def get_colorout_args(self, colortype): + if colortype == 'always': + return ['-enable-color'] + return [] + + def get_dependency_gen_args(self, outtarget, outfile): + # LDC using the -deps flag returns a non-Makefile dependency-info file, which + # the backends can not use. So we disable this feature for now. + return [] + + def get_output_args(self, target): + return ['-of', target] + + def get_compile_only_args(self): + return ['-c'] + + def get_linker_output_args(self, target): + return ['-of', target] + + def get_include_args(self, path, is_system): + return ['-I' + path] + + def get_warn_args(self, level): + if level == '2': + return ['-wi'] + else: + return ['-w'] + + def get_coverage_args(self): + return ['-cov'] + + def get_buildtype_args(self, buildtype): + return d_ldc_buildtype_args[buildtype] + + def get_pic_args(self): + return ['-relocation-model=pic'] + + def get_linker_search_args(self, dirname): + # -L is recognized as "add this to the search path" by the linker, + # while the compiler recognizes it as "pass to linker". So, the first + # -L is for the compiler, telling it to pass the second -L to the linker. + return ['-L-L'+dirname] + + def unix_link_flags_to_native(self, args): + return self.translate_args_to_nongnu(args) + + def unix_compile_flags_to_native(self, args): + return self.translate_args_to_nongnu(args) + +class DmdDCompiler(DCompiler): + def __init__(self, exelist, version, is_cross): + DCompiler.__init__(self, exelist, version, is_cross) + self.id = 'dmd' + self.base_options = ['b_coverage', 'b_colorout'] + + def get_colorout_args(self, colortype): + if colortype == 'always': + return ['-color=on'] + return [] + + def get_dependency_gen_args(self, outtarget, outfile): + # LDC using the -deps flag returns a non-Makefile dependency-info file, which + # the backends can not use. So we disable this feature for now. + return [] + + def get_output_args(self, target): + return ['-of' + target] + + def get_werror_args(self): + return ['-w'] + + def get_compile_only_args(self): + return ['-c'] + + def get_linker_output_args(self, target): + return ['-of' + target] + + def get_include_args(self, path, is_system): + return ['-I' + path] + + def get_warn_args(self, level): + return [] + + def get_coverage_args(self): + return ['-cov'] + + def get_linker_search_args(self, dirname): + # -L is recognized as "add this to the search path" by the linker, + # while the compiler recognizes it as "pass to linker". So, the first + # -L is for the compiler, telling it to pass the second -L to the linker. + return ['-L-L'+dirname] + + def get_buildtype_args(self, buildtype): + return d_dmd_buildtype_args[buildtype] + + def get_std_shared_lib_link_args(self): + return ['-shared', '-defaultlib=libphobos2.so'] + + def unix_link_flags_to_native(self, args): + return self.translate_args_to_nongnu(args) + + def unix_compile_flags_to_native(self, args): + return self.translate_args_to_nongnu(args) + class VisualStudioCCompiler(CCompiler): std_warn_args = ['/W3'] std_opt_args= ['/O2'] @@ -1586,9 +1866,8 @@ class VisualStudioCCompiler(CCompiler): code = 'int i;\n' (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix) os.close(fd) - ofile = open(srcname, 'w') - ofile.write(code) - ofile.close() + with open(srcname, 'w') as ofile: + ofile.write(code) # Read c_args/cpp_args/etc from the cross-info file (if needed) extra_args = self.get_cross_extra_flags(env, compile=True, link=False) extra_args += self.get_compile_only_args() @@ -1860,6 +2139,9 @@ class ClangCCompiler(CCompiler): def get_option_link_args(self, options): return [] + def has_argument(self, arg, env): + return super().has_argument(['-Werror=unknown-warning-option', arg], env) + class GnuCPPCompiler(CPPCompiler): # may need to separate the latter to extra_debug_args or something std_debug_args = ['-g'] @@ -1882,6 +2164,11 @@ class GnuCPPCompiler(CPPCompiler): return gnu_color_args[colortype][:] return [] + def get_pic_args(self): + if self.gcc_type == GCC_MINGW: + return [] # On Window gcc defaults to fpic being always on. + return ['-fPIC'] + def get_always_args(self): return ['-pipe'] @@ -1969,6 +2256,9 @@ class ClangCPPCompiler(CPPCompiler): def get_option_link_args(self, options): return [] + def has_argument(self, arg, env): + return super().has_argument(['-Werror=unknown-warning-option', arg], env) + class FortranCompiler(Compiler): def __init__(self, exelist, version, is_cross, exe_wrapper=None): super().__init__(exelist, version) @@ -2005,12 +2295,11 @@ class FortranCompiler(Compiler): def sanity_check(self, work_dir, environment): source_name = os.path.join(work_dir, 'sanitycheckf.f90') binary_name = os.path.join(work_dir, 'sanitycheckf') - ofile = open(source_name, 'w') - ofile.write('''program prog + with open(source_name, 'w') as ofile: + ofile.write('''program prog print *, "Fortran compilation is working." end program prog ''') - ofile.close() extra_flags = self.get_cross_extra_flags(environment, compile=True, link=True) pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) pc.wait() diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 7c8e458..7a6eada 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -15,7 +15,7 @@ import pickle, os, uuid from .mesonlib import MesonException, default_libdir, default_libexecdir, default_prefix -version = '0.34.0.dev1' +version = '0.35.0.dev1' backendlist = ['ninja', 'vs2010', 'vs2015', 'xcode'] class UserOption: @@ -154,7 +154,8 @@ class CoreData(): raise RuntimeError('Tried to set unknown builtin option %s.' % optname) def load(filename): - obj = pickle.load(open(filename, 'rb')) + with open(filename, 'rb') as f: + obj = pickle.load(f) if not isinstance(obj, CoreData): raise RuntimeError('Core data file is corrupted.') if obj.version != version: @@ -165,7 +166,8 @@ def load(filename): def save(obj, filename): if obj.version != version: raise RuntimeError('Fatal version mismatch corruption.') - pickle.dump(obj, open(filename, 'wb')) + with open(filename, 'wb') as f: + pickle.dump(obj, f) def get_builtin_options(): return list(builtin_options.keys()) @@ -211,6 +213,11 @@ builtin_options = { 'datadir' : [ UserStringOption, 'Data file directory.', 'share' ], 'mandir' : [ UserStringOption, 'Manual page directory.', 'share/man' ], 'localedir' : [ UserStringOption, 'Locale data directory.', 'share/locale' ], + # Sysconfdir is a bit special. It defaults to ${prefix}/etc but nobody + # uses that. Instead they always set it manually to /etc. This default + # value is thus pointless and not really used but we set it to this + # for consistency with other systems. + 'sysconfdir' : [ UserStringOption, 'Sysconf data directory.', 'etc' ], 'werror' : [ UserBooleanOption, 'Treat warnings as errors.', False ], 'warning_level' : [ UserComboOption, 'Compiler warning level to use.', [ '1', '2', '3' ], '1'], 'layout' : [ UserComboOption, 'Build directory layout.', ['mirror', 'flat' ], 'mirror' ], diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index b4f825b..106273c 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -154,7 +154,7 @@ class PkgConfigDependency(Dependency): out = p.communicate()[0] if p.returncode != 0: raise DependencyException('Could not generate cargs for %s:\n\n%s' % \ - (name, out.decode(errors='ignore'))) + (self.name, out.decode(errors='ignore'))) self.cargs = out.decode().split() def _set_libs(self): @@ -166,7 +166,7 @@ class PkgConfigDependency(Dependency): out = p.communicate()[0] if p.returncode != 0: raise DependencyException('Could not generate libs for %s:\n\n%s' % \ - (name, out.decode(errors='ignore'))) + (self.name, out.decode(errors='ignore'))) self.libs = [] for lib in out.decode().split(): if lib.endswith(".la"): @@ -228,10 +228,11 @@ class PkgConfigDependency(Dependency): return self.is_found def extract_field(self, la_file, fieldname): - for line in open(la_file): - arr = line.strip().split('=') - if arr[0] == fieldname: - return arr[1][1:-1] + with open(la_file) as f: + for line in f: + arr = line.strip().split('=') + if arr[0] == fieldname: + return arr[1][1:-1] return None def extract_dlname_field(self, la_file): @@ -374,7 +375,8 @@ class ExternalProgram(): shebang and manually parse it to figure out the interpreter to use """ try: - first_line = open(script).readline().strip() + with open(script) as f: + first_line = f.readline().strip() if first_line.startswith('#!'): commands = first_line[2:].split('#')[0].strip().split() if mesonlib.is_windows(): @@ -552,12 +554,13 @@ class BoostDependency(Dependency): except FileNotFoundError: self.version = None return - for line in ifile: - if line.startswith("#define") and 'BOOST_LIB_VERSION' in line: - ver = line.split()[-1] - ver = ver[1:-1] - self.version = ver.replace('_', '.') - return + with ifile: + for line in ifile: + if line.startswith("#define") and 'BOOST_LIB_VERSION' in line: + ver = line.split()[-1] + ver = ver[1:-1] + self.version = ver.replace('_', '.') + return self.version = None def detect_src_modules(self): @@ -1048,6 +1051,17 @@ class SDL2Dependency(Dependency): self.is_found = False self.cargs = [] self.linkargs = [] + try: + pcdep = PkgConfigDependency('sdl2', environment, kwargs) + if pcdep.found(): + self.is_found = True + self.cargs = pcdep.get_compile_args() + self.linkargs = pcdep.get_link_args() + self.version = pcdep.get_version() + return + except Exception as e: + mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e)) + pass sdlconf = shutil.which('sdl2-config') if sdlconf: pc = subprocess.Popen(['sdl2-config', '--cflags'], @@ -1064,16 +1078,7 @@ class SDL2Dependency(Dependency): mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'), '(%s)' % sdlconf) self.version = '2' # FIXME return - try: - pcdep = PkgConfigDependency('sdl2', kwargs) - if pcdep.found(): - self.is_found = True - self.cargs = pcdep.get_compile_args() - self.linkargs = pcdep.get_link_args() - self.version = pcdep.get_version() - return - except Exception: - pass + mlog.debug('Could not find sdl2-config binary, trying next.') if mesonlib.is_osx(): fwdep = ExtraFrameworkDependency('sdl2', kwargs.get('required', True)) if fwdep.found(): diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 16af0d1..341e5e8 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -18,6 +18,7 @@ from . import mesonlib from . import mlog from .compilers import * import configparser +import shutil build_filename = 'meson.build' @@ -580,6 +581,48 @@ class Environment(): return RustCompiler(exelist, version) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + def detect_d_compiler(self): + exelist = None + is_cross = False + # Search for a D compiler. + # We prefer LDC over GDC unless overridden with the DC + # environment variable because LDC has a much more + # up to date language version at time (2016). + if 'DC' in os.environ: + exelist = os.environ['DC'].split() + elif self.is_cross_build() and want_cross: + exelist = [self.cross_info.config['binaries']['d']] + is_cross = True + elif shutil.which("ldc2"): + exelist = ['ldc2'] + elif shutil.which("ldc"): + exelist = ['ldc'] + elif shutil.which("gdc"): + exelist = ['gdc'] + elif shutil.which("dmd"): + exelist = ['dmd'] + else: + raise EnvironmentException('Could not find any supported D compiler.') + + try: + p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except OSError: + raise EnvironmentException('Could not execute D compiler "%s"' % ' '.join(exelist)) + (out, _) = p.communicate() + out = out.decode(errors='ignore') + vmatch = re.search(Environment.version_regex, out) + if vmatch: + version = vmatch.group(0) + else: + version = 'unknown version' + if 'LLVM D compiler' in out: + return LLVMDCompiler(exelist, version, is_cross) + elif 'gdc' in out: + return GnuDCompiler(exelist, version, is_cross) + elif 'Digital Mars' in out: + return DmdDCompiler(exelist, version, is_cross) + raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + def detect_swift_compiler(self): exelist = ['swiftc'] try: @@ -712,13 +755,14 @@ def get_args_from_envvars(lang, compiler_is_linker): if val: mlog.log('Appending {} from environment: {!r}'.format(var, val)) - if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran'): + if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'): return ([], []) # Compile flags cflags_mapping = {'c': 'CFLAGS', 'cpp': 'CXXFLAGS', 'objc': 'OBJCFLAGS', 'objcpp': 'OBJCXXFLAGS', - 'fortran': 'FFLAGS'} + 'fortran': 'FFLAGS', + 'd': 'DFLAGS'} compile_flags = os.environ.get(cflags_mapping[lang], '') log_var(cflags_mapping[lang], compile_flags) compile_flags = compile_flags.split() @@ -755,7 +799,7 @@ class CrossBuildInfo(): raise mesonlib.MesonException('Cross file is missing "binaries".') def ok_type(self, i): - return isinstance(i, str) or isinstance(i, int) or isinstance(i, bool) + return isinstance(i, (str, int, bool)) def parse_datafile(self, filename): config = configparser.ConfigParser() diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 8645b68..1ef4133 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -144,6 +144,7 @@ class RunProcess(InterpreterObject): cwd = os.path.join(source_dir, subdir) child_env = os.environ.copy() child_env.update(env) + mlog.debug('Running command:', ' '.join(command_array)) try: return subprocess.Popen(command_array, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=child_env, cwd=cwd) @@ -454,7 +455,10 @@ class Man(InterpreterObject): def validate_sources(self): for s in self.sources: - num = int(s.split('.')[-1]) + try: + num = int(s.split('.')[-1]) + except (IndexError, ValueError): + num = 0 if num < 1 or num > 8: raise InvalidArguments('Man file must have a file extension of a number between 1 and 8') @@ -564,7 +568,7 @@ class SubprojectHolder(InterpreterObject): def __init__(self, subinterpreter): super().__init__() - self.subinterpreter = subinterpreter + self.held_object = subinterpreter self.methods.update({'get_variable' : self.get_variable_method, }) @@ -574,7 +578,7 @@ class SubprojectHolder(InterpreterObject): varname = args[0] if not isinstance(varname, str): raise InterpreterException('Get_variable takes a string argument.') - return self.subinterpreter.variables[varname] + return self.held_object.variables[varname] class CompilerHolder(InterpreterObject): def __init__(self, compiler, env): @@ -590,6 +594,7 @@ class CompilerHolder(InterpreterObject): 'run' : self.run_method, 'has_function' : self.has_function_method, 'has_member' : self.has_member_method, + 'has_members' : self.has_members_method, 'has_type' : self.has_type_method, 'alignment' : self.alignment_method, 'version' : self.version_method, @@ -597,6 +602,7 @@ class CompilerHolder(InterpreterObject): 'find_library': self.find_library_method, 'has_argument' : self.has_argument_method, 'first_supported_argument' : self.first_supported_argument_method, + 'unittest_args' : self.unittest_args_method, }) def version_method(self, args, kwargs): @@ -650,6 +656,12 @@ class CompilerHolder(InterpreterObject): def get_id_method(self, args, kwargs): return self.compiler.get_id() + def unittest_args_method(self, args, kwargs): + # At time, only D compilers have this feature. + if not hasattr(self.compiler, 'get_unittest_args'): + raise InterpreterException('This {} compiler has no unittest arguments.'.format(self.compiler.language)) + return self.compiler.get_unittest_args() + def has_member_method(self, args, kwargs): if len(args) != 2: raise InterpreterException('Has_member takes exactly two arguments.') @@ -658,9 +670,10 @@ class CompilerHolder(InterpreterObject): membername = args[1] prefix = kwargs.get('prefix', '') if not isinstance(prefix, str): - raise InterpreterException('Prefix argument of has_function must be a string.') + raise InterpreterException('Prefix argument of has_member must be a string.') extra_args = self.determine_args(kwargs) - had = self.compiler.has_member(typename, membername, prefix, self.environment, extra_args) + had = self.compiler.has_members(typename, [membername], prefix, + self.environment, extra_args) if had: hadtxt = mlog.green('YES') else: @@ -669,6 +682,25 @@ class CompilerHolder(InterpreterObject): '" has member "', mlog.bold(membername), '": ', hadtxt, sep='') return had + def has_members_method(self, args, kwargs): + check_stringlist(args) + typename = args[0] + membernames = args[1:] + prefix = kwargs.get('prefix', '') + if not isinstance(prefix, str): + raise InterpreterException('Prefix argument of has_members must be a string.') + extra_args = self.determine_args(kwargs) + had = self.compiler.has_members(typename, membernames, prefix, + self.environment, extra_args) + if had: + hadtxt = mlog.green('YES') + else: + hadtxt = mlog.red('NO') + members = mlog.bold(', '.join(['"{}"'.format(m) for m in membernames])) + mlog.log('Checking whether type "', mlog.bold(typename), + '" has members ', members, ': ', hadtxt, sep='') + return had + def has_function_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Has_function takes exactly one argument.') @@ -1004,7 +1036,8 @@ class Interpreter(): mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename) if not os.path.isfile(mesonfile): raise InvalidArguments('Missing Meson file in %s' % mesonfile) - code = open(mesonfile, encoding='utf8').read() + with open(mesonfile, encoding='utf8') as mf: + code = mf.read() if len(code.strip()) == 0: raise InvalidCode('Builder file is empty.') assert(isinstance(code, str)) @@ -1097,7 +1130,7 @@ class Interpreter(): if isinstance(v, build.CustomTarget): self.add_target(v.name, v) outvalues.append(CustomTargetHolder(v, self)) - elif isinstance(v, int) or isinstance(v, str): + elif isinstance(v, (int, str)): outvalues.append(v) elif isinstance(v, build.Executable): self.add_target(v.name, v) @@ -1353,9 +1386,19 @@ class Interpreter(): cmd = [cmd] else: raise InterpreterException('First argument is of incorrect type.') - check_stringlist(cargs, 'Run_command arguments must be strings.') - args = cmd + cargs + expanded_args = [] + for a in mesonlib.flatten(cargs): + if isinstance(a, str): + expanded_args.append(a) + elif isinstance(a, mesonlib.File): + if a.is_built: + raise InterpreterException('Can not use generated files in run_command.') + expanded_args.append(os.path.join(self.environment.get_source_dir(), str(a))) + else: + raise InterpreterException('Run_command arguments must be strings or the output of files().') + args = cmd + expanded_args in_builddir = kwargs.get('in_builddir', False) + mlog.debug('Running command:', ' '.join(args)) if not isinstance(in_builddir, bool): raise InterpreterException('in_builddir must be boolean.') return RunProcess(args, self.environment.source_dir, self.environment.build_dir, @@ -1566,6 +1609,10 @@ class Interpreter(): comp = self.environment.detect_vala_compiler() if need_cross_compiler: cross_comp = comp # Vala is too (I think). + elif lang == 'd': + comp = self.environment.detect_d_compiler() + if need_cross_compiler: + cross_comp = comp # D as well (AFAIK). elif lang == 'rust': comp = self.environment.detect_rust_compiler() if need_cross_compiler: @@ -1586,7 +1633,9 @@ class Interpreter(): if cross_comp is not None: cross_comp.sanity_check(self.environment.get_scratch_dir(), self.environment) self.coredata.cross_compilers[lang] = cross_comp - new_options = comp.get_options() + new_options = cross_comp.get_options() + else: + new_options = comp.get_options() optprefix = lang + '_' for i in new_options: if not i.startswith(optprefix): @@ -1685,14 +1734,23 @@ class Interpreter(): dep = cached_dep else: # We need to actually search for this dep + exception = None + dep = None try: dep = dependencies.find_external_dependency(name, self.environment, kwargs) - except dependencies.DependencyException: + except dependencies.DependencyException as e: + exception = e + pass + + if not dep or not dep.found(): if 'fallback' in kwargs: - dep = self.dependency_fallback(name, kwargs) - self.coredata.deps[identifier] = dep.held_object - return dep - raise + fallback_dep = self.dependency_fallback(name, kwargs) + if fallback_dep: + return fallback_dep + + if not dep: + raise exception + self.coredata.deps[identifier] = dep return DependencyHolder(dep) @@ -1706,9 +1764,12 @@ class Interpreter(): self.do_subproject(dirname, {}) except: mlog.log('Also couldn\'t find a fallback subproject in', - mlog.bold(os.path.join(self.subproject_dir, dirname)), - 'for the dependency', mlog.bold(name)) - raise + mlog.bold(os.path.join(self.subproject_dir, dirname)), + 'for the dependency', mlog.bold(name)) + if kwargs.get('required', True): + raise + else: + return None dep = self.subprojects[dirname].get_variable_method([varname], {}) if not isinstance(dep, (DependencyHolder, InternalDependencyHolder)): raise InterpreterException('Fallback variable is not a dependency object.') @@ -1823,12 +1884,12 @@ class Interpreter(): raise InterpreterException('Run_target needs at least one positional argument.') cleaned_args = [] - for i in all_args: + for i in mesonlib.flatten(all_args): try: i = i.held_object except AttributeError: pass - if not isinstance(i, (str, build.BuildTarget, build.CustomTarget, dependencies.ExternalProgram)): + if not isinstance(i, (str, build.BuildTarget, build.CustomTarget, dependencies.ExternalProgram, mesonlib.File)): mlog.debug('Wrong type:', str(i)) raise InterpreterException('Invalid argument to run_target.') cleaned_args.append(i) @@ -1962,7 +2023,8 @@ class Interpreter(): if not os.path.isfile(absname): self.subdir = prev_subdir raise InterpreterException('Nonexistant build def file %s.' % buildfilename) - code = open(absname, encoding='utf8').read() + with open(absname, encoding='utf8') as f: + code = f.read() assert(isinstance(code, str)) try: codeblock = mparser.Parser(code).parse() @@ -2078,11 +2140,7 @@ class Interpreter(): def flatten(self, args): if isinstance(args, mparser.StringNode): return args.value - if isinstance(args, str): - return args - if isinstance(args, InterpreterObject): - return args - if isinstance(args, int): + if isinstance(args, (int, str, InterpreterObject)): return args result = [] for a in args: @@ -2098,8 +2156,8 @@ class Interpreter(): def source_strings_to_files(self, sources): results = [] for s in sources: - if isinstance(s, mesonlib.File) or isinstance(s, GeneratedListHolder) or \ - isinstance(s, CustomTargetHolder): + if isinstance(s, (mesonlib.File, GeneratedListHolder, + CustomTargetHolder)): pass elif isinstance(s, str): s = mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s) @@ -2197,14 +2255,8 @@ class Interpreter(): raise InvalidCode('Unknown function "%s".' % func_name) def is_assignable(self, value): - if isinstance(value, InterpreterObject) or \ - isinstance(value, dependencies.Dependency) or\ - isinstance(value, str) or\ - isinstance(value, int) or \ - isinstance(value, list) or \ - isinstance(value, mesonlib.File): - return True - return False + return isinstance(value, (InterpreterObject, dependencies.Dependency, + str, int, list, mesonlib.File)) def assignment(self, node): assert(isinstance(node, mparser.AssignmentNode)) @@ -2257,6 +2309,22 @@ class Interpreter(): else: raise InterpreterException('Unknown method "%s" for a boolean.' % method_name) + def int_method_call(self, obj, method_name, args): + obj = self.to_native(obj) + (posargs, _) = self.reduce_arguments(args) + if method_name == 'is_even': + if len(posargs) == 0: + return obj % 2 == 0 + else: + raise InterpreterException('int.is_even() must have no arguments.') + elif method_name == 'is_odd': + if len(posargs) == 0: + return obj % 2 != 0 + else: + raise InterpreterException('int.is_odd() must have no arguments.') + else: + raise InterpreterException('Unknown method "%s" for an integer.' % method_name) + def string_method_call(self, obj, method_name, args): obj = self.to_native(obj) (posargs, _) = self.reduce_arguments(args) @@ -2310,9 +2378,8 @@ class Interpreter(): raise InterpreterException('Unknown method "%s" for a string.' % method_name) def to_native(self, arg): - if isinstance(arg, mparser.StringNode) or \ - isinstance(arg, mparser.NumberNode) or \ - isinstance(arg, mparser.BooleanNode): + if isinstance(arg, (mparser.StringNode, mparser.NumberNode, + mparser.BooleanNode)): return arg.value return arg @@ -2344,6 +2411,8 @@ class Interpreter(): return self.string_method_call(obj, method_name, args) if isinstance(obj, bool): return self.bool_method_call(obj, method_name, args) + if isinstance(obj, int): + return self.int_method_call(obj, method_name, args) if isinstance(obj, list): return self.array_method_call(obj, method_name, self.reduce_arguments(args)[0]) if not isinstance(obj, InterpreterObject): @@ -2465,9 +2534,7 @@ class Interpreter(): return iobject[index] def is_elementary_type(self, v): - if isinstance(v, (int, float, str, bool, list)): - return True - return False + return isinstance(v, (int, float, str, bool, list)) def evaluate_comparison(self, node): v1 = self.evaluate_statement(node.left) @@ -2562,6 +2629,10 @@ class Interpreter(): if not isinstance(l, int) or not isinstance(r, int): raise InvalidCode('Division works only with integers.') return l // r + elif cur.operation == 'mod': + if not isinstance(l, int) or not isinstance(r, int): + raise InvalidCode('Modulo works only with integers.') + return l % r else: raise InvalidCode('You broke me.') diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 4b11c10..afabc62 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -36,15 +36,18 @@ class Conf: self.build_file = os.path.join(build_dir, 'meson-private/build.dat') if not os.path.isfile(self.coredata_file) or not os.path.isfile(self.build_file): raise ConfException('Directory %s does not seem to be a Meson build directory.' % build_dir) - self.coredata = pickle.load(open(self.coredata_file, 'rb')) - self.build = pickle.load(open(self.build_file, 'rb')) + with open(self.coredata_file, 'rb') as f: + self.coredata = pickle.load(f) + with open(self.build_file, 'rb') as f: + self.build = pickle.load(f) if self.coredata.version != coredata.version: raise ConfException('Version mismatch (%s vs %s)' % (coredata.version, self.coredata.version)) def save(self): # Only called if something has changed so overwrite unconditionally. - pickle.dump(self.coredata, open(self.coredata_file, 'wb')) + with open(self.coredata_file, 'wb') as f: + pickle.dump(self.coredata, f) # We don't write the build file because any changes to it # are erased when Meson is executed the nex time, i.e. the next # time Ninja is run. diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 1672d95..d06e4eb 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -95,11 +95,7 @@ def is_32bit(): return not(sys.maxsize > 2**32) def is_debianlike(): - try: - open('/etc/debian_version', 'r') - return True - except FileNotFoundError: - return False + return os.path.isfile('/etc/debian_version') def exe_exists(arglist): try: @@ -114,7 +110,7 @@ def exe_exists(arglist): def detect_vcs(source_dir): vcs_systems = [ dict(name = 'git', cmd = 'git', repo_dir = '.git', get_rev = 'git describe --dirty=+', rev_regex = '(.*)', dep = '.git/logs/HEAD'), - dict(name = 'mercurial', cmd = 'hg', repo_dir = '.hg', get_rev = 'hg id -n', rev_regex = '(.*)', dep = '.hg/dirstate'), + dict(name = 'mercurial', cmd = 'hg', repo_dir = '.hg', get_rev = 'hg id -i', rev_regex = '(.*)', dep = '.hg/dirstate'), dict(name = 'subversion', cmd = 'svn', repo_dir = '.svn', get_rev = 'svn info', rev_regex = 'Revision: (.*)', dep = '.svn/wc.db'), dict(name = 'bazaar', cmd = 'bzr', repo_dir = '.bzr', get_rev = 'bzr revno', rev_regex = '(.*)', dep = '.bzr'), ] @@ -261,7 +257,11 @@ def do_mesondefine(line, confdata): def do_conf_file(src, dst, confdata): - data = open(src).readlines() + try: + with open(src) as f: + data = f.readlines() + except Exception: + raise MesonException('Could not read input file %s.' % src) # Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define # Also allow escaping '@' with '\@' regex = re.compile(r'[^\\]?@([-a-zA-Z0-9_]+)@') @@ -273,7 +273,8 @@ def do_conf_file(src, dst, confdata): line = do_replacement(regex, line, confdata) result.append(line) dst_tmp = dst + '~' - open(dst_tmp, 'w').writelines(result) + with open(dst_tmp, 'w') as f: + f.writelines(result) shutil.copymode(src, dst_tmp) replace_if_different(dst, dst_tmp) @@ -303,9 +304,10 @@ def replace_if_different(dst, dst_tmp): # If contents are identical, don't touch the file to prevent # unnecessary rebuilds. try: - if open(dst, 'r').read() == open(dst_tmp, 'r').read(): - os.unlink(dst_tmp) - return + with open(dst, 'r') as f1, open(dst_tmp, 'r') as f2: + if f1.read() == f2.read(): + os.unlink(dst_tmp) + return except FileNotFoundError: pass os.replace(dst_tmp, dst) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index e002d9a..f35d821 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -47,6 +47,7 @@ add_builtin_argument('includedir') add_builtin_argument('datadir') add_builtin_argument('mandir') add_builtin_argument('localedir') +add_builtin_argument('sysconfdir') add_builtin_argument('backend') add_builtin_argument('buildtype') add_builtin_argument('strip', action='store_true') @@ -60,8 +61,8 @@ parser.add_argument('--cross-file', default=None, help='File describing cross compilation environment.') parser.add_argument('-D', action='append', dest='projectoptions', default=[], help='Set project options.') -parser.add_argument('-v', '--version', action='store_true', dest='print_version', default=False, - help='Print version information.') +parser.add_argument('-v', '--version', action='version', + version=coredata.version) parser.add_argument('directories', nargs='*') class MesonApp(): @@ -168,7 +169,8 @@ itself as required.''' g.generate(intr) g.run_postconf_scripts() dumpfile = os.path.join(env.get_scratch_dir(), 'build.dat') - pickle.dump(b, open(dumpfile, 'wb')) + with open(dumpfile, 'wb') as f: + pickle.dump(b, f) # Write this last since we use the existence of this file to check if # we generated the build file successfully, so we don't want an error # that pops up during generation, post-conf scripts, etc to cause us to @@ -244,9 +246,6 @@ def run(mainfile, args): handshake = False args = mesonlib.expand_arguments(args) options = parser.parse_args(args) - if options.print_version: - print(coredata.version) - return 0 args = options.directories if len(args) == 0 or len(args) > 2: # if there's a meson.build in the dir above, and not in the current diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 629b0fc..2086c37 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -177,10 +177,14 @@ def run(args): buildfile = os.path.join(bdir, 'meson-private/build.dat') testfile = os.path.join(bdir, 'meson-private/meson_test_setup.dat') benchmarkfile = os.path.join(bdir, 'meson-private/meson_benchmark_setup.dat') - coredata = pickle.load(open(corefile, 'rb')) - builddata = pickle.load(open(buildfile, 'rb')) - testdata = pickle.load(open(testfile, 'rb')) - benchmarkdata = pickle.load(open(benchmarkfile, 'rb')) + with open(corefile, 'rb') as f: + coredata = pickle.load(f) + with open(buildfile, 'rb') as f: + builddata = pickle.load(f) + with open(testfile, 'rb') as f: + testdata = pickle.load(f) + with open(benchmarkfile, 'rb') as f: + benchmarkdata = pickle.load(f) if options.list_targets: list_targets(coredata, builddata) elif options.target_files is not None: diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index be3c3ac..d6a0fcf 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -19,6 +19,7 @@ from .. import build import os, sys import subprocess from ..mesonlib import MesonException +from .. import dependencies from .. import mlog from .. import mesonlib @@ -61,6 +62,8 @@ class GnomeModule: cmd += ['--c-name', kwargs.pop('c_name')] cmd += ['--generate', '--target', '@OUTPUT@'] + cmd += mesonlib.stringlistify(kwargs.pop('extra_args', [])) + kwargs['command'] = cmd kwargs['input'] = args[1] kwargs['output'] = args[0] + '.c' @@ -88,6 +91,33 @@ class GnomeModule: return stdout.split('\n')[:-1] + def get_link_args(self, state, lib, depends): + link_command = ['-l%s' % lib.name] + if isinstance(lib, build.SharedLibrary): + link_command += ['-L%s' % + os.path.join(state.environment.get_build_dir(), + lib.subdir)] + depends.append(lib) + return link_command + + def get_include_args(self, state, include_dirs): + if not include_dirs: + return [] + + dirs_str = [] + for incdirs in include_dirs: + if hasattr(incdirs, "held_object"): + dirs = incdirs.held_object + else: + dirs = incdirs + for incdir in dirs.get_incdirs(): + if os.path.isabs(incdir): + dirs_str += ['-I%s' % os.path.join(incdir)] + else: + dirs_str += ['-I%s' % os.path.join(state.environment.get_source_dir(), + dirs.curdir, incdir)] + return dirs_str + def generate_gir(self, state, args, kwargs): if len(args) != 1: raise MesonException('Gir takes one argument') @@ -116,27 +146,16 @@ class GnomeModule: scan_command += ['--no-libtool', '--namespace='+ns, '--nsversion=' + nsversion, '--warn-all', '--output', '@OUTPUT@'] - extra_args = kwargs.pop('extra_args', []) - if not isinstance(extra_args, list): - extra_args = [extra_args] + extra_args = mesonlib.stringlistify(kwargs.pop('extra_args', [])) scan_command += extra_args - - for incdirs in girtarget.include_dirs: - for incdir in incdirs.get_incdirs(): - scan_command += ['-I%s' % os.path.join(state.environment.get_source_dir(), incdir)] + scan_command += self.get_include_args(state, girtarget.include_dirs) if 'link_with' in kwargs: link_with = kwargs.pop('link_with') if not isinstance(link_with, list): link_with = [link_with] for link in link_with: - lib = link.held_object - scan_command += ['-l%s' % lib.name] - if isinstance(lib, build.SharedLibrary): - scan_command += ['-L%s' % - os.path.join(state.environment.get_build_dir(), - lib.subdir)] - depends.append(lib) + scan_command += self.get_link_args(state, link.held_object, depends) if 'includes' in kwargs: includes = kwargs.pop('includes') @@ -175,24 +194,43 @@ class GnomeModule: if not isinstance (deps, list): deps = [deps] for dep in deps: - girdir = dep.held_object.get_variable ("girdir") - if girdir: - scan_command += ["--add-include-path=%s" % girdir] - for lib in dep.held_object.libs: - if os.path.isabs(lib) and dep.held_object.is_libtool: - scan_command += ["-L%s" % os.path.dirname(lib)] - libname = os.path.basename(lib) - if libname.startswith("lib"): - libname = libname[3:] - libname = libname.split(".so")[0] - lib = "-l%s" % libname - scan_command += [lib] + if isinstance(dep.held_object, dependencies.InternalDependency): + scan_command += self.get_include_args(state, dep.held_object.include_directories) + for lib in dep.held_object.libraries: + scan_command += self.get_link_args(state, lib.held_object, depends) + for source in dep.held_object.sources: + if isinstance(source.held_object, GirTarget): + scan_command += ["--add-include-path=%s" % + os.path.join(state.environment.get_build_dir(), + source.held_object.get_subdir())] + elif isinstance(dep.held_object, dependencies.PkgConfigDependency): + for lib in dep.held_object.libs: + if os.path.isabs(lib) and dep.held_object.is_libtool: + scan_command += ["-L%s" % os.path.dirname(lib)] + libname = os.path.basename(lib) + if libname.startswith("lib"): + libname = libname[3:] + libname = libname.split(".so")[0] + lib = "-l%s" % libname + # Hack to avoid passing some compiler options in + if lib.startswith("-W"): + continue + scan_command += [lib] + + girdir = dep.held_object.get_variable ("girdir") + if girdir: + scan_command += ["--add-include-path=%s" % girdir] + else: + mlog.log('dependency %s not handled to build gir files' % dep) + continue inc_dirs = None if kwargs.get('include_directories'): inc_dirs = kwargs.pop('include_directories') + if not isinstance(inc_dirs, list): inc_dirs = [inc_dirs] + for ind in inc_dirs: if isinstance(ind.held_object, build.IncludeDirs): scan_command += ['--add-include-path=%s' % inc for inc in ind.held_object.get_incdirs()] @@ -222,9 +260,16 @@ class GnomeModule: incd.held_object.get_incdirs()] if deps: for dep in deps: - girdir = dep.held_object.get_variable ("girdir") - if girdir: - typelib_cmd += ["--includedir=%s" % girdir] + if isinstance(dep.held_object, dependencies.InternalDependency): + for source in dep.held_object.sources: + if isinstance(source.held_object, GirTarget): + typelib_cmd += ["--includedir=%s" % + os.path.join(state.environment.get_build_dir(), + source.held_object.get_subdir())] + elif isinstance(dep.held_object, dependencies.PkgConfigDependency): + girdir = dep.held_object.get_variable ("girdir") + if girdir: + typelib_cmd += ["--includedir=%s" % girdir] kwargs['output'] = typelib_output kwargs['command'] = typelib_cmd diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 6ee558b..3bf7658 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -18,42 +18,43 @@ import os class PkgConfigModule: - def print_hello(self, state, args, kwargs): - print('Hello from a Meson module') - def generate_pkgconfig_file(self, state, libraries, subdirs, name, description, version, filebase, pub_reqs, priv_reqs, priv_libs): + coredata = state.environment.get_coredata() outdir = state.environment.scratch_dir fname = os.path.join(outdir, filebase + '.pc') - ofile = open(fname, 'w') - coredata = state.environment.get_coredata() - ofile.write('prefix=%s\n' % coredata.get_builtin_option('prefix')) - ofile.write('libdir=${prefix}/%s\n' % coredata.get_builtin_option('libdir')) - ofile.write('includedir=${prefix}/%s\n\n' % coredata.get_builtin_option('includedir')) - ofile.write('Name: %s\n' % name) - if len(description) > 0: - ofile.write('Description: %s\n' % description) - if len(version) > 0: - ofile.write('Version: %s\n' % version) - if len(pub_reqs) > 0: - ofile.write('Requires: {}\n'.format(' '.join(pub_reqs))) - if len(priv_reqs) > 0: - ofile.write('Requires.private: {}\n'.format(' '.join(priv_reqs))) - if len(priv_libs) > 0: - ofile.write('Libraries.private: {}\n'.format(' '.join(priv_libs))) - ofile.write('Libs: -L${libdir} ') - for l in libraries: - if l.custom_install_dir: - ofile.write('-L${prefix}/%s ' % l.custom_install_dir) - ofile.write('-l%s ' % l.name) - ofile.write('\n') - ofile.write('CFlags: ') - for h in subdirs: - if h == '.': - h = '' - ofile.write(os.path.join('-I${includedir}', h)) - ofile.write(' ') - ofile.write('\n') + with open(fname, 'w') as ofile: + ofile.write('prefix=%s\n' % coredata.get_builtin_option('prefix')) + ofile.write('libdir=${prefix}/%s\n' % + coredata.get_builtin_option('libdir')) + ofile.write('includedir=${prefix}/%s\n\n' % + coredata.get_builtin_option('includedir')) + ofile.write('Name: %s\n' % name) + if len(description) > 0: + ofile.write('Description: %s\n' % description) + if len(version) > 0: + ofile.write('Version: %s\n' % version) + if len(pub_reqs) > 0: + ofile.write('Requires: {}\n'.format(' '.join(pub_reqs))) + if len(priv_reqs) > 0: + ofile.write( + 'Requires.private: {}\n'.format(' '.join(priv_reqs))) + if len(priv_libs) > 0: + ofile.write( + 'Libraries.private: {}\n'.format(' '.join(priv_libs))) + ofile.write('Libs: -L${libdir} ') + for l in libraries: + if l.custom_install_dir: + ofile.write('-L${prefix}/%s ' % l.custom_install_dir) + ofile.write('-l%s ' % l.name) + ofile.write('\n') + ofile.write('CFlags: ') + for h in subdirs: + if h == '.': + h = '' + ofile.write(os.path.join('-I${includedir}', h)) + ofile.write(' ') + ofile.write('\n') def generate(self, state, args, kwargs): if len(args) > 0: @@ -65,7 +66,7 @@ class PkgConfigModule: for l in libs: if hasattr(l, 'held_object'): l = l.held_object - if not (isinstance(l, build.SharedLibrary) or isinstance(l, build.StaticLibrary)): + if not isinstance(l, (build.SharedLibrary, build.StaticLibrary)): raise mesonlib.MesonException('Library argument not a library object.') processed_libs.append(l) libs = processed_libs @@ -86,7 +87,11 @@ class PkgConfigModule: priv_reqs = mesonlib.stringlistify(kwargs.get('requires_private', [])) priv_libs = mesonlib.stringlistify(kwargs.get('libraries_private', [])) pcfile = filebase + '.pc' - pkgroot = os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'pkgconfig') + pkgroot = kwargs.get('install_dir',None) + if pkgroot is None: + pkgroot = os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'pkgconfig') + if not isinstance(pkgroot, str): + raise mesonlib.MesonException('Install_dir must be a string.') self.generate_pkgconfig_file(state, libs, subdirs, name, description, version, filebase, pub_reqs, priv_reqs, priv_libs) return build.Data(False, state.environment.get_scratch_dir(), [pcfile], pkgroot) diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index acad204..89194e9 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -80,82 +80,87 @@ class RPMModule: files.add('%%{_mandir}/man%u/%s.*' % (int(man_file.split('.')[-1]), man_file)) if len(files_devel) > 0: devel_subpkg = True - fn = open('%s.spec' % os.path.join(state.environment.get_build_dir(), proj), 'w+') - fn.write('Name: %s\n' % proj) - fn.write('Version: # FIXME\n') - fn.write('Release: 1%{?dist}\n') - fn.write('Summary: # FIXME\n') - fn.write('License: # FIXME\n') - fn.write('\n') - fn.write('Source0: %{name}-%{version}.tar.xz # FIXME\n') - fn.write('\n') - for compiler in compiler_deps: - fn.write('BuildRequires: %s\n' % compiler) - for dep in state.environment.coredata.deps: - fn.write('BuildRequires: pkgconfig(%s)\n' % dep) - for lib in state.environment.coredata.ext_libs.values(): - fn.write('BuildRequires: %s # FIXME\n' % lib.fullpath) - mlog.log('Warning, replace', mlog.bold(lib.fullpath), 'with real package.', - 'You can use following command to find package which contains this lib:', - mlog.bold('dnf provides %s' % lib.fullpath)) - for prog in state.environment.coredata.ext_progs.values(): - if not prog.found(): - fn.write('BuildRequires: /usr/bin/%s # FIXME\n' % prog.get_name()) - else: - fn.write('BuildRequires: %s\n' % ' '.join(prog.fullpath)) - fn.write('BuildRequires: meson\n') - fn.write('\n') - fn.write('%description\n') - fn.write('\n') - if devel_subpkg: - fn.write('%package devel\n') - fn.write('Summary: Development files for %{name}\n') - fn.write('Requires: %{name}%{?_isa} = %{version}-%{release}\n') + filename = os.path.join(state.environment.get_build_dir(), + '%s.spec' % proj) + with open(filename, 'w+') as fn: + fn.write('Name: %s\n' % proj) + fn.write('Version: # FIXME\n') + fn.write('Release: 1%{?dist}\n') + fn.write('Summary: # FIXME\n') + fn.write('License: # FIXME\n') + fn.write('\n') + fn.write('Source0: %{name}-%{version}.tar.xz # FIXME\n') + fn.write('\n') + for compiler in compiler_deps: + fn.write('BuildRequires: %s\n' % compiler) + for dep in state.environment.coredata.deps: + fn.write('BuildRequires: pkgconfig(%s)\n' % dep) + for lib in state.environment.coredata.ext_libs.values(): + fn.write('BuildRequires: %s # FIXME\n' % lib.fullpath) + mlog.log('Warning, replace', mlog.bold(lib.fullpath), + 'with real package.', + 'You can use following command to find package which ' + 'contains this lib:', + mlog.bold('dnf provides %s' % lib.fullpath)) + for prog in state.environment.coredata.ext_progs.values(): + if not prog.found(): + fn.write('BuildRequires: /usr/bin/%s # FIXME\n' % + prog.get_name()) + else: + fn.write('BuildRequires: %s\n' % ' '.join(prog.fullpath)) + fn.write('BuildRequires: meson\n') + fn.write('\n') + fn.write('%description\n') fn.write('\n') - fn.write('%description devel\n') - fn.write('Development files for %{name}.\n') + if devel_subpkg: + fn.write('%package devel\n') + fn.write('Summary: Development files for %{name}\n') + fn.write('Requires: %{name}%{?_isa} = %{version}-%{release}\n') + fn.write('\n') + fn.write('%description devel\n') + fn.write('Development files for %{name}.\n') + fn.write('\n') + fn.write('%prep\n') + fn.write('%autosetup\n') + fn.write('rm -rf rpmbuilddir && mkdir rpmbuilddir\n') fn.write('\n') - fn.write('%prep\n') - fn.write('%autosetup\n') - fn.write('rm -rf rpmbuilddir && mkdir rpmbuilddir\n') - fn.write('\n') - fn.write('%build\n') - fn.write('pushd rpmbuilddir\n') - fn.write(' %meson ..\n') - fn.write(' ninja-build -v\n') - fn.write('popd\n') - fn.write('\n') - fn.write('%install\n') - fn.write('pushd rpmbuilddir\n') - fn.write(' DESTDIR=%{buildroot} ninja-build -v install\n') - fn.write('popd\n') - if len(to_delete) > 0: - fn.write('rm -rf %s\n' % ' '.join(to_delete)) - fn.write('\n') - fn.write('%check\n') - fn.write('pushd rpmbuilddir\n') - fn.write(' ninja-build -v test\n') - fn.write('popd\n') - fn.write('\n') - fn.write('%files\n') - for f in files: - fn.write('%s\n' % f) - fn.write('\n') - if devel_subpkg: - fn.write('%files devel\n') - for f in files_devel: + fn.write('%build\n') + fn.write('pushd rpmbuilddir\n') + fn.write(' %meson ..\n') + fn.write(' ninja-build -v\n') + fn.write('popd\n') + fn.write('\n') + fn.write('%install\n') + fn.write('pushd rpmbuilddir\n') + fn.write(' DESTDIR=%{buildroot} ninja-build -v install\n') + fn.write('popd\n') + if len(to_delete) > 0: + fn.write('rm -rf %s\n' % ' '.join(to_delete)) + fn.write('\n') + fn.write('%check\n') + fn.write('pushd rpmbuilddir\n') + fn.write(' ninja-build -v test\n') + fn.write('popd\n') + fn.write('\n') + fn.write('%files\n') + for f in files: fn.write('%s\n' % f) fn.write('\n') - if so_installed: - fn.write('%post -p /sbin/ldconfig\n') + if devel_subpkg: + fn.write('%files devel\n') + for f in files_devel: + fn.write('%s\n' % f) + fn.write('\n') + if so_installed: + fn.write('%post -p /sbin/ldconfig\n') + fn.write('\n') + fn.write('%postun -p /sbin/ldconfig\n') + fn.write('\n') + fn.write('%changelog\n') + fn.write('* %s meson <meson@example.com> - \n' % + datetime.date.today().strftime('%a %b %d %Y')) + fn.write('- \n') fn.write('\n') - fn.write('%postun -p /sbin/ldconfig\n') - fn.write('\n') - fn.write('%changelog\n') - fn.write('* %s meson <meson@example.com> - \n' % datetime.date.today().strftime('%a %b %d %Y')) - fn.write('- \n') - fn.write('\n') - fn.close() mlog.log('RPM spec template written to %s.spec.\n' % proj) def initialize(): diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 2f0eb2d..f593c8e 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -58,6 +58,7 @@ class Lexer: ('plus', re.compile(r'\+')), ('dash', re.compile(r'-')), ('star', re.compile(r'\*')), + ('percent', re.compile(r'\%')), ('fslash', re.compile(r'/')), ('colon', re.compile(r':')), ('equal', re.compile(r'==')), @@ -434,11 +435,17 @@ class Parser: return left def e5sub(self): - left = self.e5mul() + left = self.e5mod() if self.accept('dash'): return ArithmeticNode(left.lineno, left.colno, 'sub', left, self.e5sub()) return left + def e5mod(self): + left = self.e5mul() + if self.accept('percent'): + return ArithmeticNode(left.lineno, left.colno, 'mod', left, self.e5mod()) + return left + def e5mul(self): left = self.e5div() if self.accept('star'): diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 409f9dc..9f57fd6 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -20,6 +20,7 @@ import os, re forbidden_option_names = coredata.get_builtin_options() forbidden_prefixes = {'c_': True, 'cpp_': True, + 'd_': True, 'rust_': True, 'fortran_': True, 'objc_': True, @@ -77,7 +78,8 @@ class OptionInterpreter: def process(self, option_file): try: - ast = mparser.Parser(open(option_file, 'r', encoding='utf8').read()).parse() + with open(option_file, 'r', encoding='utf8') as f: + ast = mparser.Parser(f.read()).parse() except mesonlib.MesonException as me: me.file = option_file raise me @@ -97,14 +99,11 @@ class OptionInterpreter: def reduce_single(self, arg): if isinstance(arg, str): return arg - elif isinstance(arg, mparser.StringNode): - return arg.value - elif isinstance(arg, mparser.BooleanNode): + elif isinstance(arg, (mparser.StringNode, mparser.BooleanNode, + mparser.NumberNode)): return arg.value elif isinstance(arg, mparser.ArrayNode): return [self.reduce_single(curarg) for curarg in arg.args.arguments] - elif isinstance(arg, mparser.NumberNode): - return arg.value else: raise OptionException('Arguments may only be string, int, bool, or array of those.') diff --git a/mesonbuild/scripts/__init__.py b/mesonbuild/scripts/__init__.py index 19c4fc7..b2f2258 100644 --- a/mesonbuild/scripts/__init__.py +++ b/mesonbuild/scripts/__init__.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - # Copyright 2016 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index cb136f4..7124c6f 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -115,11 +115,21 @@ class Elf(DataSizes): self.bfile = bfile self.verbose = verbose self.bf = open(bfile, 'r+b') - (self.ptrsize, self.is_le) = self.detect_elf_type() - super().__init__(self.ptrsize, self.is_le) - self.parse_header() - self.parse_sections() - self.parse_dynamic() + try: + (self.ptrsize, self.is_le) = self.detect_elf_type() + super().__init__(self.ptrsize, self.is_le) + self.parse_header() + self.parse_sections() + self.parse_dynamic() + except: + self.bf.close() + raise + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.bf.close() def detect_elf_type(self): data = self.bf.read(6) @@ -308,13 +318,13 @@ def run(args): print('Don\'t run this unless you know what you are doing.') print('%s: <binary file> <prefix>' % sys.argv[0]) exit(1) - e = Elf(args[0]) - if len(args) == 1: - e.print_rpath() - e.print_runpath() - else: - new_rpath = args[1] - e.fix_rpath(new_rpath) + with Elf(args[0]) as e: + if len(args) == 1: + e.print_rpath() + e.print_runpath() + else: + new_rpath = args[1] + e.fix_rpath(new_rpath) return 0 if __name__ == '__main__': diff --git a/mesonbuild/scripts/meson_benchmark.py b/mesonbuild/scripts/meson_benchmark.py index d1107b6..6d138b0 100644 --- a/mesonbuild/scripts/meson_benchmark.py +++ b/mesonbuild/scripts/meson_benchmark.py @@ -52,33 +52,34 @@ def run_benchmarks(options, datafile): failed_tests = 0 logfile_base = 'meson-logs/benchmarklog' jsonlogfilename = logfile_base+ '.json' - jsonlogfile = open(jsonlogfilename, 'w') - tests = pickle.load(open(datafile, 'rb')) + with open(datafile, 'rb') as f: + tests = pickle.load(f) num_tests = len(tests) if num_tests == 0: print('No benchmarks defined.') return 0 iteration_count = 5 wrap = [] # Benchmarks on cross builds are pointless so don't support them. - for i, test in enumerate(tests): - runs = [] - durations = [] - failed = False - for _ in range(iteration_count): - res = meson_test.run_single_test(wrap, test) - runs.append(res) - durations.append(res.duration) - if res.returncode != 0: - failed = True - mean = statistics.mean(durations) - stddev = statistics.stdev(durations) - if failed: - resultstr = 'FAIL' - failed_tests += 1 - else: - resultstr = 'OK' - print_stats(3, num_tests, test.name, resultstr, i, mean, stddev) - print_json_log(jsonlogfile, runs, test.name, i) + with open(jsonlogfilename, 'w') as jsonlogfile: + for i, test in enumerate(tests): + runs = [] + durations = [] + failed = False + for _ in range(iteration_count): + res = meson_test.run_single_test(wrap, test) + runs.append(res) + durations.append(res.duration) + if res.returncode != 0: + failed = True + mean = statistics.mean(durations) + stddev = statistics.stdev(durations) + if failed: + resultstr = 'FAIL' + failed_tests += 1 + else: + resultstr = 'OK' + print_stats(3, num_tests, test.name, resultstr, i, mean, stddev) + print_json_log(jsonlogfile, runs, test.name, i) print('\nFull log written to meson-logs/benchmarklog.json.') return failed_tests diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index f075fa0..d2ae357 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -59,6 +59,13 @@ def run_exe(exe): stderr=subprocess.PIPE, env=child_env, cwd=exe.workdir) + stdout, stderr = p.communicate() + if exe.capture and p.returncode == 0: + with open(exe.capture, 'wb') as output: + output.write(stdout) + if stderr: + sys.stderr.buffer.write(stderr) + return p.returncode def run(args): global options @@ -67,8 +74,9 @@ def run(args): print('Test runner for Meson. Do not run on your own, mmm\'kay?') print(sys.argv[0] + ' [data file]') exe_data_file = options.args[0] - exe = pickle.load(open(exe_data_file, 'rb')) - run_exe(exe) + with open(exe_data_file, 'rb') as f: + exe = pickle.load(f) + return run_exe(exe) if __name__ == '__main__': sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index 1924b95..5cf02e6 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -37,9 +37,16 @@ def do_copy(from_file, to_file): shutil.copystat(from_file, to_file) append_to_log(to_file) +def get_destdir_path(d, path): + if os.path.isabs(path): + output = destdir_join(d.destdir, path) + else: + output = os.path.join(d.fullprefix, path) + return output + def do_install(datafilename): - ifile = open(datafilename, 'rb') - d = pickle.load(ifile) + with open(datafilename, 'rb') as ifile: + d = pickle.load(ifile) d.destdir = os.environ.get('DESTDIR', '') d.fullprefix = destdir_join(d.destdir, d.prefix) @@ -56,10 +63,7 @@ def install_subdirs(data): src_dir = src_dir[:-1] src_prefix = os.path.join(src_dir, inst_dir) print('Installing subdir %s to %s.' % (src_prefix, dst_dir)) - if os.path.isabs(dst_dir): - dst_dir = destdir_join(data.destdir, dst_dir) - else: - dst_dir = data.fullprefix + dst_dir + dst_dir = get_destdir_path(data, dst_dir) if not os.path.exists(dst_dir): os.makedirs(dst_dir) for root, dirs, files in os.walk(src_prefix): @@ -92,27 +96,23 @@ def install_subdirs(data): def install_data(d): for i in d.data: fullfilename = i[0] - outfilename = i[1] - if os.path.isabs(outfilename): - outdir = destdir_join(d.destdir, os.path.split(outfilename)[0]) - outfilename = destdir_join(d.destdir, outfilename) - else: - outdir = os.path.join(d.fullprefix, os.path.split(outfilename)[0]) - outfilename = os.path.join(outdir, os.path.split(outfilename)[1]) + outfilename = get_destdir_path(d, i[1]) + outdir = os.path.split(outfilename)[0] os.makedirs(outdir, exist_ok=True) print('Installing %s to %s.' % (fullfilename, outdir)) do_copy(fullfilename, outfilename) def install_man(d): for m in d.man: - outfileroot = m[1] - outfilename = os.path.join(d.fullprefix, outfileroot) full_source_filename = m[0] + outfilename = get_destdir_path(d, m[1]) outdir = os.path.split(outfilename)[0] os.makedirs(outdir, exist_ok=True) print('Installing %s to %s.' % (full_source_filename, outdir)) if outfilename.endswith('.gz') and not full_source_filename.endswith('.gz'): - open(outfilename, 'wb').write(gzip.compress(open(full_source_filename, 'rb').read())) + with open(outfilename, 'wb') as of: + with open(full_source_filename, 'rb') as sf: + of.write(gzip.compress(sf.read())) shutil.copystat(full_source_filename, outfilename) append_to_log(outfilename) else: @@ -121,8 +121,8 @@ def install_man(d): def install_headers(d): for t in d.headers: fullfilename = t[0] - outdir = os.path.join(d.fullprefix, t[1]) fname = os.path.split(fullfilename)[1] + outdir = get_destdir_path(d, t[1]) outfilename = os.path.join(outdir, fname) print('Installing %s to %s' % (fname, outdir)) os.makedirs(outdir, exist_ok=True) @@ -142,7 +142,8 @@ def run_install_script(d): print('Running custom install script %s' % script) suffix = os.path.splitext(script)[1].lower() if platform.system().lower() == 'windows' and suffix != '.bat': - first_line = open(script, encoding='latin_1', errors='ignore').readline().strip() + with open(script, encoding='latin_1', errors='ignore') as f: + first_line = f.readline().strip() if first_line.startswith('#!'): if shutil.which(first_line[2:]): commands = [first_line[2:]] @@ -194,9 +195,9 @@ def check_for_stampfile(fname): def install_targets(d): for t in d.targets: fname = check_for_stampfile(t[0]) - outdir = os.path.join(d.fullprefix, t[1]) - aliases = t[2] + outdir = get_destdir_path(d, t[1]) outname = os.path.join(outdir, os.path.split(fname)[-1]) + aliases = t[2] should_strip = t[3] install_rpath = t[4] print('Installing %s to %s' % (fname, outname)) diff --git a/mesonbuild/scripts/meson_test.py b/mesonbuild/scripts/meson_test.py index 33b6165..ab21654 100644 --- a/mesonbuild/scripts/meson_test.py +++ b/mesonbuild/scripts/meson_test.py @@ -44,13 +44,15 @@ parser.add_argument('args', nargs='+') class TestRun(): - def __init__(self, res, returncode, should_fail, duration, stdo, stde, cmd): + def __init__(self, res, returncode, should_fail, duration, stdo, stde, cmd, + env): self.res = res self.returncode = returncode self.duration = duration self.stdo = stdo self.stde = stde self.cmd = cmd + self.env = env self.should_fail = should_fail def get_log(self): @@ -58,7 +60,9 @@ class TestRun(): if self.cmd is None: res += 'NONE\n' else: - res += ' '.join(self.cmd) + '\n' + res += "\n%s %s\n" %(' '.join( + ["%s='%s'" % (k, v) for k, v in self.env.items()]), + ' ' .join(self.cmd)) if self.stdo: res += '--- stdout ---\n' res += self.stdo @@ -84,7 +88,8 @@ def write_json_log(jsonlogfile, test_name, result): 'result' : result.res, 'duration' : result.duration, 'returncode' : result.returncode, - 'command' : result.cmd} + 'command' : result.cmd, + 'env' : result.env} if result.stde: jresult['stderr'] = result.stde jsonlogfile.write(json.dumps(jresult) + '\n') @@ -162,7 +167,7 @@ def run_single_test(wrap, test): else: res = 'FAIL' returncode = p.returncode - return TestRun(res, returncode, test.should_fail, duration, stdo, stde, cmd) + return TestRun(res, returncode, test.should_fail, duration, stdo, stde, cmd, test.env) def print_stats(numlen, tests, name, result, i, logfile, jsonlogfile): global collected_logs, error_count, options @@ -202,10 +207,8 @@ def run_tests(datafilename): wrap = [options.wrapper] logfilename = logfile_base + '-' + options.wrapper.replace(' ', '_') + '.txt' jsonlogfilename = logfile_base + '-' + options.wrapper.replace(' ', '_') + '.json' - logfile = open(logfilename, 'w') - jsonlogfile = open(jsonlogfilename, 'w') - logfile.write('Log of Meson test suite run on %s.\n\n' % datetime.datetime.now().isoformat()) - tests = pickle.load(open(datafilename, 'rb')) + with open(datafilename, 'rb') as f: + tests = pickle.load(f) if len(tests) == 0: print('No tests defined.') return @@ -222,24 +225,31 @@ def run_tests(datafilename): executor = conc.ThreadPoolExecutor(max_workers=num_workers) futures = [] filtered_tests = filter_tests(options.suite, tests) - for i, test in enumerate(filtered_tests): - if test.suite[0] == '': - visible_name = test.name - else: - if options.suite is not None: - visible_name = options.suite + ' / ' + test.name + + with open(jsonlogfilename, 'w') as jsonlogfile, \ + open(logfilename, 'w') as logfile: + logfile.write('Log of Meson test suite run on %s.\n\n' % + datetime.datetime.now().isoformat()) + for i, test in enumerate(filtered_tests): + if test.suite[0] == '': + visible_name = test.name else: - visible_name = test.suite[0] + ' / ' + test.name + if options.suite is not None: + visible_name = options.suite + ' / ' + test.name + else: + visible_name = test.suite[0] + ' / ' + test.name - if not test.is_parallel: - drain_futures(futures) - futures = [] - res = run_single_test(wrap, test) - print_stats(numlen, filtered_tests, visible_name, res, i, logfile, jsonlogfile) - else: - f = executor.submit(run_single_test, wrap, test) - futures.append((f, numlen, filtered_tests, visible_name, i, logfile, jsonlogfile)) - drain_futures(futures) + if not test.is_parallel: + drain_futures(futures) + futures = [] + res = run_single_test(wrap, test) + print_stats(numlen, filtered_tests, visible_name, res, i, + logfile, jsonlogfile) + else: + f = executor.submit(run_single_test, wrap, test) + futures.append((f, numlen, filtered_tests, visible_name, i, + logfile, jsonlogfile)) + drain_futures(futures) return logfilename def run(args): diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py index f65f3bd..ddf4943 100644 --- a/mesonbuild/scripts/regen_checker.py +++ b/mesonbuild/scripts/regen_checker.py @@ -48,8 +48,10 @@ def run(args): private_dir = args[0] dumpfile = os.path.join(private_dir, 'regeninfo.dump') coredata = os.path.join(private_dir, 'coredata.dat') - regeninfo = pickle.load(open(dumpfile, 'rb')) - coredata = pickle.load(open(coredata, 'rb')) + with open(dumpfile, 'rb') as f: + regeninfo = pickle.load(f) + with open(coredata, 'rb') as f: + coredata = pickle.load(f) mesonscript = coredata.meson_script_file backend = coredata.get_builtin_option('backend') regen_timestamp = os.stat(dumpfile).st_mtime diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index 79c1264..c117301 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -34,16 +34,19 @@ parser.add_argument('args', nargs='+') def dummy_syms(outfilename): """Just touch it so relinking happens always.""" - open(outfilename, 'w').close() + with open(outfilename, 'w'): + pass def write_if_changed(text, outfilename): try: - oldtext = open(outfilename, 'r').read() + with open(outfilename, 'r') as f: + oldtext = f.read() if text == oldtext: return except FileNotFoundError: pass - open(outfilename, 'w').write(text) + with open(outfilename, 'w') as f: + f.write(text) def linux_syms(libfilename, outfilename): pe = subprocess.Popen(['readelf', '-d', libfilename], stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff --git a/mesonbuild/scripts/vcstagger.py b/mesonbuild/scripts/vcstagger.py index 390e37a..3f36e0a 100644 --- a/mesonbuild/scripts/vcstagger.py +++ b/mesonbuild/scripts/vcstagger.py @@ -23,9 +23,16 @@ def config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_ except Exception: new_string = fallback - new_data = open(infile).read().replace(replace_string, new_string) - if (not os.path.exists(outfile)) or (open(outfile).read() != new_data): - open(outfile, 'w').write(new_data) + with open(infile) as f: + new_data = f.read().replace(replace_string, new_string) + if os.path.exists(outfile): + with open(outfile) as f: + needs_update = (f.read() != new_data) + else: + needs_update = True + if needs_update: + with open(outfile, 'w') as f: + f.write(new_data) def run(args): infile, outfile, fallback, source_dir, replace_string, regex_selector = args[0:6] diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 6e3383c..f03af67 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -13,6 +13,7 @@ # limitations under the License. from .. import mlog +import contextlib import urllib.request, os, hashlib, shutil import subprocess import sys @@ -58,23 +59,23 @@ def open_wrapdburl(urlstring): class PackageDefinition: def __init__(self, fname): self.values = {} - ifile = open(fname) - first = ifile.readline().strip() + with open(fname) as ifile: + first = ifile.readline().strip() - if first == '[wrap-file]': - self.type = 'file' - elif first == '[wrap-git]': - self.type = 'git' - else: - raise RuntimeError('Invalid format of package file') - for line in ifile: - line = line.strip() - if line == '': - continue - (k, v) = line.split('=', 1) - k = k.strip() - v = v.strip() - self.values[k] = v + if first == '[wrap-file]': + self.type = 'file' + elif first == '[wrap-git]': + self.type = 'git' + else: + raise RuntimeError('Invalid format of package file') + for line in ifile: + line = line.strip() + if line == '': + continue + (k, v) = line.split('=', 1) + k = k.strip() + v = v.strip() + self.values[k] = v def get(self, key): return self.values[key] @@ -114,7 +115,10 @@ class Resolver: is_there = os.path.isdir(checkoutdir) if is_there: if revno.lower() == 'head': - subprocess.check_call(['git', 'pull'], cwd=checkoutdir) + # Failure to do pull is not a fatal error, + # because otherwise you can't develop without + # a working net connection. + subprocess.call(['git', 'pull'], cwd=checkoutdir) else: if subprocess.call(['git', 'checkout', revno], cwd=checkoutdir) != 0: subprocess.check_call(['git', 'fetch'], cwd=checkoutdir) @@ -134,26 +138,26 @@ class Resolver: resp = open_wrapdburl(url) else: resp = urllib.request.urlopen(url) - dlsize = int(resp.info()['Content-Length']) - 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) - ratio = int(downloaded/dlsize * 10) - while printed_dots < ratio: - print('.', end='') - sys.stdout.flush() - printed_dots += 1 - print('') - resp.close() + with contextlib.closing(resp) as resp: + dlsize = int(resp.info()['Content-Length']) + 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) + ratio = int(downloaded/dlsize * 10) + while printed_dots < ratio: + print('.', end='') + sys.stdout.flush() + printed_dots += 1 + print('') return b''.join(blocks) def get_hash(self, data): @@ -174,7 +178,8 @@ class Resolver: expected = p.get('source_hash') if dhash != expected: raise RuntimeError('Incorrect hash for source %s:\n %s expected\n %s actual.' % (packagename, expected, dhash)) - open(ofname, 'wb').write(srcdata) + with open(ofname, 'wb') as f: + f.write(srcdata) if p.has_patch(): purl = p.get('patch_url') mlog.log('Downloading patch from', mlog.bold(purl)) @@ -183,7 +188,9 @@ class Resolver: expected = p.get('patch_hash') if phash != expected: raise RuntimeError('Incorrect hash for patch %s:\n %s expected\n %s actual' % (packagename, expected, phash)) - open(os.path.join(self.cachedir, p.get('patch_filename')), 'wb').write(pdata) + filename = os.path.join(self.cachedir, p.get('patch_filename')) + with open(filename, 'wb') as f: + f.write(pdata) else: mlog.log('Package does not require patch.') diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index c5f8eef..e94a2c5 100755 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -92,7 +92,8 @@ def install(name): (branch, revision) = get_latest_version(name) u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%s/get_wrap' % (name, branch, revision)) data = u.read() - open(wrapfile, 'wb').write(data) + with open(wrapfile, 'wb') as f: + f.write(data) print('Installed', name, 'branch', branch, 'revision', revision) def get_current_version(wrapfile): @@ -129,7 +130,8 @@ def update(name): os.unlink(os.path.join('subprojects/packagecache', patch_file)) except FileNotFoundError: pass - open(wrapfile, 'wb').write(data) + with open(wrapfile, 'wb') as f: + f.write(data) print('Updated', name, 'to branch', new_branch, 'revision', new_revision) def info(name): diff --git a/run_tests.py b/run_tests.py index b71ab6d..b57dd39 100755 --- a/run_tests.py +++ b/run_tests.py @@ -156,8 +156,9 @@ def validate_install(srcdir, installdir): if os.path.exists(os.path.join(installdir, noinst_file)): expected[noinst_file] = False elif os.path.exists(info_file): - for line in open(info_file): - expected[platform_fix_exe_name(line.strip())] = False + with open(info_file) as f: + for line in f: + expected[platform_fix_exe_name(line.strip())] = False # Check if expected files were found for fname in expected: if os.path.exists(os.path.join(installdir, fname)): @@ -249,7 +250,8 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c (returncode, stdo, stde) = run_configure_inprocess(gen_command) try: logfile = os.path.join(test_build_dir, 'meson-logs/meson-log.txt') - mesonlog = open(logfile, errors='ignore').read() + with open(logfile, errors='ignore') as f: + mesonlog = f.read() except Exception: mesonlog = 'No meson-log.txt found.' gen_time = time.time() - gen_start @@ -304,6 +306,17 @@ def gather_tests(testdir): tests = [os.path.join(testdir, t[1]) for t in testlist] return tests +def have_d_compiler(): + if shutil.which("ldc2"): + return True + elif shutil.which("ldc"): + return True + elif shutil.which("gdc"): + return True + elif shutil.which("dmd"): + return True + return False + def detect_tests_to_run(): all_tests = [] all_tests.append(('common', gather_tests('test cases/common'), False)) @@ -318,6 +331,7 @@ def detect_tests_to_run(): all_tests.append(('C#', gather_tests('test cases/csharp'), False if shutil.which('mcs') else True)) all_tests.append(('vala', gather_tests('test cases/vala'), False if shutil.which('valac') else True)) all_tests.append(('rust', gather_tests('test cases/rust'), False if shutil.which('rustc') else True)) + all_tests.append(('d', gather_tests('test cases/d'), False if have_d_compiler() else True)) all_tests.append(('objective c', gather_tests('test cases/objc'), False if not mesonlib.is_windows() else True)) all_tests.append(('fortran', gather_tests('test cases/fortran'), False if shutil.which('gfortran') else True)) all_tests.append(('swift', gather_tests('test cases/swift'), False if shutil.which('swiftc') else True)) @@ -389,7 +403,9 @@ def run_tests(extra_args): def check_file(fname): linenum = 1 - for line in open(fname, 'rb').readlines(): + with open(fname, 'rb') as f: + lines = f.readlines() + for line in lines: if b'\t' in line: print("File %s contains a literal tab on line %d. Only spaces are permitted." % (fname, linenum)) sys.exit(1) diff --git a/test cases/common/103 manygen/subdir/manygen.py b/test cases/common/103 manygen/subdir/manygen.py index 4411183..4fd2f25 100755 --- a/test cases/common/103 manygen/subdir/manygen.py +++ b/test cases/common/103 manygen/subdir/manygen.py @@ -6,7 +6,8 @@ import sys, os import shutil, subprocess -funcname = open(sys.argv[1]).readline().strip() +with open(sys.argv[1]) as f: + funcname = f.readline().strip() outdir = sys.argv[2] if not os.path.isdir(outdir): @@ -21,7 +22,8 @@ if not compiler: shutil.which('clang') or \ shutil.which('cc') -if 'cl' in os.path.basename(compiler): +compbase = os.path.basename(compiler) +if 'cl' in compbase and 'clang' not in compbase: libsuffix = '.lib' is_vs = True compiler = 'cl' @@ -44,19 +46,22 @@ outc = os.path.join(outdir, funcname + '.c') tmpc = 'diibadaaba.c' tmpo = 'diibadaaba' + objsuffix -open(outc, 'w').write('''#include"%s.h" +with open(outc, 'w') as f: + f.write('''#include"%s.h" int %s_in_src() { return 0; } ''' % (funcname, funcname)) -open(outh, 'w').write('''#pragma once +with open(outh, 'w') as f: + f.write('''#pragma once int %s_in_lib(); int %s_in_obj(); int %s_in_src(); ''' % (funcname, funcname, funcname)) -open(tmpc, 'w').write('''int %s_in_obj() { +with open(tmpc, 'w') as f: + f.write('''int %s_in_obj() { return 0; } ''' % funcname) @@ -66,7 +71,8 @@ if is_vs: else: subprocess.check_call([compiler, '-c', '-o', outo, tmpc]) -open(tmpc, 'w').write('''int %s_in_lib() { +with open(tmpc, 'w') as f: + f.write('''int %s_in_lib() { return 0; } ''' % funcname) diff --git a/test cases/common/107 postconf/postconf.py b/test cases/common/107 postconf/postconf.py index 209b7af..50c91ca 100644 --- a/test cases/common/107 postconf/postconf.py +++ b/test cases/common/107 postconf/postconf.py @@ -7,5 +7,10 @@ template = '''#pragma once #define THE_NUMBER {} ''' -data = open(os.path.join(os.environ['MESON_SOURCE_ROOT'], 'raw.dat')).readline().strip() -open(os.path.join(os.environ['MESON_BUILD_ROOT'], 'generated.h'), 'w').write(template.format(data)) +input_file = os.path.join(os.environ['MESON_SOURCE_ROOT'], 'raw.dat') +output_file = os.path.join(os.environ['MESON_BUILD_ROOT'], 'generated.h') + +with open(input_file) as f: + data = f.readline().strip() +with open(output_file, 'w') as f: + f.write(template.format(data)) diff --git a/test cases/common/108 postconf with args/postconf.py b/test cases/common/108 postconf with args/postconf.py index 4cfbb7c..cef7f79 100644 --- a/test cases/common/108 postconf with args/postconf.py +++ b/test cases/common/108 postconf with args/postconf.py @@ -9,5 +9,10 @@ template = '''#pragma once #define THE_ARG2 {} ''' -data = open(os.path.join(os.environ['MESON_SOURCE_ROOT'], 'raw.dat')).readline().strip() -open(os.path.join(os.environ['MESON_BUILD_ROOT'], 'generated.h'), 'w').write(template.format(data, sys.argv[1], sys.argv[2])) +input_file = os.path.join(os.environ['MESON_SOURCE_ROOT'], 'raw.dat') +output_file = os.path.join(os.environ['MESON_BUILD_ROOT'], 'generated.h') + +with open(input_file) as f: + data = f.readline().strip() +with open(output_file, 'w') as f: + f.write(template.format(data, sys.argv[1], sys.argv[2])) diff --git a/test cases/common/113 generatorcustom/catter.py b/test cases/common/113 generatorcustom/catter.py index 354d6e0..7a6c085 100755 --- a/test cases/common/113 generatorcustom/catter.py +++ b/test cases/common/113 generatorcustom/catter.py @@ -8,6 +8,7 @@ inputs = sys.argv[1:-1] with open(output, 'w') as ofile: ofile.write('#pragma once\n') for i in inputs: - content = open(i, 'r').read() + with open(i, 'r') as ifile: + content = ifile.read() ofile.write(content) ofile.write('\n') diff --git a/test cases/common/113 generatorcustom/gen.py b/test cases/common/113 generatorcustom/gen.py index ba02e3f..c843497 100755 --- a/test cases/common/113 generatorcustom/gen.py +++ b/test cases/common/113 generatorcustom/gen.py @@ -5,7 +5,9 @@ import sys, os ifile = sys.argv[1] ofile = sys.argv[2] -resname = open(ifile, 'r').readline().strip() +with open(ifile, 'r') as f: + resname = f.readline().strip() templ = 'const char %s[] = "%s";\n' -open(ofile, 'w').write(templ % (resname, resname)) +with open(ofile, 'w') as f: + f.write(templ % (resname, resname)) diff --git a/test cases/common/117 custom target capture/data_source.txt b/test cases/common/117 custom target capture/data_source.txt new file mode 100644 index 0000000..0c23cc0 --- /dev/null +++ b/test cases/common/117 custom target capture/data_source.txt @@ -0,0 +1 @@ +This is a text only input file. diff --git a/test cases/common/117 custom target capture/installed_files.txt b/test cases/common/117 custom target capture/installed_files.txt new file mode 100644 index 0000000..d90a6b0 --- /dev/null +++ b/test cases/common/117 custom target capture/installed_files.txt @@ -0,0 +1 @@ +usr/subdir/data.dat diff --git a/test cases/common/117 custom target capture/meson.build b/test cases/common/117 custom target capture/meson.build new file mode 100644 index 0000000..6c19752 --- /dev/null +++ b/test cases/common/117 custom target capture/meson.build @@ -0,0 +1,16 @@ +project('custom target', 'c') + +python = find_program('python3') + +# Note that this will not add a dependency to the compiler executable. +# Code will not be rebuilt if it changes. +comp = '@0@/@1@'.format(meson.current_source_dir(), 'my_compiler.py') + +mytarget = custom_target('bindat', +output : 'data.dat', +input : 'data_source.txt', +capture : true, +command : [python, comp, '@INPUT@'], +install : true, +install_dir : 'subdir' +) diff --git a/test cases/common/117 custom target capture/my_compiler.py b/test cases/common/117 custom target capture/my_compiler.py new file mode 100755 index 0000000..b60722a --- /dev/null +++ b/test cases/common/117 custom target capture/my_compiler.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys + +if __name__ == '__main__': + if len(sys.argv) != 2: + print(sys.argv[0], 'input_file') + sys.exit(1) + with open(sys.argv[1]) as f: + ifile = f.read() + if ifile != 'This is a text only input file.\n': + print('Malformed input') + sys.exit(1) + print('This is a binary output file.') diff --git a/test cases/common/16 configure file/generator.py b/test cases/common/16 configure file/generator.py index de9a423..31a6944 100755 --- a/test cases/common/16 configure file/generator.py +++ b/test cases/common/16 configure file/generator.py @@ -1,13 +1,11 @@ #!/usr/bin/env python3 -import sys +import sys, os if len(sys.argv) != 3: print("Wrong amount of parameters.") -# Just test that it exists. -ifile = open(sys.argv[1], 'r') +assert(os.path.exists(sys.argv[1])) -ofile = open(sys.argv[2], 'w') -ofile.write("#define ZERO_RESULT 0\n") -ofile.close() +with open(sys.argv[2], 'w') as ofile: + ofile.write("#define ZERO_RESULT 0\n") diff --git a/test cases/common/29 pipeline/meson.build b/test cases/common/29 pipeline/meson.build index 8418381..200a6d8 100644 --- a/test cases/common/29 pipeline/meson.build +++ b/test cases/common/29 pipeline/meson.build @@ -6,8 +6,9 @@ e1 = executable('srcgen', 'srcgen.c', native : true) # Generate a source file that needs to be included in the build. gen = generator(e1, \ - output : '@BASENAME@.c', # Line continuation inside arguments should work without needing a "\". - arguments : ['@INPUT@', '@OUTPUT@']) + depfile : '@BASENAME@.d', + output : '@BASENAME@.c', # Line continuation inside arguments should work without needing a "\". + arguments : ['@INPUT@', '@OUTPUT@', '@DEPFILE@']) generated = gen.process(['input_src.dat']) diff --git a/test cases/common/29 pipeline/srcgen.c b/test cases/common/29 pipeline/srcgen.c index 8095724..ceb9ecc 100644 --- a/test cases/common/29 pipeline/srcgen.c +++ b/test cases/common/29 pipeline/srcgen.c @@ -1,5 +1,6 @@ #include<stdio.h> #include<assert.h> +#include<string.h> #define ARRSIZE 80 @@ -7,17 +8,20 @@ int main(int argc, char **argv) { char arr[ARRSIZE]; char *ofilename; char *ifilename; + char *dfilename; FILE *ifile; FILE *ofile; + FILE *depfile; size_t bytes; + int i; - if(argc != 3) { - fprintf(stderr, "%s <input file> <output file>\n", argv[0]); + if(argc != 4) { + fprintf(stderr, "%s <input file> <output file> <dependency file>\n", argv[0]); return 1; } ifilename = argv[1]; ofilename = argv[2]; - printf("%s\n", ifilename); + dfilename = argv[3]; ifile = fopen(argv[1], "r"); if(!ifile) { fprintf(stderr, "Could not open source file %s.\n", argv[1]); @@ -34,7 +38,32 @@ int main(int argc, char **argv) { assert(bytes > 0); fwrite(arr, 1, bytes, ofile); + depfile = fopen(dfilename, "w"); + if(!depfile) { + fprintf(stderr, "Could not open depfile %s\n", ofilename); + fclose(ifile); + fclose(ofile); + return 1; + } + for(i=0; i<strlen(ofilename); i++) { + if(ofilename[i] == ' ') { + fwrite("\\ ", 1, 2, depfile); + } else { + fwrite(&ofilename[i], 1, 1, depfile); + } + } + fwrite(": ", 1, 2, depfile); + for(i=0; i<strlen(ifilename); i++) { + if(ifilename[i] == ' ') { + fwrite("\\ ", 1, 2, depfile); + } else { + fwrite(&ifilename[i], 1, 1, depfile); + } + } + fwrite("\n", 1, 1, depfile); + fclose(ifile); fclose(ofile); + fclose(depfile); return 0; } diff --git a/test cases/common/38 run program/meson.build b/test cases/common/38 run program/meson.build index 20a8107..1563dec 100644 --- a/test cases/common/38 run program/meson.build +++ b/test cases/common/38 run program/meson.build @@ -41,3 +41,17 @@ endif if cs.stderr() != '' error('Extra text in stderr (script).') endif + +# We should be able to have files() in argument +f = files('meson.build') + +if build_machine.system() == 'windows' + c = run_command('cmd', '/c', 'echo', f) +else + c = run_command('echo', f) +endif + +if c.returncode() != 0 + error('Using files() in argument failed.') +endif + diff --git a/test cases/common/44 has member/meson.build b/test cases/common/44 has member/meson.build index fa01877..e60aeb3 100644 --- a/test cases/common/44 has member/meson.build +++ b/test cases/common/44 has member/meson.build @@ -3,9 +3,17 @@ project('has member', 'c') cc = meson.get_compiler('c') if not cc.has_member('struct tm', 'tm_sec', prefix : '#include<time.h>') - error('Did not detect member that exists.') + error('Did not detect member of "struct tm" that exists: "tm_sec"') endif if cc.has_member('struct tm', 'tm_nonexistent', prefix : '#include<time.h>') - error('Not existing member found.') + error('Not existing member "tm_nonexistent" found.') +endif + +if not cc.has_members('struct tm', 'tm_sec', 'tm_min', prefix : '#include<time.h>') + error('Did not detect members of "struct tm" that exist: "tm_sec" "tm_min"') +endif + +if cc.has_members('struct tm', 'tm_sec', 'tm_nonexistent2', prefix : '#include<time.h>') + error('Not existing member "tm_nonexistent2" found.') endif diff --git a/test cases/common/48 test args/tester.py b/test cases/common/48 test args/tester.py index 36e510d..0b4010a 100755 --- a/test cases/common/48 test args/tester.py +++ b/test cases/common/48 test args/tester.py @@ -2,5 +2,6 @@ import sys -if open(sys.argv[1]).read() != 'contents\n': - sys.exit(1) +with open(sys.argv[1]) as f: + if f.read() != 'contents\n': + sys.exit(1) diff --git a/test cases/common/52 custom install dirs/installed_files.txt b/test cases/common/52 custom install dirs/installed_files.txt index 1b8b561..0cc533a 100644 --- a/test cases/common/52 custom install dirs/installed_files.txt +++ b/test cases/common/52 custom install dirs/installed_files.txt @@ -1,4 +1,10 @@ usr/dib/dab/dub/prog?exe +usr/dib/dab/dub2/prog2?exe usr/some/dir/sample.h +usr/some/dir2/sample.h usr/woman/prog.1.gz +usr/woman2/prog.1.gz usr/meow/datafile.cat +usr/meow2/datafile.cat +usr/woof/subdir/datafile.dog +usr/woof2/subdir/datafile.dog diff --git a/test cases/common/52 custom install dirs/meson.build b/test cases/common/52 custom install dirs/meson.build index 622ecad..494ff0e 100644 --- a/test cases/common/52 custom install dirs/meson.build +++ b/test cases/common/52 custom install dirs/meson.build @@ -1,5 +1,11 @@ project('custom install dirs', 'c') executable('prog', 'prog.c', install : true, install_dir : 'dib/dab/dub') +executable('prog2', 'prog.c', install : true, install_dir : get_option('prefix') + '/dib/dab/dub2') install_headers('sample.h', install_dir : 'some/dir') +install_headers('sample.h', install_dir : get_option('prefix') + '/some/dir2') install_man('prog.1', install_dir : 'woman') +install_man('prog.1', install_dir : get_option('prefix') + '/woman2') install_data('datafile.cat', install_dir : 'meow') +install_data('datafile.cat', install_dir : get_option('prefix') + '/meow2') +install_subdir('subdir', install_dir : 'woof') +install_subdir('subdir', install_dir : get_option('prefix') + '/woof2') diff --git a/test cases/common/52 custom install dirs/subdir/datafile.dog b/test cases/common/52 custom install dirs/subdir/datafile.dog new file mode 100644 index 0000000..7a5bcb7 --- /dev/null +++ b/test cases/common/52 custom install dirs/subdir/datafile.dog @@ -0,0 +1 @@ +Installed dog is installed. diff --git a/test cases/common/56 custom target/depfile/dep.py b/test cases/common/56 custom target/depfile/dep.py new file mode 100755 index 0000000..585e192 --- /dev/null +++ b/test cases/common/56 custom target/depfile/dep.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +import sys, os +from glob import glob + +_, srcdir, depfile, output = sys.argv + +depfiles = glob(os.path.join(srcdir, '*')) + +quoted_depfiles = [x.replace(' ', '\ ') for x in depfiles] + +with open(output, 'w') as f: + f.write('I am the result of globbing.') +with open(depfile, 'w') as f: + f.write('%s: %s\n' % (output, ' '.join(quoted_depfiles))) diff --git a/test cases/common/56 custom target/depfile/meson.build b/test cases/common/56 custom target/depfile/meson.build new file mode 100644 index 0000000..46bca74 --- /dev/null +++ b/test cases/common/56 custom target/depfile/meson.build @@ -0,0 +1,7 @@ + + +mytarget = custom_target('depfile', + output : 'dep.dat', + depfile : 'dep.dat.d', + command : [find_program('dep.py'), meson.current_source_dir(), '@DEPFILE@', '@OUTPUT@'], +) diff --git a/test cases/common/56 custom target/meson.build b/test cases/common/56 custom target/meson.build index db81824..e216bae 100644 --- a/test cases/common/56 custom target/meson.build +++ b/test cases/common/56 custom target/meson.build @@ -13,3 +13,5 @@ command : [python, comp, '@INPUT@', '@OUTPUT@'], install : true, install_dir : 'subdir' ) + +subdir('depfile') diff --git a/test cases/common/56 custom target/my_compiler.py b/test cases/common/56 custom target/my_compiler.py index 43e7143..d99029b 100755 --- a/test cases/common/56 custom target/my_compiler.py +++ b/test cases/common/56 custom target/my_compiler.py @@ -6,9 +6,10 @@ if __name__ == '__main__': if len(sys.argv) != 3: print(sys.argv[0], 'input_file output_file') sys.exit(1) - ifile = open(sys.argv[1]).read() + with open(sys.argv[1]) as f: + ifile = f.read() if ifile != 'This is a text only input file.\n': print('Malformed input') sys.exit(1) - ofile = open(sys.argv[2], 'w') - ofile.write('This is a binary output file.\n') + with open(sys.argv[2], 'w') as ofile: + ofile.write('This is a binary output file.\n') diff --git a/test cases/common/57 custom target chain/installed_files.txt b/test cases/common/57 custom target chain/installed_files.txt index 4e326a2..7feb072 100644 --- a/test cases/common/57 custom target chain/installed_files.txt +++ b/test cases/common/57 custom target chain/installed_files.txt @@ -1 +1,2 @@ usr/subdir/data2.dat +usr/subdir/data3.dat diff --git a/test cases/common/57 custom target chain/meson.build b/test cases/common/57 custom target chain/meson.build index 286ff61..1af0425 100644 --- a/test cases/common/57 custom target chain/meson.build +++ b/test cases/common/57 custom target chain/meson.build @@ -4,7 +4,7 @@ python = find_program('python3') comp = '@0@/@1@'.format(meson.current_source_dir(), 'my_compiler.py') comp2 = '@0@/@1@'.format(meson.current_source_dir(), 'my_compiler2.py') -infile = '@0@/@1@'.format(meson.current_source_dir(), 'data_source.txt') +infile = files('data_source.txt')[0] mytarget = custom_target('bindat', output : 'data.dat', @@ -18,4 +18,12 @@ mytarget2 = custom_target('bindat2', install_dir : 'subdir' ) +mytarget3 = custom_target('bindat3', + output : 'data3.dat', + input : [mytarget], + command : [python, comp2, '@INPUT@', '@OUTPUT@'], + install : true, + install_dir : 'subdir' +) + subdir('usetarget') diff --git a/test cases/common/57 custom target chain/my_compiler.py b/test cases/common/57 custom target chain/my_compiler.py index 43e7143..d99029b 100755 --- a/test cases/common/57 custom target chain/my_compiler.py +++ b/test cases/common/57 custom target chain/my_compiler.py @@ -6,9 +6,10 @@ if __name__ == '__main__': if len(sys.argv) != 3: print(sys.argv[0], 'input_file output_file') sys.exit(1) - ifile = open(sys.argv[1]).read() + with open(sys.argv[1]) as f: + ifile = f.read() if ifile != 'This is a text only input file.\n': print('Malformed input') sys.exit(1) - ofile = open(sys.argv[2], 'w') - ofile.write('This is a binary output file.\n') + with open(sys.argv[2], 'w') as ofile: + ofile.write('This is a binary output file.\n') diff --git a/test cases/common/57 custom target chain/my_compiler2.py b/test cases/common/57 custom target chain/my_compiler2.py index 22a4160..22ec789 100755 --- a/test cases/common/57 custom target chain/my_compiler2.py +++ b/test cases/common/57 custom target chain/my_compiler2.py @@ -6,9 +6,10 @@ if __name__ == '__main__': if len(sys.argv) != 3: print(sys.argv[0], 'input_file output_file') sys.exit(1) - ifile = open(sys.argv[1]).read() + with open(sys.argv[1]) as f: + ifile = f.read() if ifile != 'This is a binary output file.\n': print('Malformed input') sys.exit(1) - ofile = open(sys.argv[2], 'w') - ofile.write('This is a different binary output file.\n') + with open(sys.argv[2], 'w') as ofile: + ofile.write('This is a different binary output file.\n') diff --git a/test cases/common/57 custom target chain/usetarget/subcomp.py b/test cases/common/57 custom target chain/usetarget/subcomp.py index 207a8ad..6f4b686 100755 --- a/test cases/common/57 custom target chain/usetarget/subcomp.py +++ b/test cases/common/57 custom target chain/usetarget/subcomp.py @@ -3,5 +3,5 @@ import sys, os with open(sys.argv[1], 'rb') as ifile: - open(sys.argv[2], 'w').write('Everything ok.\n') - + with open(sys.argv[2], 'w') as ofile: + ofile.write('Everything ok.\n') diff --git a/test cases/common/58 run target/converter.py b/test cases/common/58 run target/converter.py index 6acbc84..8dd31fe 100644 --- a/test cases/common/58 run target/converter.py +++ b/test cases/common/58 run target/converter.py @@ -2,4 +2,5 @@ import sys -open(sys.argv[2], 'wb').write(open(sys.argv[1], 'rb').read()) +with open(sys.argv[1], 'rb') as ifile, open(sys.argv[2], 'wb') as ofile: + ofile.write(ifile.read()) diff --git a/test cases/common/58 run target/fakeburner.py b/test cases/common/58 run target/fakeburner.py index a100a6f..5728002 100755 --- a/test cases/common/58 run target/fakeburner.py +++ b/test cases/common/58 run target/fakeburner.py @@ -5,7 +5,8 @@ import sys plain_arg = sys.argv[1] _, filename, _ = plain_arg.split(':') try: - content = open(filename, 'rb').read() + with open(filename, 'rb') as f: + content = f.read() except FileNotFoundError: print('Could not open file. Missing dependency?') sys.exit(1) diff --git a/test cases/common/58 run target/meson.build b/test cases/common/58 run target/meson.build index 0febe35..0ab41b3 100644 --- a/test cases/common/58 run target/meson.build +++ b/test cases/common/58 run target/meson.build @@ -34,3 +34,12 @@ run_target('upload2', python3 = find_program('python3') run_target('py3hi', command : [python3, '-c', 'print("I am Python3.")']) + +run_target('ct_in_arg', + command : ['echo', hex, files('helloprinter.c')]) + +# What if the output of a custom_target is the command to +# execute. Obviously this will not work as hex is not an +# executable but test that the output is generated correctly. +run_target('donotrunme', + command : [hex]) diff --git a/test cases/common/61 custom target source output/generator.py b/test cases/common/61 custom target source output/generator.py index 4bf5c84..3464b0a 100755 --- a/test cases/common/61 custom target source output/generator.py +++ b/test cases/common/61 custom target source output/generator.py @@ -7,8 +7,10 @@ if len(sys.argv) != 2: odir = sys.argv[1] -open(os.path.join(odir, 'mylib.h'), 'w').write('int func();\n') -open(os.path.join(odir, 'mylib.c'), 'w').write('''int func() { +with open(os.path.join(odir, 'mylib.h'), 'w') as f: + f.write('int func();\n') +with open(os.path.join(odir, 'mylib.c'), 'w') as f: + f.write('''int func() { return 0; } ''') diff --git a/test cases/common/64 custom header generator/makeheader.py b/test cases/common/64 custom header generator/makeheader.py index 9ef2bd5..f156834 100644 --- a/test cases/common/64 custom header generator/makeheader.py +++ b/test cases/common/64 custom header generator/makeheader.py @@ -6,5 +6,7 @@ import sys template = '#define RET_VAL %s\n' -output = template % (open(sys.argv[1]).readline().strip()) -open(sys.argv[2], 'w').write(output) +with open(sys.argv[1]) as f: + output = template % (f.readline().strip(), ) +with open(sys.argv[2], 'w') as f: + f.write(output) diff --git a/test cases/common/65 multiple generators/mygen.py b/test cases/common/65 multiple generators/mygen.py index cd786ea..99dc331 100755 --- a/test cases/common/65 multiple generators/mygen.py +++ b/test cases/common/65 multiple generators/mygen.py @@ -6,14 +6,17 @@ if len(sys.argv) != 3: print("You is fail.") sys.exit(1) -val = open(sys.argv[1]).read().strip() +with open(sys.argv[1]) as f: + val = f.read().strip() outdir = sys.argv[2] outhdr = os.path.join(outdir, 'source%s.h' % val) outsrc = os.path.join(outdir, 'source%s.cpp' % val) -open(outhdr, 'w').write('int func%s();\n' % val) -open(outsrc, 'w').write('''int func%s() { +with open(outhdr, 'w') as f: + f.write('int func%s();\n' % val) +with open(outsrc, 'w') as f: + f.write('''int func%s() { return 0; } ''' % val) diff --git a/test cases/common/68 number arithmetic/meson.build b/test cases/common/68 number arithmetic/meson.build index 4b98d73..02c7878 100644 --- a/test cases/common/68 number arithmetic/meson.build +++ b/test cases/common/68 number arithmetic/meson.build @@ -21,6 +21,27 @@ if (5 / 3) * 3 != 3 error('Integer division is broken') endif +assert((5 % 2) == 1, 'Integer modulo (odd) is broken') +assert((4 % 2) == 0, 'Integer modulo (even) is broken') + +if 2 * 1 % 2 != 0 + error('Modulo precendence with multiplication is broken') +endif +if 2 + 1 % 2 != 3 + error('Modulo precendence with addition is broken') +endif +if 9 / 9 % 2 != 1 + error('Modulo precendence with division is broken') +endif +if 9 - 9 % 2 != 8 + error('Modulo precendence with subtraction is broken') +endif + +assert(2.is_even(), 'int is_even() broken for even value') +assert(not(2.is_odd()), 'int is_odd() broken for even value') +assert(not(3.is_even()), 'int is_even() broken for odd value') +assert(3.is_odd(), 'int is_odd() broken for odd value') + assert(3 < 4, 'Lt broken') assert(not(4 < 3), 'Lt broken') assert(3 <= 4, 'Lte broken') diff --git a/test cases/common/72 build always/version_gen.py b/test cases/common/72 build always/version_gen.py index c82678d..d7b01ca 100755 --- a/test cases/common/72 build always/version_gen.py +++ b/test cases/common/72 build always/version_gen.py @@ -14,14 +14,17 @@ def generate(infile, outfile, fallback): version = stdo.decode().strip() except: pass - newdata = open(infile).read().replace('@VERSION@', version) + with open(infile) as f: + newdata = f.read().replace('@VERSION@', version) try: - olddata = open(outfile).read() + with open(outfile) as f: + olddata = f.read() if olddata == newdata: return except: pass - open(outfile, 'w').write(newdata) + with open(outfile, 'w') as f: + f.write(newdata) if __name__ == '__main__': infile = sys.argv[1] diff --git a/test cases/common/76 configure file in custom target/src/mycompiler.py b/test cases/common/76 configure file in custom target/src/mycompiler.py index d5dcab5..b00c862 100644 --- a/test cases/common/76 configure file in custom target/src/mycompiler.py +++ b/test cases/common/76 configure file in custom target/src/mycompiler.py @@ -2,7 +2,8 @@ import sys -ifile = open(sys.argv[1]) -if ifile.readline().strip() != '42': - print('Incorrect input') -open(sys.argv[2], 'w').write('Success\n') +with open(sys.argv[1]) as ifile: + if ifile.readline().strip() != '42': + print('Incorrect input') +with open(sys.argv[2], 'w') as ofile: + ofile.write('Success\n') diff --git a/test cases/common/78 ctarget dependency/gen1.py b/test cases/common/78 ctarget dependency/gen1.py index 64b8e6d..0fa6ea1 100755 --- a/test cases/common/78 ctarget dependency/gen1.py +++ b/test cases/common/78 ctarget dependency/gen1.py @@ -6,5 +6,7 @@ import time, sys # is missing. time.sleep(0.5) -contents = open(sys.argv[1], 'r').read() -open(sys.argv[2], 'w').write(contents) +with open(sys.argv[1], 'r') as f: + contents = f.read() +with open(sys.argv[2], 'w') as f: + f.write(contents) diff --git a/test cases/common/78 ctarget dependency/gen2.py b/test cases/common/78 ctarget dependency/gen2.py index 3a8be7d..b087b02 100755 --- a/test cases/common/78 ctarget dependency/gen2.py +++ b/test cases/common/78 ctarget dependency/gen2.py @@ -6,4 +6,5 @@ from glob import glob files = glob(os.path.join(sys.argv[1], '*.tmp')) assert(len(files) == 1) -open(sys.argv[2], 'w').write(open(files[0], 'r').read()) +with open(files[0], 'r') as ifile, open(sys.argv[2], 'w') as ofile: + ofile.write(ifile.read()) diff --git a/test cases/common/93 private include/stlib/compiler.py b/test cases/common/93 private include/stlib/compiler.py index 3e74c88..98dbe46 100755 --- a/test cases/common/93 private include/stlib/compiler.py +++ b/test cases/common/93 private include/stlib/compiler.py @@ -26,5 +26,7 @@ hfile = os.path.join(outdir, base + '.h') c_code = c_templ % (base, base) h_code = h_templ % base -open(cfile, 'w').write(c_code) -open(hfile, 'w').write(h_code) +with open(cfile, 'w') as f: + f.write(c_code) +with open(hfile, 'w') as f: + f.write(h_code) diff --git a/test cases/common/95 dep fallback/meson.build b/test cases/common/95 dep fallback/meson.build index 86fb6b2..4cf0577 100644 --- a/test cases/common/95 dep fallback/meson.build +++ b/test cases/common/95 dep fallback/meson.build @@ -1,6 +1,10 @@ project('dep fallback', 'c') -bob = dependency('boblib', fallback : ['boblib', 'bob_dep']) +bob = dependency('boblib', fallback : ['boblib', 'bob_dep'], required: false) +if not bob.found() + error('Bob is actually needed') +endif +jimmy = dependency('jimmylib', fallback : ['jimmylib', 'jimmy_dep'], required: false) exe = executable('bobtester', 'tester.c', dependencies : bob) test('bobtester', exe) diff --git a/test cases/common/98 gen extra/srcgen.py b/test cases/common/98 gen extra/srcgen.py index 55e777e..73bc337 100755 --- a/test cases/common/98 gen extra/srcgen.py +++ b/test cases/common/98 gen extra/srcgen.py @@ -19,8 +19,10 @@ c_templ = '''int %s() { options = parser.parse_args(sys.argv[1:]) -funcname = open(options.input).readline().strip() +with open(options.input) as f: + funcname = f.readline().strip() if options.upper: funcname = funcname.upper() -open(options.output, 'w').write(c_templ % funcname) +with open(options.output, 'w') as f: + f.write(c_templ % funcname) diff --git a/test cases/d/1 simple/app.d b/test cases/d/1 simple/app.d new file mode 100644 index 0000000..0be1d2c --- /dev/null +++ b/test cases/d/1 simple/app.d @@ -0,0 +1,8 @@ + +import std.stdio; +import utils; + +void main () +{ + printGreeting ("a Meson D test"); +} diff --git a/test cases/d/1 simple/installed_files.txt b/test cases/d/1 simple/installed_files.txt new file mode 100644 index 0000000..9374c54 --- /dev/null +++ b/test cases/d/1 simple/installed_files.txt @@ -0,0 +1 @@ +usr/bin/dsimpleapp?exe diff --git a/test cases/d/1 simple/meson.build b/test cases/d/1 simple/meson.build new file mode 100644 index 0000000..a10b67b --- /dev/null +++ b/test cases/d/1 simple/meson.build @@ -0,0 +1,4 @@ +project('D Simple Test', 'd') + +e = executable('dsimpleapp', ['app.d', 'utils.d'], install : true) +test('apptest', e) diff --git a/test cases/d/1 simple/utils.d b/test cases/d/1 simple/utils.d new file mode 100644 index 0000000..8645548 --- /dev/null +++ b/test cases/d/1 simple/utils.d @@ -0,0 +1,8 @@ + +import std.stdio; +import std.string : format; + +void printGreeting (string name) +{ + writeln ("Hello, I am %s.".format (name)); +} diff --git a/test cases/d/2 static library/app.d b/test cases/d/2 static library/app.d new file mode 100644 index 0000000..5d84a69 --- /dev/null +++ b/test cases/d/2 static library/app.d @@ -0,0 +1,8 @@ + +import libstuff; + +void main () +{ + immutable ret = printLibraryString ("foo"); + assert (ret == 4); +} diff --git a/test cases/d/2 static library/installed_files.txt b/test cases/d/2 static library/installed_files.txt new file mode 100644 index 0000000..0f2bab4 --- /dev/null +++ b/test cases/d/2 static library/installed_files.txt @@ -0,0 +1,2 @@ +usr/bin/app_s?exe +usr/lib/libstuff.a diff --git a/test cases/d/2 static library/libstuff.d b/test cases/d/2 static library/libstuff.d new file mode 100644 index 0000000..fd3b4d0 --- /dev/null +++ b/test cases/d/2 static library/libstuff.d @@ -0,0 +1,9 @@ + +import std.stdio; +import std.string : format; + +int printLibraryString (string str) +{ + writeln ("Static Library says: %s".format (str)); + return 4; +} diff --git a/test cases/d/2 static library/meson.build b/test cases/d/2 static library/meson.build new file mode 100644 index 0000000..88ed2cb --- /dev/null +++ b/test cases/d/2 static library/meson.build @@ -0,0 +1,5 @@ +project('D Static Library', 'd') + +lstatic = static_library('stuff', 'libstuff.d', install : true) +es = executable('app_s', 'app.d', link_with : lstatic, install : true) +test('linktest_static', es) diff --git a/test cases/d/3 shared library/app.d b/test cases/d/3 shared library/app.d new file mode 100644 index 0000000..5d84a69 --- /dev/null +++ b/test cases/d/3 shared library/app.d @@ -0,0 +1,8 @@ + +import libstuff; + +void main () +{ + immutable ret = printLibraryString ("foo"); + assert (ret == 4); +} diff --git a/test cases/d/3 shared library/installed_files.txt b/test cases/d/3 shared library/installed_files.txt new file mode 100644 index 0000000..d6a4dad --- /dev/null +++ b/test cases/d/3 shared library/installed_files.txt @@ -0,0 +1,2 @@ +usr/bin/app_d?exe +usr/lib/libstuff.so diff --git a/test cases/d/3 shared library/libstuff.d b/test cases/d/3 shared library/libstuff.d new file mode 100644 index 0000000..676a643 --- /dev/null +++ b/test cases/d/3 shared library/libstuff.d @@ -0,0 +1,9 @@ + +import std.stdio; +import std.string : format; + +int printLibraryString (string str) +{ + writeln ("Library says: %s".format (str)); + return 4; +} diff --git a/test cases/d/3 shared library/meson.build b/test cases/d/3 shared library/meson.build new file mode 100644 index 0000000..5dae66b --- /dev/null +++ b/test cases/d/3 shared library/meson.build @@ -0,0 +1,12 @@ +project('D Shared Library', 'd') + +if meson.get_compiler('d').get_id() != 'gcc' + + ldyn = shared_library('stuff', 'libstuff.d', install : true) + ed = executable('app_d', 'app.d', link_with : ldyn, install : true) + test('linktest_dyn', ed) + +else + message('GDC can not build shared libraries. Test skipped.') + install_data('no-installed-files', install_dir : '') +endif diff --git a/test cases/d/3 shared library/no-installed-files b/test cases/d/3 shared library/no-installed-files new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/d/3 shared library/no-installed-files diff --git a/test cases/d/4 library versions/installed_files.txt b/test cases/d/4 library versions/installed_files.txt new file mode 100644 index 0000000..b997e53 --- /dev/null +++ b/test cases/d/4 library versions/installed_files.txt @@ -0,0 +1,9 @@ +usr/lib/libsome.so +usr/lib/libsome.so.0 +usr/lib/libsome.so.1.2.3 +usr/lib/libnoversion.so +usr/lib/libonlyversion.so +usr/lib/libonlyversion.so.1 +usr/lib/libonlyversion.so.1.4.5 +usr/lib/libonlysoversion.so +usr/lib/libonlysoversion.so.5 diff --git a/test cases/d/4 library versions/lib.d b/test cases/d/4 library versions/lib.d new file mode 100644 index 0000000..94df91e --- /dev/null +++ b/test cases/d/4 library versions/lib.d @@ -0,0 +1,10 @@ + +import std.stdio; +import std.string : format; + +@safe +int printLibraryString (string str) +{ + writeln ("Library says: %s".format (str)); + return 4; +} diff --git a/test cases/d/4 library versions/meson.build b/test cases/d/4 library versions/meson.build new file mode 100644 index 0000000..26cc38a --- /dev/null +++ b/test cases/d/4 library versions/meson.build @@ -0,0 +1,25 @@ +project('D library versions', 'd') + +if meson.get_compiler('d').get_id() == 'gcc' + message('GDC can not build shared libraries. Test skipped.') + install_data('no-installed-files', install_dir : '') +else + + shared_library('some', 'lib.d', + version : '1.2.3', + soversion : '0', + install : true) + + shared_library('noversion', 'lib.d', + install : true) + + shared_library('onlyversion', 'lib.d', + version : '1.4.5', + install : true) + + shared_library('onlysoversion', 'lib.d', + # Also test that int soversion is acceptable + soversion : 5, + install : true) + +endif diff --git a/test cases/d/4 library versions/no-installed-files b/test cases/d/4 library versions/no-installed-files new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/d/4 library versions/no-installed-files diff --git a/test cases/d/5 mixed/app.d b/test cases/d/5 mixed/app.d new file mode 100644 index 0000000..6ab5d97 --- /dev/null +++ b/test cases/d/5 mixed/app.d @@ -0,0 +1,8 @@ + +extern(C) int printLibraryString(const char *str); + +void main () +{ + immutable ret = printLibraryString ("C foo"); + assert (ret == 3); +} diff --git a/test cases/d/5 mixed/installed_files.txt b/test cases/d/5 mixed/installed_files.txt new file mode 100644 index 0000000..9e7fccc --- /dev/null +++ b/test cases/d/5 mixed/installed_files.txt @@ -0,0 +1,4 @@ +usr/bin/appdc_d?exe +usr/lib/libstuff.so +usr/bin/appdc_s?exe +usr/lib/libstuff.a diff --git a/test cases/d/5 mixed/libstuff.c b/test cases/d/5 mixed/libstuff.c new file mode 100644 index 0000000..92d6600 --- /dev/null +++ b/test cases/d/5 mixed/libstuff.c @@ -0,0 +1,18 @@ +#if defined _WIN32 || defined __CYGWIN__ + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +#include <stdio.h> + +int DLL_PUBLIC printLibraryString(const char *str) +{ + printf("C library says: %s", str); + return 3; +} diff --git a/test cases/d/5 mixed/meson.build b/test cases/d/5 mixed/meson.build new file mode 100644 index 0000000..3dad66d --- /dev/null +++ b/test cases/d/5 mixed/meson.build @@ -0,0 +1,9 @@ +project('Mixing C and D', 'd', 'c') + +ldyn = shared_library('stuff', 'libstuff.c', install : true) +ed = executable('appdc_d', 'app.d', link_with : ldyn, install : true) +test('linktest_cdyn', ed) + +lstatic = static_library('stuff', 'libstuff.c', install : true) +es = executable('appdc_s', 'app.d', link_with : lstatic, install : true) +test('linktest_cstatic', es) diff --git a/test cases/d/6 unittest/app.d b/test cases/d/6 unittest/app.d new file mode 100644 index 0000000..751e754 --- /dev/null +++ b/test cases/d/6 unittest/app.d @@ -0,0 +1,34 @@ + +import std.stdio; + +uint getFour () +{ + auto getTwo () + { + return 1 + 1; + } + + return getTwo () + getTwo (); +} + +void main () +{ + import core.stdc.stdlib : exit; + + writeln ("Four: ", getFour ()); + exit (4); +} + +unittest +{ + writeln ("TEST"); + import core.stdc.stdlib : exit; + + assert (getFour () > 2); + assert (getFour () == 4); + + // we explicitly terminate here to give the unittest program a different exit + // code than the main application has. + // (this prevents the regular main() from being executed) + exit (0); +} diff --git a/test cases/d/6 unittest/installed_files.txt b/test cases/d/6 unittest/installed_files.txt new file mode 100644 index 0000000..e718389 --- /dev/null +++ b/test cases/d/6 unittest/installed_files.txt @@ -0,0 +1 @@ +usr/bin/dapp?exe diff --git a/test cases/d/6 unittest/meson.build b/test cases/d/6 unittest/meson.build new file mode 100644 index 0000000..1551e94 --- /dev/null +++ b/test cases/d/6 unittest/meson.build @@ -0,0 +1,8 @@ +project('D Unittests', 'd') + +e = executable('dapp', 'app.d', install : true) +test('dapp_run', e, should_fail: true) + +e_test = executable('dapp_test', 'app.d', + d_args: meson.get_compiler('d').unittest_args()) +test('dapp_test', e_test) diff --git a/test cases/failing/31 invalid man extension/meson.build b/test cases/failing/31 invalid man extension/meson.build new file mode 100644 index 0000000..45eddca --- /dev/null +++ b/test cases/failing/31 invalid man extension/meson.build @@ -0,0 +1,2 @@ +project('man install', 'c') +m1 = install_man('foo.a1') diff --git a/test cases/failing/32 no man extension/meson.build b/test cases/failing/32 no man extension/meson.build new file mode 100644 index 0000000..bf83571 --- /dev/null +++ b/test cases/failing/32 no man extension/meson.build @@ -0,0 +1,2 @@ +project('man install', 'c') +m1 = install_man('foo') diff --git a/test cases/frameworks/11 gir subproject/gir/meson-subsample.c b/test cases/frameworks/11 gir subproject/gir/meson-subsample.c new file mode 100644 index 0000000..2d58a10 --- /dev/null +++ b/test cases/frameworks/11 gir subproject/gir/meson-subsample.c @@ -0,0 +1,124 @@ +#include "meson-subsample.h" + +struct _MesonSubSample +{ + MesonSample parent_instance; + + gchar *msg; +}; + +G_DEFINE_TYPE (MesonSubSample, meson_sub_sample, MESON_TYPE_SAMPLE) + +enum { + PROP_0, + PROP_MSG, + LAST_PROP +}; + +static GParamSpec *gParamSpecs [LAST_PROP]; + +/** + * meson_sub_sample_new: + * @msg: The message to set. + * + * Allocates a new #MesonSubSample. + * + * Returns: (transfer full): a #MesonSubSample. + */ +MesonSubSample * +meson_sub_sample_new (const gchar *msg) +{ + g_return_val_if_fail (msg != NULL, NULL); + + return g_object_new (MESON_TYPE_SUB_SAMPLE, + "message", msg, + NULL); +} + +static void +meson_sub_sample_finalize (GObject *object) +{ + MesonSubSample *self = (MesonSubSample *)object; + + g_clear_pointer (&self->msg, g_free); + + G_OBJECT_CLASS (meson_sub_sample_parent_class)->finalize (object); +} + +static void +meson_sub_sample_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MesonSubSample *self = MESON_SUB_SAMPLE (object); + + switch (prop_id) + { + case PROP_MSG: + g_value_set_string (value, self->msg); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meson_sub_sample_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MesonSubSample *self = MESON_SUB_SAMPLE (object); + + switch (prop_id) + { + case PROP_MSG: + self->msg = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meson_sub_sample_class_init (MesonSubSampleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meson_sub_sample_finalize; + object_class->get_property = meson_sub_sample_get_property; + object_class->set_property = meson_sub_sample_set_property; + + gParamSpecs [PROP_MSG] = + g_param_spec_string ("message", + "Message", + "The message to print.", + NULL, + (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs); +} + +static void +meson_sub_sample_init (MesonSubSample *self) +{ +} + +/** + * meson_sub_sample_print_message: + * @self: a #MesonSubSample. + * + * Prints the message. + * + * Returns: Nothing. + */ +void +meson_sub_sample_print_message (MesonSubSample *self) +{ + g_return_if_fail (MESON_IS_SUB_SAMPLE (self)); + + g_print ("Message: %s\n", self->msg); +} diff --git a/test cases/frameworks/11 gir subproject/gir/meson-subsample.h b/test cases/frameworks/11 gir subproject/gir/meson-subsample.h new file mode 100644 index 0000000..666d59f --- /dev/null +++ b/test cases/frameworks/11 gir subproject/gir/meson-subsample.h @@ -0,0 +1,21 @@ +#ifndef MESON_SUB_SAMPLE_H +#define MESON_SUB_SAMPLE_H + +#if !defined (MESON_TEST) +#error "MESON_TEST not defined." +#endif + +#include <glib-object.h> +#include <meson-sample.h> + +G_BEGIN_DECLS + +#define MESON_TYPE_SUB_SAMPLE (meson_sub_sample_get_type()) + +G_DECLARE_FINAL_TYPE (MesonSubSample, meson_sub_sample, MESON, SUB_SAMPLE, MesonSample) + +MesonSubSample *meson_sub_sample_new (const gchar *msg); + +G_END_DECLS + +#endif /* MESON_SUB_SAMPLE_H */ diff --git a/test cases/frameworks/11 gir subproject/gir/meson.build b/test cases/frameworks/11 gir subproject/gir/meson.build new file mode 100644 index 0000000..e92c641 --- /dev/null +++ b/test cases/frameworks/11 gir subproject/gir/meson.build @@ -0,0 +1,35 @@ +libsources = ['meson-subsample.c', 'meson-subsample.h'] + +girsubproject = shared_library( + 'girsubproject', + sources : libsources, + dependencies : [gobj, meson_gir], + install : true +) + +girexe = executable( + 'girprog', + sources : 'prog.c', + dependencies : [gobj, meson_gir], + link_with : girsubproject +) + +gnome.generate_gir( + girsubproject, + sources : libsources, + dependencies : [gobj, meson_gir], + nsversion : '1.0', + namespace : 'MesonSub', + symbol_prefix : 'meson_sub_', + identifier_prefix : 'MesonSub', + includes : ['GObject-2.0', 'Meson-1.0'], + install : true +) + +message('TEST: ' + girsubproject.outdir()) + +test('gobject introspection/subproject/c', girexe) +test('gobject introspection/subproject/py', find_program('prog.py'), + env : ['GI_TYPELIB_PATH=' + girsubproject.outdir() + ':subprojects/mesongir', + 'LD_LIBRARY_PATH=' + girsubproject.outdir() + ':subprojects/mesongir', + ]) diff --git a/test cases/frameworks/11 gir subproject/gir/prog.c b/test cases/frameworks/11 gir subproject/gir/prog.c new file mode 100644 index 0000000..f25c9d8 --- /dev/null +++ b/test cases/frameworks/11 gir subproject/gir/prog.c @@ -0,0 +1,12 @@ +#include "meson-subsample.h" + +gint +main (gint argc, + gchar *argv[]) +{ + MesonSample * i = (MesonSample*) meson_sub_sample_new ("Hello, sub/meson/c!"); + meson_sample_print_message (i); + g_object_unref (i); + + return 0; +} diff --git a/test cases/frameworks/11 gir subproject/gir/prog.py b/test cases/frameworks/11 gir subproject/gir/prog.py new file mode 100755 index 0000000..ea4da5b --- /dev/null +++ b/test cases/frameworks/11 gir subproject/gir/prog.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +from gi.repository import MesonSub + +if __name__ == "__main__": + s = MesonSub.Sample.new("Hello, sub/meson/py!") + s.print_message() diff --git a/test cases/frameworks/11 gir subproject/installed_files.txt b/test cases/frameworks/11 gir subproject/installed_files.txt new file mode 100644 index 0000000..434481e --- /dev/null +++ b/test cases/frameworks/11 gir subproject/installed_files.txt @@ -0,0 +1,6 @@ +usr/lib/girepository-1.0/Meson-1.0.typelib +usr/lib/girepository-1.0/MesonSub-1.0.typelib +usr/share/gir-1.0/Meson-1.0.gir +usr/share/gir-1.0/MesonSub-1.0.gir +usr/lib/libgirsubproject.so +usr/lib/libgirlib.so diff --git a/test cases/frameworks/11 gir subproject/meson.build b/test cases/frameworks/11 gir subproject/meson.build new file mode 100644 index 0000000..f3bde40 --- /dev/null +++ b/test cases/frameworks/11 gir subproject/meson.build @@ -0,0 +1,10 @@ +project('gobject-introspection-with-subproject', 'c') + +gnome = import('gnome') +gobj = dependency('gobject-2.0') + +add_global_arguments('-DMESON_TEST', language : 'c') +meson_gir = dependency('meson-gir', fallback : ['mesongir', 'meson_gir']) + +subdir('gir') + diff --git a/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson-sample.c b/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson-sample.c new file mode 100644 index 0000000..2e78b07 --- /dev/null +++ b/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson-sample.c @@ -0,0 +1,127 @@ +#include "meson-sample.h" + +typedef struct _MesonSamplePrivate +{ + gchar *msg; +} MesonSamplePrivate; + + +G_DEFINE_TYPE_WITH_PRIVATE (MesonSample, meson_sample, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_MSG, + LAST_PROP +}; + +static GParamSpec *gParamSpecs [LAST_PROP]; + +/** + * meson_sample_new: + * @msg: The message to set. + * + * Allocates a new #MesonSample. + * + * Returns: (transfer full): a #MesonSample. + */ +MesonSample * +meson_sample_new (const gchar *msg) +{ + g_return_val_if_fail (msg != NULL, NULL); + + return g_object_new (MESON_TYPE_SAMPLE, + "message", msg, + NULL); +} + +static void +meson_sample_finalize (GObject *object) +{ + MesonSamplePrivate *priv = meson_sample_get_instance_private ((MesonSample *) object); + + g_clear_pointer (&priv->msg, g_free); + + G_OBJECT_CLASS (meson_sample_parent_class)->finalize (object); +} + +static void +meson_sample_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MesonSamplePrivate *priv = meson_sample_get_instance_private ((MesonSample *) object); + + switch (prop_id) + { + case PROP_MSG: + g_value_set_string (value, priv->msg); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meson_sample_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MesonSamplePrivate *priv = meson_sample_get_instance_private ((MesonSample *) object); + + switch (prop_id) + { + case PROP_MSG: + priv->msg = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meson_sample_class_init (MesonSampleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meson_sample_finalize; + object_class->get_property = meson_sample_get_property; + object_class->set_property = meson_sample_set_property; + + gParamSpecs [PROP_MSG] = + g_param_spec_string ("message", + "Message", + "The message to print.", + NULL, + (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs); +} + +static void +meson_sample_init (MesonSample *self) +{ +} + +/** + * meson_sample_print_message: + * @self: a #MesonSample. + * + * Prints the message. + * + * Returns: Nothing. + */ +void +meson_sample_print_message (MesonSample *self) +{ + MesonSamplePrivate *priv; + + g_return_if_fail (MESON_IS_SAMPLE (self)); + + priv = meson_sample_get_instance_private (self); + + g_print ("Message: %s\n", priv->msg); +} diff --git a/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson-sample.h b/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson-sample.h new file mode 100644 index 0000000..e4c07a8 --- /dev/null +++ b/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson-sample.h @@ -0,0 +1,26 @@ +#ifndef MESON_SAMPLE_H +#define MESON_SAMPLE_H + +#if !defined (MESON_TEST) +#error "MESON_TEST not defined." +#endif + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define MESON_TYPE_SAMPLE (meson_sample_get_type()) + +G_DECLARE_DERIVABLE_TYPE (MesonSample, meson_sample, MESON, SAMPLE, GObject) + +struct _MesonSampleClass { + GObjectClass parent_class; +}; + + +MesonSample *meson_sample_new (const gchar *msg); +void meson_sample_print_message (MesonSample *self); + +G_END_DECLS + +#endif /* MESON_SAMPLE_H */ diff --git a/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson.build b/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson.build new file mode 100644 index 0000000..027b4ee --- /dev/null +++ b/test cases/frameworks/11 gir subproject/subprojects/mesongir/meson.build @@ -0,0 +1,31 @@ +project('gobject-introspection-subproject', 'c') + +gnome = import('gnome') +gobj = dependency('gobject-2.0') + +libsources = ['meson-sample.c', 'meson-sample.h'] + +girlib = shared_library( + 'girlib', + sources : libsources, + dependencies : gobj, + install : true +) + +girtarget = gnome.generate_gir( + girlib, + sources : libsources, + nsversion : '1.0', + namespace : 'Meson', + symbol_prefix : 'meson_', + identifier_prefix : 'Meson', + includes : ['GObject-2.0'], + install : true +) + +meson_gir = declare_dependency(link_with : girlib, + include_directories : [include_directories('.')], + dependencies : [gobj], + # Everything that uses libgst needs this built to compile + sources : girtarget, +) diff --git a/test cases/frameworks/6 gettext/src/meson.build b/test cases/frameworks/6 gettext/src/meson.build index db2490f..2c10027 100644 --- a/test cases/frameworks/6 gettext/src/meson.build +++ b/test cases/frameworks/6 gettext/src/meson.build @@ -1 +1,2 @@ -executable('intlprog', 'intlmain.c', install : true) +executable('intlprog', 'intlmain.c', install : true, + dependencies : meson.get_compiler('c').find_library('intl', required : false)) diff --git a/test cases/frameworks/7 gnome/gir/prog.c b/test cases/frameworks/7 gnome/gir/prog.c index 1116285..c855a6b 100644 --- a/test cases/frameworks/7 gnome/gir/prog.c +++ b/test cases/frameworks/7 gnome/gir/prog.c @@ -6,18 +6,26 @@ gint main (gint argc, gchar *argv[]) { - g_autoptr(GError) error = NULL; + GError * error = NULL; - g_autoptr(GOptionContext) ctx = g_option_context_new (NULL); + GOptionContext * ctx = g_option_context_new (NULL); g_option_context_add_group (ctx, g_irepository_get_option_group ()); if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_print ("sample: %s\n", error->message); + g_option_context_free (ctx); + if (error) { + g_error_free (error); + } + return 1; } - g_autoptr(MesonSample) i = meson_sample_new ("Hello, meson/c!"); + MesonSample * i = meson_sample_new ("Hello, meson/c!"); meson_sample_print_message (i); + g_object_unref (i); + g_option_context_free (ctx); + return 0; } diff --git a/test cases/frameworks/9 wxwidgets/meson.build b/test cases/frameworks/9 wxwidgets/meson.build index 4f4d251..c1fa367 100644 --- a/test cases/frameworks/9 wxwidgets/meson.build +++ b/test cases/frameworks/9 wxwidgets/meson.build @@ -1,8 +1,9 @@ project('wxwidgets test', 'cpp') -wxd = dependency('wxwidgets', version : '>=3.0.0') +wxd = dependency('wxwidgets', version : '>=3.0.0', required : false) -wp = executable('wxprog', 'wxprog.cpp', -dependencies : wxd) +if wxd.found() + wp = executable('wxprog', 'wxprog.cpp', dependencies : wxd) -test('wxtest', wp) + test('wxtest', wp) +endif diff --git a/test cases/osx/2 library versions/exe.orig.c b/test cases/osx/2 library versions/exe.orig.c new file mode 100644 index 0000000..1a8cc62 --- /dev/null +++ b/test cases/osx/2 library versions/exe.orig.c @@ -0,0 +1,4 @@ +int +main (int argc, char *argv[]) +{ +} diff --git a/test cases/osx/2 library versions/installed_files.txt b/test cases/osx/2 library versions/installed_files.txt index 66470ab..4d58502 100644 --- a/test cases/osx/2 library versions/installed_files.txt +++ b/test cases/osx/2 library versions/installed_files.txt @@ -1,4 +1,4 @@ -usr/lib/libsome.0.dylib +usr/lib/libsome.dylib usr/lib/libnoversion.dylib -usr/lib/libonlyversion.1.dylib -usr/lib/libonlysoversion.5.dylib +usr/lib/libonlyversion.dylib +usr/lib/libonlysoversion.dylib diff --git a/test cases/osx/2 library versions/meson.build b/test cases/osx/2 library versions/meson.build index 504aa4e..107b467 100644 --- a/test cases/osx/2 library versions/meson.build +++ b/test cases/osx/2 library versions/meson.build @@ -1,18 +1,41 @@ project('library versions', 'c') -shared_library('some', 'lib.c', +some = shared_library('some', 'lib.c', version : '1.2.3', soversion : '0', install : true) -shared_library('noversion', 'lib.c', +noversion = shared_library('noversion', 'lib.c', install : true) -shared_library('onlyversion', 'lib.c', +onlyversion = shared_library('onlyversion', 'lib.c', version : '1.4.5', install : true) -shared_library('onlysoversion', 'lib.c', +onlysoversion = shared_library('onlysoversion', 'lib.c', # Also test that int soversion is acceptable soversion : 5, install : true) + +# Hack to make the executables below depend on the shared libraries above +# without actually adding them as `link_with` dependencies since we want to try +# linking to them with -lfoo linker arguments. +out = custom_target('library-dependency-hack', + input : 'exe.orig.c', + output : 'exe.c', + depends : [some, noversion, onlyversion, onlysoversion], + command : ['cp', '@INPUT@', '@OUTPUT@']) + +# Manually test if the linker can find the above libraries +# i.e., whether they were generated with the right naming scheme +executable('manuallink1', out, + link_args : ['-L.', '-lsome']) + +executable('manuallink2', out, + link_args : ['-L.', '-lnoversion']) + +executable('manuallink3', out, + link_args : ['-L.', '-lonlyversion']) + +executable('manuallink4', out, + link_args : ['-L.', '-lonlysoversion']) diff --git a/tools/ac_converter.py b/tools/ac_converter.py index 571481e..c7c9f44 100755 --- a/tools/ac_converter.py +++ b/tools/ac_converter.py @@ -25,28 +25,6 @@ that are unrelated to configure checks. import sys -print('''cc = meson.get_compiler('c') -cdata = configuration_data()''') - -print('check_headers = [') - -for line in open(sys.argv[1]): - line = line.strip() - if line.startswith('#mesondefine') and \ - line.endswith('_H'): - token = line.split()[1] - tarr = token.split('_')[1:-1] - tarr = [x.lower() for x in tarr] - hname = '/'.join(tarr) + '.h' - print(" ['%s', '%s']," % (token, hname)) -print(']\n') - -print('''foreach h : check_headers - if cc.has_header(h.get(1)) - cdata.set(h.get(0), 1) - endif -endforeach -''') # Add stuff here as it is encountered. function_data = \ @@ -242,18 +220,71 @@ function_data = \ 'HAVE_PTHREAD_SET_NAME_NP': ('pthread_set_name_np', 'pthread.h'), } -print('check_functions = [') +headers = [] +functions = [] +sizes = [] +with open(sys.argv[1]) as f: + for line in f: + line = line.strip() + arr = line.split() + + # Check for headers. + if line.startswith('#mesondefine') and line.endswith('_H'): + token = line.split()[1] + tarr = token.split('_')[1:-1] + tarr = [x.lower() for x in tarr] + hname = '/'.join(tarr) + '.h' + headers.append((token, hname)) + + # Check for functions. + try: + token = arr[1] + if token in function_data: + fdata = function_data[token] + functions.append((token, fdata[0], fdata[1])) + elif token.startswith('HAVE_') and not token.endswith('_H'): + functions.append((token, )) + except Exception: + pass + + # Check for sizeof tests. + if len(arr) != 2: + continue + elem = arr[1] + if elem.startswith('SIZEOF_'): + typename = elem.split('_', 1)[1] \ + .replace('_P', '*') \ + .replace('_', ' ') \ + .lower() \ + .replace('size t', 'size_t') + sizes.append((elem, typename)) -for line in open(sys.argv[1]): - try: - token = line.split()[1] - if token in function_data: - fdata = function_data[token] - print(" ['%s', '%s', '#include<%s>']," % (token, fdata[0], fdata[1])) - elif token.startswith('HAVE_') and not token.endswith('_H'): - print('# check token', token) - except Exception: - pass +print('''cc = meson.get_compiler('c') +cdata = configuration_data()''') + +# Convert header checks. + +print('check_headers = [') +for token, hname in headers: + print(" ['%s', '%s']," % (token, hname)) +print(']\n') + +print('''foreach h : check_headers + if cc.has_header(h.get(1)) + cdata.set(h.get(0), 1) + endif +endforeach +''') + +# Convert function checks. + +print('check_functions = [') +for token in functions: + if len(func) == 3: + token, fdata0, fdata1 = token + print(" ['%s', '%s', '#include<%s>']," % (token, fdata0, fdata1)) + else: + print('# check token', token) print(']\n') print('''foreach f : check_functions @@ -265,14 +296,8 @@ endforeach # Convert sizeof checks. -for line in open(sys.argv[1]): - arr = line.strip().split() - if len(arr) != 2: - continue - elem = arr[1] - if elem.startswith('SIZEOF_'): - typename = elem.split('_', 1)[1].replace('_P', '*').replace('_', ' ').lower().replace('size t', 'size_t') - print("cdata.set('%s', cc.sizeof('%s'))" % (elem, typename)) +for elem, typename in sizes: + print("cdata.set('%s', cc.sizeof('%s'))" % (elem, typename)) print(''' configure_file(input : 'config.h.meson', diff --git a/tools/cmake2meson.py b/tools/cmake2meson.py index 098a6e0..7465d45 100755 --- a/tools/cmake2meson.py +++ b/tools/cmake2meson.py @@ -252,39 +252,46 @@ class Converter: subdir = self.cmake_root cfile = os.path.join(subdir, 'CMakeLists.txt') try: - cmakecode = open(cfile).read() + with open(cfile) as f: + cmakecode = f.read() except FileNotFoundError: print('\nWarning: No CMakeLists.txt in', subdir, '\n') return p = Parser(cmakecode) - outfile = open(os.path.join(subdir, 'meson.build'), 'w') - for t in p.parse(): - if t.name == 'add_subdirectory': - #print('\nRecursing to subdir', os.path.join(self.cmake_root, t.args[0].value), '\n') - self.convert(os.path.join(subdir, t.args[0].value)) - #print('\nReturning to', self.cmake_root, '\n') - self.write_entry(outfile, t) + with open(os.path.join(subdir, 'meson.build'), 'w') as outfile: + for t in p.parse(): + if t.name == 'add_subdirectory': + # print('\nRecursing to subdir', + # os.path.join(self.cmake_root, t.args[0].value), + # '\n') + self.convert(os.path.join(subdir, t.args[0].value)) + # print('\nReturning to', self.cmake_root, '\n') + self.write_entry(outfile, t) if subdir == self.cmake_root and len(self.options) > 0: self.write_options() def write_options(self): - optfile = open(os.path.join(self.cmake_root, 'meson_options.txt'), 'w') - for o in self.options: - (optname, description, default) = o - if default is None: - defaultstr = '' - else: - if default == 'OFF': - typestr = ' type : boolean,' - default = 'false' - elif default == 'ON': - default = 'true' - typestr = ' type : boolean,' + filename = os.path.join(self.cmake_root, 'meson_options.txt') + with open(filename, 'w') as optfile: + for o in self.options: + (optname, description, default) = o + if default is None: + defaultstr = '' else: - typestr = ' type : string,' - defaultstr = ' value : %s,' % default - line = "option(%s,%s%s description : '%s')\n" % (optname, typestr, defaultstr, description) - optfile.write(line) + if default == 'OFF': + typestr = ' type : boolean,' + default = 'false' + elif default == 'ON': + default = 'true' + typestr = ' type : boolean,' + else: + typestr = ' type : string,' + defaultstr = ' value : %s,' % default + line = "option(%s,%s%s description : '%s')\n" % (optname, + typestr, + defaultstr, + description) + optfile.write(line) if __name__ == '__main__': if len(sys.argv) != 2: |