(env: 'Environment', lang: str, for_machine: MachineChoice, *, override_compiler: T.Optional[T.List[str]] = None)
Shared implementation for finding the C or C++ compiler to use.

    the override_compiler option is provided to allow compilers which use
    the compiler (GCC or Clang usually) as their shared linker, to find
    the linker they need.
Shared implementation for finding the C or C++ compiler to use.
Shared implementation for finding the C or C ++ compiler to use .
def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: MachineChoice, *, override_compiler: T.Optional[T.List[str]] = None) -> Compiler:
    """Shared implementation for finding the C or C++ compiler to use.

    the override_compiler option is provided to allow compilers which use
    the compiler (GCC or Clang usually) as their shared linker, to find
    the linker they need.
    """
    popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
    compilers, ccache, exe_wrap = _get_compilers(env, lang, for_machine)
    if override_compiler is not None:
        compilers = [override_compiler]
    is_cross = env.is_cross_build(for_machine)
    info = env.machines[for_machine]
    cls: T.Union[T.Type[CCompiler], T.Type[CPPCompiler]]

    for compiler in compilers:
        if isinstance(compiler, str):
            compiler = [compiler]
        compiler_name = os.path.basename(compiler[0])

        if any(os.path.basename(x) in {'cl', 'cl.exe', 'clang-cl', 'clang-cl.exe'} for x in compiler):
            # Watcom C provides it's own cl.exe clone that mimics an older
            # version of Microsoft's compiler. Since Watcom's cl.exe is
            # just a wrapper, we skip using it if we detect its presence
            # so as not to confuse Meson when configuring for MSVC.
            #
            # Additionally the help text of Watcom's cl.exe is paged, and
            # the binary will not exit without human intervention. In
            # practice, Meson will block waiting for Watcom's cl.exe to
            # exit, which requires user input and thus will never exit.
            if 'WATCOM' in os.environ:
                def sanitize(p: str) -> str:
                    return os.path.normcase(os.path.abspath(p))

                watcom_cls = [sanitize(os.path.join(os.environ['WATCOM'], 'BINNT', 'cl')),
                                sanitize(os.path.join(os.environ['WATCOM'], 'BINNT', 'cl.exe')),
                                sanitize(os.path.join(os.environ['WATCOM'], 'BINNT64', 'cl')),
                                sanitize(os.path.join(os.environ['WATCOM'], 'BINNT64', 'cl.exe')),]
                found_cl = sanitize(shutil.which('cl'))
                if found_cl in watcom_cls:
                    mlog.debug('Skipping unsupported cl.exe clone at:', found_cl)
                    continue
            arg = '/?'
        elif 'armcc' in compiler_name:
            arg = '--vsn'
        elif 'ccrx' in compiler_name:
            arg = '-v'
        elif 'xc16' in compiler_name:
            arg = '--version'
        elif 'ccomp' in compiler_name:
            arg = '-version'
        elif 'cl2000' in compiler_name:
            arg = '-version'
        elif compiler_name in {'icl', 'icl.exe'}:
            # if you pass anything to icl you get stuck in a pager
            arg = ''
        else:
            arg = '--version'

        try:
            p, out, err = Popen_safe(compiler + [arg])
        except OSError as e:
            popen_exceptions[' '.join(compiler + [arg])] = e
            continue

        if 'ccrx' in compiler_name:
            out = err

        full_version = out.split('\n', 1)[0]
        version = search_version(out)

        guess_gcc_or_lcc: T.Optional[str] = None
        if 'Free Software Foundation' in out or 'xt-' in out:
            guess_gcc_or_lcc = 'gcc'
        if 'e2k' in out and 'lcc' in out:
            guess_gcc_or_lcc = 'lcc'
        if 'Microchip Technology' in out:
            # this output has "Free Software Foundation" in its version
            guess_gcc_or_lcc = None

        if guess_gcc_or_lcc:
            defines = _get_gnu_compiler_defines(compiler)
            if not defines:
                popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
                continue

            if guess_gcc_or_lcc == 'lcc':
                version = _get_lcc_version_from_defines(defines)
                cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler
            else:
                version = _get_gnu_version_from_defines(defines)
                cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler

            linker = guess_nix_linker(env, compiler, cls, for_machine)

            return cls(
                ccache + compiler, version, for_machine, is_cross,
                info, exe_wrap, defines=defines, full_version=full_version,
                linker=linker)

        if 'Emscripten' in out:
            cls = EmscriptenCCompiler if lang == 'c' else EmscriptenCPPCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)

            # emcc requires a file input in order to pass arguments to the
            # linker. It'll exit with an error code, but still print the
            # linker version. Old emcc versions ignore -Wl,--version completely,
            # however. We'll report "unknown version" in that case.
            with tempfile.NamedTemporaryFile(suffix='.c') as f:
                cmd = compiler + [cls.LINKER_PREFIX + "--version", f.name]
                _, o, _ = Popen_safe(cmd)

            linker = WASMDynamicLinker(
                compiler, for_machine, cls.LINKER_PREFIX,
                [], version=search_version(o))
            return cls(
                ccache + compiler, version, for_machine, is_cross, info,
                exe_wrap, linker=linker, full_version=full_version)

        if 'armclang' in out:
            # The compiler version is not present in the first line of output,
            # instead it is present in second line, startswith 'Component:'.
            # So, searching for the 'Component' in out although we know it is
            # present in second line, as we are not sure about the
            # output format in future versions
            arm_ver_match = re.search('.*Component.*', out)
            if arm_ver_match is None:
                popen_exceptions[' '.join(compiler)] = 'version string not found'
                continue
            arm_ver_str = arm_ver_match.group(0)
            # Override previous values
            version = search_version(arm_ver_str)
            full_version = arm_ver_str
            cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler
            linker = ArmClangDynamicLinker(for_machine, version=version)
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            return cls(
                ccache + compiler, version, for_machine, is_cross, info,
                exe_wrap, full_version=full_version, linker=linker)
        if 'CL.EXE COMPATIBILITY' in out:
            # if this is clang-cl masquerading as cl, detect it as cl, not
            # clang
            arg = '--version'
            try:
                p, out, err = Popen_safe(compiler + [arg])
            except OSError as e:
                popen_exceptions[' '.join(compiler + [arg])] = e
            version = search_version(out)
            match = re.search('^Target: (.*?)-', out, re.MULTILINE)
            if match:
                target = match.group(1)
            else:
                target = 'unknown target'
            cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler
            linker = guess_win_linker(env, ['lld-link'], cls, for_machine)
            return cls(
                compiler, version, for_machine, is_cross, info, target,
                exe_wrap, linker=linker)
        if 'clang' in out or 'Clang' in out:
            linker = None

            defines = _get_clang_compiler_defines(compiler)

            # Even if the for_machine is darwin, we could be using vanilla
            # clang.
            if 'Apple' in out:
                cls = AppleClangCCompiler if lang == 'c' else AppleClangCPPCompiler
            else:
                cls = ClangCCompiler if lang == 'c' else ClangCPPCompiler

            if 'windows' in out or env.machines[for_machine].is_windows():
                # If we're in a MINGW context this actually will use a gnu
                # style ld, but for clang on "real" windows we'll use
                # either link.exe or lld-link.exe
                try:
                    linker = guess_win_linker(env, compiler, cls, for_machine, invoked_directly=False)
                except MesonException:
                    pass
            if linker is None:
                linker = guess_nix_linker(env, compiler, cls, for_machine)

            return cls(
                ccache + compiler, version, for_machine, is_cross, info,
                exe_wrap, defines=defines, full_version=full_version, linker=linker)

        if 'Intel(R) C++ Intel(R)' in err:
            version = search_version(err)
            target = 'x86' if 'IA-32' in err else 'x86_64'
            cls = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            linker = XilinkDynamicLinker(for_machine, [], version=version)
            return cls(
                compiler, version, for_machine, is_cross, info, target,
                exe_wrap, linker=linker)
        if 'Microsoft' in out or 'Microsoft' in err:
            # Latest versions of Visual Studio print version
            # number to stderr but earlier ones print version
            # on stdout.  Why? Lord only knows.
            # Check both outputs to figure out version.
            for lookat in [err, out]:
                version = search_version(lookat)
                if version != 'unknown version':
                    break
            else:
                raise EnvironmentException(f'Failed to detect MSVC compiler version: stderr was\n{err!r}')
            cl_signature = lookat.split('\n')[0]
            match = re.search(r'.*(x86|x64|ARM|ARM64)([^_A-Za-z0-9]|$)', cl_signature)
            if match:
                target = match.group(1)
            else:
                m = f'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{cl_signature}'
                raise EnvironmentException(m)
            cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler
            linker = guess_win_linker(env, ['link'], cls, for_machine)
            # As of this writing, CCache does not support MSVC but sccache does.
            if 'sccache' in ccache:
                final_compiler = ccache + compiler
            else:
                final_compiler = compiler
            return cls(
                final_compiler, version, for_machine, is_cross, info, target,
                exe_wrap, full_version=cl_signature, linker=linker)
        if 'PGI Compilers' in out:
            cls = PGICCompiler if lang == 'c' else PGICPPCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            linker = PGIDynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
            return cls(
                ccache + compiler, version, for_machine, is_cross,
                info, exe_wrap, linker=linker)
        if 'NVIDIA Compilers and Tools' in out:
            cls = NvidiaHPC_CCompiler if lang == 'c' else NvidiaHPC_CPPCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            linker = NvidiaHPC_DynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
            return cls(
                ccache + compiler, version, for_machine, is_cross,
                info, exe_wrap, linker=linker)
        if '(ICC)' in out:
            cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler
            l = guess_nix_linker(env, compiler, cls, for_machine)
            return cls(
                ccache + compiler, version, for_machine, is_cross, info,
                exe_wrap, full_version=full_version, linker=l)
        if 'ARM' in out:
            cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            linker = ArmDynamicLinker(for_machine, version=version)
            return cls(
                ccache + compiler, version, for_machine, is_cross,
                info, exe_wrap, full_version=full_version, linker=linker)
        if 'RX Family' in out:
            cls = CcrxCCompiler if lang == 'c' else CcrxCPPCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            linker = CcrxDynamicLinker(for_machine, version=version)
            return cls(
                ccache + compiler, version, for_machine, is_cross, info,
                exe_wrap, full_version=full_version, linker=linker)

        if 'Microchip Technology' in out:
            cls = Xc16CCompiler if lang == 'c' else Xc16CCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            linker = Xc16DynamicLinker(for_machine, version=version)
            return cls(
                ccache + compiler, version, for_machine, is_cross, info,
                exe_wrap, full_version=full_version, linker=linker)

        if 'CompCert' in out:
            cls = CompCertCCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            linker = CompCertDynamicLinker(for_machine, version=version)
            return cls(
                ccache + compiler, version, for_machine, is_cross, info,
                exe_wrap, full_version=full_version, linker=linker)

        if 'TMS320C2000 C/C++' in out:
            cls = C2000CCompiler if lang == 'c' else C2000CPPCompiler
            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
            linker = C2000DynamicLinker(compiler, for_machine, version=version)
            return cls(
                ccache + compiler, version, for_machine, is_cross, info,
                exe_wrap, full_version=full_version, linker=linker)

    _handle_exceptions(popen_exceptions, compilers)
    raise EnvironmentException(f'Unknown compiler {compilers}')
def _detect_c_or_cpp_compiler ( env : 'Environment' , lang : str , for_machine : MachineChoice , * , override_compiler : T . Optional [ T . List [ str ] ] = None ) -> Compiler : popen_exceptions : T . Dict [ str , T . Union [ Exception , str ] ] = { } compilers , ccache , exe_wrap = _get_compilers ( env , lang , for_machine ) if override_compiler is not None : compilers = [ override_compiler ] is_cross = env . is_cross_build ( for_machine ) info = env . machines [ for_machine ] cls : T . Union [ T . Type [ CCompiler ] , T . Type [ CPPCompiler ] ] for compiler in compilers : if isinstance ( compiler , str ) : compiler = [ compiler ] compiler_name = os . path . basename ( compiler [ 0 ] ) if any ( os . path . basename ( x ) in { 'cl' , 'cl.exe' , 'clang-cl' , 'clang-cl.exe' } for x in compiler ) : # Watcom C provides it's own cl.exe clone that mimics an older # version of Microsoft's compiler. Since Watcom's cl.exe is # just a wrapper, we skip using it if we detect its presence # so as not to confuse Meson when configuring for MSVC. # # Additionally the help text of Watcom's cl.exe is paged, and # the binary will not exit without human intervention. In # practice, Meson will block waiting for Watcom's cl.exe to # exit, which requires user input and thus will never exit. if 'WATCOM' in os . environ : def sanitize ( p : str ) -> str : return os . path . normcase ( os . path . abspath ( p ) ) watcom_cls = [ sanitize ( os . path . join ( os . environ [ 'WATCOM' ] , 'BINNT' , 'cl' ) ) , sanitize ( os . path . join ( os . environ [ 'WATCOM' ] , 'BINNT' , 'cl.exe' ) ) , sanitize ( os . path . join ( os . environ [ 'WATCOM' ] , 'BINNT64' , 'cl' ) ) , sanitize ( os . path . join ( os . environ [ 'WATCOM' ] , 'BINNT64' , 'cl.exe' ) ) , ] found_cl = sanitize ( shutil . which ( 'cl' ) ) if found_cl in watcom_cls : mlog . debug ( 'Skipping unsupported cl.exe clone at:' , found_cl ) continue arg = '/?' elif 'armcc' in compiler_name : arg = '--vsn' elif 'ccrx' in compiler_name : arg = '-v' elif 'xc16' in compiler_name : arg = '--version' elif 'ccomp' in compiler_name : arg = '-version' elif 'cl2000' in compiler_name : arg = '-version' elif compiler_name in { 'icl' , 'icl.exe' } : # if you pass anything to icl you get stuck in a pager arg = '' else : arg = '--version' try : p , out , err = Popen_safe ( compiler + [ arg ] ) except OSError as e : popen_exceptions [ ' ' . join ( compiler + [ arg ] ) ] = e continue if 'ccrx' in compiler_name : out = err full_version = out . split ( '\n' , 1 ) [ 0 ] version = search_version ( out ) guess_gcc_or_lcc : T . Optional [ str ] = None if 'Free Software Foundation' in out or 'xt-' in out : guess_gcc_or_lcc = 'gcc' if 'e2k' in out and 'lcc' in out : guess_gcc_or_lcc = 'lcc' if 'Microchip Technology' in out : # this output has "Free Software Foundation" in its version guess_gcc_or_lcc = None if guess_gcc_or_lcc : defines = _get_gnu_compiler_defines ( compiler ) if not defines : popen_exceptions [ ' ' . join ( compiler ) ] = 'no pre-processor defines' continue if guess_gcc_or_lcc == 'lcc' : version = _get_lcc_version_from_defines ( defines ) cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler else : version = _get_gnu_version_from_defines ( defines ) cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler linker = guess_nix_linker ( env , compiler , cls , for_machine ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , defines = defines , full_version = full_version , linker = linker ) if 'Emscripten' in out : cls = EmscriptenCCompiler if lang == 'c' else EmscriptenCPPCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) # emcc requires a file input in order to pass arguments to the # linker. It'll exit with an error code, but still print the # linker version. Old emcc versions ignore -Wl,--version completely, # however. We'll report "unknown version" in that case. with tempfile . NamedTemporaryFile ( suffix = '.c' ) as f : cmd = compiler + [ cls . LINKER_PREFIX + "--version" , f . name ] _ , o , _ = Popen_safe ( cmd ) linker = WASMDynamicLinker ( compiler , for_machine , cls . LINKER_PREFIX , [ ] , version = search_version ( o ) ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , linker = linker , full_version = full_version ) if 'armclang' in out : # The compiler version is not present in the first line of output, # instead it is present in second line, startswith 'Component:'. # So, searching for the 'Component' in out although we know it is # present in second line, as we are not sure about the # output format in future versions arm_ver_match = re . search ( '.*Component.*' , out ) if arm_ver_match is None : popen_exceptions [ ' ' . join ( compiler ) ] = 'version string not found' continue arm_ver_str = arm_ver_match . group ( 0 ) # Override previous values version = search_version ( arm_ver_str ) full_version = arm_ver_str cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler linker = ArmClangDynamicLinker ( for_machine , version = version ) env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , full_version = full_version , linker = linker ) if 'CL.EXE COMPATIBILITY' in out : # if this is clang-cl masquerading as cl, detect it as cl, not # clang arg = '--version' try : p , out , err = Popen_safe ( compiler + [ arg ] ) except OSError as e : popen_exceptions [ ' ' . join ( compiler + [ arg ] ) ] = e version = search_version ( out ) match = re . search ( '^Target: (.*?)-' , out , re . MULTILINE ) if match : target = match . group ( 1 ) else : target = 'unknown target' cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler linker = guess_win_linker ( env , [ 'lld-link' ] , cls , for_machine ) return cls ( compiler , version , for_machine , is_cross , info , target , exe_wrap , linker = linker ) if 'clang' in out or 'Clang' in out : linker = None defines = _get_clang_compiler_defines ( compiler ) # Even if the for_machine is darwin, we could be using vanilla # clang. if 'Apple' in out : cls = AppleClangCCompiler if lang == 'c' else AppleClangCPPCompiler else : cls = ClangCCompiler if lang == 'c' else ClangCPPCompiler if 'windows' in out or env . machines [ for_machine ] . is_windows ( ) : # If we're in a MINGW context this actually will use a gnu # style ld, but for clang on "real" windows we'll use # either link.exe or lld-link.exe try : linker = guess_win_linker ( env , compiler , cls , for_machine , invoked_directly = False ) except MesonException : pass if linker is None : linker = guess_nix_linker ( env , compiler , cls , for_machine ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , defines = defines , full_version = full_version , linker = linker ) if 'Intel(R) C++ Intel(R)' in err : version = search_version ( err ) target = 'x86' if 'IA-32' in err else 'x86_64' cls = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) linker = XilinkDynamicLinker ( for_machine , [ ] , version = version ) return cls ( compiler , version , for_machine , is_cross , info , target , exe_wrap , linker = linker ) if 'Microsoft' in out or 'Microsoft' in err : # Latest versions of Visual Studio print version # number to stderr but earlier ones print version # on stdout.  Why? Lord only knows. # Check both outputs to figure out version. for lookat in [ err , out ] : version = search_version ( lookat ) if version != 'unknown version' : break else : raise EnvironmentException ( f'Failed to detect MSVC compiler version: stderr was\n{err!r}' ) cl_signature = lookat . split ( '\n' ) [ 0 ] match = re . search ( r'.*(x86|x64|ARM|ARM64)([^_A-Za-z0-9]|$)' , cl_signature ) if match : target = match . group ( 1 ) else : m = f'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{cl_signature}' raise EnvironmentException ( m ) cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler linker = guess_win_linker ( env , [ 'link' ] , cls , for_machine ) # As of this writing, CCache does not support MSVC but sccache does. if 'sccache' in ccache : final_compiler = ccache + compiler else : final_compiler = compiler return cls ( final_compiler , version , for_machine , is_cross , info , target , exe_wrap , full_version = cl_signature , linker = linker ) if 'PGI Compilers' in out : cls = PGICCompiler if lang == 'c' else PGICPPCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) linker = PGIDynamicLinker ( compiler , for_machine , cls . LINKER_PREFIX , [ ] , version = version ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , linker = linker ) if 'NVIDIA Compilers and Tools' in out : cls = NvidiaHPC_CCompiler if lang == 'c' else NvidiaHPC_CPPCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) linker = NvidiaHPC_DynamicLinker ( compiler , for_machine , cls . LINKER_PREFIX , [ ] , version = version ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , linker = linker ) if '(ICC)' in out : cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler l = guess_nix_linker ( env , compiler , cls , for_machine ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , full_version = full_version , linker = l ) if 'ARM' in out : cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) linker = ArmDynamicLinker ( for_machine , version = version ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , full_version = full_version , linker = linker ) if 'RX Family' in out : cls = CcrxCCompiler if lang == 'c' else CcrxCPPCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) linker = CcrxDynamicLinker ( for_machine , version = version ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , full_version = full_version , linker = linker ) if 'Microchip Technology' in out : cls = Xc16CCompiler if lang == 'c' else Xc16CCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) linker = Xc16DynamicLinker ( for_machine , version = version ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , full_version = full_version , linker = linker ) if 'CompCert' in out : cls = CompCertCCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) linker = CompCertDynamicLinker ( for_machine , version = version ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , full_version = full_version , linker = linker ) if 'TMS320C2000 C/C++' in out : cls = C2000CCompiler if lang == 'c' else C2000CPPCompiler env . coredata . add_lang_args ( cls . language , cls , for_machine , env ) linker = C2000DynamicLinker ( compiler , for_machine , version = version ) return cls ( ccache + compiler , version , for_machine , is_cross , info , exe_wrap , full_version = full_version , linker = linker ) _handle_exceptions ( popen_exceptions , compilers ) raise EnvironmentException ( f'Unknown compiler {compilers}' )