<commit_before># Copyright 2012-2017 The Meson development team

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import re
import glob
import os.path
import subprocess
import functools
import itertools
from pathlib import Path

from .. import mlog
from .. import coredata
from . import compilers
from ..mesonlib import (
    EnvironmentException, MesonException, version_compare, Popen_safe, listify,
    for_windows, for_darwin, for_cygwin, for_haiku, for_openbsd,
)
from .c_function_attributes import C_FUNC_ATTRIBUTES

from .compilers import (
    CompilerType,
    get_largefile_args,
    gnu_winlibs,
    msvc_winlibs,
    vs32_instruction_set_args,
    vs64_instruction_set_args,
    ArmCompiler,
    ArmclangCompiler,
    ClangCompiler,
    Compiler,
    CompilerArgs,
    CrossNoRunException,
    GnuCompiler,
    ElbrusCompiler,
    IntelCompiler,
    RunResult,
    CcrxCompiler,
)

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 = {}
    internal_libs = gnu_compiler_internal_libs

    @staticmethod
    def attribute_check_func(name):
        try:
            return C_FUNC_ATTRIBUTES[name]
        except KeyError:
            raise MesonException('Unknown function attribute "{}"'.format(name))

    def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwargs):
        # If a child ObjC or CPP class has already set it, don't set it ourselves
        if not hasattr(self, 'language'):
            self.language = 'c'
        super().__init__(exelist, version, **kwargs)
        self.id = 'unknown'
        self.is_cross = is_cross
        self.can_compile_suffixes.add('h')
        # If the exe wrapper was not found, pretend it wasn't set so that the
        # sanity check is skipped and compiler checks use fallbacks.
        if not exe_wrapper or not exe_wrapper.found():
            self.exe_wrapper = None
        else:
            self.exe_wrapper = exe_wrapper.get_command()

        # Set to None until we actually need to check this
        self.has_fatal_warnings_link_arg = None

    def needs_static_linker(self):
        return True # When compiling static libraries, so yes.

    def get_always_args(self):
        '''
        Args that are always-on for all C compilers other than MSVC
        '''
        return ['-pipe'] + get_largefile_args(self)

    def get_linker_debug_crt_args(self):
        """
        Arguments needed to select a debug crt for the linker
        This is only needed for MSVC
        """
        return []

    def get_no_stdinc_args(self):
        return ['-nostdinc']

    def get_no_stdlib_link_args(self):
        return ['-nostdlib']

    def get_warn_args(self, level):
        return self.warn_args[level]

    def get_no_warn_args(self):
        # Almost every compiler uses this for disabling warnings
        return ['-w']

    def get_soname_args(self, *args):
        return []

    def split_shlib_to_parts(self, fname):
        return None, fname

    # The default behavior is this, override in MSVC
    @functools.lru_cache(maxsize=None)
    def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
        if self.compiler_type.is_windows_compiler:
            return []
        return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath)

    def get_dependency_gen_args(self, outtarget, outfile):
        return ['-MD', '-MQ', outtarget, '-MF', outfile]

    def depfile_for_object(self, objfile):
        return objfile + '.' + self.get_depfile_suffix()

    def get_depfile_suffix(self):
        return 'd'

    def get_exelist(self):
        return self.exelist[:]

    def get_linker_exelist(self):
        return self.exelist[:]

    def get_preprocess_only_args(self):
        return ['-E', '-P']

    def get_compile_only_args(self):
        return ['-c']

    def get_no_optimization_args(self):
        return ['-O0']

    def get_compiler_check_args(self):
        '''
        Get arguments useful for compiler checks such as being permissive in
        the code quality and not doing any optimization.
        '''
        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 in ('clang', 'gcc'):
            if self.compiler_type.is_osx_compiler:
                # Apple ld
                return ['-Wl,-undefined,dynamic_lookup']
            elif self.compiler_type.is_windows_compiler:
                # For PE/COFF this is impossible
                return []
            else:
                # GNU ld and LLVM lld
                return ['-Wl,--allow-shlib-undefined']
        elif isinstance(self, VisualStudioCCompiler):
            # link.exe
            return ['/FORCE:UNRESOLVED']
        # FIXME: implement other linkers
        return []

    def get_output_args(self, target):
        return ['-o', target]

    def get_linker_output_args(self, outputname):
        return ['-o', outputname]

    def get_coverage_args(self):
        return ['--coverage']

    def get_coverage_link_args(self):
        return ['--coverage']

    def get_werror_args(self):
        return ['-Werror']

    def get_std_exe_link_args(self):
        return []

    def get_include_args(self, path, is_system):
        if path == '':
            path = '.'
        if is_system:
            return ['-isystem', path]
        return ['-I' + path]

    def get_std_shared_lib_link_args(self):
        return ['-shared']

    @functools.lru_cache()
    def _get_search_dirs(self, env):
        extra_args = ['--print-search-dirs']
        stdo = None
        with self._build_wrapper('', env, extra_args=extra_args,
                                 dependencies=None, mode='compile',
                                 want_output=True) as p:
            stdo = p.stdo
        return stdo

    @staticmethod
    def _split_fetch_real_dirs(pathstr, sep=':'):
        paths = []
        for p in pathstr.split(sep):
            p = Path(p)
            if p.exists() and p.resolve().as_posix() not in paths:
                paths.append(p.resolve().as_posix())
        return tuple(paths)

    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 ()

    @functools.lru_cache()
    def get_library_dirs(self, env, elf_class = None):
        dirs = self.get_compiler_dirs(env, 'libraries')
        if elf_class is None or elf_class == 0:
            return dirs

        # if we do have an elf class for 32-bit or 64-bit, we want to check that
        # the directory in question contains libraries of the appropriate class. Since
        # system directories aren't mixed, we only need to check one file for each
        # directory and go by that. If we can't check the file for some reason, assume
        # the compiler knows what it's doing, and accept the directory anyway.
        retval = []
        for d in dirs:<commit_msg>Store unexpanded library directory paths. Closes #4392.<commit_after># Copyright 2012-2017 The Meson development team

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import re
import glob
import os.path
import subprocess
import functools
import itertools
from pathlib import Path

from .. import mlog
from .. import coredata
from . import compilers
from ..mesonlib import (
    EnvironmentException, MesonException, version_compare, Popen_safe, listify,
    for_windows, for_darwin, for_cygwin, for_haiku, for_openbsd,
)
from .c_function_attributes import C_FUNC_ATTRIBUTES

from .compilers import (
    CompilerType,
    get_largefile_args,
    gnu_winlibs,
    msvc_winlibs,
    vs32_instruction_set_args,
    vs64_instruction_set_args,
    ArmCompiler,
    ArmclangCompiler,
    ClangCompiler,
    Compiler,
    CompilerArgs,
    CrossNoRunException,
    GnuCompiler,
    ElbrusCompiler,
    IntelCompiler,
    RunResult,
    CcrxCompiler,
)

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 = {}
    internal_libs = gnu_compiler_internal_libs

    @staticmethod
    def attribute_check_func(name):
        try:
            return C_FUNC_ATTRIBUTES[name]
        except KeyError:
            raise MesonException('Unknown function attribute "{}"'.format(name))

    def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwargs):
        # If a child ObjC or CPP class has already set it, don't set it ourselves
        if not hasattr(self, 'language'):
            self.language = 'c'
        super().__init__(exelist, version, **kwargs)
        self.id = 'unknown'
        self.is_cross = is_cross
        self.can_compile_suffixes.add('h')
        # If the exe wrapper was not found, pretend it wasn't set so that the
        # sanity check is skipped and compiler checks use fallbacks.
        if not exe_wrapper or not exe_wrapper.found():
            self.exe_wrapper = None
        else:
            self.exe_wrapper = exe_wrapper.get_command()

        # Set to None until we actually need to check this
        self.has_fatal_warnings_link_arg = None

    def needs_static_linker(self):
        return True # When compiling static libraries, so yes.

    def get_always_args(self):
        '''
        Args that are always-on for all C compilers other than MSVC
        '''
        return ['-pipe'] + get_largefile_args(self)

    def get_linker_debug_crt_args(self):
        """
        Arguments needed to select a debug crt for the linker
        This is only needed for MSVC
        """
        return []

    def get_no_stdinc_args(self):
        return ['-nostdinc']

    def get_no_stdlib_link_args(self):
        return ['-nostdlib']

    def get_warn_args(self, level):
        return self.warn_args[level]

    def get_no_warn_args(self):
        # Almost every compiler uses this for disabling warnings
        return ['-w']

    def get_soname_args(self, *args):
        return []

    def split_shlib_to_parts(self, fname):
        return None, fname

    # The default behavior is this, override in MSVC
    @functools.lru_cache(maxsize=None)
    def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
        if self.compiler_type.is_windows_compiler:
            return []
        return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath)

    def get_dependency_gen_args(self, outtarget, outfile):
        return ['-MD', '-MQ', outtarget, '-MF', outfile]

    def depfile_for_object(self, objfile):
        return objfile + '.' + self.get_depfile_suffix()

    def get_depfile_suffix(self):
        return 'd'

    def get_exelist(self):
        return self.exelist[:]

    def get_linker_exelist(self):
        return self.exelist[:]

    def get_preprocess_only_args(self):
        return ['-E', '-P']

    def get_compile_only_args(self):
        return ['-c']

    def get_no_optimization_args(self):
        return ['-O0']

    def get_compiler_check_args(self):
        '''
        Get arguments useful for compiler checks such as being permissive in
        the code quality and not doing any optimization.
        '''
        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 in ('clang', 'gcc'):
            if self.compiler_type.is_osx_compiler:
                # Apple ld
                return ['-Wl,-undefined,dynamic_lookup']
            elif self.compiler_type.is_windows_compiler:
                # For PE/COFF this is impossible
                return []
            else:
                # GNU ld and LLVM lld
                return ['-Wl,--allow-shlib-undefined']
        elif isinstance(self, VisualStudioCCompiler):
            # link.exe
            return ['/FORCE:UNRESOLVED']
        # FIXME: implement other linkers
        return []

    def get_output_args(self, target):
        return ['-o', target]

    def get_linker_output_args(self, outputname):
        return ['-o', outputname]

    def get_coverage_args(self):
        return ['--coverage']

    def get_coverage_link_args(self):
        return ['--coverage']

    def get_werror_args(self):
        return ['-Werror']

    def get_std_exe_link_args(self):
        return []

    def get_include_args(self, path, is_system):
        if path == '':
            path = '.'
        if is_system:
            return ['-isystem', path]
        return ['-I' + path]

    def get_std_shared_lib_link_args(self):
        return ['-shared']

    @functools.lru_cache()
    def _get_search_dirs(self, env):
        extra_args = ['--print-search-dirs']
        stdo = None
        with self._build_wrapper('', env, extra_args=extra_args,
                                 dependencies=None, mode='compile',
                                 want_output=True) as p:
            stdo = p.stdo
        return stdo

    @staticmethod
    def _split_fetch_real_dirs(pathstr, sep=':'):
        paths = []
        for p in pathstr.split(sep):
            # GCC returns paths like this:
            # /usr/lib/gcc/x86_64-linux-gnu/8/../../../../x86_64-linux-gnu/lib
            # It would make sense to normalize them to get rid of the .. parts
            # Sadly when you are on a merged /usr fs it also kills these:
            # /lib/x86_64-linux-gnu
            # since /lib is a symlink to /usr/lib. This would mean
            # paths under /lib would be considered not a "system path",
            # which is wrong and breaks things. Store everything, just to be sure.
            pobj = Path(p)
            unresolved = pobj.as_posix()
            resolved = Path(p).resolve().as_posix()
            if pobj.exists():
                if unresolved not in paths:
                    paths.append(unresolved)
                if resolved not in paths:
                    paths.append(resolved)
        return tuple(paths)

    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 ()

    @functools.lru_cache()
    def get_library_dirs(self, env, elf_class = None):
        dirs = self.get_compiler_dirs(env, 'libraries')
        if elf_class is None or elf_class == 0:
            return dirs

        # if we do have an elf class for 32-bit or 64-bit, we want to check that
        # the directory in question contains libraries of the appropriate class. Since
        # system directories aren't mixed, we only need to check one file for each
        # directory and go by that. If we can't check the file for some reason, assume
        # the compiler knows what it's doing, and accept the directory anyway.
        retval = []
        for d in dirs:
{'fraction_non_alphanumeric': 0.08958998133542055, 'fraction_numerical': 0.003913540851345656, 'mean_word_length': 2.410677618069815, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6686619', 'n_tokens_mistral': 5053, 'n_tokens_neox': 4823, 'n_words': 1450}