diff options
24 files changed, 366 insertions, 191 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 3ae740d..6cf7552 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -13,13 +13,22 @@ afterwards](#returned-objects). void add_global_arguments(arg1, arg2, ...) ``` -Adds the positional arguments to the compiler command line for the -language specified in `language` keyword argument. If a list of -languages is given, the arguments are added to each of the -corresponding compiler command lines. Note that there is no way to -remove an argument set in this way. If you have an argument that is -only used in a subset of targets, you have to specify it in per-target -flags. +Adds the positional arguments to the compiler command line. This +function has two keyword arguments: + +- `language` specifies the language(s) that the arguments should be +applied to. If a list of languages is given, the arguments are added +to each of the corresponding compiler command lines. Note that there +is no way to remove an argument set in this way. If you have an +argument that is only used in a subset of targets, you have to specify +it in per-target flags. + +- `native` is a boolean specifying whether the arguments should be + applied to the native or cross compilation. If `true` the arguments + will only be used for native compilations. If `false` the arguments + will only be used in cross compilations. If omitted, the flags are + added to native compilations if compiling natively and cross + compilations (only) when cross compiling. Available since 0.48.0 The arguments are used in all compiler invocations with the exception of compile tests, because you might need to run a compile test with @@ -60,8 +69,9 @@ endif Takes one keyword argument, `required`. It defaults to `true`, which means that if any of the languages specified is not found, Meson will halt. Returns true if all languages specified were found and false -otherwise. Since *0.47.0* the value of a [`feature`](Build-options.md#features) -option can also be passed to the `required` keyword argument. +otherwise. Since *0.47.0* the value of a +[`feature`](Build-options.md#features) option can also be passed to +the `required` keyword argument. ### add_project_arguments() diff --git a/docs/markdown/snippets/native_args.md b/docs/markdown/snippets/native_args.md new file mode 100644 index 0000000..54c6de2 --- /dev/null +++ b/docs/markdown/snippets/native_args.md @@ -0,0 +1,34 @@ +## Projects args can be set separately for cross and native builds (potentially breaking change) + +It has been a longstanding bug (or let's call it a "delayed bug fix") +that if yo do this: + +```meson +add_project_arguments('-DFOO', language : 'c') +``` + +Then the flag is used both in native and cross compilations. This is +very confusing and almost never what you want. To fix this a new +keyword `native` has been added to all functions that add arguments, +namely `add_global_arguments`, `add_global_link_arguments`, +`add_project_arguments` and `add_project_link_arguments` that behaves +like the following: + +``` +## Added to native builds when compiling natively and to cross +## compilations when doing cross compiles. +add_project_arguments(...) + +## Added only to native compilations, not used in cross compilations. +add_project_arguments(..., native : true) + +## Added only to cross compilations, not used in native compilations. +add_project_arguments(..., native : false) +``` + +Also remember that cross compilation is a property of each +target. There can be target that are compiled with the native compiler +and some which are compiled with the cross compiler. + +Unfortunately this change is backwards incompatible and may cause some +projects to fail building. However this should be very rare in practice. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 7ed97b2..bdc3fad 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -355,9 +355,9 @@ class Backend: return l, stdlib_args @staticmethod - def _libdir_is_system(libdir, compilers): + def _libdir_is_system(libdir, compilers, env): for cc in compilers.values(): - if libdir in cc.get_library_dirs(): + if libdir in cc.get_library_dirs(env): return True return False @@ -372,7 +372,7 @@ class Backend: # The only link argument is an absolute path to a library file. libpath = la[0] libdir = os.path.dirname(libpath) - if exclude_system and self._libdir_is_system(libdir, target.compilers): + if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment): # No point in adding system paths. continue # Windows doesn't support rpaths, but we use this function to @@ -535,10 +535,10 @@ class Backend: commands += compiler.get_optimization_args(self.get_option_for_target('optimization', target)) commands += compiler.get_debug_args(self.get_option_for_target('debug', target)) # Add compile args added using add_project_arguments() - commands += self.build.get_project_args(compiler, target.subproject) + commands += self.build.get_project_args(compiler, target.subproject, target.is_cross) # Add compile args added using add_global_arguments() # These override per-project arguments - commands += self.build.get_global_args(compiler) + commands += self.build.get_global_args(compiler, target.is_cross) if not target.is_cross: # Compile args added from the env: CFLAGS/CXXFLAGS, etc. We want these # to override all the defaults, but not the per-target compile args. @@ -612,8 +612,8 @@ class Backend: # Get program and library dirs from all target compilers if isinstance(target, build.BuildTarget): for cc in target.compilers.values(): - paths.update(cc.get_program_dirs()) - paths.update(cc.get_library_dirs()) + paths.update(cc.get_program_dirs(self.environment)) + paths.update(cc.get_library_dirs(self.environment)) return list(paths) def determine_windows_extra_paths(self, target, extra_bdeps, is_cross=False): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index ace0693..83bff8a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -855,8 +855,8 @@ int dummy; for dep in target.get_external_deps(): commands.extend_direct(dep.get_link_args()) - commands += self.build.get_project_args(compiler, target.subproject) - commands += self.build.get_global_args(compiler) + commands += self.build.get_project_args(compiler, target.subproject, target.is_cross) + commands += self.build.get_global_args(compiler, target.is_cross) elem = NinjaBuildElement(self.all_outputs, outputs, 'cs_COMPILER', rel_srcs) elem.add_dep(deps) @@ -869,8 +869,8 @@ int dummy; deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] args = [] args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target)) - args += self.build.get_global_args(compiler) - args += self.build.get_project_args(compiler, target.subproject) + args += self.build.get_global_args(compiler, target.is_cross) + args += self.build.get_project_args(compiler, target.subproject, target.is_cross) args += target.get_java_args() args += compiler.get_output_args(self.get_target_private_dir(target)) args += target.get_classpath_args() @@ -1247,8 +1247,8 @@ int dummy; compile_args += swiftc.get_optimization_args(self.get_option_for_target('optimization', target)) compile_args += swiftc.get_debug_args(self.get_option_for_target('debug', target)) compile_args += swiftc.get_module_args(module_name) - compile_args += self.build.get_project_args(swiftc, target.subproject) - compile_args += self.build.get_global_args(swiftc) + compile_args += self.build.get_project_args(swiftc, target.subproject, target.is_cross) + compile_args += self.build.get_global_args(swiftc, target.is_cross) for i in reversed(target.get_include_dirs()): basedir = i.get_curdir() for d in i.get_incdirs(): @@ -1260,8 +1260,8 @@ int dummy; sargs = swiftc.get_include_args(srctreedir) compile_args += sargs link_args = swiftc.get_output_args(os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))) - link_args += self.build.get_project_link_args(swiftc, target.subproject) - link_args += self.build.get_global_link_args(swiftc) + link_args += self.build.get_project_link_args(swiftc, target.subproject, target.is_cross) + link_args += self.build.get_global_link_args(swiftc, target.is_cross) rundir = self.get_target_private_dir(target) out_module_name = self.swift_module_file_name(target) in_module_files = self.determine_swift_dep_modules(target) @@ -2333,7 +2333,7 @@ rule FORTRAN_DEP_HACK%s guessed_dependencies = [] # TODO The get_library_naming requirement currently excludes link targets that use d or fortran as their main linker if hasattr(linker, 'get_library_naming'): - search_dirs = list(search_dirs) + linker.get_library_dirs() + search_dirs = list(search_dirs) + linker.get_library_dirs(self.environment) static_patterns = linker.get_library_naming(self.environment, 'static', strict=True) shared_patterns = linker.get_library_naming(self.environment, 'shared', strict=True) for libname in libs: @@ -2395,10 +2395,10 @@ rule FORTRAN_DEP_HACK%s if not isinstance(target, build.StaticLibrary): # Add link args added using add_project_link_arguments() - commands += self.build.get_project_link_args(linker, target.subproject) + commands += self.build.get_project_link_args(linker, target.subproject, target.is_cross) # Add link args added using add_global_link_arguments() # These override per-project link arguments - commands += self.build.get_global_link_args(linker) + commands += self.build.get_global_link_args(linker, target.is_cross) if not target.is_cross: # Link args added from the env: LDFLAGS. We want these to # override all the defaults but not the per-target link args. diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 4c799d0..2e86ca9 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -998,10 +998,10 @@ class Vs2010Backend(backends.Backend): options = self.environment.coredata.base_options extra_link_args += compiler.get_std_shared_module_link_args(options) # Add link args added using add_project_link_arguments() - extra_link_args += self.build.get_project_link_args(compiler, target.subproject) + extra_link_args += self.build.get_project_link_args(compiler, target.subproject, target.is_cross) # Add link args added using add_global_link_arguments() # These override per-project link arguments - extra_link_args += self.build.get_global_link_args(compiler) + extra_link_args += self.build.get_global_link_args(compiler, target.is_cross) if not target.is_cross: # Link args added from the env: LDFLAGS. We want these to # override all the defaults but not the per-target link args. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c1cb8a8..caaadd8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -108,6 +108,10 @@ class Build: self.projects_args = {} self.global_link_args = {} self.projects_link_args = {} + self.cross_global_args = {} + self.cross_projects_args = {} + self.cross_global_link_args = {} + self.cross_projects_link_args = {} self.tests = [] self.benchmarks = [] self.headers = [] @@ -168,20 +172,25 @@ class Build: def get_install_subdirs(self): return self.install_dirs - def get_global_args(self, compiler): - return self.global_args.get(compiler.get_language(), []) + def get_global_args(self, compiler, for_cross): + d = self.cross_global_args if for_cross else self.global_args + return d.get(compiler.get_language(), []) - def get_project_args(self, compiler, project): - args = self.projects_args.get(project) + def get_project_args(self, compiler, project, for_cross): + d = self.cross_projects_args if for_cross else self.projects_args + args = d.get(project) if not args: return [] return args.get(compiler.get_language(), []) - def get_global_link_args(self, compiler): - return self.global_link_args.get(compiler.get_language(), []) + def get_global_link_args(self, compiler, for_cross): + d = self.cross_global_link_args if for_cross else self.global_link_args + return d.get(compiler.get_language(), []) - def get_project_link_args(self, compiler, project): - link_args = self.projects_link_args.get(project) + def get_project_link_args(self, compiler, project, for_cross): + d = self.cross_projects_link_args if for_cross else self.projects_link_args + + link_args = d.get(project) if not link_args: return [] diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index c7092b8..436f699 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -16,6 +16,8 @@ import re import glob import os.path import subprocess +import functools +import itertools from pathlib import Path from .. import mlog @@ -49,6 +51,7 @@ gnu_compiler_internal_libs = ('m', 'c', 'pthread', 'dl', 'rt') class CCompiler(Compiler): + # TODO: Replace this manual cache with functools.lru_cache library_dirs_cache = {} program_dirs_cache = {} find_library_cache = {} @@ -144,6 +147,30 @@ class CCompiler(Compiler): ''' return self.get_no_optimization_args() + def get_allow_undefined_link_args(self): + ''' + Get args for allowing undefined symbols when linking to a shared library + ''' + if self.id == 'clang': + if self.clang_type == compilers.CLANG_OSX: + # Apple ld + return ['-Wl,-undefined,dynamic_lookup'] + else: + # GNU ld and LLVM lld + return ['-Wl,--allow-shlib-undefined'] + elif self.id == 'gcc': + if self.gcc_type == compilers.GCC_OSX: + # Apple ld + return ['-Wl,-undefined,dynamic_lookup'] + else: + # GNU ld and LLVM lld + return ['-Wl,--allow-shlib-undefined'] + elif self.id == 'msvc': + # link.exe + return ['/FORCE:UNRESOLVED'] + # FIXME: implement other linkers + return [] + def get_output_args(self, target): return ['-o', target] @@ -172,42 +199,47 @@ class CCompiler(Compiler): def get_std_shared_lib_link_args(self): return ['-shared'] - def get_library_dirs_real(self): - env = os.environ.copy() - env['LC_ALL'] = 'C' - stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] + @functools.lru_cache() + def _get_search_dirs(self, env): + extra_args = ['--print-search-dirs'] + stdo = None + with self._build_wrapper('', env, extra_args, None, 'compile', True) as p: + stdo = p.stdo + return stdo + + @staticmethod + def _split_fetch_real_dirs(pathstr, sep=':'): paths = [] - for line in stdo.split('\n'): - if line.startswith('libraries:'): - libstr = line.split('=', 1)[1] - paths = [os.path.realpath(p) for p in libstr.split(':') if os.path.exists(os.path.realpath(p))] + for p in pathstr.split(sep): + p = Path(p) + if p.exists(): + paths.append(p.resolve().as_posix()) return paths - def get_library_dirs(self): - key = tuple(self.exelist) + def get_compiler_dirs(self, env, name): + ''' + Get dirs from the compiler, either `libraries:` or `programs:` + ''' + stdo = self._get_search_dirs(env) + for line in stdo.split('\n'): + if line.startswith(name + ':'): + return CCompiler._split_fetch_real_dirs(line.split('=', 1)[1]) + return [] + + def get_library_dirs(self, env): + key = (tuple(self.exelist), env) if key not in self.library_dirs_cache: - self.library_dirs_cache[key] = self.get_library_dirs_real() + self.library_dirs_cache[key] = self.get_compiler_dirs(env, 'libraries') return self.library_dirs_cache[key][:] - def get_program_dirs_real(self): - env = os.environ.copy() - env['LC_ALL'] = 'C' - stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] - paths = [] - for line in stdo.split('\n'): - if line.startswith('programs:'): - libstr = line.split('=', 1)[1] - paths = [os.path.realpath(p) for p in libstr.split(':')] - return paths - - def get_program_dirs(self): + def get_program_dirs(self, env): ''' Programs used by the compiler. Also where toolchain DLLs such as libstdc++-6.dll are found with MinGW. ''' - key = tuple(self.exelist) + key = (tuple(self.exelist), env) if key not in self.program_dirs_cache: - self.program_dirs_cache[key] = self.get_program_dirs_real() + self.program_dirs_cache[key] = self.get_compiler_dirs(env, 'programs') return self.program_dirs_cache[key][:] def get_pic_args(self): @@ -908,7 +940,8 @@ class CCompiler(Compiler): # Only try to find std libs if no extra dirs specified. if not extra_dirs or libname in self.internal_libs: args = ['-l' + libname] - if self.links(code, env, extra_args=args): + largs = self.linker_to_compiler_args(self.get_allow_undefined_link_args()) + if self.links(code, env, extra_args=(args + largs)): return args # Don't do a manual search for internal libs if libname in self.internal_libs: @@ -916,35 +949,19 @@ class CCompiler(Compiler): # Not found or we want to use a specific libtype? Try to find the # library file itself. patterns = self.get_library_naming(env, libtype) - for d in extra_dirs: + # Search in the specified dirs, and then in the system libraries + for d in itertools.chain(extra_dirs, self.get_library_dirs(env)): for p in patterns: trial = self._get_trials_from_pattern(p, d, libname) if not trial: continue + # We just check whether the library exists. We can't do a link + # check because the library might have unresolved symbols that + # require other libraries. trial = self._get_file_from_list(trial) if not trial: continue return [trial] - # Search in the system libraries too - for d in self.get_library_dirs(): - for p in patterns: - trial = self._get_trials_from_pattern(p, d, libname) - if not trial: - continue - trial = self._get_file_from_list(trial) - if not trial: - continue - # When searching the system paths used by the compiler, we - # need to check linking with link-whole, as static libs - # (.a) need to be checked to ensure they are the right - # architecture, e.g. 32bit or 64-bit. - # Just a normal test link won't work as the .a file doesn't - # seem to be checked by linker if there are no unresolved - # symbols from the main C file. - extra_link_args = self.get_link_whole_for([trial]) - extra_link_args = self.linker_to_compiler_args(extra_link_args) - if self.links(code, env, extra_args=extra_link_args): - return [trial] return None def find_library_impl(self, libname, env, extra_dirs, code, libtype): @@ -1230,7 +1247,10 @@ class VisualStudioCCompiler(CCompiler): return ['/MDd'] def get_buildtype_args(self, buildtype): - return compilers.msvc_buildtype_args[buildtype] + args = compilers.msvc_buildtype_args[buildtype] + if version_compare(self.version, '<18.0'): + args = [arg for arg in args if arg != '/Gw'] + return args def get_buildtype_linker_args(self, buildtype): return compilers.msvc_buildtype_linker_args[buildtype] diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index b400478..d08671e 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -145,8 +145,8 @@ arm_buildtype_args = {'plain': [], msvc_buildtype_args = {'plain': [], 'debug': ["/ZI", "/Ob0", "/Od", "/RTC1"], 'debugoptimized': ["/Zi", "/Ob1"], - 'release': ["/Ob2"], - 'minsize': ["/Zi", "/Ob1"], + 'release': ["/Ob2", "/Gw"], + 'minsize': ["/Zi", "/Gw"], } apple_buildtype_linker_args = {'plain': [], @@ -267,7 +267,7 @@ msvc_optimization_args = {'0': [], '1': ['/O1'], '2': ['/O2'], '3': ['/O3'], - 's': ['/Os'], + 's': ['/O1'], # Implies /Os. } clike_debug_args = {False: [], @@ -908,7 +908,7 @@ class Compiler: def find_library(self, *args, **kwargs): raise EnvironmentException('Language {} does not support library finding.'.format(self.get_display_language())) - def get_library_dirs(self): + def get_library_dirs(self, *args, **kwargs): return [] def has_multi_arguments(self, args, env): @@ -995,7 +995,9 @@ class Compiler: mlog.debug('Working directory: ', tmpdirname) mlog.debug('Command line: ', ' '.join(commands), '\n') mlog.debug('Code:\n', code) - p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname) + os_env = os.environ.copy() + os_env['LC_ALL'] = 'C' + p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname, env=os_env) mlog.debug('Compiler stdout:\n', p.stdo) mlog.debug('Compiler stderr:\n', p.stde) p.commands = commands @@ -1353,10 +1355,12 @@ class ElbrusCompiler(GnuCompiler): 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded'] - def get_library_dirs(self): - env = os.environ.copy() - env['LC_ALL'] = 'C' - stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] + # FIXME: use _build_wrapper to call this so that linker flags from the env + # get applied + def get_library_dirs(self, env): + os_env = os.environ.copy() + os_env['LC_ALL'] = 'C' + stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=os_env)[1] paths = [] for line in stdo.split('\n'): if line.startswith('libraries:'): @@ -1366,10 +1370,10 @@ class ElbrusCompiler(GnuCompiler): break return paths - def get_program_dirs(self): - env = os.environ.copy() - env['LC_ALL'] = 'C' - stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] + def get_program_dirs(self, env): + os_env = os.environ.copy() + os_env['LC_ALL'] = 'C' + stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=os_env)[1] paths = [] for line in stdo.split('\n'): if line.startswith('programs:'): diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 5648a6f..1ee1fcb 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -144,6 +144,9 @@ end program prog def get_compiler_check_args(self): return CCompiler.get_compiler_check_args(self) + def get_allow_undefined_link_args(self): + return CCompiler.get_allow_undefined_link_args(self) + def get_output_args(self, target): return CCompiler.get_output_args(self, target) @@ -177,11 +180,14 @@ end program prog def get_std_shared_lib_link_args(self): return CCompiler.get_std_shared_lib_link_args(self) - def get_library_dirs_real(self): - return CCompiler.get_library_dirs_real(self) + def _get_search_dirs(self, *args, **kwargs): + return CCompiler._get_search_dirs(self, *args, **kwargs) + + def get_compiler_dirs(self, *args, **kwargs): + return CCompiler.get_compiler_dirs(self, *args, **kwargs) - def get_library_dirs(self): - return CCompiler.get_library_dirs(self) + def get_library_dirs(self, *args, **kwargs): + return CCompiler.get_library_dirs(self, *args, **kwargs) def get_pic_args(self): return CCompiler.get_pic_args(self) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index afd39c8..4b32c19 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -19,6 +19,7 @@ from pathlib import PurePath from collections import OrderedDict from .mesonlib import MesonException from .mesonlib import default_libdir, default_libexecdir, default_prefix +from .wrap import WrapMode import ast import argparse @@ -222,7 +223,7 @@ class CoreData: self.base_options = {} self.external_preprocess_args = {} # CPPFLAGS only self.cross_file = self.__load_cross_file(options.cross_file) - self.wrap_mode = options.wrap_mode + self.wrap_mode = options.wrap_mode if options.wrap_mode is not None else WrapMode.default self.compilers = OrderedDict() self.cross_compilers = OrderedDict() self.deps = OrderedDict() diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 17f9240..b06f62d 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -443,7 +443,7 @@ class BoostDependency(ExternalDependency): if self.libdir: libdirs = [self.libdir] elif self.boost_root is None: - libdirs = mesonlib.get_library_dirs() + libdirs = mesonlib.get_library_dirs(self.env) else: libdirs = [os.path.join(self.boost_root, 'lib')] for libdir in libdirs: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 707cf9e..04e963a 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1808,11 +1808,11 @@ known_build_target_kwargs = ( {'target_type'} ) -permitted_kwargs = {'add_global_arguments': {'language'}, - 'add_global_link_arguments': {'language'}, - 'add_project_link_arguments': {'language'}, +permitted_kwargs = {'add_global_arguments': {'language', 'native'}, + 'add_global_link_arguments': {'language', 'native'}, 'add_languages': {'required'}, - 'add_project_arguments': {'language'}, + 'add_project_link_arguments': {'language', 'native'}, + 'add_project_arguments': {'language', 'native'}, 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'}, 'build_target': known_build_target_kwargs, @@ -2279,21 +2279,19 @@ external dependencies (including libraries) must go to "dependencies".''') return self.subprojects[dirname] def get_option_internal(self, optname): + # Some base options are not defined in some environments, return the + # default value from compilers.base_options in that case. + for d in [self.coredata.base_options, compilers.base_options, + self.coredata.builtins, self.coredata.compiler_options]: + try: + return d[optname] + except KeyError: + pass + raw_optname = optname - try: - return self.coredata.base_options[optname] - except KeyError: - pass - try: - return self.coredata.builtins[optname] - except KeyError: - pass - try: - return self.coredata.compiler_options[optname] - except KeyError: - pass - if not coredata.is_builtin_option(optname) and self.is_subproject(): + if self.is_subproject(): optname = self.subproject + ':' + optname + try: opt = self.coredata.user_options[optname] if opt.yielding and ':' in optname and raw_optname in self.coredata.user_options: @@ -2313,11 +2311,7 @@ external dependencies (including libraries) must go to "dependencies".''') return opt except KeyError: pass - # Some base options are not defined in some environments, return the default value. - try: - return compilers.base_options[optname] - except KeyError: - pass + raise InterpreterException('Tried to access unknown option "%s".' % optname) @stringArgs @@ -3653,25 +3647,45 @@ different subdirectory. timeout_multiplier=timeout_multiplier, env=env) + def get_argdict_on_crossness(self, native_dict, cross_dict, kwargs): + for_native = kwargs.get('native', not self.environment.is_cross_build()) + if not isinstance(for_native, bool): + raise InterpreterException('Keyword native must be a boolean.') + if for_native: + return native_dict + else: + return cross_dict + @permittedKwargs(permitted_kwargs['add_global_arguments']) @stringArgs def func_add_global_arguments(self, node, args, kwargs): - self.add_global_arguments(node, self.build.global_args, args, kwargs) + argdict = self.get_argdict_on_crossness(self.build.global_args, + self.build.cross_global_args, + kwargs) + self.add_global_arguments(node, argdict, args, kwargs) @permittedKwargs(permitted_kwargs['add_global_link_arguments']) @stringArgs def func_add_global_link_arguments(self, node, args, kwargs): - self.add_global_arguments(node, self.build.global_link_args, args, kwargs) + argdict = self.get_argdict_on_crossness(self.build.global_link_args, + self.build.cross_global_link_args, + kwargs) + self.add_global_arguments(node, argdict, args, kwargs) @permittedKwargs(permitted_kwargs['add_project_arguments']) @stringArgs def func_add_project_arguments(self, node, args, kwargs): - self.add_project_arguments(node, self.build.projects_args, args, kwargs) + argdict = self.get_argdict_on_crossness(self.build.projects_args, + self.build.cross_projects_args, + kwargs) + self.add_project_arguments(node, argdict, args, kwargs) @permittedKwargs(permitted_kwargs['add_project_link_arguments']) @stringArgs def func_add_project_link_arguments(self, node, args, kwargs): - self.add_project_arguments(node, self.build.projects_link_args, args, kwargs) + argdict = self.get_argdict_on_crossness(self.build.projects_link_args, + self.build.cross_projects_link_args, kwargs) + self.add_project_arguments(node, argdict, args, kwargs) def add_global_arguments(self, node, argsdict, args, kwargs): if self.is_subproject(): diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 513c238..2fd69b0 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -13,7 +13,6 @@ # limitations under the License. import os -import sys import argparse from . import (coredata, mesonlib, build) @@ -172,7 +171,3 @@ def run(args): print('Meson configurator encountered an error:') raise e return 0 - - -if __name__ == '__main__': - sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 68a2ddb..e8b7b30 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -36,7 +36,7 @@ def create_parser(): p.add_argument('-v', '--version', action='version', version=coredata.version) # See the mesonlib.WrapMode enum for documentation - p.add_argument('--wrap-mode', default=WrapMode.default, + p.add_argument('--wrap-mode', default=None, type=wrapmodetype, choices=WrapMode, help='Special wrap mode to use') p.add_argument('--profile-self', action='store_true', dest='profile', @@ -71,18 +71,18 @@ class MesonApp: if not os.path.exists(ndir2): os.makedirs(ndir2) if not stat.S_ISDIR(os.stat(ndir1).st_mode): - raise RuntimeError('%s is not a directory' % dir1) + raise MesonException('%s is not a directory' % dir1) if not stat.S_ISDIR(os.stat(ndir2).st_mode): - raise RuntimeError('%s is not a directory' % dir2) + raise MesonException('%s is not a directory' % dir2) if os.path.samefile(dir1, dir2): - raise RuntimeError('Source and build directories must not be the same. Create a pristine build directory.') + raise MesonException('Source and build directories must not be the same. Create a pristine build directory.') if self.has_build_file(ndir1): if self.has_build_file(ndir2): - raise RuntimeError('Both directories contain a build file %s.' % environment.build_filename) + raise MesonException('Both directories contain a build file %s.' % environment.build_filename) return ndir1, ndir2 if self.has_build_file(ndir2): return ndir2, ndir1 - raise RuntimeError('Neither directory contains a build file %s.' % environment.build_filename) + raise MesonException('Neither directory contains a build file %s.' % environment.build_filename) def validate_dirs(self, dir1, dir2, handshake): (src_dir, build_dir) = self.validate_core_dirs(dir1, dir2) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 94bc00b..188459a 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -249,6 +249,3 @@ def run(args): print('No command specified') return 1 return 0 - -if __name__ == '__main__': - sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 1654824..b8d3ccc 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -59,9 +59,12 @@ def set_timestamp_start(start): def shutdown(): global log_file if log_file is not None: + path = log_file.name exception_around_goer = log_file log_file = None exception_around_goer.close() + return path + return None class AnsiDecorator: plain_code = "\033[0m" diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index 570e691..364452d 100644 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -213,6 +213,4 @@ def run(args): add_arguments(parser) options = parser.parse_args(args) options.wrap_func(options) - -if __name__ == '__main__': - sys.exit(run(sys.argv[1:])) + return 0 diff --git a/run_tests.py b/run_tests.py index 6e441d3..a5fd7a5 100755 --- a/run_tests.py +++ b/run_tests.py @@ -31,7 +31,32 @@ from mesonbuild import mesonlib from mesonbuild import mesonmain from mesonbuild import mtest from mesonbuild import mlog -from mesonbuild.environment import detect_ninja +from mesonbuild.environment import Environment, detect_ninja + + +# Fake classes and objects for mocking +class FakeBuild: + def __init__(self, env): + self.environment = env + +class FakeCompilerOptions: + def __init__(self): + self.value = [] + +def get_fake_options(prefix): + import argparse + opts = argparse.Namespace() + opts.cross_file = None + opts.wrap_mode = None + opts.prefix = prefix + opts.cmd_line_options = {} + return opts + +def get_fake_env(sdir, bdir, prefix): + env = Environment(sdir, bdir, get_fake_options(prefix)) + env.coredata.compiler_options['c_args'] = FakeCompilerOptions() + return env + Backend = Enum('Backend', 'ninja vs xcode') @@ -148,15 +173,6 @@ def ensure_backend_detects_changes(backend): if backend is Backend.ninja: time.sleep(1) -def get_fake_options(prefix): - import argparse - opts = argparse.Namespace() - opts.cross_file = None - opts.wrap_mode = None - opts.prefix = prefix - opts.cmd_line_options = {} - return opts - def run_mtest_inprocess(commandlist): old_stdout = sys.stdout sys.stdout = mystdout = StringIO() diff --git a/run_unittests.py b/run_unittests.py index 262bf7b..f4c50a5 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -41,24 +41,16 @@ from mesonbuild.mesonlib import ( windows_proof_rmtree, python_command, version_compare, grab_leading_numbers, BuildDirLock ) -from mesonbuild.environment import Environment, detect_ninja +from mesonbuild.environment import detect_ninja from mesonbuild.mesonlib import MesonException, EnvironmentException from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram import mesonbuild.modules.pkgconfig -from run_tests import exe_suffix, get_fake_options, get_meson_script +from run_tests import exe_suffix, get_fake_env, get_meson_script from run_tests import get_builddir_target_args, get_backend_commands, Backend from run_tests import ensure_backend_detects_changes, run_configure_inprocess from run_tests import run_mtest_inprocess - -# Fake classes for mocking -class FakeBuild: - def __init__(self, env): - self.environment = env - -class FakeCompilerOptions: - def __init__(self): - self.value = [] +from run_tests import FakeBuild, FakeCompilerOptions def get_dynamic_section_entry(fname, entry): if is_cygwin() or is_osx(): @@ -563,7 +555,7 @@ class InternalTests(unittest.TestCase): 'windows-mingw': {'shared': ['lib{}.dll.a', 'lib{}.lib', 'lib{}.dll', '{}.dll.a', '{}.lib', '{}.dll'], 'static': msvc_static}} - env = Environment('', '', get_fake_options('')) + env = get_fake_env('', '', '') cc = env.detect_c_compiler(False) if is_osx(): self._test_all_naming(cc, env, patterns, 'darwin') @@ -606,7 +598,7 @@ class InternalTests(unittest.TestCase): ''' with tempfile.TemporaryDirectory() as tmpdir: pkgbin = ExternalProgram('pkg-config', command=['pkg-config'], silent=True) - env = Environment('', '', get_fake_options('')) + env = get_fake_env('', '', '') compiler = env.detect_c_compiler(False) env.coredata.compilers = {'c': compiler} env.coredata.compiler_options['c_link_args'] = FakeCompilerOptions() @@ -672,6 +664,8 @@ class DataTests(unittest.TestCase): self.assertTrue(snippet_dir.is_dir()) for f in snippet_dir.glob('*'): self.assertTrue(f.is_file()) + if f.parts[-1].endswith('~'): + continue if f.suffix == '.md': with f.open() as snippet: for line in snippet: @@ -692,7 +686,7 @@ class DataTests(unittest.TestCase): with open('docs/markdown/Builtin-options.md') as f: md = f.read() self.assertIsNotNone(md) - env = Environment('', '', get_fake_options('')) + env = get_fake_env('', '', '') # FIXME: Support other compilers cc = env.detect_c_compiler(False) cpp = env.detect_cpp_compiler(False) @@ -738,7 +732,7 @@ class DataTests(unittest.TestCase): Ensure that syntax highlighting files were updated for new functions in the global namespace in build files. ''' - env = Environment('', '', get_fake_options('')) + env = get_fake_env('', '', '') interp = Interpreter(FakeBuild(env), mock=True) with open('data/syntax-highlighting/vim/syntax/meson.vim') as f: res = re.search(r'syn keyword mesonBuiltin(\s+\\\s\w+)+', f.read(), re.MULTILINE) @@ -1172,7 +1166,7 @@ class AllPlatformTests(BasePlatformTests): https://github.com/mesonbuild/meson/issues/1355 ''' testdir = os.path.join(self.common_test_dir, '3 static') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) cc = env.detect_c_compiler(False) static_linker = env.detect_static_linker(cc) if is_windows(): @@ -1459,7 +1453,7 @@ class AllPlatformTests(BasePlatformTests): if not is_windows(): langs += [('objc', 'OBJC'), ('objcpp', 'OBJCXX')] testdir = os.path.join(self.unit_test_dir, '5 compiler detection') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) for lang, evar in langs: # Detect with evar and do sanity checks on that if evar in os.environ: @@ -1561,7 +1555,7 @@ class AllPlatformTests(BasePlatformTests): def test_always_prefer_c_compiler_for_asm(self): testdir = os.path.join(self.common_test_dir, '138 c cpp and asm') # Skip if building with MSVC - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) if env.detect_c_compiler(False).get_id() == 'msvc': raise unittest.SkipTest('MSVC can\'t compile assembly') self.init(testdir) @@ -1819,7 +1813,7 @@ int main(int argc, char **argv) { self.assertPathExists(os.path.join(testdir, i)) def detect_prebuild_env(self): - env = Environment('', self.builddir, get_fake_options(self.prefix)) + env = get_fake_env('', self.builddir, self.prefix) cc = env.detect_c_compiler(False) stlinker = env.detect_static_linker(cc) if mesonbuild.mesonlib.is_windows(): @@ -1985,7 +1979,7 @@ int main(int argc, char **argv) { '--libdir=' + libdir]) # Find foo dependency os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) kwargs = {'required': True, 'silent': True} foo_dep = PkgConfigDependency('libfoo', env, kwargs) # Ensure link_args are properly quoted @@ -2292,7 +2286,7 @@ recommended as it is not supported on some platforms''') testdirbase = os.path.join(self.unit_test_dir, '29 guessed linker dependencies') testdirlib = os.path.join(testdirbase, 'lib') extra_args = None - env = Environment(testdirlib, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdirlib, self.builddir, self.prefix) if env.detect_c_compiler(False).get_id() != 'msvc': # static libraries are not linkable with -l with msvc because meson installs them # as .a files which unix_args_to_native will not know as it expects libraries to use @@ -2746,7 +2740,7 @@ class FailureTests(BasePlatformTests): ''' Test that when we can't detect objc or objcpp, we fail gracefully. ''' - env = Environment('', self.builddir, get_fake_options(self.prefix)) + env = get_fake_env('', self.builddir, self.prefix) try: env.detect_objc_compiler(False) env.detect_objcpp_compiler(False) @@ -2883,7 +2877,7 @@ class WindowsTests(BasePlatformTests): ExternalLibraryHolder from build files. ''' testdir = os.path.join(self.platform_test_dir, '1 basic') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) cc = env.detect_c_compiler(False) if cc.id != 'msvc': raise unittest.SkipTest('Not using MSVC') @@ -2896,7 +2890,7 @@ class WindowsTests(BasePlatformTests): testdir = os.path.join(self.platform_test_dir, '5 resources') # resource compiler depfile generation is not yet implemented for msvc - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) depfile_works = env.detect_c_compiler(False).get_id() != 'msvc' self.init(testdir) @@ -2985,7 +2979,7 @@ class LinuxlikeTests(BasePlatformTests): ''' testdir = os.path.join(self.common_test_dir, '48 pkgconfig-gen') self.init(testdir) - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) kwargs = {'required': True, 'silent': True} os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir foo_dep = PkgConfigDependency('libfoo', env, kwargs) @@ -3241,7 +3235,7 @@ class LinuxlikeTests(BasePlatformTests): an ordinary test because it requires passing options to meson. ''' testdir = os.path.join(self.common_test_dir, '1 trivial') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) cc = env.detect_c_compiler(False) self._test_stds_impl(testdir, cc, 'c') @@ -3251,7 +3245,7 @@ class LinuxlikeTests(BasePlatformTests): be an ordinary test because it requires passing options to meson. ''' testdir = os.path.join(self.common_test_dir, '2 cpp') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) cpp = env.detect_cpp_compiler(False) self._test_stds_impl(testdir, cpp, 'cpp') @@ -3796,8 +3790,6 @@ endian = 'little' The system library is found with cc.find_library() and pkg-config deps. ''' - if not is_osx(): - raise unittest.SkipTest('workflow currently only works on macOS') oldprefix = self.prefix # Install external library so we can find it testdir = os.path.join(self.unit_test_dir, '39 external, internal library rpath', 'external library') @@ -3820,6 +3812,9 @@ endian = 'little' self.build() # test uninstalled self.run_tests() + if not is_osx(): + # Rest of the workflow only works on macOS + return # test running after installation self.install(use_destdir=False) prog = os.path.join(self.installdir, 'bin', 'prog') diff --git a/test cases/common/21 global arg/meson.build b/test cases/common/21 global arg/meson.build index d7fd428..699dae6 100644 --- a/test cases/common/21 global arg/meson.build +++ b/test cases/common/21 global arg/meson.build @@ -3,9 +3,18 @@ project('global arg test', 'cpp', 'c') add_global_arguments('-DMYTHING', language : 'c') add_global_arguments('-DMYCPPTHING', language : 'cpp') +add_global_arguments('-DGLOBAL_NATIVE', language : 'c', native : true) +add_global_arguments('-DGLOBAL_CROSS', language : 'c', native : false) + +if meson.is_cross_build() + c_args = ['-DARG_CROSS'] +else + c_args = ['-DARG_NATIVE'] +endif + add_global_arguments('-DMYCANDCPPTHING', language: ['c', 'cpp']) -exe1 = executable('prog', 'prog.c') +exe1 = executable('prog', 'prog.c', c_args : c_args) exe2 = executable('prog2', 'prog.cc') test('prog1', exe1) diff --git a/test cases/common/21 global arg/prog.c b/test cases/common/21 global arg/prog.c index ace5a0a..fb014c7 100644 --- a/test cases/common/21 global arg/prog.c +++ b/test cases/common/21 global arg/prog.c @@ -10,6 +10,42 @@ #error "Global argument not set" #endif +#ifdef GLOBAL_NATIVE + #ifndef ARG_NATIVE + #error "Global is native but arg_native is not set." + #endif + + #ifdef GLOBAL_CROSS + #error "Both global native and global cross set." + #endif +#else + #ifndef GLOBAL_CROSS + #error "Neither global_cross nor glogal_native is set." + #endif + + #ifndef ARG_CROSS + #error "Global is cross but arg_cross is not set." + #endif + + #ifdef ARG_NATIVE + #error "Global is cross but arg_native is set." + #endif +#endif + +#ifdef GLOBAL_CROSS + #ifndef ARG_CROSS + #error "Global is cross but arg_cross is not set." + #endif +#else + #ifdef ARG_CROSS + #error "Global is cross but arg_native is set." + #endif + + #ifdef ARG_CROSS + #error "Global is native but arg cross is set." + #endif +#endif + int main(int argc, char **argv) { return 0; } diff --git a/test cases/unit/39 external, internal library rpath/built library/meson.build b/test cases/unit/39 external, internal library rpath/built library/meson.build index 2b422f4..f633996 100644 --- a/test cases/unit/39 external, internal library rpath/built library/meson.build +++ b/test cases/unit/39 external, internal library rpath/built library/meson.build @@ -1,12 +1,21 @@ project('built library', 'c') cc = meson.get_compiler('c') + +if host_machine.system() != 'cygwin' + # bar_in_system has undefined symbols, but still must be found + bar_system_dep = cc.find_library('bar_in_system') +endif + foo_system_dep = cc.find_library('foo_in_system') + faa_pkg_dep = dependency('faa_pkg') l = shared_library('bar_built', 'bar.c', install: true, dependencies : [foo_system_dep, faa_pkg_dep]) -e = executable('prog', 'prog.c', link_with: l, install: true) -test('testprog', e) +if host_machine.system() == 'darwin' + e = executable('prog', 'prog.c', link_with: l, install: true) + test('testprog', e) +endif diff --git a/test cases/unit/39 external, internal library rpath/external library/bar.c b/test cases/unit/39 external, internal library rpath/external library/bar.c new file mode 100644 index 0000000..c6f42d6 --- /dev/null +++ b/test cases/unit/39 external, internal library rpath/external library/bar.c @@ -0,0 +1,6 @@ +int some_undefined_func (void); + +int bar_system_value (void) +{ + return some_undefined_func (); +} diff --git a/test cases/unit/39 external, internal library rpath/external library/meson.build b/test cases/unit/39 external, internal library rpath/external library/meson.build index 6dcc97e..3c311f5 100644 --- a/test cases/unit/39 external, internal library rpath/external library/meson.build +++ b/test cases/unit/39 external, internal library rpath/external library/meson.build @@ -1,9 +1,22 @@ -project('system library', 'c') +project('system library', 'c', default_options : ['b_lundef=false']) shared_library('foo_in_system', 'foo.c', install : true) l = shared_library('faa_pkg', 'faa.c', install: true) +if host_machine.system() == 'darwin' + frameworks = ['-framework', 'CoreFoundation', '-framework', 'CoreMedia'] + allow_undef_args = ['-Wl,-undefined,dynamic_lookup'] +else + frameworks = [] + allow_undef_args = [] +endif + pkg = import('pkgconfig') pkg.generate(name: 'faa_pkg', - libraries: [l, '-framework', 'CoreFoundation', '-framework', 'CoreMedia'], + libraries: [l] + frameworks, description: 'FAA, a pkg-config test library') + +# cygwin DLLs can't have undefined symbols +if host_machine.system() != 'cygwin' + shared_library('bar_in_system', 'bar.c', install : true, link_args : allow_undef_args) +endif |