diff options
25 files changed, 455 insertions, 33 deletions
diff --git a/docs/markdown/Compiler-properties.md b/docs/markdown/Compiler-properties.md index 579417a..1228f42 100644 --- a/docs/markdown/Compiler-properties.md +++ b/docs/markdown/Compiler-properties.md @@ -160,15 +160,30 @@ Does a function exist? Just having a header doesn't say anything about its contents. Sometimes you need to explicitly check if some function -exists. This is how we would check whether the function `somefunc` -exists in header `someheader.h` +exists. This is how we would check whether the function `open_memstream` +exists in header `stdio.h` ```meson -if compiler.has_function('somefunc', prefix : '#include<someheader.h>') +if compiler.has_function('open_memstream', prefix : '#include <stdio.h>') # function exists, do whatever is required. endif ``` +Note that, on macOS programs can be compiled targeting older macOS +versions than the one that the program is compiled on. It can't be +assumed that the OS version that is compiled on matches the OS +version that the binary will run on. + +Therefore when detecting function availability with `has_function`, it +is important to specify the correct header in the prefix argument. + +In the example above, the function `open_memstream` is detected, which +was introduced in macOS 10.13. When the user builds on macOS 10.13, but +targeting macOS 10.11 (`-mmacosx-version-min=10.11`), this will correctly +report the function as missing. Without the header however, it would lack +the necessary availability information and incorrectly report the function +as available. + Does a structure contain a member? == diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 7611232..5b4d7f0 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -21,6 +21,7 @@ These are return values of the `get_id` method in a compiler object. | g95 | The G95 Fortran compiler | | open64 | The Open64 Fortran Compiler | | nagfor | The NAG Fortran compiler | +| lcc | Elbrus C/C++/Fortran Compiler | ## Script environment variables @@ -42,6 +43,7 @@ set in the cross file. | x86 | 32 bit x86 processor | | x86_64 | 64 bit x86 processor | | arm | 32 bit ARM processor | +| e2k | MCST Elbrus processor | Any cpu family not listed in the above list is not guaranteed to remain stable in future releases. diff --git a/docs/markdown/snippets/lcc.md b/docs/markdown/snippets/lcc.md new file mode 100644 index 0000000..2ce300d --- /dev/null +++ b/docs/markdown/snippets/lcc.md @@ -0,0 +1,23 @@ +## Support for lcc compiler for e2k (Elbrus) architecture + +In this version, a support for lcc compiler for Elbrus processors +based on [e2k microarchitecture](https://en.wikipedia.org/wiki/Elbrus_2000) +has been added. + +Examples of such CPUs: +* [Elbrus-8S](https://en.wikipedia.org/wiki/Elbrus-8S); +* Elbrus-4S; +* [Elbrus-2S+](https://en.wikipedia.org/wiki/Elbrus-2S%2B). + +Such compiler have a similar behavior as gcc (basic option compatibility), +but, in is not strictly compatible with gcc as of current version. + +Major differences as of version 1.21.22: +* it does not support LTO and PCH; +* it suffers from the same dependency file creation error as icc; +* it has minor differences in output, especially version output; +* it differently reacts to lchmod() detection; +* some backend messages are produced in ru_RU.KOI8-R even if LANG=C; +* its preprocessor treats some characters differently. + +So every noted difference is properly handled now in meson.
\ No newline at end of file diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cee1434..41f840d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1156,7 +1156,7 @@ int dummy; abs_vala_file = os.path.join(self.environment.get_build_dir(), vala_file) if PurePath(os.path.commonpath((abs_srcbasedir, abs_vala_file))) == PurePath(abs_srcbasedir): vala_c_subdir = PurePath(abs_vala_file).parent.relative_to(abs_srcbasedir) - vala_c_file = os.path.join(vala_c_subdir, vala_c_file) + vala_c_file = os.path.join(str(vala_c_subdir), vala_c_file) else: path_to_target = os.path.join(self.build_to_src, target.get_subdir()) if vala_file.startswith(path_to_target): @@ -1660,9 +1660,9 @@ rule FORTRAN_DEP_HACK if mesonlib.is_windows(): command_template = ' command = {executable} @$out.rsp\n' \ ' rspfile = $out.rsp\n' \ - ' rspfile_content = {cross_args} $ARGS {output_args} {compile_only_args} $in\n' + ' rspfile_content = $ARGS{cross_args} {output_args} {compile_only_args} $in\n' else: - command_template = ' command = {executable} {cross_args} $ARGS {output_args} {compile_only_args} $in\n' + command_template = ' command = {executable} $ARGS {cross_args} {output_args} {compile_only_args} $in\n' command = command_template.format( executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]), cross_args=' '.join(self.get_cross_info_lang_args(compiler.language, is_cross)), @@ -1721,10 +1721,10 @@ rule FORTRAN_DEP_HACK if mesonlib.is_windows(): command_template = ''' command = {executable} @$out.rsp rspfile = $out.rsp - rspfile_content = {cross_args} $ARGS {dep_args} {output_args} {compile_only_args} $in + rspfile_content = $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in ''' else: - command_template = ' command = {executable} {cross_args} $ARGS {dep_args} {output_args} {compile_only_args} $in\n' + command_template = ' command = {executable} $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in\n' command = command_template.format( executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]), cross_args=' '.join(cross_args), @@ -1769,7 +1769,7 @@ rule FORTRAN_DEP_HACK output = '' else: output = ' '.join(compiler.get_output_args('$out')) - command = " command = {executable} {cross_args} $ARGS {dep_args} {output_args} {compile_only_args} $in\n".format( + command = " command = {executable} $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in\n".format( executable=' '.join(compiler.get_exelist()), cross_args=' '.join(cross_args), dep_args=' '.join(quoted_depargs), @@ -2410,6 +2410,74 @@ rule FORTRAN_DEP_HACK target_args = self.build_target_link_arguments(linker, target.link_whole_targets) return linker.get_link_whole_for(target_args) if len(target_args) else [] + def guess_library_absolute_path(self, libname, search_dirs, prefixes, suffixes): + for directory in search_dirs: + for suffix in suffixes: + for prefix in prefixes: + trial = os.path.join(directory, prefix + libname + '.' + suffix) + if os.path.isfile(trial): + return trial + + def guess_external_link_dependencies(self, linker, target, commands, internal): + # Ideally the linker would generate dependency information that could be used. + # But that has 2 problems: + # * currently ld can not create dependency information in a way that ninja can use: + # https://sourceware.org/bugzilla/show_bug.cgi?id=22843 + # * Meson optimizes libraries from the same build using the symbol extractor. + # Just letting ninja use ld generated dependencies would undo this optimization. + search_dirs = [] + libs = [] + absolute_libs = [] + + build_dir = self.environment.get_build_dir() + # the following loop sometimes consumes two items from command in one pass + it = iter(commands) + for item in it: + if item in internal and not item.startswith('-'): + continue + + if item.startswith('-L'): + if len(item) > 2: + path = item[2:] + else: + try: + path = next(it) + except StopIteration: + mlog.warning("Generated linker command has -L argument without following path") + break + if not os.path.isabs(path): + path = os.path.join(build_dir, path) + search_dirs.append(path) + elif item.startswith('-l'): + if len(item) > 2: + libs.append(item[2:]) + else: + try: + libs.append(next(it)) + except StopIteration: + mlog.warning("Generated linker command has '-l' argument without following library name") + break + elif os.path.isabs(item) and self.environment.is_library(item) and os.path.isfile(item): + absolute_libs.append(item) + + 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 += linker.get_library_dirs() + prefixes_static, suffixes_static = linker.get_library_naming(self.environment, 'static', strict=True) + prefixes_shared, suffixes_shared = linker.get_library_naming(self.environment, 'shared', strict=True) + for libname in libs: + # be conservative and record most likely shared and static resolution, because we don't know exactly + # which one the linker will prefer + static_resolution = self.guess_library_absolute_path(libname, search_dirs, prefixes_static, suffixes_static) + shared_resolution = self.guess_library_absolute_path(libname, search_dirs, prefixes_shared, suffixes_shared) + if static_resolution: + guessed_dependencies.append(os.path.realpath(static_resolution)) + if shared_resolution: + guessed_dependencies.append(os.path.realpath(shared_resolution)) + + return guessed_dependencies + absolute_libs + def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]): if isinstance(target, build.StaticLibrary): linker_base = 'STATIC' @@ -2476,7 +2544,8 @@ rule FORTRAN_DEP_HACK dependencies = [] else: dependencies = target.get_dependencies() - commands += self.build_target_link_arguments(linker, dependencies) + internal = self.build_target_link_arguments(linker, dependencies) + commands += internal # For 'automagic' deps: Boost and GTest. Also dependency('threads'). # pkg-config puts the thread flags itself via `Cflags:` for d in target.external_deps: @@ -2500,6 +2569,10 @@ rule FORTRAN_DEP_HACK # symbols from those can be found here. This is needed when the # *_winlibs that we want to link to are static mingw64 libraries. commands += linker.get_option_link_args(self.environment.coredata.compiler_options) + + dep_targets = [] + dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal)) + # Set runtime-paths so we can run executables without needing to set # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows. if has_path_sep(target.name): @@ -2523,7 +2596,7 @@ rule FORTRAN_DEP_HACK # Convert from GCC-style link argument naming to the naming used by the # current compiler. commands = commands.to_native() - dep_targets = [self.get_dependency_filename(t) for t in dependencies] + dep_targets.extend([self.get_dependency_filename(t) for t in dependencies]) dep_targets.extend([self.get_dependency_filename(t) for t in target.link_depends]) elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list) diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 84c87fb..89b46b5 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -54,10 +54,13 @@ __all__ = [ 'FortranCompiler', 'G95FortranCompiler', 'GnuCCompiler', + 'ElbrusCCompiler', 'GnuCompiler', 'GnuCPPCompiler', + 'ElbrusCPPCompiler', 'GnuDCompiler', 'GnuFortranCompiler', + 'ElbrusFortranCompiler', 'GnuObjCCompiler', 'GnuObjCPPCompiler', 'IntelCompiler', @@ -118,6 +121,7 @@ from .c import ( CCompiler, ClangCCompiler, GnuCCompiler, + ElbrusCCompiler, IntelCCompiler, VisualStudioCCompiler, ) @@ -125,6 +129,7 @@ from .cpp import ( CPPCompiler, ClangCPPCompiler, GnuCPPCompiler, + ElbrusCPPCompiler, IntelCPPCompiler, VisualStudioCPPCompiler, ) @@ -139,6 +144,7 @@ from .fortran import ( FortranCompiler, G95FortranCompiler, GnuFortranCompiler, + ElbrusFortranCompiler, IntelFortranCompiler, NAGFortranCompiler, Open64FortranCompiler, diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index dee5125..71fff05 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -36,6 +36,7 @@ from .compilers import ( CompilerArgs, CrossNoRunException, GnuCompiler, + ElbrusCompiler, IntelCompiler, RunResult, ) @@ -754,7 +755,7 @@ class CCompiler(Compiler): return False raise RuntimeError('BUG: {!r} check failed unexpectedly'.format(n)) - def get_library_naming(self, env, libtype): + def get_library_naming(self, env, libtype, strict=False): ''' Get library prefixes and suffixes for the target platform ordered by priority @@ -762,7 +763,10 @@ class CCompiler(Compiler): stlibext = ['a'] # We've always allowed libname to be both `foo` and `libfoo`, # and now people depend on it - prefixes = ['lib', ''] + if strict and self.id != 'msvc': # lib prefix is not usually used with msvc + prefixes = ['lib'] + else: + prefixes = ['lib', ''] # Library suffixes and prefixes if for_darwin(env.is_cross_build(), env): shlibext = ['dylib'] @@ -916,6 +920,29 @@ class GnuCCompiler(GnuCompiler, CCompiler): return ['-fpch-preprocess', '-include', os.path.basename(header)] +class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None, **kwargs): + GnuCCompiler.__init__(self, exelist, version, gcc_type, is_cross, exe_wrapper, defines, **kwargs) + ElbrusCompiler.__init__(self, gcc_type, defines) + + # It does support some various ISO standards and c/gnu 90, 9x, 1x in addition to those which GNU CC supports. + def get_options(self): + opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', + ['none', 'c89', 'c90', 'c9x', 'c99', 'c1x', 'c11', + 'gnu89', 'gnu90', 'gnu9x', 'gnu99', 'gnu1x', 'gnu11', + 'iso9899:2011', 'iso9899:1990', 'iso9899:199409', 'iso9899:1999'], + 'none')} + return opts + + # Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error. + # So we should explicitly fail at this case. + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + if funcname == 'lchmod': + return False + else: + return super().has_function(funcname, prefix, env, extra_args, dependencies) + + class IntelCCompiler(IntelCompiler, CCompiler): def __init__(self, exelist, version, icc_type, is_cross, exe_wrapper=None, **kwargs): CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwargs) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 480baa9..417cbae 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -55,7 +55,6 @@ for _l in clike_langs: clike_suffixes += lang_suffixes[_l] clike_suffixes += ('h', 'll', 's') -# XXX: Use this in is_library()? soregex = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$') # All these are only for C-like languages; see `clike_langs` above. @@ -102,6 +101,10 @@ def is_object(fname): def is_library(fname): if hasattr(fname, 'fname'): fname = fname.fname + + if soregex.match(fname): + return True + suffix = fname.split('.')[-1] return suffix in lib_suffixes @@ -941,9 +944,11 @@ def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, i sostr = '' else: sostr = '.' + soversion - if gcc_type in (GCC_STANDARD, GCC_MINGW, GCC_CYGWIN): - # Might not be correct for mingw but seems to work. + if gcc_type == GCC_STANDARD: return ['-Wl,-soname,%s%s.%s%s' % (prefix, shlib_name, suffix, sostr)] + elif gcc_type in (GCC_MINGW, GCC_CYGWIN): + # For PE/COFF the soname argument has no effect with GNU LD + return [] elif gcc_type == GCC_OSX: if is_shared_module: return [] @@ -1007,7 +1012,7 @@ def gnulike_default_include_dirs(compiler, lang): stdout=subprocess.PIPE, env=env ) - stderr = p.stderr.read().decode('utf-8') + stderr = p.stderr.read().decode('utf-8', errors='replace') parse_state = 0 paths = [] for line in stderr.split('\n'): @@ -1120,6 +1125,29 @@ class GnuCompiler: return gnulike_default_include_dirs(self.exelist, self.language) +class ElbrusCompiler(GnuCompiler): + # Elbrus compiler is nearly like GCC, but does not support + # PCH, LTO, sanitizers and color output as of version 1.21.x. + def __init__(self, gcc_type, defines): + GnuCompiler.__init__(self, gcc_type, defines) + self.id = 'lcc' + self.base_options = ['b_pgo', 'b_coverage', + '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] + for line in stdo.split('\n'): + if line.startswith('libraries:'): + # lcc does not include '=' in --print-search-dirs output. + libstr = line.split(' ', 1)[1] + return libstr.split(':') + return [] + + + class ClangCompiler: def __init__(self, clang_type): self.id = 'clang' diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 1fa6f15..3804059 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -25,6 +25,7 @@ from .compilers import ( msvc_winlibs, ClangCompiler, GnuCompiler, + ElbrusCompiler, IntelCompiler, ) @@ -133,6 +134,29 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): return ['-fpch-preprocess', '-include', os.path.basename(header)] +class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None, **kwargs): + GnuCPPCompiler.__init__(self, exelist, version, gcc_type, is_cross, exe_wrapper, defines, **kwargs) + ElbrusCompiler.__init__(self, gcc_type, defines) + + # It does not support c++/gnu++ 17 and 1z, but still does support 0x, 1y, and gnu++98. + def get_options(self): + opts = super().get_options() + opts['cpp_std'] = coredata.UserComboOption('cpp_std', 'C++ language standard to use', + ['none', 'c++98', 'c++03', 'c++0x', 'c++11', 'c++14', 'c++1y', + 'gnu++98', 'gnu++03', 'gnu++0x', 'gnu++11', 'gnu++14', 'gnu++1y'], + 'none') + return opts + + # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. + # So we should explicitly fail at this case. + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + if funcname == 'lchmod': + return False + else: + return super().has_function(funcname, prefix, env, extra_args, dependencies) + + class IntelCPPCompiler(IntelCompiler, CPPCompiler): def __init__(self, exelist, version, icc_type, is_cross, exe_wrap, **kwargs): CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap, **kwargs) diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index f9fcc1c..e61c976 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -27,6 +27,7 @@ from .compilers import ( gnulike_buildtype_args, gnulike_buildtype_linker_args, Compiler, + ElbrusCompiler, IntelCompiler, ) @@ -180,6 +181,12 @@ class GnuFortranCompiler(FortranCompiler): return ['-Wl,--out-implib=' + implibname] +class ElbrusFortranCompiler(GnuFortranCompiler, ElbrusCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None, **kwargs): + GnuFortranCompiler.__init__(self, exelist, version, gcc_type, is_cross, exe_wrapper, defines, **kwargs) + ElbrusCompiler.__init__(self, gcc_type, defines) + + class G95FortranCompiler(FortranCompiler): def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwags): super().__init__(exelist, version, is_cross, exe_wrapper=None, **kwags) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 0115fb3..cd8d92c 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -48,6 +48,9 @@ from .compilers import ( GnuFortranCompiler, GnuObjCCompiler, GnuObjCPPCompiler, + ElbrusCCompiler, + ElbrusCPPCompiler, + ElbrusFortranCompiler, IntelCCompiler, IntelCPPCompiler, IntelFortranCompiler, @@ -223,6 +226,9 @@ def detect_cpu(compilers): except mesonlib.MesonException: pass return 'x86_64' + if trial == 'e2k': + # Make more precise CPU detection for Elbrus platform. + trial = platform.processor().lower() # Add fixes here as bugs are reported. return trial @@ -421,6 +427,15 @@ class Environment: return dot.join((major, minor, patch)) @staticmethod + def get_lcc_version_from_defines(defines): + dot = '.' + generation_and_major = defines.get('__LCC__', '100') + generation = generation_and_major[:1] + major = generation_and_major[1:] + minor = defines.get('__LCC_MINOR__', '0') + return dot.join((generation, major, minor)) + + @staticmethod def get_gnu_compiler_type(defines): # Detect GCC type (Apple, MinGW, Cygwin, Unix) if '__APPLE__' in defines: @@ -513,15 +528,27 @@ class Environment: continue version = search_version(out) full_version = out.split('\n', 1)[0] + + guess_gcc_or_lcc = False if 'Free Software Foundation' in out: + guess_gcc_or_lcc = 'gcc' + if 'e2k' in out and 'lcc' in out: + guess_gcc_or_lcc = 'lcc' + + if guess_gcc_or_lcc: defines = self.get_gnu_compiler_defines(compiler) if not defines: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue gtype = self.get_gnu_compiler_type(defines) - version = self.get_gnu_version_from_defines(defines) - cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler + if guess_gcc_or_lcc == 'lcc': + version = self.get_lcc_version_from_defines(defines) + cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler + else: + version = self.get_gnu_version_from_defines(defines) + cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler return cls(ccache + compiler, version, gtype, is_cross, exe_wrap, defines, full_version=full_version) + if 'clang' in out: if 'Apple' in out or mesonlib.for_darwin(want_cross, self): cltype = CLANG_OSX @@ -574,14 +601,25 @@ class Environment: version = search_version(out) full_version = out.split('\n', 1)[0] + guess_gcc_or_lcc = False if 'GNU Fortran' in out: + guess_gcc_or_lcc = 'gcc' + if 'e2k' in out and 'lcc' in out: + guess_gcc_or_lcc = 'lcc' + + if guess_gcc_or_lcc: defines = self.get_gnu_compiler_defines(compiler) if not defines: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue gtype = self.get_gnu_compiler_type(defines) - version = self.get_gnu_version_from_defines(defines) - return GnuFortranCompiler(compiler, version, gtype, is_cross, exe_wrap, defines, full_version=full_version) + if guess_gcc_or_lcc == 'lcc': + version = self.get_lcc_version_from_defines(defines) + cls = ElbrusFortranCompiler + else: + version = self.get_gnu_version_from_defines(defines) + cls = GnuFortranCompiler + return cls(compiler, version, gtype, is_cross, exe_wrap, defines, full_version=full_version) if 'G95' in out: return G95FortranCompiler(compiler, version, is_cross, exe_wrap, full_version=full_version) @@ -626,7 +664,7 @@ class Environment: popen_exceptions[' '.join(compiler + arg)] = e continue version = search_version(out) - if 'Free Software Foundation' in out: + if 'Free Software Foundation' in out or ('e2k' in out and 'lcc' in out): defines = self.get_gnu_compiler_defines(compiler) if not defines: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' @@ -653,7 +691,7 @@ class Environment: popen_exceptions[' '.join(compiler + arg)] = e continue version = search_version(out) - if 'Free Software Foundation' in out: + if 'Free Software Foundation' in out or ('e2k' in out and 'lcc' in out): defines = self.get_gnu_compiler_defines(compiler) if not defines: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index a2a7929..ba5bcaa 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -32,10 +32,17 @@ class RPMModule(ExtensionModule): def generate_spec_template(self, state, args, kwargs): compiler_deps = set() for compiler in state.compilers.values(): + # Elbrus has one 'lcc' package for every compiler if isinstance(compiler, compilers.GnuCCompiler): compiler_deps.add('gcc') elif isinstance(compiler, compilers.GnuCPPCompiler): compiler_deps.add('gcc-c++') + elif isinstance(compiler, compilers.ElbrusCCompiler): + compiler_deps.add('lcc') + elif isinstance(compiler, compilers.ElbrusCPPCompiler): + compiler_deps.add('lcc') + elif isinstance(compiler, compilers.ElbrusFortranCompiler): + compiler_deps.add('lcc') elif isinstance(compiler, compilers.ValaCompiler): compiler_deps.add('vala') elif isinstance(compiler, compilers.GnuFortranCompiler): diff --git a/run_unittests.py b/run_unittests.py index 0f9a50b..19a8c87 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -482,6 +482,7 @@ class BasePlatformTests(unittest.TestCase): self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja')) self.meson_mainfile = os.path.join(src_root, 'meson.py') self.meson_args = ['--backend=' + self.backend.name] + self.meson_cross_file = None self.meson_command = meson_command + self.meson_args self.mconf_command = meson_command + ['configure'] self.mintro_command = meson_command + ['introspect'] @@ -567,6 +568,8 @@ class BasePlatformTests(unittest.TestCase): if default_args: args += ['--prefix', self.prefix, '--libdir', self.libdir] + if self.meson_cross_file: + args += ['--cross-file', self.meson_cross_file] self.privatedir = os.path.join(self.builddir, 'meson-private') if inprocess: try: @@ -1346,7 +1349,8 @@ class AllPlatformTests(BasePlatformTests): # \n is never substituted by the GNU pre-processor via a -D define # ' and " confuse shlex.split() even when they are escaped # % and # confuse the MSVC preprocessor - value = 'spaces and fun!@$^&*()-=_+{}[]:;<>?,./~`' + # !, ^, *, and < confuse lcc preprocessor + value = 'spaces and fun@$&()-=_+{}[]:;>?,./~`' os.environ['CPPFLAGS'] = '-D{}="{}"'.format(define, value) os.environ['CFLAGS'] = '-DMESON_FAIL_VALUE=cflags-read'.format(define) self.init(testdir, ['-D{}={}'.format(define, value)]) @@ -1949,6 +1953,55 @@ recommended as it can lead to undefined behaviour on some platforms''') exe = os.path.join(self.builddir, 'main') self.assertEqual(b'NDEBUG=0', subprocess.check_output(exe).strip()) + def test_guessed_linker_dependencies(self): + ''' + Test that meson adds dependencies for libraries based on the final + linker command line. + ''' + # build library + testdirbase = os.path.join(self.unit_test_dir, '26 guessed linker dependencies') + testdirlib = os.path.join(testdirbase, 'lib') + extra_args = None + env = Environment(testdirlib, self.builddir, self.meson_command, + get_fake_options(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 + # .lib as extension. For a DLL the import library is installed as .lib. Thus for msvc + # this tests needs to use shared libraries to test the path resolving logic in the + # dependency generation code path. + extra_args = ['--default-library', 'static'] + self.init(testdirlib, extra_args=extra_args) + self.build() + self.install() + libbuilddir = self.builddir + installdir = self.installdir + libdir = os.path.join(self.installdir, self.prefix.lstrip('/').lstrip('\\'), 'lib') + + # build user of library + self.new_builddir() + # replace is needed because meson mangles platform pathes passed via LDFLAGS + os.environ["LDFLAGS"] = '-L{}'.format(libdir.replace('\\', '/')) + self.init(os.path.join(testdirbase, 'exe')) + del os.environ["LDFLAGS"] + self.build() + self.assertBuildIsNoop() + + # rebuild library + exebuilddir = self.builddir + self.installdir = installdir + self.builddir = libbuilddir + # Microsoft's compiler is quite smart about touching import libs on changes, + # so ensure that there is actually a change in symbols. + self.setconf('-Dmore_exports=true') + self.build() + self.install() + # no ensure_backend_detects_changes needed because self.setconf did that already + + # assert user of library will be rebuild + self.builddir = exebuilddir + self.assertRebuiltTarget('app') + class FailureTests(BasePlatformTests): ''' @@ -2764,11 +2817,12 @@ cpu = 'armv7' # Not sure if correct. endian = 'little' ''' % os.path.join(testdir, 'some_cross_tool.py')) crossfile.flush() - self.init(testdir, ['--cross-file=' + crossfile.name]) + self.meson_cross_file = crossfile.name + self.init(testdir) def test_reconfigure(self): testdir = os.path.join(self.unit_test_dir, '13 reconfigure') - self.init(testdir, ['-Db_lto=true'], default_args=False) + self.init(testdir, ['-Db_coverage=true'], default_args=False) self.build('reconfigure') def test_vala_generated_source_buildir_inside_source_tree(self): @@ -2870,7 +2924,7 @@ class LinuxArmCrossCompileTests(BasePlatformTests): def setUp(self): super().setUp() src_root = os.path.dirname(__file__) - self.meson_command += ['--cross=' + os.path.join(src_root, 'cross', 'ubuntu-armhf.txt')] + self.meson_cross_file = os.path.join(src_root, 'cross', 'ubuntu-armhf.txt') def test_cflags_cross_environment_pollution(self): ''' @@ -2884,6 +2938,21 @@ class LinuxArmCrossCompileTests(BasePlatformTests): compdb = self.get_compdb() self.assertNotIn('-DBUILD_ENVIRONMENT_ONLY', compdb[0]['command']) + def test_cross_file_overrides_always_args(self): + ''' + Test that $lang_args in cross files always override get_always_args(). + Needed for overriding the default -D_FILE_OFFSET_BITS=64 on some + architectures such as some Android versions and Raspbian. + https://github.com/mesonbuild/meson/issues/3049 + https://github.com/mesonbuild/meson/issues/3089 + ''' + testdir = os.path.join(self.unit_test_dir, '29 cross file overrides always args') + self.meson_cross_file = os.path.join(self.unit_test_dir, 'ubuntu-armhf-overrides.txt') + self.init(testdir) + compdb = self.get_compdb() + self.assertRegex(compdb[0]['command'], '-D_FILE_OFFSET_BITS=64.*-U_FILE_OFFSET_BITS') + self.build() + class RewriterTests(unittest.TestCase): diff --git a/test cases/common/13 pch/meson.build b/test cases/common/13 pch/meson.build index 9ed6512..e144aa5 100644 --- a/test cases/common/13 pch/meson.build +++ b/test cases/common/13 pch/meson.build @@ -1,4 +1,10 @@ project('pch test', 'c') +cc = meson.get_compiler('c') +cc_id = cc.get_id() +if cc_id == 'lcc' + error('MESON_SKIP_TEST: Elbrus compiler does not support PCH.') +endif + exe = executable('prog', 'prog.c', c_pch : ['pch/prog_pch.c', 'pch/prog.h']) diff --git a/test cases/common/132 dependency file generation/meson.build b/test cases/common/132 dependency file generation/meson.build index dcfdcd9..cd66cb7 100644 --- a/test cases/common/132 dependency file generation/meson.build +++ b/test cases/common/132 dependency file generation/meson.build @@ -1,11 +1,13 @@ project('dep file gen', 'c') -cc_id = meson.get_compiler('c').get_id() -if cc_id == 'intel' - # ICC does not escape spaces in paths in the dependency file, so Ninja +cc_id = meson.get_compiler('c').get_id() +cc_ver = meson.get_compiler('c').version() + +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') + # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja # (correctly) thinks that the rule has multiple outputs and errors out: # 'depfile has multiple output paths' - error('MESON_SKIP_TEST: Skipping test with Intel compiler because it generates broken dependency files') + error('MESON_SKIP_TEST: Skipping test because your compiler is known to generate broken dependency files') endif e = executable('main file', 'main .c') diff --git a/test cases/common/22 header in file list/meson.build b/test cases/common/22 header in file list/meson.build index cc30c71..ff42cc4 100644 --- a/test cases/common/22 header in file list/meson.build +++ b/test cases/common/22 header in file list/meson.build @@ -1,4 +1,14 @@ project('header in file list', 'c') +cc_id = meson.get_compiler('c').get_id() +cc_ver = meson.get_compiler('c').version() + +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') + # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja + # (correctly) thinks that the rule has multiple outputs and errors out: + # 'depfile has multiple output paths' + error('MESON_SKIP_TEST: Skipping test because your compiler is known to generate broken dependency files') +endif + exe = executable('prog', 'prog.c', 'header.h') test('basic', exe) diff --git a/test cases/common/64 custom header generator/meson.build b/test cases/common/64 custom header generator/meson.build index 33ba4c5..2279513 100644 --- a/test cases/common/64 custom header generator/meson.build +++ b/test cases/common/64 custom header generator/meson.build @@ -1,5 +1,15 @@ project('custom header generator', 'c') +cc_id = meson.get_compiler('c').get_id() +cc_ver = meson.get_compiler('c').version() + +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') + # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja + # (correctly) thinks that the rule has multiple outputs and errors out: + # 'depfile has multiple output paths' + error('MESON_SKIP_TEST: Skipping test because your compiler is known to generate broken dependency files') +endif + gen = find_program('makeheader.py') generated_h = custom_target('makeheader.py', diff --git a/test cases/unit/13 reconfigure/meson.build b/test cases/unit/13 reconfigure/meson.build index 102180e..453644a 100644 --- a/test cases/unit/13 reconfigure/meson.build +++ b/test cases/unit/13 reconfigure/meson.build @@ -1,5 +1,5 @@ project('reconfigure test', ['c']) -if get_option('b_lto') != true - error('b_lto not set') +if get_option('b_coverage') != true + error('b_coverage not set') endif diff --git a/test cases/unit/26 guessed linker dependencies/exe/app.c b/test cases/unit/26 guessed linker dependencies/exe/app.c new file mode 100644 index 0000000..1031a42 --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/exe/app.c @@ -0,0 +1,6 @@ +void liba_func(); + +int main() { + liba_func(); + return 0; +} diff --git a/test cases/unit/26 guessed linker dependencies/exe/meson.build b/test cases/unit/26 guessed linker dependencies/exe/meson.build new file mode 100644 index 0000000..8bb1bd7 --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/exe/meson.build @@ -0,0 +1,7 @@ +project('exe', ['c']) + +executable('app', + 'app.c', + # Use uninterpreted strings to avoid path finding by dependency or compiler.find_library + link_args: ['-ltest-lib'] + ) diff --git a/test cases/unit/26 guessed linker dependencies/lib/lib.c b/test cases/unit/26 guessed linker dependencies/lib/lib.c new file mode 100644 index 0000000..1a8f94d --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/lib/lib.c @@ -0,0 +1,20 @@ +#if defined _WIN32 + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +void DLL_PUBLIC liba_func() { +} + +#ifdef MORE_EXPORTS + +void DLL_PUBLIC libb_func() { +} + +#endif diff --git a/test cases/unit/26 guessed linker dependencies/lib/meson.build b/test cases/unit/26 guessed linker dependencies/lib/meson.build new file mode 100644 index 0000000..36df112 --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/lib/meson.build @@ -0,0 +1,11 @@ +project('lib1', ['c']) + +c_args = [] + +# Microsoft's compiler is quite smart about touching import libs on changes, +# so ensure that there is actually a change in symbols. +if get_option('more_exports') + c_args += '-DMORE_EXPORTS' +endif + +a = library('test-lib', 'lib.c', c_args: c_args, install: true) diff --git a/test cases/unit/26 guessed linker dependencies/lib/meson_options.txt b/test cases/unit/26 guessed linker dependencies/lib/meson_options.txt new file mode 100644 index 0000000..2123e45 --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/lib/meson_options.txt @@ -0,0 +1 @@ +option('more_exports', type : 'boolean', value : false) diff --git a/test cases/unit/29 cross file overrides always args/meson.build b/test cases/unit/29 cross file overrides always args/meson.build new file mode 100644 index 0000000..ef6556e --- /dev/null +++ b/test cases/unit/29 cross file overrides always args/meson.build @@ -0,0 +1,3 @@ +project('cross compile args override always args', 'c') + +executable('no-file-offset-bits', 'test.c') diff --git a/test cases/unit/29 cross file overrides always args/test.c b/test cases/unit/29 cross file overrides always args/test.c new file mode 100644 index 0000000..315f92e --- /dev/null +++ b/test cases/unit/29 cross file overrides always args/test.c @@ -0,0 +1,8 @@ +#ifdef _FILE_OFFSET_BITS + #error "_FILE_OFFSET_BITS should not be set" +#endif + +int main(int argc, char *argv[]) +{ + return 0; +} diff --git a/test cases/unit/29 cross file overrides always args/ubuntu-armhf-overrides.txt b/test cases/unit/29 cross file overrides always args/ubuntu-armhf-overrides.txt new file mode 100644 index 0000000..a00a7d1 --- /dev/null +++ b/test cases/unit/29 cross file overrides always args/ubuntu-armhf-overrides.txt @@ -0,0 +1,19 @@ +[binaries] +# we could set exe_wrapper = qemu-arm-static but to test the case +# when cross compiled binaries can't be run we don't do that +c = '/usr/bin/arm-linux-gnueabihf-gcc' +cpp = '/usr/bin/arm-linux-gnueabihf-g++' +rust = ['rustc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7'] +ar = '/usr/arm-linux-gnueabihf/bin/ar' +strip = '/usr/arm-linux-gnueabihf/bin/strip' +pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config' + +[properties] +root = '/usr/arm-linux-gnueabihf' +c_args = ['-U_FILE_OFFSET_BITS'] + +[host_machine] +system = 'linux' +cpu_family = 'arm' +cpu = 'armv7' # Not sure if correct. +endian = 'little' |