diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 7611232d1..5b4d7f09b 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 000000000..2ce300daa --- /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 e766efa0c..c941319d7 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): diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 84c87fb09..89b46b558 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 958357b78..71fff057a 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -36,6 +36,7 @@ from .compilers import ( CompilerArgs, CrossNoRunException, GnuCompiler, + ElbrusCompiler, IntelCompiler, RunResult, ) @@ -919,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 cd2d8eb0b..934b079c5 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1010,7 +1010,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'): @@ -1123,6 +1123,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 1fa6f1573..3804059c6 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 f9fcc1cd0..e61c97618 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 0115fb353..cd8d92ce8 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 @@ -420,6 +426,15 @@ class Environment: patch = defines.get('__GNUC_PATCHLEVEL__', '0') 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) @@ -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 dbb01f717..5c9ed1487 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 0f9abcb46..8bd5ae8d9 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1346,7 +1346,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)]) @@ -2817,7 +2818,7 @@ endian = 'little' 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): diff --git a/test cases/common/13 pch/meson.build b/test cases/common/13 pch/meson.build index 9ed651242..e144aa5a3 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 dcfdcd9f3..cd66cb78a 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 cc30c71b9..ff42cc411 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 33ba4c593..2279513aa 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 102180e54..453644a02 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