1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5    # Visual Studio cl options reference:
6    #   https://msdn.microsoft.com/en-us/library/610ecb4h.aspx
7    #       "Options are specified by either a forward slash (/) or a dash (–)."
8    #   Here we use "-" better than "/" that produces invalid escaped chars using AutoTools.
9    #   -LIBPATH, -D, -I, -ZI and so on.
10
11"""
12
13from conans.client.tools.apple import to_apple_arch
14from conans.client.tools.oss import cpu_count
15from conans.client.tools.win import unix_path
16
17
18GCC_LIKE = ['clang', 'apple-clang', 'gcc']
19
20
21def _base_compiler(settings):
22    return settings.get_safe("compiler.base") or settings.get_safe("compiler")
23
24
25# FIXME : pass conanfile instead of settings and os_build
26def rpath_flags(settings, os_build, lib_paths):
27    compiler = _base_compiler(settings)
28    if not os_build:
29        return []
30    if compiler in GCC_LIKE:
31        rpath_separator = ","
32        return ['-Wl,-rpath%s"%s"' % (rpath_separator, x.replace("\\", "/"))
33                for x in lib_paths if x]
34    return []
35
36
37def architecture_flag(settings):
38    """
39    returns flags specific to the target architecture and compiler
40    """
41    compiler = settings.get_safe("compiler")
42    compiler_base = settings.get_safe("compiler.base")
43    arch = settings.get_safe("arch")
44    the_os = settings.get_safe("os")
45    subsystem = settings.get_safe("os.subsystem")
46    if not compiler or not arch:
47        return ""
48
49    if str(compiler) in ['gcc', 'apple-clang', 'clang', 'sun-cc']:
50        if str(the_os) == 'Macos' and str(subsystem) == 'catalyst':
51            apple_arch = to_apple_arch(arch)
52            if apple_arch:
53                return '--target=%s-apple-ios-macabi' % apple_arch
54        elif str(arch) in ['x86_64', 'sparcv9', 's390x']:
55            return '-m64'
56        elif str(arch) in ['x86', 'sparc']:
57            return '-m32'
58        elif str(arch) in ['s390']:
59            return '-m31'
60        elif str(the_os) == 'AIX':
61            if str(arch) in ['ppc32']:
62                return '-maix32'
63            elif str(arch) in ['ppc64']:
64                return '-maix64'
65    elif str(compiler) == "intel":
66        # https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-m32-m64-qm32-qm64
67        if str(arch) == "x86":
68            return "/Qm32" if str(compiler_base) == "Visual Studio" else "-m32"
69        elif str(arch) == "x86_64":
70            return "/Qm64" if str(compiler_base) == "Visual Studio" else "-m64"
71    elif str(compiler) == "mcst-lcc":
72        return {"e2k-v2": "-march=elbrus-v2",
73                "e2k-v3": "-march=elbrus-v3",
74                "e2k-v4": "-march=elbrus-v4",
75                "e2k-v5": "-march=elbrus-v5",
76                "e2k-v6": "-march=elbrus-v6",
77                "e2k-v7": "-march=elbrus-v7"}.get(str(arch), "")
78    return ""
79
80
81def libcxx_define(settings):
82    compiler = _base_compiler(settings)
83    libcxx = settings.get_safe("compiler.libcxx")
84    if not compiler or not libcxx:
85        return ""
86
87    if str(compiler) in GCC_LIKE:
88        if str(libcxx) == 'libstdc++':
89            return '_GLIBCXX_USE_CXX11_ABI=0'
90        elif str(libcxx) == 'libstdc++11':
91            return '_GLIBCXX_USE_CXX11_ABI=1'
92    return ""
93
94
95def libcxx_flag(settings):
96    """
97    returns flag specific to the target C++ standard library
98    """
99    compiler = _base_compiler(settings)
100    libcxx = settings.get_safe("compiler.libcxx")
101    if not compiler or not libcxx:
102        return ""
103    if str(compiler) in ['clang', 'apple-clang']:
104        if str(libcxx) in ['libstdc++', 'libstdc++11']:
105            return '-stdlib=libstdc++'
106        elif str(libcxx) == 'libc++':
107            return '-stdlib=libc++'
108    elif str(compiler) == 'sun-cc':
109        return ({"libCstd": "-library=Cstd",
110                            "libstdcxx": "-library=stdcxx4",
111                            "libstlport": "-library=stlport4",
112                            "libstdc++": "-library=stdcpp"}.get(libcxx, ""))
113    elif str(compiler) == "qcc":
114        return "-Y _%s" % str(libcxx)
115    return ""
116
117
118def pic_flag(settings):
119    """
120    returns PIC (position independent code) flags, such as -fPIC
121    """
122    compiler = _base_compiler(settings)
123    if not compiler or compiler == 'Visual Studio':
124        return ""
125    return '-fPIC'
126
127
128def build_type_flags(settings):
129    """
130    returns flags specific to the build type (Debug, Release, etc.)
131    (-s, -g, /Zi, etc.)
132    """
133    compiler = _base_compiler(settings)
134    build_type = settings.get_safe("build_type")
135    vs_toolset = settings.get_safe("compiler.toolset")
136    if not compiler or not build_type:
137        return ""
138
139    # https://github.com/Kitware/CMake/blob/d7af8a34b67026feaee558433db3a835d6007e06/
140    # Modules/Platform/Windows-MSVC.cmake
141    if str(compiler) == 'Visual Studio':
142        if vs_toolset and "clang" in str(vs_toolset):
143            flags = {"Debug": ["-gline-tables-only", "-fno-inline", "-O0"],
144                     "Release": ["-O2"],
145                     "RelWithDebInfo": ["-gline-tables-only", "-O2", "-fno-inline"],
146                     "MinSizeRel": []
147                     }.get(build_type, ["-O2", "-Ob2"])
148        else:
149            flags = {"Debug": ["-Zi", "-Ob0", "-Od"],
150                     "Release": ["-O2", "-Ob2"],
151                     "RelWithDebInfo": ["-Zi", "-O2", "-Ob1"],
152                     "MinSizeRel": ["-O1", "-Ob1"],
153                     }.get(build_type, [])
154        return flags
155    else:
156        # https://github.com/Kitware/CMake/blob/f3bbb37b253a1f4a26809d6f132b3996aa2e16fc/
157        # Modules/Compiler/GNU.cmake
158        # clang include the gnu (overriding some things, but not build type) and apple clang
159        # overrides clang but it doesn't touch clang either
160        if str(compiler) in ["clang", "gcc", "apple-clang", "qcc", "mcst-lcc"]:
161            # FIXME: It is not clear that the "-s" is something related with the build type
162            # cmake is not adjusting it
163            # -s: Remove all symbol table and relocation information from the executable.
164            flags = {"Debug": ["-g"],
165                     "Release": ["-O3", "-s"] if str(compiler) == "gcc" else ["-O3"],
166                     "RelWithDebInfo": ["-O2", "-g"],
167                     "MinSizeRel": ["-Os"],
168                     }.get(build_type, [])
169            return flags
170        elif str(compiler) == "sun-cc":
171            # https://github.com/Kitware/CMake/blob/f3bbb37b253a1f4a26809d6f132b3996aa2e16fc/
172            # Modules/Compiler/SunPro-CXX.cmake
173            flags = {"Debug": ["-g"],
174                     "Release": ["-xO3"],
175                     "RelWithDebInfo": ["-xO2", "-g"],
176                     "MinSizeRel": ["-xO2", "-xspace"],
177                     }.get(build_type, [])
178            return flags
179    return ""
180
181
182def build_type_define(build_type=None):
183    """
184    returns definitions specific to the build type (Debug, Release, etc.)
185    like DEBUG, _DEBUG, NDEBUG
186    """
187    return 'NDEBUG' if build_type in ['Release', 'RelWithDebInfo', 'MinSizeRel'] else ""
188
189
190def adjust_path(path, settings, win_bash=False, subsystem=None):
191    """
192    adjusts path to be safely passed to the compiler command line
193    for Windows bash, ensures path is in format according to the subsystem
194    for path with spaces, places double quotes around it
195    converts slashes to backslashes, or vice versa
196    """
197    compiler = _base_compiler(settings)
198    if str(compiler) == 'Visual Studio':
199        path = path.replace('/', '\\')
200    else:
201        path = path.replace('\\', '/')
202    if win_bash:
203        path = unix_path(path, subsystem)
204    return '"%s"' % path if ' ' in path else path
205
206
207def sysroot_flag(sysroot, settings, win_bash=False, subsystem=None):
208    compiler = _base_compiler(settings)
209    if str(compiler) != 'Visual Studio' and sysroot:
210        sysroot = adjust_path(sysroot, settings, win_bash=win_bash, subsystem=subsystem)
211        return '--sysroot=%s' % sysroot
212    return ""
213
214
215def visual_runtime(runtime):
216    if runtime:
217        return "-%s" % runtime
218    return ""
219
220
221def format_defines(defines):
222    return ["-D%s" % define for define in defines if define]
223
224
225include_path_option = "-I"
226visual_linker_option_separator = "-link"  # Further options will apply to the linker
227
228
229def format_include_paths(include_paths, settings, win_bash=False, subsystem=None):
230    return ["%s%s" % (include_path_option, adjust_path(include_path, settings, win_bash=win_bash,
231                                                       subsystem=subsystem))
232            for include_path in include_paths if include_path]
233
234
235def format_library_paths(library_paths, settings, win_bash=False, subsystem=None):
236    compiler = _base_compiler(settings)
237    pattern = "-LIBPATH:%s" if str(compiler) == 'Visual Studio' else "-L%s"
238    return [pattern % adjust_path(library_path, settings, win_bash=win_bash,
239                                  subsystem=subsystem)
240            for library_path in library_paths if library_path]
241
242
243def format_libraries(libraries, settings):
244    result = []
245    compiler = settings.get_safe("compiler")
246    compiler_base = settings.get_safe("compiler.base")
247    for library in libraries:
248        if str(compiler) == 'Visual Studio' or str(compiler_base) == 'Visual Studio':
249            if not library.endswith(".lib"):
250                library += ".lib"
251            result.append(library)
252        else:
253            result.append("-l%s" % library)
254    return result
255
256
257def parallel_compiler_cl_flag(output=None):
258    return "/MP%s" % cpu_count(output=output)
259
260
261def format_frameworks(frameworks, settings):
262    """
263    returns an appropriate compiler flags to link with Apple Frameworks
264    or an empty array, if Apple Frameworks aren't supported by the given compiler
265    """
266    compiler = settings.get_safe("compiler")
267    compiler_base = settings.get_safe("compiler.base")
268    if (str(compiler) not in GCC_LIKE) and (str(compiler_base) not in GCC_LIKE):
269        return []
270    return ["-framework %s" % framework for framework in frameworks]
271
272
273def format_framework_paths(framework_paths, settings):
274    """
275    returns an appropriate compiler flags to specify Apple Frameworks search paths
276    or an empty array, if Apple Frameworks aren't supported by the given compiler
277    """
278    compiler = settings.get_safe("compiler")
279    compiler_base = settings.get_safe("compiler.base")
280    if (str(compiler) not in GCC_LIKE) and (str(compiler_base) not in GCC_LIKE):
281        return []
282    return ["-F %s" % adjust_path(framework_path, settings) for framework_path in framework_paths]
283