diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 35 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 33 | ||||
-rw-r--r-- | mesonbuild/backend/vs2010backend.py | 84 | ||||
-rw-r--r-- | mesonbuild/build.py | 2 | ||||
-rw-r--r-- | mesonbuild/compilers.py | 26 | ||||
-rw-r--r-- | mesonbuild/environment.py | 36 | ||||
-rw-r--r-- | mesonbuild/modules/pkgconfig.py | 15 | ||||
-rw-r--r-- | mesonbuild/scripts/depfixer.py | 23 | ||||
-rw-r--r-- | mesonbuild/scripts/meson_install.py | 18 | ||||
-rwxr-xr-x | run_tests.py | 25 | ||||
-rw-r--r-- | setup.py | 6 | ||||
-rw-r--r-- | test cases/common/110 extract same name/lib.c | 3 | ||||
-rw-r--r-- | test cases/common/110 extract same name/main.c | 6 | ||||
-rw-r--r-- | test cases/common/110 extract same name/meson.build | 6 | ||||
-rw-r--r-- | test cases/common/110 extract same name/src/lib.c | 3 | ||||
-rw-r--r-- | test cases/common/51 pkgconfig-gen/meson.build | 5 | ||||
-rw-r--r-- | test cases/failing/28 noprog use/meson.build | 9 |
18 files changed, 213 insertions, 126 deletions
@@ -31,6 +31,10 @@ called 'meson.build'. To generate the build system run this command: `meson <source directory> <build directory>` +Depending on how you obtained Meson the command might also be called +`meson.py` instead of plain `meson`. In the rest of this document we +are going to use the latter form. + You can omit either of the two directories, and Meson will substitute the current directory and autodetect what you mean. This allows you to do things like this: diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8d0b0f6..313580b 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -21,12 +21,11 @@ import subprocess from ..coredata import MesonException class InstallData(): - def __init__(self, source_dir, build_dir, prefix, depfixer): + def __init__(self, source_dir, build_dir, prefix): self.source_dir = source_dir self.build_dir= build_dir self.prefix = prefix self.targets = [] - self.depfixer = depfixer self.headers = [] self.man = [] self.data = [] @@ -142,7 +141,7 @@ class Backend(): return os.path.relpath(os.path.join('dummyprefixdir', todir),\ os.path.join('dummyprefixdir', fromdir)) - def flatten_object_list(self, target, proj_dir_to_build_root='', include_dir_names=True): + def flatten_object_list(self, target, proj_dir_to_build_root=''): obj_list = [] for obj in target.get_objects(): if isinstance(obj, str): @@ -150,7 +149,7 @@ class Backend(): self.build_to_src, target.get_subdir(), obj) obj_list.append(o) elif isinstance(obj, build.ExtractedObjects): - obj_list += self.determine_ext_objs(obj, proj_dir_to_build_root, include_dir_names) + obj_list += self.determine_ext_objs(obj, proj_dir_to_build_root) else: raise MesonException('Unknown data type in object list.') return obj_list @@ -210,28 +209,21 @@ class Backend(): return c raise RuntimeError('Unreachable code') - def determine_ext_objs(self, extobj, proj_dir_to_build_root='', include_dir_names=True): + def object_filename_from_source(self, target, source): + return source.fname.replace('/', '_').replace('\\', '_') + '.' + self.environment.get_object_suffix() + + def determine_ext_objs(self, extobj, proj_dir_to_build_root=''): result = [] targetdir = self.get_target_private_dir(extobj.target) - suffix = '.' + self.environment.get_object_suffix() for osrc in extobj.srclist: - osrc_base = osrc.fname - if not self.source_suffix_in_objs: - osrc_base = '.'.join(osrc.split('.')[:-1]) # If extracting in a subproject, the subproject # name gets duplicated in the file name. pathsegs = osrc.subdir.split(os.sep) if pathsegs[0] == 'subprojects': pathsegs = pathsegs[2:] fixedpath = os.sep.join(pathsegs) - if include_dir_names: - objbase = osrc_base.replace('/', '_').replace('\\', '_') - else: - # vs2010 backend puts all obj files without directory prefixes into build dir, so just - # use the file name without a directory (will be stripped by os.path.basename() below). - objbase = osrc_base - objname = os.path.join(proj_dir_to_build_root, - targetdir, os.path.basename(objbase) + suffix) + objname = os.path.join(proj_dir_to_build_root, targetdir, + self.object_filename_from_source(extobj.target, osrc)) result.append(objname) return result @@ -388,6 +380,15 @@ class Backend(): exe_arr = exe.get_command() return exe_arr + def replace_extra_args(self, args, genlist): + final_args = [] + for a in args: + if a == '@EXTRA_ARGS@': + final_args += genlist.get_extra_args() + else: + final_args.append(a) + return final_args + def eval_custom_target_command(self, target, absolute_paths=False): if not absolute_paths: ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output] diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 29ce8cd..4757bd3 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -129,7 +129,6 @@ class NinjaBackend(backends.Backend): def __init__(self, build): super().__init__(build) - self.source_suffix_in_objs = True self.ninja_filename = 'build.ninja' self.fortran_deps = {} self.all_outputs = {} @@ -137,7 +136,10 @@ class NinjaBackend(backends.Backend): def detect_vs_dep_prefix(self, outfile, tempfilename): '''VS writes its dependency in a locale dependent format. Detect the search prefix to use.''' - if shutil.which('cl') is None: + # 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'), @@ -182,7 +184,8 @@ int dummy; self.generate_tests(outfile) outfile.write('# Install rules\n\n') self.generate_install(outfile) - if self.environment.coredata.base_options.get('b_coverage', False): + 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') @@ -234,7 +237,8 @@ int dummy; self.generate_cs_target(target, outfile) return if 'vala' in self.environment.coredata.compilers.keys() and self.has_vala(target): - vala_output_files = self.generate_vala_compile(target, outfile) + vc = self.environment.coredata.compilers['vala'] + vala_output_files = self.generate_vala_compile(vc, 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) @@ -443,10 +447,11 @@ int dummy; phony_elem.write(outfile) elem = NinjaBuildElement(self.all_outputs, 'coveragereport/index.html', 'CUSTOM_COMMAND', '') + htmloutdir = os.path.join(self.environment.get_log_dir(), 'coveragereport') command = [lcov_exe, '--directory', self.environment.get_build_dir(),\ '--capture', '--output-file', 'coverage.info', '--no-checksum',\ '&&', genhtml_exe, '--prefix', self.environment.get_build_dir(),\ - '--output-directory', self.environment.get_log_dir(), '--title', 'Code coverage',\ + '--output-directory', htmloutdir, '--title', 'Code coverage',\ '--legend', '--show-details', 'coverage.info'] elem.add_item('COMMAND', command) elem.add_item('DESC', 'Generating HTML coverage report.') @@ -458,10 +463,9 @@ int dummy; script_root = self.environment.get_script_dir() install_script = os.path.join(script_root, 'meson_install.py') install_data_file = os.path.join(self.environment.get_scratch_dir(), 'install.dat') - depfixer = [sys.executable, self.environment.get_build_command(), '--internal', 'depfixer'] d = InstallData(self.environment.get_source_dir(), self.environment.get_build_dir(), - self.environment.get_prefix(), depfixer) + self.environment.get_prefix()) elem = NinjaBuildElement(self.all_outputs, 'install', 'CUSTOM_COMMAND', 'PHONY') elem.add_dep('all') elem.add_item('DESC', 'Installing files.') @@ -789,7 +793,7 @@ int dummy; break return result - def generate_vala_compile(self, target, outfile): + def generate_vala_compile(self, compiler, target, outfile): """Vala is compiled into C. Set up all necessary build steps here.""" valac = self.environment.coredata.compilers['vala'] (src, vapi_src) = self.split_vala_sources(target.get_sources()) @@ -809,7 +813,10 @@ int dummy; generated_c_files = [] outputs = [vapiname] - args = ['-d', self.get_target_private_dir(target)] + args = [] + args += self.build.get_global_args(compiler) + args += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype')) + args += ['-d', self.get_target_private_dir(target)] args += ['-C']#, '-o', cname] if not isinstance(target, build.Executable): outputs.append(hname) @@ -1349,13 +1356,7 @@ rule FORTRAN_DEP_HACK relout = self.get_target_private_dir(target) args = [x.replace("@SOURCE_DIR@", self.build_to_src).replace("@BUILD_DIR@", relout) for x in args] - final_args = [] - for a in args: - if a == '@EXTRA_ARGS@': - final_args += genlist.get_extra_args() - else: - final_args.append(a) - cmdlist = exe_arr + final_args + cmdlist = exe_arr + self.replace_extra_args(args, genlist) elem = NinjaBuildElement(self.all_outputs, outfiles, 'CUSTOM_COMMAND', infilename) if len(extra_dependencies) > 0: elem.add_dep(extra_dependencies) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 84ef8c3..ae4cd33 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -16,6 +16,8 @@ import os, sys import pickle from mesonbuild import compilers +from mesonbuild.build import BuildTarget +from mesonbuild.mesonlib import File from . import backends from .. import build from .. import dependencies @@ -35,8 +37,35 @@ class Vs2010Backend(backends.Backend): def __init__(self, build): super().__init__(build) self.project_file_version = '10.0.30319.1' - # foo.c compiles to foo.obj, not foo.c.obj - self.source_suffix_in_objs = False + self.sources_conflicts = {} + + def object_filename_from_source(self, target, source): + basename = os.path.basename(source.fname) + filename_without_extension = '.'.join(basename.split('.')[:-1]) + if basename in self.sources_conflicts[target.get_id()]: + # If there are multiple source files with the same basename, we must resolve the conflict + # by giving each a unique object output file. + filename_without_extension = '.'.join(source.fname.split('.')[:-1]).replace('/', '_').replace('\\', '_') + return filename_without_extension + '.' + self.environment.get_object_suffix() + + def resolve_source_conflicts(self): + for name, target in self.build.targets.items(): + if not isinstance(target, BuildTarget): + continue + conflicts = {} + for s in target.get_sources(): + if hasattr(s, 'held_object'): + s = s.held_object + if not isinstance(s, File): + continue + basename = os.path.basename(s.fname) + conflicting_sources = conflicts.get(basename, None) + if conflicting_sources is None: + conflicting_sources = [] + conflicts[basename] = conflicting_sources + conflicting_sources.append(s) + self.sources_conflicts[target.get_id()] = {name: src_conflicts for name, src_conflicts in conflicts.items() + if len(src_conflicts) > 1} def generate_custom_generator_commands(self, target, parent_node): all_output_files = [] @@ -51,26 +80,24 @@ class Vs2010Backend(backends.Backend): exe = generator.get_exe() infilelist = genlist.get_infilelist() outfilelist = genlist.get_outfilelist() - 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()[0] + exe_arr = self.exe_object_to_cmd_array(exe) base_args = generator.get_arglist() + target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)) for i in range(len(infilelist)): if len(infilelist) == len(outfilelist): - sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i]) + sole_output = os.path.join(target_private_dir, outfilelist[i]) else: sole_output = '' curfile = infilelist[i] infilename = os.path.join(self.environment.get_source_dir(), curfile) outfiles = genlist.get_outputs_for(curfile) - outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles] + outfiles = [os.path.join(target_private_dir, of) for of in outfiles] all_output_files += outfiles args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\ for x in base_args] - args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", self.get_target_private_dir(target)) + args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", target_private_dir) for x in args] - fullcmd = [exe_file] + args + fullcmd = exe_arr + self.replace_extra_args(args, genlist) commands.append(' '.join(self.special_quote(fullcmd))) inputs.append(infilename) outputs.extend(outfiles) @@ -86,6 +113,7 @@ class Vs2010Backend(backends.Backend): return all_output_files def generate(self, interp): + self.resolve_source_conflicts() self.interpreter = interp self.platform = 'Win32' self.buildtype = self.environment.coredata.get_builtin_option('buildtype') @@ -421,11 +449,8 @@ class Vs2010Backend(backends.Backend): clconf = ET.SubElement(compiles, 'ClCompile') opt = ET.SubElement(clconf, 'Optimization') opt.text = 'disabled' - inc_dirs = [proj_to_src_dir, self.get_target_private_dir(target)] - cur_dir = target.subdir - if cur_dir == '': - cur_dir= '.' - inc_dirs.append(cur_dir) + inc_dirs = ['.', self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)), + proj_to_src_dir] extra_args = {'c': [], 'cpp': []} for l, args in self.environment.coredata.external_args.items(): @@ -437,7 +462,7 @@ class Vs2010Backend(backends.Backend): for l, args in target.extra_args.items(): if l in extra_args: extra_args[l] += args - general_args = compiler.get_buildtype_args(self.buildtype) + general_args = compiler.get_buildtype_args(self.buildtype).copy() # FIXME all the internal flags of VS (optimization etc) are represented # by their own XML elements. In theory we should split all flags to those # that have an XML element and those that don't and serialise them @@ -469,6 +494,10 @@ class Vs2010Backend(backends.Backend): curdir = os.path.join(d.curdir, i) inc_dirs.append(self.relpath(curdir, target.subdir)) # build dir inc_dirs.append(os.path.join(proj_to_src_root, curdir)) # src dir + for i in d.get_extra_build_dirs(): + curdir = os.path.join(d.curdir, i) + inc_dirs.append(self.relpath(curdir, target.subdir)) # build dir + inc_dirs.append('%(AdditionalIncludeDirectories)') ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(inc_dirs) preproc = ET.SubElement(clconf, 'PreprocessorDefinitions') @@ -519,13 +548,13 @@ class Vs2010Backend(backends.Backend): ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args) additional_links = [] - for t in target.link_targets: + for t in target.get_dependencies(): lobj = self.build.targets[t.get_id()] rel_path = self.relpath(lobj.subdir, target.subdir) linkname = os.path.join(rel_path, lobj.get_import_filename()) additional_links.append(linkname) additional_objects = [] - for o in self.flatten_object_list(target, down, include_dir_names=False): + for o in self.flatten_object_list(target, down): assert(isinstance(o, str)) additional_objects.append(o) if len(additional_links) > 0: @@ -548,17 +577,18 @@ class Vs2010Backend(backends.Backend): targetmachine = ET.SubElement(link, 'TargetMachine') targetmachine.text = 'MachineX86' - if len(headers) + len(gen_hdrs) > 0: + extra_files = target.extra_files + if len(headers) + len(gen_hdrs) + len(extra_files) > 0: inc_hdrs = ET.SubElement(root, 'ItemGroup') for h in headers: relpath = h.rel_to_builddir(proj_to_src_root) ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath) for h in gen_hdrs: - if isinstance(h, str): - relpath = h - else: - relpath = h.rel_to_builddir(proj_to_src_root) - ET.SubElement(inc_hdrs, 'CLInclude', Include = relpath) + ET.SubElement(inc_hdrs, 'CLInclude', Include=h) + for h in target.extra_files: + relpath = os.path.join(proj_to_src_dir, h) + ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath) + if len(sources) + len(gen_src) + len(pch_sources) > 0: inc_src = ET.SubElement(root, 'ItemGroup') for s in sources: @@ -566,9 +596,11 @@ class Vs2010Backend(backends.Backend): inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath) self.add_pch(inc_cl, proj_to_src_dir, pch_sources, s) self.add_additional_options(s, inc_cl, extra_args, additional_options_set) + basename = os.path.basename(s.fname) + if basename in self.sources_conflicts[target.get_id()]: + ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + self.object_filename_from_source(target, s) for s in gen_src: - relpath = self.relpath(s, target.subdir) - inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath) + inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=s) self.add_pch(inc_cl, proj_to_src_dir, pch_sources, s) self.add_additional_options(s, inc_cl, extra_args, additional_options_set) for lang in pch_sources: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 8732987..2a91aa3 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -849,6 +849,8 @@ class CustomTarget: if isinstance(c, str): 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): self.dependencies.append(c) diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index ff2df56..7bc59d8 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -126,7 +126,7 @@ base_options = { 'off'), 'b_coverage': coredata.UserBooleanOption('b_coverage', 'Enable coverage tracking.', - True), + False), } def sanitizer_compile_args(value): @@ -484,7 +484,6 @@ int someSymbolHereJustForFun; return p.returncode == 0 def links(self, code, extra_args = []): - suflen = len(self.default_suffix) (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix) os.close(fd) (fd, dstname) = tempfile.mkstemp() @@ -574,7 +573,7 @@ int main(int argc, char **argv) { ''' res = self.run(templ % (prefix, element), extra_args) if not res.compiled: - raise EnvironmentException('Could not compile sizeof test.') + return -1 if res.returncode != 0: raise EnvironmentException('Could not run sizeof test binary.') return int(res.stdout) @@ -1044,6 +1043,11 @@ class ValaCompiler(Compiler): suffix = filename.split('.')[-1] return suffix in ('vala', 'vapi') + def get_buildtype_args(self, buildtype): + if buildtype == 'debug' or buildtype == 'debugoptimized': + return ['--debug'] + return [] + class RustCompiler(Compiler): def __init__(self, exelist, version): super().__init__(exelist, version) @@ -1427,7 +1431,7 @@ class GnuCCompiler(CCompiler): self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], '2': ['-Wall', '-Wextra', '-Winvalid-pch'], '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize'] + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] if self.gcc_type != GCC_OSX: self.base_options.append('b_lundef') @@ -1492,7 +1496,7 @@ class GnuObjCCompiler(ObjCCompiler): self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], '2': ['-Wall', '-Wextra', '-Winvalid-pch'], '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize'] + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] if self.gcc_type != GCC_OSX: self.base_options.append('b_lundef') @@ -1520,7 +1524,7 @@ class GnuObjCPPCompiler(ObjCPPCompiler): self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize'] + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] if self.gcc_type != GCC_OSX: self.base_options.append('b_lundef') @@ -1540,7 +1544,7 @@ class ClangObjCCompiler(GnuObjCCompiler): def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): super().__init__(exelist, version, is_cross, exe_wrapper) self.id = 'clang' - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize'] + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] self.clang_type = cltype if self.clang_type != CLANG_OSX: self.base_options.append('b_lundef') @@ -1550,7 +1554,7 @@ class ClangObjCPPCompiler(GnuObjCPPCompiler): super().__init__(exelist, version, is_cross, exe_wrapper) self.id = 'clang' self.clang_type = cltype - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize'] + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] if self.clang_type != CLANG_OSX: self.base_options.append('b_lundef') @@ -1562,7 +1566,7 @@ class ClangCCompiler(CCompiler): self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], '2': ['-Wall', '-Wextra', '-Winvalid-pch'], '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize'] + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] if self.clang_type != CLANG_OSX: self.base_options.append('b_lundef') @@ -1610,7 +1614,7 @@ class GnuCPPCompiler(CPPCompiler): self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize'] + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] if self.gcc_type != GCC_OSX: self.base_options.append('b_lundef') @@ -1660,7 +1664,7 @@ class ClangCPPCompiler(CPPCompiler): '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} self.clang_type = cltype - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize'] + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] if self.clang_type != CLANG_OSX: self.base_options.append('b_lundef') diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index c381ab5..df848f8 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -251,11 +251,11 @@ class Environment(): for compiler in compilers: for arg in ['--version', '-V']: try: - p = subprocess.Popen([compiler] + [arg], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + p = subprocess.Popen([compiler] + [arg], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) except OSError: - continue + continue (out, err) = p.communicate() out = out.decode(errors='ignore') err = err.decode(errors='ignore') @@ -266,32 +266,32 @@ class Environment(): version = vmatch.group(0) if 'GNU Fortran' in out: - return GnuFortranCompiler([compiler], version, GCC_STANDARD, is_cross, exe_wrap) + return GnuFortranCompiler([compiler], version, GCC_STANDARD, is_cross, exe_wrap) if 'G95' in out: - return G95FortranCompiler([compiler], version, is_cross, exe_wrap) + return G95FortranCompiler([compiler], version, is_cross, exe_wrap) if 'Sun Fortran' in err: - version = 'unknown version' - vmatch = re.search(Environment.version_regex, err) - if vmatch: - version = vmatch.group(0) - return SunFortranCompiler([compiler], version, is_cross, exe_wrap) + version = 'unknown version' + vmatch = re.search(Environment.version_regex, err) + if vmatch: + version = vmatch.group(0) + return SunFortranCompiler([compiler], version, is_cross, exe_wrap) if 'ifort (IFORT)' in out: - return IntelFortranCompiler([compiler], version, is_cross, exe_wrap) - + return IntelFortranCompiler([compiler], version, is_cross, exe_wrap) + if 'PathScale EKOPath(tm)' in err: - return PathScaleFortranCompiler([compiler], version, is_cross, exe_wrap) + return PathScaleFortranCompiler([compiler], version, is_cross, exe_wrap) if 'pgf90' in out: - return PGIFortranCompiler([compiler], version, is_cross, exe_wrap) + return PGIFortranCompiler([compiler], version, is_cross, exe_wrap) if 'Open64 Compiler Suite' in err: - return Open64FortranCompiler([compiler], version, is_cross, exe_wrap) + return Open64FortranCompiler([compiler], version, is_cross, exe_wrap) if 'NAG Fortran' in err: - return NAGFortranCompiler([compiler], version, is_cross, exe_wrap) + return NAGFortranCompiler([compiler], version, is_cross, exe_wrap) raise EnvironmentException('Unknown compiler(s): "' + ', '.join(compilers) + '"') @@ -514,7 +514,7 @@ class Environment(): evar = 'AR' if evar in os.environ: linker = os.environ[evar].strip() - if isinstance(compiler, VisualStudioCCompiler): + elif isinstance(compiler, VisualStudioCCompiler): linker= self.vs_static_linker else: linker = self.default_static_linker diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index f18decf..ffe03e2 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -21,7 +21,8 @@ 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): + def generate_pkgconfig_file(self, state, libraries, subdirs, name, description, version, filebase, + pub_reqs, priv_reqs, priv_libs): outdir = state.environment.scratch_dir fname = os.path.join(outdir, filebase + '.pc') ofile = open(fname, 'w') @@ -34,6 +35,12 @@ class PkgConfigModule: 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: ofile.write('-l%s ' % l.name) @@ -73,9 +80,13 @@ class PkgConfigModule: description = kwargs.get('description', None) if not isinstance(description, str): raise coredata.MesonException('Description is not a string.') + pub_reqs = mesonlib.stringlistify(kwargs.get('requires', [])) + 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') - self.generate_pkgconfig_file(state, libs, subdirs, name, description, version, filebase) + 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) def initialize(): diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index 1ab83b6..8ff0dd1 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2013-2014 The Meson development team +# Copyright 2013-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. @@ -110,8 +110,9 @@ class SectionHeader(DataSizes): self.sh_entsize = struct.unpack(self.Word, ifile.read(self.WordSize))[0] class Elf(DataSizes): - def __init__(self, bfile): + def __init__(self, bfile, verbose=True): 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) @@ -124,22 +125,21 @@ class Elf(DataSizes): if data[1:4] != b'ELF': # This script gets called to non-elf targets too # so just ignore them. - print('File "%s" is not an ELF file.' % self.bfile) + if self.verbose: + print('File "%s" is not an ELF file.' % self.bfile) sys.exit(0) if data[4] == 1: ptrsize = 32 elif data[4] == 2: ptrsize = 64 else: - print('File "%s" has unknown ELF class.' % self.bfile) - sys.exit(1) + sys.exit('File "%s" has unknown ELF class.' % self.bfile) if data[5] == 1: is_le = True elif data[5] == 2: is_le = False else: - print('File "%s" has unknown ELF endianness.' % self.bfile) - sys.exit(1) + sys.exit('File "%s" has unknown ELF endianness.' % self.bfile) return (ptrsize, is_le) def parse_header(self): @@ -257,14 +257,17 @@ class Elf(DataSizes): self.bf.write(newname) def fix_rpath(self, new_rpath): + if isinstance(new_rpath, str): + new_rpath = new_rpath.encode('utf8') rp_off = self.get_rpath_offset() if rp_off is None: - print('File does not have rpath. It should be a fully static executable.') + if self.verbose: + print('File does not have rpath. It should be a fully static executable.') return self.bf.seek(rp_off) old_rpath = self.read_str() if len(old_rpath) < len(new_rpath): - print("New rpath must not be longer than the old one.") + sys.exit("New rpath must not be longer than the old one.") self.bf.seek(rp_off) self.bf.write(new_rpath) self.bf.write(b'\0'*(len(old_rpath) - len(new_rpath) + 1)) @@ -295,7 +298,7 @@ def run(args): e.print_rpath() else: new_rpath = args[1] - e.fix_rpath(new_rpath.encode('utf8')) + e.fix_rpath(new_rpath) return 0 if __name__ == '__main__': diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index 8e3d0ca..cc86b62 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -16,6 +16,7 @@ import sys, pickle, os, shutil, subprocess, gzip, platform from glob import glob +from mesonbuild.scripts import depfixer def do_install(datafilename): ifile = open(datafilename, 'rb') @@ -203,15 +204,14 @@ def install_targets(d): print("Symlink creation does not work on this platform.") printed_symlink_error = True if is_elf_platform(): - p = subprocess.Popen(d.depfixer + [outname, install_rpath], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (stdo, stde) = p.communicate() - if p.returncode != 0: - print('Could not fix dependency info.\n') - print('Stdout:\n%s\n' % stdo.decode()) - print('Stderr:\n%s\n' % stde.decode()) - sys.exit(1) + try: + e = depfixer.Elf(outname, False) + e.fix_rpath(install_rpath) + except SystemExit as e: + if isinstance(e.code, int) and e.code == 0: + pass + else: + raise def run(args): if len(args) != 1: diff --git a/run_tests.py b/run_tests.py index c4f14ec..a3ec0dc 100755 --- a/run_tests.py +++ b/run_tests.py @@ -18,7 +18,7 @@ from glob import glob import os, subprocess, shutil, sys, signal from io import StringIO from ast import literal_eval -import sys +import sys, tempfile from mesonbuild import environment from mesonbuild import mesonlib from mesonbuild import mlog @@ -45,8 +45,6 @@ failing_tests = 0 skipped_tests = 0 print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ -test_build_dir = 'work area' -install_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], 'install dir') meson_command = os.path.join(os.getcwd(), 'meson') if not os.path.exists(meson_command): meson_command += '.py' @@ -198,12 +196,15 @@ def parse_test_args(testdir): return args def run_test(testdir, extra_args, should_succeed): + with tempfile.TemporaryDirectory(prefix='b ', dir='.') as build_dir: + with tempfile.TemporaryDirectory(prefix='i ', dir=os.getcwd()) as install_dir: + try: + return _run_test(testdir, build_dir, install_dir, extra_args, should_succeed) + finally: + mlog.shutdown() # Close the log file because otherwise Windows wets itself. + +def _run_test(testdir, test_build_dir, install_dir, extra_args, should_succeed): global compile_commands - mlog.shutdown() # Close the log file because otherwise Windows wets itself. - shutil.rmtree(test_build_dir) - shutil.rmtree(install_dir) - os.mkdir(test_build_dir) - os.mkdir(install_dir) print('Running test: ' + testdir) test_args = parse_test_args(testdir) gen_start = time.time() @@ -290,14 +291,6 @@ def run_tests(extra_args): conf_time = 0 build_time = 0 test_time = 0 - try: - os.mkdir(test_build_dir) - except OSError: - pass - try: - os.mkdir(install_dir) - except OSError: - pass for name, test_cases, skipped in all_tests: current_suite = ET.SubElement(junit_root, 'testsuite', {'name' : name, 'tests' : str(len(test_cases))}) @@ -14,6 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import sys + +if sys.version_info[0] < 3: + print('Tried to install with Python 2, Meson only supports Python 3.') + sys.exit(1) + # We need to support Python installations that have nothing but the basic # Python installation. Use setuptools when possible and fall back to # plain distutils when setuptools is not available. diff --git a/test cases/common/110 extract same name/lib.c b/test cases/common/110 extract same name/lib.c new file mode 100644 index 0000000..6bdeda7 --- /dev/null +++ b/test cases/common/110 extract same name/lib.c @@ -0,0 +1,3 @@ +int func1() { + return 23; +} diff --git a/test cases/common/110 extract same name/main.c b/test cases/common/110 extract same name/main.c new file mode 100644 index 0000000..dc57dd5 --- /dev/null +++ b/test cases/common/110 extract same name/main.c @@ -0,0 +1,6 @@ +int func1(); +int func2(); + +int main(int argc, char **argv) { + return !(func1() == 23 && func2() == 42); +} diff --git a/test cases/common/110 extract same name/meson.build b/test cases/common/110 extract same name/meson.build new file mode 100644 index 0000000..9384c47 --- /dev/null +++ b/test cases/common/110 extract same name/meson.build @@ -0,0 +1,6 @@ +project('object extraction', 'c') + +lib = shared_library('somelib', ['lib.c', 'src/lib.c']) +obj = lib.extract_objects(['lib.c', 'src/lib.c']) +exe = executable('main', 'main.c', objects: obj) +test('extraction', exe) diff --git a/test cases/common/110 extract same name/src/lib.c b/test cases/common/110 extract same name/src/lib.c new file mode 100644 index 0000000..68e6ab9 --- /dev/null +++ b/test cases/common/110 extract same name/src/lib.c @@ -0,0 +1,3 @@ +int func2() { + return 42; +} diff --git a/test cases/common/51 pkgconfig-gen/meson.build b/test cases/common/51 pkgconfig-gen/meson.build index a54fd66..4044b3d 100644 --- a/test cases/common/51 pkgconfig-gen/meson.build +++ b/test cases/common/51 pkgconfig-gen/meson.build @@ -12,5 +12,8 @@ pkgg.generate( version : libver, name : 'libsimple', filebase : 'simple', - description : 'A simple demo library.' + description : 'A simple demo library.', + requires : 'glib-2.0', # Not really, but only here to test that this works. + requires_private : ['gio-2.0', 'gobject-2.0'], + libraries_private : '-lz', ) diff --git a/test cases/failing/28 noprog use/meson.build b/test cases/failing/28 noprog use/meson.build new file mode 100644 index 0000000..e4de42f --- /dev/null +++ b/test cases/failing/28 noprog use/meson.build @@ -0,0 +1,9 @@ +project('using not found exe', 'c') + +nope = find_program('nonexisting', required : false) + +custom_target( 'aa', + input: 'meson.build', + output: 'foobar', + command: [nope, '@INPUT@', '@OUTPUT@'] +) |