diff options
48 files changed, 558 insertions, 334 deletions
diff --git a/MANIFEST.in b/MANIFEST.in index 8d8f136..2381203 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,11 +5,9 @@ graft data graft graphics graft man graft tools -include run_tests.py -include run_cross_test.py -include readme.txt include authors.txt include contributing.txt include COPYING +include readme.txt include run_cross_test.py include run_tests.py diff --git a/authors.txt b/authors.txt index 0d043eb..4552b23 100644 --- a/authors.txt +++ b/authors.txt @@ -27,3 +27,6 @@ Wink Saville Yoav Alon Martin Ejdestig Rémi Nicole +Damián Nohales +Nirbheek Chauhan +Nicolas Schneider diff --git a/cross/ubuntu-mingw.txt b/cross/ubuntu-mingw.txt index 2373565..4c8fcac 100644 --- a/cross/ubuntu-mingw.txt +++ b/cross/ubuntu-mingw.txt @@ -7,6 +7,7 @@ c = '/usr/bin/i686-w64-mingw32-gcc' cpp = '/usr/bin/i686-w64-mingw32-g++' ar = '/usr/bin/i686-w64-mingw32-ar' strip = '/usr/bin/i686-w64-mingw32-strip' +pkgconfig = '/usr/bin/mingw32-pkg-config' [properties] root = '/usr/i686-w64-mingw32' diff --git a/man/meson.1 b/man/meson.1 index 15b17e4..19721c1 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "December 2015" "meson 0.28.0" "User Commands" +.TH MESON "1" "January 2016" "meson 0.29.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/man/mesonconf.1 b/man/mesonconf.1 index d8137a9..be690ae 100644 --- a/man/mesonconf.1 +++ b/man/mesonconf.1 @@ -1,4 +1,4 @@ -.TH MESONCONF "1" "December 2015" "mesonconf 0.28.0" "User Commands" +.TH MESONCONF "1" "January 2016" "mesonconf 0.29.0" "User Commands" .SH NAME mesonconf - a tool to configure Meson builds .SH DESCRIPTION diff --git a/man/mesongui.1 b/man/mesongui.1 index 4d41015..d1eff90 100644 --- a/man/mesongui.1 +++ b/man/mesongui.1 @@ -1,4 +1,4 @@ -.TH MESONGUI "1" "December 2015" "mesongui 0.28.0" "User Commands" +.TH MESONGUI "1" "January 2016" "mesongui 0.29.0" "User Commands" .SH NAME mesongui - a gui for the Meson build system .SH DESCRIPTION diff --git a/man/mesonintrospect.1 b/man/mesonintrospect.1 index de0b766..9fa629c 100644 --- a/man/mesonintrospect.1 +++ b/man/mesonintrospect.1 @@ -1,4 +1,4 @@ -.TH MESONCONF "1" "December 2015" "mesonintrospect 0.28.0" "User Commands" +.TH MESONCONF "1" "January 2016" "mesonintrospect 0.29.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 8a6f03c..b37c129 100644 --- a/man/wraptool.1 +++ b/man/wraptool.1 @@ -1,4 +1,4 @@ -.TH WRAPTOOL "1" "December 2015" "meson 0.28.0" "User Commands" +.TH WRAPTOOL "1" "January 2016" "meson 0.29.0" "User Commands" .SH NAME wraptool - source dependency downloader .SH DESCRIPTION diff --git a/mesonbuild/backend/__init__.py b/mesonbuild/backend/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mesonbuild/backend/__init__.py diff --git a/mesonbuild/backends.py b/mesonbuild/backend/backends.py index c583a7b..d2176c7 100644 --- a/mesonbuild/backends.py +++ b/mesonbuild/backend/backends.py @@ -1,4 +1,4 @@ -# Copyright 2012-2014 The Meson development team +# Copyright 2012-2016 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,11 +13,11 @@ # limitations under the License. import os, pickle, re -from . import build -from . import dependencies -from . import mesonlib +from .. import build +from .. import dependencies +from .. import mesonlib import json -from .coredata import MesonException +from ..coredata import MesonException class InstallData(): def __init__(self, source_dir, build_dir, prefix, depfixer): @@ -83,10 +83,20 @@ class Backend(): targetdir = self.get_target_dir(target) fname = target.get_filename() if isinstance(fname, list): - fname = fname[0] # HORROR, HORROR! Fix this. + # FIXME FIXME FIXME: build.CustomTarget has multiple output files + # and get_filename() returns them all + fname = fname[0] filename = os.path.join(targetdir, fname) return filename + def get_target_filename_for_linking(self, target): + # On some platforms (msvc for instance), the file that is used for + # dynamic linking is not the same as the dynamic library itself. This + # file is called an import library, and we want to link against that. + # On platforms where this distinction is not important, the import + # library is the same as the dynamic library itself. + return os.path.join(self.get_target_dir(target), target.get_import_filename()) + def get_target_dir(self, target): if self.environment.coredata.get_builtin_option('layout') == 'mirror': dirname = target.get_subdir() @@ -115,6 +125,9 @@ class Backend(): 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) @@ -231,8 +244,7 @@ class Backend(): def generate_basic_compiler_args(self, target, compiler): commands = [] commands += compiler.get_always_args() - if self.environment.coredata.get_builtin_option('buildtype') != 'plain': - commands += compiler.get_warn_args(self.environment.coredata.get_builtin_option('warning_level')) + commands += compiler.get_warn_args(self.environment.coredata.get_builtin_option('warning_level')) commands += compiler.get_option_compile_args(self.environment.coredata.compiler_options) commands += self.build.get_global_args(compiler) commands += self.environment.coredata.external_args[compiler.get_language()] @@ -245,7 +257,9 @@ class Backend(): if isinstance(target, build.SharedLibrary): commands += compiler.get_pic_args() for dep in target.get_external_deps(): - commands += dep.get_compile_args() + # Cflags required by external deps might have UNIX-specific flags, + # so filter them out if needed + commands += compiler.unix_compile_flags_to_native(dep.get_compile_args()) if isinstance(target, build.Executable): commands += dep.get_exe_args() @@ -263,11 +277,7 @@ class Backend(): if not isinstance(d, build.StaticLibrary) and\ not isinstance(d, build.SharedLibrary): raise RuntimeError('Tried to link with a non-library target "%s".' % d.get_basename()) - fname = self.get_target_filename(d) - if compiler.id == 'msvc': - if fname.endswith('dll'): - fname = fname[:-3] + 'lib' - args.append(fname) + 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 diff --git a/mesonbuild/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 840957c..f5c06dc 100644 --- a/mesonbuild/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -13,14 +13,14 @@ # limitations under the License. from . import backends -from . import environment, mesonlib -from . import build -from . import mlog -from . import dependencies -from .mesonlib import File +from .. import environment, mesonlib +from .. import build +from .. import mlog +from .. import dependencies +from ..mesonlib import File from .backends import InstallData -from .build import InvalidArguments -from .coredata import MesonException +from ..build import InvalidArguments +from ..coredata import MesonException import os, sys, pickle, re import subprocess, shutil @@ -45,7 +45,7 @@ class RawFilename(): return self.fname.startswith(s) class NinjaBuildElement(): - def __init__(self, outfilenames, rule, infilenames): + def __init__(self, all_outputs, outfilenames, rule, infilenames): if isinstance(outfilenames, str): self.outfilenames = [outfilenames] else: @@ -59,6 +59,7 @@ class NinjaBuildElement(): self.deps = [] self.orderdeps = [] self.elems = [] + self.all_outputs = all_outputs def add_dep(self, dep): if isinstance(dep, list): @@ -78,6 +79,7 @@ class NinjaBuildElement(): self.elems.append((name, elems)) def write(self, outfile): + self.check_outputs() line = 'build %s: %s %s' % (' '.join([ninja_quote(i) for i in self.outfilenames]),\ self.rule, ' '.join([ninja_quote(i) for i in self.infilenames])) @@ -116,6 +118,12 @@ class NinjaBuildElement(): outfile.write(line) outfile.write('\n') + def check_outputs(self): + for n in self.outfilenames: + if n in self.all_outputs: + raise MesonException('Multiple producers for Ninja target "%s". Please rename your targets.' % n) + self.all_outputs[n] = True + class NinjaBackend(backends.Backend): def __init__(self, build): @@ -125,12 +133,6 @@ class NinjaBackend(backends.Backend): self.fortran_deps = {} self.all_outputs = {} - def check_outputs(self, elem): - for n in elem.outfilenames: - if n in self.all_outputs: - raise MesonException('Multiple producers for Ninja target "%s". Please rename your targets.' % n) - self.all_outputs[n] = True - def detect_vs_dep_prefix(self, outfile, tempfilename): '''VS writes its dependency in a locale dependent format. Detect the search prefix to use.''' @@ -231,7 +233,8 @@ int dummy; self.generate_cs_target(target, outfile) return if 'vala' in self.environment.coredata.compilers.keys() and self.has_vala(target): - gen_src_deps += self.generate_vala_compile(target, outfile) + vala_output_files = self.generate_vala_compile(target, outfile) + gen_src_deps += vala_output_files if 'swift' in self.environment.coredata.compilers.keys() and self.has_swift(target): self.generate_swift_target(target, outfile) return @@ -344,7 +347,7 @@ int dummy; deps.append(os.path.join(self.get_target_dir(i), fname)) if target.build_always: deps.append('PHONY') - elem = NinjaBuildElement(ofilenames, 'CUSTOM_COMMAND', srcs) + elem = NinjaBuildElement(self.all_outputs, ofilenames, 'CUSTOM_COMMAND', srcs) for i in target.depend_files: if isinstance(i, mesonlib.File): deps.append(i.rel_to_builddir(self.build_to_src)) @@ -361,7 +364,6 @@ int dummy; elem.add_item('COMMAND', cmd) elem.add_item('description', 'Generating %s with a custom command.' % target.name) elem.write(outfile) - self.check_outputs(elem) self.processed_targets[target.name + target.type_suffix()] = True def generate_run_target(self, target, outfile): @@ -378,7 +380,7 @@ int dummy; else: mlog.debug(str(i)) raise MesonException('Unreachable code in generate_run_target.') - elem = NinjaBuildElement(target.name, 'CUSTOM_COMMAND', deps) + elem = NinjaBuildElement(self.all_outputs, target.name, 'CUSTOM_COMMAND', deps) cmd = runnerscript + [self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir] texe = target.command try: @@ -399,51 +401,47 @@ int dummy; elem.add_item('description', 'Running external command %s.' % target.name) elem.add_item('pool', 'console') elem.write(outfile) - self.check_outputs(elem) self.processed_targets[target.name + target.type_suffix()] = True def generate_po(self, outfile): for p in self.build.pot: (packagename, languages, subdir) = p input_file = os.path.join(subdir, 'POTFILES') - elem = NinjaBuildElement('pot', 'GEN_POT', []) + elem = NinjaBuildElement(self.all_outputs, 'pot', 'GEN_POT', []) elem.add_item('PACKAGENAME', packagename) elem.add_item('OUTFILE', packagename + '.pot') elem.add_item('FILELIST', os.path.join(self.environment.get_source_dir(), input_file)) elem.add_item('OUTDIR', os.path.join(self.environment.get_source_dir(), subdir)) elem.write(outfile) - self.check_outputs(elem) for l in languages: infile = os.path.join(self.environment.get_source_dir(), subdir, l + '.po') outfilename = os.path.join(subdir, l + '.gmo') - lelem = NinjaBuildElement(outfilename, 'GEN_GMO', infile) + lelem = NinjaBuildElement(self.all_outputs, outfilename, 'GEN_GMO', infile) lelem.add_item('INFILE', infile) lelem.add_item('OUTFILE', outfilename) lelem.write(outfile) - self.check_outputs(lelem) def generate_coverage_rules(self, outfile): (gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools() added_rule = False if gcovr_exe: added_rule = True - elem = NinjaBuildElement('coverage-xml', 'CUSTOM_COMMAND', '') + elem = NinjaBuildElement(self.all_outputs, 'coverage-xml', 'CUSTOM_COMMAND', '') elem.add_item('COMMAND', [gcovr_exe, '-x', '-r', self.environment.get_build_dir(),\ '-o', os.path.join(self.environment.get_log_dir(), 'coverage.xml')]) elem.add_item('DESC', 'Generating XML coverage report.') elem.write(outfile) - elem = NinjaBuildElement('coverage-text', 'CUSTOM_COMMAND', '') + elem = NinjaBuildElement(self.all_outputs, 'coverage-text', 'CUSTOM_COMMAND', '') elem.add_item('COMMAND', [gcovr_exe, '-r', self.environment.get_build_dir(),\ '-o', os.path.join(self.environment.get_log_dir(), 'coverage.txt')]) elem.add_item('DESC', 'Generating text coverage report.') elem.write(outfile) - self.check_outputs(elem) if lcov_exe and genhtml_exe: added_rule = True - phony_elem = NinjaBuildElement('coverage-html', 'phony', 'coveragereport/index.html') + phony_elem = NinjaBuildElement(self.all_outputs, 'coverage-html', 'phony', 'coveragereport/index.html') phony_elem.write(outfile) - elem = NinjaBuildElement('coveragereport/index.html', 'CUSTOM_COMMAND', '') + elem = NinjaBuildElement(self.all_outputs, 'coveragereport/index.html', 'CUSTOM_COMMAND', '') command = [lcov_exe, '--directory', self.environment.get_build_dir(),\ '--capture', '--output-file', 'coverage.info', '--no-checksum',\ '&&', genhtml_exe, '--prefix', self.environment.get_build_dir(),\ @@ -451,7 +449,6 @@ int dummy; '--legend', '--show-details', 'coverage.info'] elem.add_item('COMMAND', command) elem.add_item('DESC', 'Generating HTML coverage report.') - self.check_outputs(elem) elem.write(outfile) if not added_rule: mlog.log(mlog.red('Warning:'), 'coverage requested but neither gcovr nor lcov/genhtml found.') @@ -464,7 +461,7 @@ int dummy; d = InstallData(self.environment.get_source_dir(), self.environment.get_build_dir(), self.environment.get_prefix(), depfixer) - elem = NinjaBuildElement('install', 'CUSTOM_COMMAND', 'PHONY') + elem = NinjaBuildElement(self.all_outputs, 'install', 'CUSTOM_COMMAND', 'PHONY') elem.add_dep('all') elem.add_item('DESC', 'Installing files.') elem.add_item('COMMAND', [sys.executable, self.environment.get_build_command(), '--internal', 'install', install_data_file]) @@ -478,7 +475,6 @@ int dummy; self.generate_custom_install_script(d) self.generate_subdir_install(d) elem.write(outfile) - self.check_outputs(elem) ofile = open(install_data_file, 'wb') pickle.dump(d, ofile) @@ -574,12 +570,11 @@ int dummy; visible_name = 'for top level tests' else: visible_name = s - elem = NinjaBuildElement('test-' + s, 'CUSTOM_COMMAND', ['all', 'PHONY']) + elem = NinjaBuildElement(self.all_outputs, 'test-' + s, 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd + ['--suite=' + s]) elem.add_item('DESC', 'Running test suite %s.' % visible_name) elem.add_item('pool', 'console') elem.write(outfile) - self.check_outputs(elem) def generate_tests(self, outfile): self.serialise_tests() @@ -587,32 +582,29 @@ int dummy; script_root = self.environment.get_script_dir() test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat') cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'test', test_data] - elem = NinjaBuildElement('test', 'CUSTOM_COMMAND', ['all', 'PHONY']) + elem = NinjaBuildElement(self.all_outputs, 'test', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) elem.add_item('DESC', 'Running all tests.') elem.add_item('pool', 'console') elem.write(outfile) - self.check_outputs(elem) self.write_test_suite_targets(cmd, outfile) if valgrind: - velem = NinjaBuildElement('test-valgrind', 'CUSTOM_COMMAND', ['all', 'PHONY']) + velem = NinjaBuildElement(self.all_outputs, 'test-valgrind', 'CUSTOM_COMMAND', ['all', 'PHONY']) velem.add_item('COMMAND', cmd + ['--wrapper=' + valgrind]) velem.add_item('DESC', 'Running test suite under Valgrind.') velem.add_item('pool', 'console') velem.write(outfile) - self.check_outputs(velem) # And then benchmarks. benchmark_script = os.path.join(script_root, 'meson_benchmark.py') benchmark_data = os.path.join(self.environment.get_scratch_dir(), 'meson_benchmark_setup.dat') cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'benchmark', benchmark_data] - elem = NinjaBuildElement('benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY']) + elem = NinjaBuildElement(self.all_outputs, 'benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) elem.add_item('DESC', 'Running benchmark suite.') elem.add_item('pool', 'console') elem.write(outfile) - self.check_outputs(elem) def generate_rules(self, outfile): outfile.write('# Rules for compiling.\n\n') @@ -689,11 +681,10 @@ int dummy; commands.append(self.get_target_filename(target)) for cls in class_list: commands += ['-C', self.get_target_private_dir(target), cls] - elem = NinjaBuildElement(outname_rel, jar_rule, []) + elem = NinjaBuildElement(self.all_outputs, outname_rel, jar_rule, []) elem.add_dep(class_dep_list) elem.add_item('ARGS', commands) elem.write(outfile) - self.check_outputs(elem) def generate_cs_resource_tasks(self, target, outfile): args = [] @@ -705,11 +696,10 @@ int dummy; elif r.endswith('.txt') or r.endswith('.resx'): ofilebase = os.path.splitext(os.path.basename(r))[0] + '.resources' ofilename = os.path.join(self.get_target_private_dir(target), ofilebase) - elem = NinjaBuildElement(ofilename, "CUSTOM_COMMAND", rel_sourcefile) + elem = NinjaBuildElement(self.all_outputs, ofilename, "CUSTOM_COMMAND", rel_sourcefile) elem.add_item('COMMAND', ['resgen', rel_sourcefile, ofilename]) elem.add_item('DESC', 'Compiling resource %s.' % rel_sourcefile) elem.write(outfile) - self.check_outputs(elem) deps.append(ofilename) a = '-resource:' + ofilename else: @@ -746,10 +736,9 @@ int dummy; outputs = [outname_rel, outname_rel + '.mdb'] else: outputs = [outname_rel] - elem = NinjaBuildElement(outputs, 'cs_COMPILER', rel_srcs) + elem = NinjaBuildElement(self.all_outputs, outputs, 'cs_COMPILER', rel_srcs) elem.add_dep(deps) elem.add_item('ARGS', commands) - self.check_outputs(elem) elem.write(outfile) def generate_single_java_compile(self, src, target, compiler, outfile): @@ -762,10 +751,9 @@ int dummy; rel_src = src.rel_to_builddir(self.build_to_src) plain_class_path = src.fname[:-4] + 'class' rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path) - element = NinjaBuildElement(rel_obj, compiler.get_language() + '_COMPILER', rel_src) + element = NinjaBuildElement(self.all_outputs, rel_obj, compiler.get_language() + '_COMPILER', rel_src) element.add_item('ARGS', args) element.write(outfile) - self.check_outputs(element) return plain_class_path def generate_java_link(self, outfile): @@ -795,7 +783,7 @@ int dummy; i = i.fname if i.endswith('vala'): vapiname = os.path.splitext(os.path.split(i)[1])[0] + '.vapi' - fullname = os.path.join(self.get_target_private_dir(dep), vapiname) + fullname = os.path.join(self.get_target_dir(dep), vapiname) result.append(fullname) break return result @@ -810,20 +798,28 @@ int dummy; for s in src: if s.endswith('.vala'): vala_input_files.append(s.rel_to_builddir(self.build_to_src)) - namebase = os.path.splitext(os.path.split(vala_input_files[0])[1])[0] - hname = namebase + '.h' - vapiname = namebase + '.vapi' + if len(src) == 0: + raise InvalidArguments('Vala library has no Vala source files.') + namebase = os.path.splitext(os.path.split(src[0].fname)[1])[0] + base_h = namebase + '.h' + base_vapi = namebase + '.vapi' + hname = os.path.normpath(os.path.join(self.get_target_dir(target), base_h)) + vapiname = os.path.normpath(os.path.join(self.get_target_dir(target), base_vapi)) + + generated_c_files = [] outputs = [vapiname] - args = ['-d', self.get_target_private_dir(target)] args += ['-C']#, '-o', cname] if not isinstance(target, build.Executable): outputs.append(hname) args += ['-H', hname] - args += ['--vapi=' + vapiname] + args += ['--library=' + target.name] + args += ['--vapi=' + os.path.join('..', base_vapi)] for src in vala_input_files: namebase = os.path.splitext(os.path.split(src)[1])[0] + '.c' - outputs.append(namebase) + full_c = os.path.join(self.get_target_private_dir(target), namebase) + generated_c_files.append(full_c) + outputs.append(full_c) if self.environment.coredata.get_builtin_option('werror'): args += valac.get_werror_args() for d in target.external_deps: @@ -845,15 +841,13 @@ int dummy; extra_dep_files += dependency_vapis args += extra_args args += dependency_vapis - outputs = [os.path.join(self.get_target_private_dir(target), x) for x in outputs] - element = NinjaBuildElement(outputs, + element = NinjaBuildElement(self.all_outputs, outputs, valac.get_language() + '_COMPILER', vala_input_files + vapi_src) element.add_item('ARGS', args) element.add_dep(extra_dep_files) element.write(outfile) - self.check_outputs(element) - return outputs + return generated_c_files def generate_rust_target(self, target, outfile): rustc = self.environment.coredata.compilers['rust'] @@ -885,14 +879,13 @@ int dummy; if d == '': d = '.' args += ['-L', d] - element = NinjaBuildElement(target_name, 'rust_COMPILER', relsrc) + element = NinjaBuildElement(self.all_outputs, target_name, 'rust_COMPILER', relsrc) if len(orderdeps) > 0: element.add_orderdep(orderdeps) element.add_item('ARGS', args) element.add_item('targetdep', depfile) element.add_item('cratetype', cratetype) element.write(outfile) - self.check_outputs(element) def swift_module_file_name(self, target): return os.path.join(self.get_target_private_dir(target), @@ -990,7 +983,7 @@ int dummy; rel_objects.append(os.path.join(self.get_target_private_dir(target), oname)) # Swiftc does not seem to be able to emit objects and module files in one go. - elem = NinjaBuildElement(rel_objects, + elem = NinjaBuildElement(self.all_outputs, rel_objects, 'swift_COMPILER', abssrc) elem.add_dep(in_module_files + rel_generated) @@ -998,27 +991,24 @@ int dummy; elem.add_item('ARGS', compile_args + header_imports + abs_generated + module_includes) elem.add_item('RUNDIR', rundir) elem.write(outfile) - self.check_outputs(elem) - elem = NinjaBuildElement(out_module_name, + elem = NinjaBuildElement(self.all_outputs, out_module_name, 'swift_COMPILER', abssrc) elem.add_dep(in_module_files + rel_generated) elem.add_item('ARGS', compile_args + abs_generated + module_includes + swiftc.get_mod_gen_args()) elem.add_item('RUNDIR', rundir) elem.write(outfile) - self.check_outputs(elem) if isinstance(target, build.StaticLibrary): elem = self.generate_link(target, outfile, self.get_target_filename(target), rel_objects, self.build.static_linker) elem.write(outfile) elif isinstance(target, build.Executable): - elem = NinjaBuildElement(self.get_target_filename(target), 'swift_COMPILER', []) + elem = NinjaBuildElement(self.all_outputs, self.get_target_filename(target), 'swift_COMPILER', []) elem.add_dep(rel_objects) elem.add_dep(link_deps) elem.add_item('ARGS', link_args + swiftc.get_std_exe_link_args() + objects + abs_link_deps) elem.add_item('RUNDIR', rundir) elem.write(outfile) - self.check_outputs(elem) else: raise MesonException('Swift supports only executable and static library targets.') @@ -1365,7 +1355,7 @@ rule FORTRAN_DEP_HACK else: final_args.append(a) cmdlist = exe_arr + final_args - elem = NinjaBuildElement(outfiles, 'CUSTOM_COMMAND', infilename) + elem = NinjaBuildElement(self.all_outputs, outfiles, 'CUSTOM_COMMAND', infilename) if len(extra_dependencies) > 0: elem.add_dep(extra_dependencies) elem.add_item('DESC', 'Generating $out') @@ -1373,7 +1363,6 @@ rule FORTRAN_DEP_HACK elem.add_dep(self.get_target_filename(exe)) elem.add_item('COMMAND', cmdlist) elem.write(outfile) - self.check_outputs(elem) def scan_fortran_module_outputs(self, target): compiler = None @@ -1446,6 +1435,23 @@ rule FORTRAN_DEP_HACK if curdir == '': curdir = '.' commands += compiler.get_include_args(curdir, False) + # -I args work differently than other ones. In them the + # first found directory is used whereas for other flags + # (such as -ffoo -fno-foo) the latest one is used. + # Therefore put the internal include directories here + # at the beginning so they override args coming from + # e.g. pkg-config. + for i in target.get_include_dirs(): + basedir = i.get_curdir() + for d in i.get_incdirs(): + expdir = os.path.join(basedir, d) + srctreedir = os.path.join(self.build_to_src, expdir) + bargs = compiler.get_include_args(expdir, i.is_system) + sargs = compiler.get_include_args(srctreedir, i.is_system) + commands += bargs + commands += sargs + for d in i.get_extra_build_dirs(): + commands += compiler.get_include_args(d, i.is_system) for d in target.external_deps: if d.need_threads(): commands += compiler.thread_flags() @@ -1487,17 +1493,6 @@ rule FORTRAN_DEP_HACK i = os.path.join(self.get_target_private_dir(target), compiler.get_pch_name(pchlist[0])) arr.append(i) pch_dep = arr - for i in target.get_include_dirs(): - basedir = i.get_curdir() - for d in i.get_incdirs(): - expdir = os.path.join(basedir, d) - srctreedir = os.path.join(self.build_to_src, expdir) - bargs = compiler.get_include_args(expdir, i.is_system) - sargs = compiler.get_include_args(srctreedir, i.is_system) - commands += bargs - commands += sargs - for d in i.get_extra_build_dirs(): - commands += compiler.get_include_args(d, i.is_system) custom_target_include_dirs = [] for i in target.generated: if isinstance(i, build.CustomTarget): @@ -1521,12 +1516,11 @@ rule FORTRAN_DEP_HACK modfile = os.path.join(self.get_target_private_dir(target), compiler.module_name_to_filename(modname)) if srcfile == src: - depelem = NinjaBuildElement(modfile, 'FORTRAN_DEP_HACK', rel_obj) + depelem = NinjaBuildElement(self.all_outputs, modfile, 'FORTRAN_DEP_HACK', rel_obj) depelem.write(outfile) - self.check_outputs(depelem) commands += compiler.get_module_outdir_args(self.get_target_private_dir(target)) - element = NinjaBuildElement(rel_obj, compiler_name, rel_src) + element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src) for d in header_deps: if isinstance(d, RawFilename): d = d.fname @@ -1548,7 +1542,6 @@ rule FORTRAN_DEP_HACK element.add_item('DEPFILE', dep_file) element.add_item('ARGS', commands) element.write(outfile) - self.check_outputs(element) return rel_obj def has_dir_part(self, fname): @@ -1611,24 +1604,22 @@ rule FORTRAN_DEP_HACK extradep = None pch_objects += objs rulename = compiler.get_language() + cstr + '_PCH' - elem = NinjaBuildElement(dst, rulename, src) + elem = NinjaBuildElement(self.all_outputs, dst, rulename, src) if extradep is not None: elem.add_dep(extradep) elem.add_item('ARGS', commands) elem.add_item('DEPFILE', dep) elem.write(outfile) - self.check_outputs(elem) return pch_objects def generate_shsym(self, outfile, target): target_name = self.get_target_filename(target) targetdir = self.get_target_private_dir(target) symname = os.path.join(targetdir, target_name + '.symbols') - elem = NinjaBuildElement(symname, 'SHSYM', target_name) + elem = NinjaBuildElement(self.all_outputs, symname, 'SHSYM', target_name) if self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler(): elem.add_item('CROSS', '--cross-host=' + self.environment.cross_info.config['host_machine']['system']) elem.write(outfile) - self.check_outputs(elem) def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]): if isinstance(target, build.StaticLibrary): @@ -1690,14 +1681,13 @@ rule FORTRAN_DEP_HACK custom_target_libraries = self.get_custom_target_provided_libraries(target) commands += extra_args commands += custom_target_libraries - commands = linker.unixtype_flags_to_native(commands) + commands = linker.unix_link_flags_to_native(commands) dep_targets = [self.get_dependency_filename(t) for t in dependencies] dep_targets += [os.path.join(self.environment.source_dir, target.subdir, t) for t in target.link_depends] - elem = NinjaBuildElement(outname, linker_rule, obj_list) + elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list) elem.add_dep(dep_targets + custom_target_libraries) elem.add_item('LINK_ARGS', commands) - self.check_outputs(elem) return elem def get_custom_target_provided_libraries(self, target): @@ -1727,33 +1717,33 @@ rule FORTRAN_DEP_HACK def generate_shlib_aliases(self, target, outdir): basename = target.get_filename() aliases = target.get_aliaslist() - if not mesonlib.is_windows(): - for alias in aliases: - aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias) - try: - os.remove(aliasfile) - except Exception: - pass + for alias in aliases: + aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias) + try: + os.remove(aliasfile) + except Exception: + pass + try: os.symlink(basename, aliasfile) - else: - mlog.debug("Library versioning disabled because host does not support symlinks.") + except NotImplementedError: + mlog.debug("Library versioning disabled because symlinks are not supported.") + except OSError: + mlog.debug("Library versioning disabled because we do not have symlink creation privileges.") def generate_gcov_clean(self, outfile): - gcno_elem = NinjaBuildElement('clean-gcno', 'CUSTOM_COMMAND', 'PHONY') + gcno_elem = NinjaBuildElement(self.all_outputs, 'clean-gcno', 'CUSTOM_COMMAND', 'PHONY') script_root = self.environment.get_script_dir() clean_script = os.path.join(script_root, 'delwithsuffix.py') gcno_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcno']) gcno_elem.add_item('description', 'Deleting gcno files') gcno_elem.write(outfile) - self.check_outputs(gcno_elem) - gcda_elem = NinjaBuildElement('clean-gcda', 'CUSTOM_COMMAND', 'PHONY') + gcda_elem = NinjaBuildElement(self.all_outputs, 'clean-gcda', 'CUSTOM_COMMAND', 'PHONY') script_root = self.environment.get_script_dir() clean_script = os.path.join(script_root, 'delwithsuffix.py') gcda_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcda']) gcda_elem.add_item('description', 'Deleting gcda files') gcda_elem.write(outfile) - self.check_outputs(gcda_elem) def is_compilable_file(self, filename): if filename.endswith('.cpp') or\ @@ -1777,9 +1767,8 @@ rule FORTRAN_DEP_HACK outname = rule.name_templ.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) outfilename = os.path.join(self.get_target_private_dir(target), outname) infilename = os.path.join(self.build_to_src, target.get_source_subdir(), src) - elem = NinjaBuildElement(outfilename, rule.name, infilename) + elem = NinjaBuildElement(self.all_outputs, outfilename, rule.name, infilename) elem.write(outfile) - self.check_outputs(elem) if self.is_compilable_file(outfilename): src_deps.append(outfilename) else: @@ -1790,9 +1779,8 @@ rule FORTRAN_DEP_HACK targetlist = [self.get_target_filename(t) for t in self.build.get_targets().values()\ if not isinstance(t, build.RunTarget)] - elem = NinjaBuildElement('all', 'phony', targetlist) + elem = NinjaBuildElement(self.all_outputs, 'all', 'phony', targetlist) elem.write(outfile) - self.check_outputs(elem) default = 'default all\n\n' outfile.write(default) @@ -1800,7 +1788,7 @@ rule FORTRAN_DEP_HACK ninja_command = environment.detect_ninja() if ninja_command is None: raise MesonException('Could not detect ninja command') - elem = NinjaBuildElement('clean', 'CUSTOM_COMMAND', 'PHONY') + elem = NinjaBuildElement(self.all_outputs, 'clean', 'CUSTOM_COMMAND', 'PHONY') elem.add_item('COMMAND', [ninja_command, '-t', 'clean']) elem.add_item('description', 'Cleaning') if self.environment.coredata.get_builtin_option('coverage'): @@ -1808,13 +1796,11 @@ rule FORTRAN_DEP_HACK elem.add_dep('clean-gcda') elem.add_dep('clean-gcno') elem.write(outfile) - self.check_outputs(elem) deps = self.get_regen_filelist() - elem = NinjaBuildElement('build.ninja', 'REGENERATE_BUILD', deps) + elem = NinjaBuildElement(self.all_outputs, 'build.ninja', 'REGENERATE_BUILD', deps) elem.add_item('pool', 'console') elem.write(outfile) - elem = NinjaBuildElement(deps, 'phony', '') + elem = NinjaBuildElement(self.all_outputs, deps, 'phony', '') elem.write(outfile) - self.check_outputs(elem) diff --git a/mesonbuild/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 33e9646..fc241e3 100644 --- a/mesonbuild/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1,4 +1,4 @@ -# Copyright 2014-2015 The Meson development team +# Copyright 2014-2016 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,12 +14,13 @@ import os, sys import pickle -from . import backends, build -from . import dependencies -from . import mlog +from . import backends +from .. import build +from .. import dependencies +from .. import mlog import xml.etree.ElementTree as ET import xml.dom.minidom -from .coredata import MesonException +from ..coredata import MesonException class RegenInfo(): def __init__(self, source_dir, build_dir, depfiles, solutionfile): @@ -36,8 +37,10 @@ class Vs2010Backend(backends.Backend): self.source_suffix_in_obj = False def generate_custom_generator_commands(self, target, parent_node): - idgroup = ET.SubElement(parent_node, 'ItemDefinitionGroup') all_output_files = [] + commands = [] + inputs = [] + outputs = [] for genlist in target.get_generated_sources(): if isinstance(genlist, build.CustomTarget): all_output_files += [os.path.join(self.get_target_dir(genlist), i) for i in genlist.output] @@ -49,7 +52,7 @@ class Vs2010Backend(backends.Backend): if isinstance(exe, build.BuildTarget): exe_file = os.path.join(self.environment.get_build_dir(), self.get_target_filename(exe)) else: - exe_file = exe.get_command() + exe_file = exe.get_command()[0] base_args = generator.get_arglist() for i in range(len(infilelist)): if len(infilelist) == len(outfilelist): @@ -66,13 +69,18 @@ class Vs2010Backend(backends.Backend): args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", self.get_target_private_dir(target)) for x in args] fullcmd = [exe_file] + args - cbs = ET.SubElement(idgroup, 'CustomBuildStep') - ET.SubElement(cbs, 'Command').text = ' '.join(self.special_quote(fullcmd)) - ET.SubElement(cbs, 'Inputs').text = infilename - ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) - ET.SubElement(cbs, 'Message').text = 'Generating sources from %s.' % infilename - pg = ET.SubElement(parent_node, 'PropertyGroup') - ET.SubElement(pg, 'CustomBuildBeforeTargets').text = 'ClCompile' + commands.append(' '.join(self.special_quote(fullcmd))) + inputs.append(infilename) + outputs.extend(outfiles) + if len(commands) > 0: + idgroup = ET.SubElement(parent_node, 'ItemDefinitionGroup') + cbs = ET.SubElement(idgroup, 'CustomBuildStep') + ET.SubElement(cbs, 'Command').text = '\r\n'.join(commands) + ET.SubElement(cbs, 'Inputs').text = ";".join(inputs) + ET.SubElement(cbs, 'Outputs').text = ';'.join(outputs) + ET.SubElement(cbs, 'Message').text = 'Generating custom sources.' + pg = ET.SubElement(parent_node, 'PropertyGroup') + ET.SubElement(pg, 'CustomBuildBeforeTargets').text = 'ClCompile' return all_output_files def generate(self, interp): @@ -205,12 +213,15 @@ class Vs2010Backend(backends.Backend): def split_sources(self, srclist): sources = [] headers = [] + objects = [] for i in srclist: if self.environment.is_header(i): headers.append(i) + elif self.environment.is_object(i): + objects.append(i) else: sources.append(i) - return (sources, headers) + return (sources, headers, objects) def target_to_build_root(self, target): if target.subdir == '': @@ -322,7 +333,7 @@ class Vs2010Backend(backends.Backend): down = self.target_to_build_root(target) proj_to_src_root = os.path.join(down, self.build_to_src) proj_to_src_dir = os.path.join(proj_to_src_root, target.subdir) - (sources, headers) = self.split_sources(target.sources) + (sources, headers, objects) = self.split_sources(target.sources) buildtype = self.buildtype project_name = target.name target_name = target.name @@ -355,7 +366,7 @@ class Vs2010Backend(backends.Backend): ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props') generated_files = self.generate_custom_generator_commands(target, root) - (gen_src, gen_hdrs) = self.split_sources(generated_files) + (gen_src, gen_hdrs, gen_objs) = self.split_sources(generated_files) direlem = ET.SubElement(root, 'PropertyGroup') fver = ET.SubElement(direlem, '_ProjectFileVersion') fver.text = self.project_file_version @@ -482,6 +493,13 @@ class Vs2010Backend(backends.Backend): for s in gen_src: relpath = self.relpath(s, target.subdir) ET.SubElement(inc_src, 'CLCompile', Include=relpath) + if len(objects) > 0: + # Do not add gen_objs to project file. Those are automatically used by MSBuild, because they are part of + # the CustomBuildStep Outputs. + inc_objs = ET.SubElement(root, 'ItemGroup') + for s in objects: + relpath = s.rel_to_builddir(proj_to_src_root) + ET.SubElement(inc_objs, 'Object', Include=relpath) ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') # Reference the regen target. ig = ET.SubElement(root, 'ItemGroup') @@ -542,11 +560,13 @@ class Vs2010Backend(backends.Backend): ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' - script_root = self.environment.get_script_dir() - regen_script = os.path.join(script_root, 'regen_checker.py') + regen_command = [sys.executable, + self.environment.get_build_command(), + '--internal', + 'regencheck'] private_dir = self.environment.get_scratch_dir() cmd_templ = '''setlocal -"%s" "%s" "%s" +"%s" "%s" if %%errorlevel%% neq 0 goto :cmEnd :cmEnd endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone @@ -559,7 +579,7 @@ if %%errorlevel%% neq 0 goto :VCEnd''' message = ET.SubElement(custombuild, 'Message') message.text = 'Checking whether solution needs to be regenerated.' ET.SubElement(custombuild, 'Command').text = cmd_templ % \ - (sys.executable, regen_script, private_dir) + ('" "'.join(regen_command), private_dir) ET.SubElement(custombuild, 'Outputs').text = os.path.join(self.environment.get_scratch_dir(), 'regen.stamp') deps = self.get_regen_filelist() depstr = ';'.join([os.path.join(self.environment.get_source_dir(), d) for d in deps]) @@ -616,11 +636,13 @@ if %%errorlevel%% neq 0 goto :VCEnd''' ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' postbuild = ET.SubElement(action, 'PostBuildEvent') ET.SubElement(postbuild, 'Message') - script_root = self.environment.get_script_dir() - test_script = os.path.join(script_root, 'meson_test.py') test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat') + test_command = [sys.executable, + self.environment.get_build_command(), + '--internal', + 'test'] cmd_templ = '''setlocal -"%s" "%s" "%s" +"%s" "%s" if %%errorlevel%% neq 0 goto :cmEnd :cmEnd endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone @@ -628,7 +650,8 @@ endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone exit /b %%1 :cmDone if %%errorlevel%% neq 0 goto :VCEnd''' - ET.SubElement(postbuild, 'Command').text = cmd_templ % (sys.executable, test_script, test_data) + ET.SubElement(postbuild, 'Command').text =\ + cmd_templ % ('" "'.join(test_command), test_data) ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') tree = ET.ElementTree(root) tree.write(ofname, encoding='utf-8', xml_declaration=True) diff --git a/mesonbuild/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 8ac3f67..d874be9 100644 --- a/mesonbuild/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1,4 +1,4 @@ -# Copyright 2014 The Meson development team +# Copyright 2014-2016 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,11 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from . import backends, build -from . import mesonlib +from . import backends +from .. import build +from .. import mesonlib import uuid, os, sys -from .coredata import MesonException +from ..coredata import MesonException class XCodeBackend(backends.Backend): def __init__(self, build): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c0ba895..f9e628d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -808,6 +808,8 @@ class CustomTarget: if not isinstance(s, str): raise InvalidArguments('Array as argument %d contains a non-string.' % i) final_cmd.append(s) + elif isinstance(c, File): + final_cmd.append(os.path.join(c.subdir, c.fname)) else: raise InvalidArguments('Argument %s in "command" is invalid.' % i) self.command = final_cmd diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index ec0181e..3d2f2b3 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -61,7 +61,7 @@ gnulike_buildtype_args = {'plain' : [], msvc_buildtype_args = {'plain' : [], 'debug' : ["/MDd", "/ZI", "/Ob0", "/Od", "/RTC1"], - 'debugoptimized' : ["/MD", "/Zi", "/O2", "/Ob1", "/D"], + 'debugoptimized' : ["/MD", "/Zi", "/O2", "/Ob1"], 'release' : ["/MD", "/O2", "/Ob2"]} gnulike_buildtype_linker_args = {} @@ -184,7 +184,10 @@ class Compiler(): def has_function(self, *args, **kwargs): raise EnvironmentException('Language %s does not support function checks.' % self.language) - def unixtype_flags_to_native(self, args): + def unix_link_flags_to_native(self, args): + return args + + def unix_compile_flags_to_native(self, args): return args class CCompiler(Compiler): @@ -437,7 +440,13 @@ int someSymbolHereJustForFun; se = se.decode() mlog.debug('Program stdout:\n', so) mlog.debug('Program stderr:\n', se) - os.remove(exename) + try: + os.remove(exename) + except PermissionError: + # On Windows antivirus programs and the like hold + # on to files so they can't be deleted. There's not + # much to do in this case. + pass return RunResult(True, pe.returncode, so, se) def cross_sizeof(self, element, prefix, env, extra_args=[]): @@ -890,6 +899,7 @@ class ValaCompiler(Compiler): self.version = version self.id = 'unknown' self.language = 'vala' + self.is_cross = False def name_string(self): return ' '.join(self.exelist) @@ -1122,7 +1132,7 @@ class VisualStudioCCompiler(CCompiler): return ['/OUT:' + outputname] def get_pic_args(self): - return ['/LD'] + return [] # PIC is handled by the loader on Windows def get_std_shared_lib_link_args(self): return ['/DLL'] @@ -1168,11 +1178,23 @@ class VisualStudioCCompiler(CCompiler): def get_option_link_args(self, options): return options['c_winlibs'].value - def unixtype_flags_to_native(self, args): + def unix_link_flags_to_native(self, args): result = [] for i in args: if i.startswith('-L'): i = '/LIBPATH:' + i[2:] + # Translate GNU-style -lfoo library name to the import library + if i.startswith('-l'): + i = i[2:] + '.lib' + result.append(i) + return result + + def unix_compile_flags_to_native(self, args): + result = [] + for i in args: + # -mms-bitfields is specific to MinGW-GCC + if i == '-mms-bitfields': + continue result.append(i) return result @@ -1374,7 +1396,7 @@ class ClangCCompiler(CCompiler): self.id = 'clang' self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], '2': ['-Wall', '-Wextra', '-Winvalid-pch'], - '3' : ['-Weverything']} + '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} def get_buildtype_args(self, buildtype): return gnulike_buildtype_args[buildtype] @@ -1465,7 +1487,7 @@ class ClangCPPCompiler(CPPCompiler): self.id = 'clang' self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], - '3': ['-Weverything']} + '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} def get_buildtype_args(self, buildtype): return gnulike_buildtype_args[buildtype] @@ -1793,15 +1815,24 @@ class VisualStudioLinker(): def get_option_link_args(self, options): return [] - def unixtype_flags_to_native(self, args): + def unix_link_flags_to_native(self, args): + return args + + def unix_compile_flags_to_native(self, args): return args class ArLinker(): - std_args = ['csr'] def __init__(self, exelist): self.exelist = exelist self.id = 'ar' + pc = subprocess.Popen(self.exelist + ['-h'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + (stdo, _) = pc.communicate() + # Enable deterministic builds if they are available. + if b'[D]' in stdo: + self.std_args = ['csrD'] + else: + self.std_args = ['csr'] def build_rpath_args(self, build_dir, rpath_paths, install_rpath): return [] @@ -1833,5 +1864,8 @@ class ArLinker(): def get_option_link_args(self, options): return [] - def unixtype_flags_to_native(self, args): + def unix_link_flags_to_native(self, args): + return args + + def unix_compile_flags_to_native(self, args): return args diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 7f2254b..0cfa279 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -1,4 +1,4 @@ -# Copyright 2012-2015 The Meson development team +# Copyright 2012-2016 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ import pickle, os, uuid -version = '0.29.0.dev2' +version = '0.30.0.dev1' build_types = ['plain', 'debug', 'debugoptimized', 'release'] layouts = ['mirror', 'flat'] @@ -60,9 +60,9 @@ class UserStringOption(UserOption): def validate(self, value): if not isinstance(value, str): - raise MesonException('Value "%s" for string option "%s" is not a string.' % (str(newvalue), self.name)) + raise MesonException('Value "%s" for string option "%s" is not a string.' % (str(value), self.name)) if self.name == 'prefix' and not os.path.isabs(value): - raise MesonException('Prefix option must be an absolute path.') + raise MesonException('Prefix option value \'{0}\' must be an absolute path.'.format(value)) if self.name in ('libdir', 'bindir', 'includedir', 'datadir', 'mandir', 'localedir') \ and os.path.isabs(value): raise MesonException('Option %s must not be an absolute path.' % self.name) @@ -154,7 +154,6 @@ class CoreData(): self.compilers = {} self.cross_compilers = {} self.deps = {} - self.ext_progs = {} self.modules = {} def init_builtins(self, options): diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 974559f..b89bc11 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -72,6 +72,9 @@ class PkgConfigDependency(Dependency): Dependency.__init__(self) self.is_libtool = False self.required = kwargs.get('required', True) + self.static = kwargs.get('static', False) + if not isinstance(self.static, bool): + raise DependencyException('Static keyword must be boolean') if 'native' in kwargs and environment.is_cross_build(): want_cross = not kwargs['native'] else: @@ -133,7 +136,10 @@ class PkgConfigDependency(Dependency): (name, out.decode(errors='ignore'))) self.cargs = out.decode().split() - p = subprocess.Popen([pkgbin, '--libs', name], stdout=subprocess.PIPE, + libcmd = [pkgbin, '--libs'] + if self.static: + libcmd.append('--static') + p = subprocess.Popen(libcmd + [name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out = p.communicate()[0] if p.returncode != 0: @@ -1074,7 +1080,7 @@ def get_dep_identifier(name, kwargs): modlist = [modlist] for module in modlist: elements.append(module) - return '/'.join(elements) + '/main' + str(kwargs.get('main', False)) + return '/'.join(elements) + '/main' + str(kwargs.get('main', False)) + '/static' + str(kwargs.get('static', False)) def find_external_dependency(name, environment, kwargs): required = kwargs.get('required', True) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 8df856c..369ca20 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -58,7 +58,7 @@ class Environment(): log_dir = 'meson-logs' coredata_file = os.path.join(private_dir, 'coredata.dat') version_regex = '\d+(\.\d+)+(-[a-zA-Z0-9]+)?' - def __init__(self, source_dir, build_dir, main_script_file, options): + def __init__(self, source_dir, build_dir, main_script_file, options, original_cmd_line_args): assert(os.path.isabs(main_script_file)) assert(not os.path.islink(main_script_file)) self.source_dir = source_dir @@ -80,6 +80,7 @@ class Environment(): else: self.cross_info = None self.cmd_line_options = options + self.original_cmd_line_args = original_cmd_line_args # List of potential compilers. if mesonlib.is_windows(): @@ -98,7 +99,12 @@ class Environment(): if (not cross and mesonlib.is_windows()) \ or (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'windows'): self.exe_suffix = 'exe' - self.import_lib_suffix = 'lib' + if self.detect_c_compiler(cross).get_id() == 'msvc': + self.import_lib_suffix = 'lib' + else: + # MinGW-GCC doesn't generate and can't link with a .lib + # It uses the DLL file as the import library + self.import_lib_suffix = 'dll' self.shared_lib_suffix = 'dll' self.shared_lib_prefix = '' self.static_lib_suffix = 'lib' @@ -148,6 +154,18 @@ class Environment(): def is_library(self, fname): return is_library(fname) + def had_argument_for(self, option): + trial1 = '--' + option + trial2 = '-D' + option + previous_is_plaind = False + for i in self.original_cmd_line_args: + if i.startswith(trial1) or i.startswith(trial2): + return True + if previous_is_plaind and i.startswith(option): + return True + previous_is_plaind = i == '-D' + return False + def merge_options(self, options): for (name, value) in options.items(): if name not in self.coredata.user_options: @@ -546,7 +564,7 @@ class Environment(): def get_exe_suffix(self): return self.exe_suffix - # On Windows the library has suffix dll + # On Windows (MSVC) the library has suffix dll # but you link against a file that has suffix lib. def get_import_lib_suffix(self): return self.import_lib_suffix diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 4894ac7..b64eb7f 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1,4 +1,4 @@ -# Copyright 2012-2015 The Meson development team +# Copyright 2012-2016 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -242,11 +242,15 @@ class ExternalProgramHolder(InterpreterObject): def __init__(self, ep): InterpreterObject.__init__(self) self.held_object = ep - self.methods.update({'found': self.found_method}) + self.methods.update({'found': self.found_method, + 'path': self.path_method}) def found_method(self, args, kwargs): return self.found() + def path_method(self, args, kwargs): + return self.get_command() + def found(self): return self.held_object.found() @@ -1327,7 +1331,7 @@ class Interpreter(): key, value = option.split('=', 1) builtin_options = self.coredata.builtin_options if key in builtin_options: - if not hasattr(self.environment.cmd_line_options, value): + if not self.environment.had_argument_for(key): self.coredata.set_builtin_option(key, value) # If this was set on the command line, do not override. else: @@ -1360,16 +1364,15 @@ class Interpreter(): raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv)) self.build.projects[self.subproject] = args[0] mlog.log('Project name: ', mlog.bold(args[0]), sep='') - self.add_languages(node, args[1:]) + self.add_languages(node, args[1:], True) langs = self.coredata.compilers.keys() if 'vala' in langs: if not 'c' in langs: raise InterpreterException('Compiling Vala requires C. Add C to your project languages and rerun Meson.') - @noKwargs @stringArgs def func_add_languages(self, node, args, kwargs): - self.add_languages(node, args) + return self.add_languages(node, args, kwargs.get('required', True)) @noKwargs def func_message(self, node, args, kwargs): @@ -1397,7 +1400,72 @@ class Interpreter(): self.validate_arguments(args, 1, [str]) raise InterpreterException('Error encountered: ' + args[0]) - def add_languages(self, node, args): + def detect_compilers(self, lang, need_cross_compiler): + cross_comp = None + if lang == 'c': + comp = self.environment.detect_c_compiler(False) + if need_cross_compiler: + cross_comp = self.environment.detect_c_compiler(True) + elif lang == 'cpp': + comp = self.environment.detect_cpp_compiler(False) + if need_cross_compiler: + cross_comp = self.environment.detect_cpp_compiler(True) + elif lang == 'objc': + comp = self.environment.detect_objc_compiler(False) + if need_cross_compiler: + cross_comp = self.environment.detect_objc_compiler(True) + elif lang == 'objcpp': + comp = self.environment.detect_objcpp_compiler(False) + if need_cross_compiler: + cross_comp = self.environment.detect_objcpp_compiler(True) + elif lang == 'java': + comp = self.environment.detect_java_compiler() + if need_cross_compiler: + cross_comp = comp # Java is platform independent. + elif lang == 'cs': + comp = self.environment.detect_cs_compiler() + if need_cross_compiler: + cross_comp = comp # C# is platform independent. + elif lang == 'vala': + comp = self.environment.detect_vala_compiler() + if need_cross_compiler: + cross_comp = comp # Vala is too (I think). + elif lang == 'rust': + comp = self.environment.detect_rust_compiler() + if need_cross_compiler: + cross_comp = comp # FIXME, probably not correct. + elif lang == 'fortran': + comp = self.environment.detect_fortran_compiler(False) + if need_cross_compiler: + cross_comp = self.environment.detect_fortran_compiler(True) + elif lang == 'swift': + comp = self.environment.detect_swift_compiler() + if need_cross_compiler: + raise InterpreterException('Cross compilation with Swift is not working yet.') + # cross_comp = self.environment.detect_fortran_compiler(True) + else: + raise InvalidCode('Tried to use unknown language "%s".' % lang) + comp.sanity_check(self.environment.get_scratch_dir()) + self.coredata.compilers[lang] = comp + if cross_comp is not None: + cross_comp.sanity_check(self.environment.get_scratch_dir()) + self.coredata.cross_compilers[lang] = cross_comp + new_options = comp.get_options() + optprefix = lang + '_' + for i in new_options: + if not i.startswith(optprefix): + raise InterpreterException('Internal error, %s has incorrect prefix.' % i) + cmd_prefix = i + '=' + for cmd_arg in self.environment.cmd_line_options.projectoptions: + if cmd_arg.startswith(cmd_prefix): + value = cmd_arg.split('=', 1)[1] + new_options[i].set_value(value) + new_options.update(self.coredata.compiler_options) + self.coredata.compiler_options = new_options + return (comp, cross_comp) + + def add_languages(self, node, args, required): + success = True need_cross_compiler = self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler() for lang in args: lang = lang.lower() @@ -1405,67 +1473,15 @@ class Interpreter(): comp = self.coredata.compilers[lang] cross_comp = self.coredata.cross_compilers.get(lang, None) else: - cross_comp = None - if lang == 'c': - comp = self.environment.detect_c_compiler(False) - if need_cross_compiler: - cross_comp = self.environment.detect_c_compiler(True) - elif lang == 'cpp': - comp = self.environment.detect_cpp_compiler(False) - if need_cross_compiler: - cross_comp = self.environment.detect_cpp_compiler(True) - elif lang == 'objc': - comp = self.environment.detect_objc_compiler(False) - if need_cross_compiler: - cross_comp = self.environment.detect_objc_compiler(True) - elif lang == 'objcpp': - comp = self.environment.detect_objcpp_compiler(False) - if need_cross_compiler: - cross_comp = self.environment.detect_objcpp_compiler(True) - elif lang == 'java': - comp = self.environment.detect_java_compiler() - if need_cross_compiler: - cross_comp = comp # Java is platform independent. - elif lang == 'cs': - comp = self.environment.detect_cs_compiler() - if need_cross_compiler: - cross_comp = comp # C# is platform independent. - elif lang == 'vala': - comp = self.environment.detect_vala_compiler() - if need_cross_compiler: - cross_comp = comp # Vala is too (I think). - elif lang == 'rust': - comp = self.environment.detect_rust_compiler() - if need_cross_compiler: - cross_comp = comp # FIXME, probably not correct. - elif lang == 'fortran': - comp = self.environment.detect_fortran_compiler(False) - if need_cross_compiler: - cross_comp = self.environment.detect_fortran_compiler(True) - elif lang == 'swift': - comp = self.environment.detect_swift_compiler() - if need_cross_compiler: - raise InterpreterException('Cross compilation with Swift is not working yet.') - #cross_comp = self.environment.detect_fortran_compiler(True) - else: - raise InvalidCode('Tried to use unknown language "%s".' % lang) - comp.sanity_check(self.environment.get_scratch_dir()) - self.coredata.compilers[lang] = comp - if cross_comp is not None: - cross_comp.sanity_check(self.environment.get_scratch_dir()) - self.coredata.cross_compilers[lang] = cross_comp - new_options = comp.get_options() - optprefix = lang + '_' - for i in new_options: - if not i.startswith(optprefix): - raise InterpreterException('Internal error, %s has incorrect prefix.' % i) - cmd_prefix = i + '=' - for cmd_arg in self.environment.cmd_line_options.projectoptions: - if cmd_arg.startswith(cmd_prefix): - value = cmd_arg.split('=', 1)[1] - new_options[i].set_value(value) - new_options.update(self.coredata.compiler_options) - self.coredata.compiler_options = new_options + try: + (comp, cross_comp) = self.detect_compilers(lang, need_cross_compiler) + except Exception: + if not required: + mlog.log('Compiler for language', mlog.bold(lang), 'not found.') + success = False + continue + else: + raise mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='') if not comp.get_language() in self.coredata.external_args: (ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp.get_language()) @@ -1477,6 +1493,7 @@ class Interpreter(): self.build.add_cross_compiler(cross_comp) if self.environment.is_cross_build() and not need_cross_compiler: self.build.add_cross_compiler(comp) + return success def func_find_program(self, node, args, kwargs): self.validate_arguments(args, 1, [str]) @@ -1484,14 +1501,12 @@ class Interpreter(): if not isinstance(required, bool): raise InvalidArguments('"required" argument must be a boolean.') exename = args[0] - if exename in self.coredata.ext_progs and\ - self.coredata.ext_progs[exename].found(): - return ExternalProgramHolder(self.coredata.ext_progs[exename]) # Search for scripts relative to current subdir. + # Do not cache found programs because find_program('foobar') + # might give different results when run from different source dirs. search_dir = os.path.join(self.environment.get_source_dir(), self.subdir) extprog = dependencies.ExternalProgram(exename, search_dir=search_dir) progobj = ExternalProgramHolder(extprog) - self.coredata.ext_progs[exename] = extprog if required and not progobj.found(): raise InvalidArguments('Program "%s" not found.' % exename) return progobj @@ -1805,6 +1820,8 @@ class Interpreter(): raise InterpreterException('Input must be a string.') if not isinstance(output, str): raise InterpreterException('Output must be a string.') + if os.path.split(output)[0] != '': + raise InterpreterException('Output file name must not contain a subdirectory.') if 'configuration' in kwargs: conf = kwargs['configuration'] if not isinstance(conf, ConfigurationDataHolder): @@ -2019,6 +2036,11 @@ class Interpreter(): if method_name == 'startswith': return obj.startswith(s) return obj.endswith(s) + elif method_name == 'to_int': + try: + return int(obj) + except Exception: + raise InterpreterException('String can not be converted to int: ' + obj) raise InterpreterException('Unknown method "%s" for a string.' % method_name) def to_native(self, arg): @@ -2176,8 +2198,16 @@ class Interpreter(): return val1 == val2 elif node.ctype == '!=': return val1 != val2 + elif node.ctype == '<': + return val1 < val2 + elif node.ctype == '<=': + return val1 <= val2 + elif node.ctype == '>': + return val1 > val2 + elif node.ctype == '>=': + return val1 >= val2 else: - raise InvalidCode('You broke me.') + raise InvalidCode('You broke my compare eval.') def evaluate_andstatement(self, cur): l = self.evaluate_statement(cur.left) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index f174425..03cbe55 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -123,6 +123,7 @@ class Conf: booleans = '[true, false]' carr.append(['buildtype', 'Build type', self.coredata.get_builtin_option('buildtype'), build_types]) carr.append(['warning_level', 'Warning level', self.coredata.get_builtin_option('warning_level'), warning_levels]) + carr.append(['werror', 'Treat warnings as errors', self.coredata.get_builtin_option('werror'), booleans]) carr.append(['strip', 'Strip on install', self.coredata.get_builtin_option('strip'), booleans]) carr.append(['coverage', 'Coverage report', self.coredata.get_builtin_option('coverage'), booleans]) carr.append(['use_pch', 'Precompiled headers', self.coredata.get_builtin_option('use_pch'), booleans]) @@ -182,7 +183,7 @@ class Conf: def run(args): args = mesonlib.expand_arguments(args) if not args: - sys.exit(1) + args = [os.getcwd()] options = parser.parse_args(args) if len(options.directory) > 1: print('%s <build directory>' % args[0]) diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index a814567..a20ddf0 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -161,9 +161,13 @@ def version_compare(vstr1, vstr2): def default_libdir(): try: - archpath = subprocess.check_output(['dpkg-architecture', '-qDEB_HOST_MULTIARCH']).decode().strip() - return 'lib/' + archpath - except: + pc = subprocess.Popen(['dpkg-architecture', '-qDEB_HOST_MULTIARCH'], + stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + (stdo, _) = pc.communicate() + if pc.returncode == 0: + archpath = stdo.decode().strip() + return 'lib/' + archpath + except Exception: pass if os.path.isdir('/usr/lib64'): return 'lib64' diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index dfeb06e..1a7b084 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -79,12 +79,20 @@ parser.add_argument('directories', nargs='*') class MesonApp(): - def __init__(self, dir1, dir2, script_file, handshake, options): + def __init__(self, dir1, dir2, script_file, handshake, options, original_cmd_line_args): (self.source_dir, self.build_dir) = self.validate_dirs(dir1, dir2, handshake) if not os.path.isabs(options.prefix): - raise RuntimeError('--prefix must be an absolute path.') + raise RuntimeError('--prefix value \'{0}\' must be an absolute path: '.format(options.prefix)) + if options.prefix.endswith('/') or options.prefix.endswith('\\'): + # On Windows we need to preserve the trailing slash if the + # string is of type 'C:\' because 'C:' is not an absolute path. + if len(options.prefix) == 3 and options.prefix[1] == ':': + pass + else: + options.prefix = options.prefix[:-1] self.meson_script_file = script_file self.options = options + self.original_cmd_line_args = original_cmd_line_args def has_build_file(self, dirname): fname = os.path.join(dirname, environment.build_filename) @@ -123,7 +131,7 @@ itself as required.''' return (src_dir, build_dir) def generate(self): - env = environment.Environment(self.source_dir, self.build_dir, self.meson_script_file, self.options) + env = environment.Environment(self.source_dir, self.build_dir, self.meson_script_file, self.options, self.original_cmd_line_args) mlog.initialize(env.get_log_dir()) mlog.debug('Build started at', datetime.datetime.now().isoformat()) mlog.debug('Python binary:', sys.executable) @@ -138,13 +146,13 @@ itself as required.''' mlog.log('Build type:', mlog.bold('native build')) b = build.Build(env) if self.options.backend == 'ninja': - from . import ninjabackend + from .backend import ninjabackend g = ninjabackend.NinjaBackend(b) elif self.options.backend == 'vs2010': - from . import vs2010backend + from .backend import vs2010backend g = vs2010backend.Vs2010Backend(b) elif self.options.backend == 'xcode': - from . import xcodebackend + from .backend import xcodebackend g = xcodebackend.XCodeBackend(b) else: raise RuntimeError('Unknown backend "%s".' % self.options.backend) @@ -246,7 +254,7 @@ def run(mainfile, args): else: mainfile = resolved try: - app = MesonApp(dir1, dir2, mainfile, handshake, options) + app = MesonApp(dir1, dir2, mainfile, handshake, options, sys.argv) except Exception as e: # Log directory does not exist, so just print # to stdout. diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index a351694..1e55b55 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -20,63 +20,52 @@ import os, sys import subprocess from ..coredata import MesonException from .. import mlog -import xml.etree.ElementTree as ET -from ..mesonlib import File girwarning_printed = False class GnomeModule: def compile_resources(self, state, args, kwargs): - cmd = ['glib-compile-resources', '@INPUT@', '--generate'] - if 'source_dir' in kwargs: - resource_loc = os.path.join(state.subdir, kwargs.pop('source_dir')) - d = os.path.join(state.build_to_src, resource_loc) - cmd += ['--sourcedir', d] - else: - resource_loc = state.subdir + cmd = ['glib-compile-resources', '@INPUT@'] + + source_dirs = kwargs.pop('source_dir', []) + if not isinstance(source_dirs, list): + source_dirs = [source_dirs] + + kwargs['depend_files'] = self.get_gresource_dependencies(state, args[1], source_dirs) + + for source_dir in source_dirs: + sourcedir = os.path.join(state.build_to_src, state.subdir, source_dir) + cmd += ['--sourcedir', sourcedir] + if 'c_name' in kwargs: cmd += ['--c-name', kwargs.pop('c_name')] - cmd += ['--target', '@OUTPUT@'] + cmd += ['--generate', '--target', '@OUTPUT@'] + kwargs['command'] = cmd - output_c = args[0] + '.c' - output_h = args[0] + '.h' - resfile = args[1] - kwargs['depend_files'] = self.parse_gresource_xml(state, resfile, resource_loc) - kwargs['input'] = resfile - kwargs['output'] = output_c - target_c = build.CustomTarget(args[0]+'_c', state.subdir, kwargs) - kwargs['output'] = output_h + kwargs['input'] = args[1] + kwargs['output'] = args[0] + '.c' + target_c = build.CustomTarget(args[0] + '_c', state.subdir, kwargs) + kwargs['output'] = args[0] + '.h' target_h = build.CustomTarget(args[0] + '_h', state.subdir, kwargs) return [target_c, target_h] - def parse_gresource_xml(self, state, fobj, resource_loc): - if isinstance(fobj, File): - fname = fobj.fname - subdir = fobj.subdir - else: - fname = fobj - subdir = state.subdir - abspath = os.path.join(state.environment.source_dir, state.subdir, fname) - relative_part = os.path.split(fname)[0] - try: - tree = ET.parse(abspath) - root = tree.getroot() - result = [] - for child in root[0]: - if child.tag != 'file': - mlog.log("Warning, malformed rcc file: ", os.path.join(state.subdir, fname)) - break - else: - relfname = os.path.join(resource_loc, child.text) - absfname = os.path.join(state.environment.source_dir, relfname) - if os.path.isfile(absfname): - result.append(relfname) - else: - mlog.log('Warning, resource file points to nonexisting file %s.' % relfname) - return result - except Exception: - return [] + def get_gresource_dependencies(self, state, input_file, source_dirs): + cmd = ['glib-compile-resources', + os.path.join(state.subdir, input_file), + '--generate-dependencies'] + + for source_dir in source_dirs: + cmd += ['--sourcedir', os.path.join(state.subdir, source_dir)] + + pc = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True, + cwd=state.environment.get_source_dir()) + (stdout, _) = pc.communicate() + if pc.returncode != 0: + mlog.log(mlog.bold('Warning:'), 'glib-compile-resources has failed to get the dependencies for {}'.format(cmd[1])) + raise subprocess.CalledProcessError(pc.returncode, cmd) + + return stdout.split('\n')[:-1] def generate_gir(self, state, args, kwargs): if len(args) != 1: @@ -203,7 +192,7 @@ class GnomeModule: scankwargs['install'] = kwargs['install'] scankwargs['install_dir'] = os.path.join(state.environment.get_datadir(), 'gir-1.0') scan_target = GirTarget(girfile, state.subdir, scankwargs) - + typelib_output = '%s-%s.typelib' % (ns, nsversion) typelib_cmd = ['g-ir-compiler', scan_target, '--output', '@OUTPUT@'] if inc_dirs: @@ -317,7 +306,7 @@ class GnomeModule: return build.CustomTarget(namebase + '-gdbus', state.subdir, custom_kwargs) def initialize(): - mlog.log('Warning, glib compiled dependencies will not work until this upstream issue is fixed:', + mlog.log('Warning, glib compiled dependencies will not work reliably until this upstream issue is fixed:', mlog.bold('https://bugzilla.gnome.org/show_bug.cgi?id=745754')) return GnomeModule() diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py index 81edc76..cb743a6 100644 --- a/mesonbuild/modules/qt5.py +++ b/mesonbuild/modules/qt5.py @@ -157,6 +157,6 @@ class Qt5Module(): return sources def initialize(): - mlog.log('Warning, rcc dependencies will not work properly until this upstream issue is fixed:', + mlog.log('Warning, rcc dependencies will not work reliably until this upstream issue is fixed:', mlog.bold('https://bugreports.qt.io/browse/QTBUG-45460')) return Qt5Module() diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 1d569d5..090684c 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -1,4 +1,4 @@ -# Copyright 2014-2015 The Meson development team +# Copyright 2014-2016 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -63,6 +63,10 @@ class Lexer: ('equal', re.compile(r'==')), ('nequal', re.compile(r'\!=')), ('assign', re.compile(r'=')), + ('le', re.compile(r'<=')), + ('lt', re.compile(r'<')), + ('ge', re.compile(r'>=')), + ('gt', re.compile(r'>')), ] def lex(self, code): @@ -313,6 +317,14 @@ class ArgumentNode(): def __len__(self): return self.num_args() # Fixme +comparison_map = {'equal': '==', + 'nequal': '!=', + 'lt': '<', + 'le': '<=', + 'gt': '>', + 'ge': '>=' + } + # Recursive descent parser for Meson's definition language. # Very basic apart from the fact that we have many precedence # levels so there are not enough words to describe them all. @@ -387,10 +399,9 @@ class Parser: def e4(self): left = self.e5() - if self.accept('equal'): - return ComparisonNode(left.lineno, left.colno, '==', left, self.e5()) - if self.accept('nequal'): - return ComparisonNode(left.lineno, left.colno, '!=', left, self.e5()) + for nodename, operator_type in comparison_map.items(): + if self.accept(nodename): + return ComparisonNode(left.lineno, left.colno, operator_type, left, self.e5()) return left def e5(self): diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index 1ede757..792af6c 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -125,7 +125,13 @@ def run_install_script(d): final_command = commands + [script] + i.cmd_arr[1:] else: final_command = i.cmd_arr - subprocess.check_call(final_command, env=child_env) + try: + rc = subprocess.call(final_command, env=child_env) + if rc != 0: + sys.exit(rc) + except Exception: + print('Failed to run install script:', i.cmd_arr[0]) + sys.exit(1) def is_elf_platform(): platname = platform.system().lower() @@ -188,7 +194,7 @@ def install_targets(d): except FileNotFoundError: pass os.symlink(os.path.split(fname)[-1], symlinkfilename) - except NotImplementedError: + except (NotImplementedError, OSError): if not printed_symlink_error: print("Symlink creation does not work on this platform.") printed_symlink_error = True diff --git a/mesonbuild/scripts/meson_test.py b/mesonbuild/scripts/meson_test.py index 03fd073..453ea61 100644 --- a/mesonbuild/scripts/meson_test.py +++ b/mesonbuild/scripts/meson_test.py @@ -19,6 +19,7 @@ import sys, os, subprocess, time, datetime, pickle, multiprocessing, json import concurrent.futures as conc import argparse import platform +import signal def is_windows(): platname = platform.system().lower() @@ -110,14 +111,28 @@ def run_single_test(wrap, test): child_env.update(test.env) if len(test.extra_paths) > 0: child_env['PATH'] = child_env['PATH'] + ';'.join([''] + test.extra_paths) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=child_env, cwd=test.workdir) + if is_windows(): + setsid = None + else: + setsid = os.setsid + p = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=child_env, + cwd=test.workdir, + preexec_fn=setsid) timed_out = False try: (stdo, stde) = p.communicate(timeout=test.timeout) except subprocess.TimeoutExpired: timed_out = True - p.kill() + # Python does not provide multiplatform support for + # killing a process and all its children so we need + # to roll our own. + if is_windows(): + subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)]) + else: + os.killpg(os.getpgid(p.pid), signal.SIGKILL) (stdo, stde) = p.communicate() endtime = time.time() duration = endtime - starttime @@ -208,6 +223,7 @@ def run_tests(options, datafilename): def run(args): global tests_failed + tests_failed = [] # To avoid state leaks when invoked multiple times (running tests in-process) options = parser.parse_args(args) if len(options.args) != 1: print('Test runner for Meson. Do not run on your own, mmm\'kay?') diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py index f360a7c..a5e5fab 100644 --- a/mesonbuild/scripts/regen_checker.py +++ b/mesonbuild/scripts/regen_checker.py @@ -30,9 +30,14 @@ def need_regen(regeninfo): def regen(regeninfo): scriptdir = os.path.split(__file__)[0] - mesonscript = os.path.join(scriptdir, 'meson.py') - cmd = [sys.executable, mesonscript, regeninfo.build_dir, regeninfo.source_dir, - '--backend=vs2010', 'secret-handshake'] + mesonscript = os.path.join(scriptdir, '../../', 'meson') + cmd = [sys.executable, + mesonscript, + '--internal', + 'regenerate', + regeninfo.build_dir, + regeninfo.source_dir, + '--backend=vs2010'] subprocess.check_call(cmd) def run(args): diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 2818fa0..ad8f106 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -25,6 +25,8 @@ except ImportError: has_ssl = False API_ROOT = 'http://wrapdb.mesonbuild.com/v1/' +ssl_warning_printed = False + def build_ssl_context(): ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) ctx.options |= ssl.OP_NO_SSLv2 diff --git a/run_tests.py b/run_tests.py index 2211949..d4c6ff2 100755 --- a/run_tests.py +++ b/run_tests.py @@ -207,8 +207,8 @@ def run_test(testdir, extra_args, should_succeed): stdout=subprocess.PIPE, stderr=subprocess.PIPE) (o, e) = pc.communicate() build_time = time.time() - build_start - stdo += o.decode('utf-8') - stde += e.decode('utf-8') + stdo += o.decode(sys.stdout.encoding) + stde += e.decode(sys.stdout.encoding) if pc.returncode != 0: return TestResult('Compiling source code failed.', stdo, stde, gen_time, build_time) test_start = time.time() @@ -230,8 +230,8 @@ def run_test(testdir, extra_args, should_succeed): pi = subprocess.Popen(install_commands, cwd=test_build_dir, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (o, e) = pi.communicate() - stdo += o.decode('utf-8') - stde += e.decode('utf-8') + stdo += o.decode(sys.stdout.encoding) + stde += e.decode(sys.stdout.encoding) if pi.returncode != 0: return TestResult('Running install failed.', stdo, stde, gen_time, build_time, test_time) return TestResult(validate_install(testdir, install_dir), stdo, stde, gen_time, build_time, test_time) @@ -34,6 +34,7 @@ setup(name='meson', packages=['mesonbuild', 'mesonbuild.modules', 'mesonbuild.scripts', + 'mesonbuild.backend', 'mesonbuild.wrap'], package_data={'mesonbuild': ['*.ui']}, scripts=['meson', 'mesonconf', 'mesongui', 'mesonintrospect', 'wraptool'], diff --git a/test cases/common/105 find program path/meson.build b/test cases/common/105 find program path/meson.build new file mode 100644 index 0000000..2ece2bc --- /dev/null +++ b/test cases/common/105 find program path/meson.build @@ -0,0 +1,9 @@ +project('find program', 'c') + +prog = find_program('program.py') + +# Python 3 is guaranteed to be available because Meson +# is implemented in it. +python = find_program('python3') + +run_command(python, prog.path()) diff --git a/test cases/common/105 find program path/program.py b/test cases/common/105 find program path/program.py new file mode 100644 index 0000000..2ebc564 --- /dev/null +++ b/test cases/common/105 find program path/program.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 + +print("Found") diff --git a/test cases/common/42 string formatting/meson.build b/test cases/common/42 string formatting/meson.build index 81f5268..39737f7 100644 --- a/test cases/common/42 string formatting/meson.build +++ b/test cases/common/42 string formatting/meson.build @@ -40,3 +40,5 @@ endif if long.endswith(prefix) error('Not suffix.') endif + +assert('3'.to_int() == 3, 'String int conversion does not work.') diff --git a/test cases/common/59 object generator/obj_generator.py b/test cases/common/59 object generator/obj_generator.py index 6960059..204f1eb 100755 --- a/test cases/common/59 object generator/obj_generator.py +++ b/test cases/common/59 object generator/obj_generator.py @@ -12,7 +12,7 @@ if __name__ == '__main__': ifile = sys.argv[2] ofile = sys.argv[3] if compiler.endswith('cl'): - cmd = [compiler, '/nologo', '/Fo'+ofile, '/c', ifile] + cmd = [compiler, '/nologo', '/MDd', '/Fo'+ofile, '/c', ifile] else: cmd = [compiler, '-c', ifile, '-o', ofile] sys.exit(subprocess.call(cmd)) diff --git a/test cases/common/60 install script/myinstall.sh b/test cases/common/60 install script/myinstall.sh index 4739dee..42a415e 100755 --- a/test cases/common/60 install script/myinstall.sh +++ b/test cases/common/60 install script/myinstall.sh @@ -1,5 +1,7 @@ #!/bin/sh +set -eu + echo Starting custom installation step # These commands fail on Windows, but we don't really care. diff --git a/test cases/common/68 number arithmetic/meson.build b/test cases/common/68 number arithmetic/meson.build index 894c065..3872a11 100644 --- a/test cases/common/68 number arithmetic/meson.build +++ b/test cases/common/68 number arithmetic/meson.build @@ -20,3 +20,15 @@ endif if (5 / 3) * 3 != 3 error('Integer division is broken') endif + +assert(3 < 4, 'Lt broken') +assert(not(4 < 3), 'Lt broken') +assert(3 <= 4, 'Lte broken') +assert(not(4 <= 3), 'Lte broken') +assert(3 <= 3, 'Lte broken') + +assert(4 > 3, 'Gt broken') +assert(not(3 > 4), 'Gt broken') +assert(4 >= 3, 'Gte broken') +assert(not(3 >= 4), 'Gte broken') +assert(3 >= 3, 'Gte broken') diff --git a/test cases/common/89 add language/meson.build b/test cases/common/89 add language/meson.build index 339fe22..d9bc0fa 100644 --- a/test cases/common/89 add language/meson.build +++ b/test cases/common/89 add language/meson.build @@ -2,6 +2,7 @@ project('add language', 'c') test('C', executable('cprog', 'prog.c')) -add_languages('cpp') +assert(add_languages('cpp'), 'Add_languages returned false on success') +assert(not add_languages('klingon', required : false), 'Add_languages returned true on failure.') test('C++', executable('cppprog', 'prog.cc')) diff --git a/test cases/failing/25 int conversion/meson.build b/test cases/failing/25 int conversion/meson.build new file mode 100644 index 0000000..51f6c7e --- /dev/null +++ b/test cases/failing/25 int conversion/meson.build @@ -0,0 +1,3 @@ +project('int conversion', 'c') + +'notanumber'.to_int() diff --git a/test cases/failing/26 badlang/meson.build b/test cases/failing/26 badlang/meson.build new file mode 100644 index 0000000..f6bf0cc --- /dev/null +++ b/test cases/failing/26 badlang/meson.build @@ -0,0 +1,3 @@ +project('badlang', 'c') + +add_languages('nonexisting') diff --git a/test cases/failing/27 output subdir/foo.in b/test cases/failing/27 output subdir/foo.in new file mode 100644 index 0000000..3d1bf19 --- /dev/null +++ b/test cases/failing/27 output subdir/foo.in @@ -0,0 +1 @@ +Nothing here. diff --git a/test cases/failing/27 output subdir/meson.build b/test cases/failing/27 output subdir/meson.build new file mode 100644 index 0000000..282d0b7 --- /dev/null +++ b/test cases/failing/27 output subdir/meson.build @@ -0,0 +1,5 @@ +project('outdir path', 'c') + +configure_file(input : 'foo.in', + output : 'subdir/foo', + configuration : configuration_data()) diff --git a/test cases/failing/27 output subdir/subdir/dummy.txt b/test cases/failing/27 output subdir/subdir/dummy.txt new file mode 100644 index 0000000..f10acf3 --- /dev/null +++ b/test cases/failing/27 output subdir/subdir/dummy.txt @@ -0,0 +1,2 @@ +I'm only here because Git is stupid about empty dirs. + diff --git a/test cases/vala/7 shared library/lib/meson.build b/test cases/vala/7 shared library/lib/meson.build new file mode 100644 index 0000000..8eca0d4 --- /dev/null +++ b/test cases/vala/7 shared library/lib/meson.build @@ -0,0 +1 @@ +l = shared_library('valalib', 'mylib.vala', dependencies : valadeps) diff --git a/test cases/vala/7 shared library/lib/mylib.vala b/test cases/vala/7 shared library/lib/mylib.vala new file mode 100644 index 0000000..5cc903b --- /dev/null +++ b/test cases/vala/7 shared library/lib/mylib.vala @@ -0,0 +1,5 @@ +public class LibraryObject : Object { + public void func() { + stdout.printf("Method in library called."); + } +} diff --git a/test cases/vala/7 shared library/meson.build b/test cases/vala/7 shared library/meson.build new file mode 100644 index 0000000..9c56f14 --- /dev/null +++ b/test cases/vala/7 shared library/meson.build @@ -0,0 +1,10 @@ +project('shared library', 'vala', 'c') + +valadeps = [dependency('glib-2.0'), dependency('gobject-2.0')] + +libinc = include_directories('lib') + +subdir('lib') +subdir('prog') + +test('valasharedtest', e) diff --git a/test cases/vala/7 shared library/prog/meson.build b/test cases/vala/7 shared library/prog/meson.build new file mode 100644 index 0000000..2b842ea --- /dev/null +++ b/test cases/vala/7 shared library/prog/meson.build @@ -0,0 +1,4 @@ +e = executable('valaprog', 'prog.vala', + link_with : l, + include_directories : libinc, + dependencies : valadeps) diff --git a/test cases/vala/7 shared library/prog/prog.vala b/test cases/vala/7 shared library/prog/prog.vala new file mode 100644 index 0000000..3c4a017 --- /dev/null +++ b/test cases/vala/7 shared library/prog/prog.vala @@ -0,0 +1,7 @@ +class MainApp : Object { + public static int main(string[] args) { + var l = new LibraryObject(); + l.func(); + return 0; + } +} |