aboutsummaryrefslogtreecommitdiffstats
path: root/clang-plugin/FindLLVM.cmake
blob: 8da444d52e120c3e7ca9b0de6608dc95d1075518 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#===------------------------------------------------------------------------===#
#
#                     The KLEE Symbolic Virtual Machine
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
#
# This file provides multiple methods to detect LLVM.
#
# * llvm-config executable. This method is portable across LLVM build systems
# (i.e. works if LLVM was built with autoconf/Makefile or with CMake).
#
# * find_package(LLVM CONFIG). This method only works if LLVM was built with
# CMake or with LLVM >= 3.5 when built with the autoconf/Makefile build system
# This method relies on the `LLVMConfig.cmake` file generated to be generated
# by LLVM's build system.
#
#===------------------------------------------------------------------------===#

function(string_to_list s output_var)
    string(REPLACE " " ";" _output "${s}")
    set(${output_var} ${_output} PARENT_SCOPE)
endfunction()

option(USE_CMAKE_FIND_PACKAGE_LLVM "Use find_package(LLVM CONFIG) to find LLVM" OFF)

if (USE_CMAKE_FIND_PACKAGE_LLVM)
    find_package(LLVM CONFIG REQUIRED)

    # Provide function to map LLVM components to libraries.
    function(klee_get_llvm_libs output_var)
        if (${LLVM_PACKAGE_VERSION} VERSION_LESS "3.5")
            llvm_map_components_to_libraries(${output_var} ${ARGN})
        else()
            llvm_map_components_to_libnames(${output_var} ${ARGN})
        endif()
        set(${output_var} ${${output_var}} PARENT_SCOPE)
    endfunction()
    # HACK: This information is not exported so just pretend its OFF for now.
    set(LLVM_ENABLE_VISIBILITY_INLINES_HIDDEN OFF)
else()
    # Use the llvm-config binary to get the information needed.
    # Try to detect it in the user's environment. The user can
    # force a particular binary by passing `-DLLVM_CONFIG_BINARY=/path/to/llvm-config`
    # to CMake.
    find_program(LLVM_CONFIG_BINARY
            NAMES llvm-config)
    message(STATUS "LLVM_CONFIG_BINARY: ${LLVM_CONFIG_BINARY}")

    if (NOT LLVM_CONFIG_BINARY)
        message(FATAL_ERROR
                "Failed to find llvm-config.\n"
                "Try passing -DLLVM_CONFIG_BINARY=/path/to/llvm-config to cmake")
    endif()

    function(_run_llvm_config output_var)
        set(_command "${LLVM_CONFIG_BINARY}" ${ARGN})
        execute_process(COMMAND ${_command}
                RESULT_VARIABLE _exit_code
                OUTPUT_VARIABLE ${output_var}
                OUTPUT_STRIP_TRAILING_WHITESPACE
                ERROR_STRIP_TRAILING_WHITESPACE
                )
        if (NOT ("${_exit_code}" EQUAL "0"))
            message(FATAL_ERROR "Failed running ${_command}")
        endif()
        set(${output_var} ${${output_var}} PARENT_SCOPE)
    endfunction()

    # Get LLVM version
    _run_llvm_config(LLVM_PACKAGE_VERSION "--version")
    # Try x.y.z patern
    set(_llvm_version_regex "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(svn)?$")
    if ("${LLVM_PACKAGE_VERSION}" MATCHES "${_llvm_version_regex}")
        string(REGEX REPLACE
                "${_llvm_version_regex}"
                "\\1"
                LLVM_VERSION_MAJOR
                "${LLVM_PACKAGE_VERSION}")
        string(REGEX REPLACE
                "${_llvm_version_regex}"
                "\\2"
                LLVM_VERSION_MINOR
                "${LLVM_PACKAGE_VERSION}")
        string(REGEX REPLACE
                "${_llvm_version_regex}"
                "\\3"
                LLVM_VERSION_PATCH
                "${LLVM_PACKAGE_VERSION}")
    else()
        # try x.y pattern
        set(_llvm_version_regex "^([0-9]+)\\.([0-9]+)(svn)?$")
        if ("${LLVM_PACKAGE_VERSION}" MATCHES "${_llvm_version_regex}")
            string(REGEX REPLACE
                    "${_llvm_version_regex}"
                    "\\1"
                    LLVM_VERSION_MAJOR
                    "${LLVM_PACKAGE_VERSION}")
            string(REGEX REPLACE
                    "${_llvm_version_regex}"
                    "\\2"
                    LLVM_VERSION_MINOR
                    "${LLVM_PACKAGE_VERSION}")
            set(LLVM_VERSION_PATCH 0)
        else()
            message(FATAL_ERROR
                    "Failed to parse LLVM version from \"${LLVM_PACKAGE_VERSION}\"")
        endif()
    endif()

    set(LLVM_DEFINITIONS "")
    _run_llvm_config(_llvm_cpp_flags "--cppflags")
    string_to_list("${_llvm_cpp_flags}" _llvm_cpp_flags_list)
    foreach (flag ${_llvm_cpp_flags_list})
        # Filter out -I flags by only looking for -D flags.
        if ("${flag}" MATCHES "^-D" AND NOT ("${flag}" STREQUAL "-D_DEBUG"))
            list(APPEND LLVM_DEFINITIONS "${flag}")
        endif()
    endforeach()

    set(LLVM_ENABLE_ASSERTIONS ON)
    set(LLVM_ENABLE_EH ON)
    set(LLVM_ENABLE_RTTI ON)
    set(LLVM_ENABLE_VISIBILITY_INLINES_HIDDEN OFF)
    _run_llvm_config(_llvm_cxx_flags "--cxxflags")
    string_to_list("${_llvm_cxx_flags}" _llvm_cxx_flags_list)
    foreach (flag ${_llvm_cxx_flags_list})
        if ("${flag}" STREQUAL "-DNDEBUG")
            # Note we don't rely on `llvm-config --build-mode` because
            # that seems broken when LLVM is built with CMake.
            set(LLVM_ENABLE_ASSERTIONS OFF)
        elseif ("${flag}" STREQUAL "-fno-exceptions")
            set(LLVM_ENABLE_EH OFF)
        elseif ("${flag}" STREQUAL "-fno-rtti")
            set(LLVM_ENABLE_RTTI OFF)
        elseif ("${flag}" STREQUAL "-fvisibility-inlines-hidden")
            set(LLVM_ENABLE_VISIBILITY_INLINES_HIDDEN ON)
        endif()
    endforeach()

    set(LLVM_INCLUDE_DIRS "")
    foreach (flag ${_llvm_cpp_flags_list})
        # Filter out -D flags by only looking for -I flags.
        if ("${flag}" MATCHES "^-I")
            string(REGEX REPLACE "^-I(.+)$" "\\1" _include_dir "${flag}")
            list(APPEND LLVM_INCLUDE_DIRS "${_include_dir}")
        endif()
    endforeach()

    _run_llvm_config(LLVM_LIBRARY_DIRS "--libdir")
    _run_llvm_config(LLVM_TOOLS_BINARY_DIR "--bindir")
    _run_llvm_config(TARGET_TRIPLE "--host-target")

    # Provide function to map LLVM components to libraries.
    function(klee_get_llvm_libs OUTPUT_VAR)
        _run_llvm_config(_llvm_libs "--libfiles" ${ARGN})
        string_to_list("${_llvm_libs}" _llvm_libs_list)

        # Now find the system libs that are needed.
        if (${LLVM_PACKAGE_VERSION} VERSION_LESS "3.5")
            # For LLVM 3.4 and older system libraries
            # appeared in the output of `--ldflags`.
            _run_llvm_config(_system_libs "--ldflags")
            # TODO: Filter out `-L<path>` flag.
        else()
            _run_llvm_config(_system_libs "--system-libs")
        endif()
        string_to_list("${_system_libs}" _system_libs_list)

        # Create an imported target for each LLVM library
        # if it doesn't already exist. We need to do this
        # so we can tell CMake that these libraries depend
        # on the necessary libraries so that CMake
        # can get the link order right.
        set(targets_to_return "")
        set(created_targets "")
        foreach (llvm_lib ${_llvm_libs_list})
            # a bug in llvm-config from LLVM 3.9
            string(REGEX REPLACE "lib(libLLVM[-.a-zA-Z0-9]+\\.so)\\.so$" "\\1" llvm_lib "${llvm_lib}")

            get_filename_component(llvm_lib_file_name "${llvm_lib}" NAME)

            string(REGEX REPLACE "^(lib)?(LLVM[-.a-zA-Z0-9]+)\\..+$" "\\2" target_name "${llvm_lib_file_name}")
            list(APPEND targets_to_return "${target_name}")
            if (NOT TARGET "${target_name}")
                # DEBUG: message(STATUS "Creating imported target \"${target_name}\"" " for \"${llvm_lib}\"")
                list(APPEND created_targets "${target_name}")

                set(import_library_type "STATIC")
                if ("${llvm_lib_file_name}" MATCHES "(so|dylib|dll)$")
                    set(import_library_type "SHARED")
                endif()
                # Create an imported target for the library
                add_library("${target_name}" "${import_library_type}" IMPORTED GLOBAL)
                set_property(TARGET "${target_name}" PROPERTY
                        IMPORTED_LOCATION "${llvm_lib}"
                        )
            endif()
        endforeach()

        # Now state the dependencies of the created imported targets which we
        # assume to be for each imported target the libraries which appear after
        # the library in `{_llvm_libs_list}` and then finally the system libs.
        # It is **essential** that we do this otherwise CMake will get the
        # link order of the imported targets wrong.
        list(LENGTH targets_to_return length_targets_to_return)
        if ("${length_targets_to_return}" GREATER 0)
            math(EXPR targets_to_return_last_index "${length_targets_to_return} -1")
            foreach (llvm_target_lib ${created_targets})
                # DEBUG: message(STATUS "Adding deps for target ${llvm_target_lib}")
                # Find position in `targets_to_return`
                list(FIND targets_to_return "${llvm_target_lib}" position)
                if ("${position}" EQUAL "-1")
                    message(FATAL_ERROR "couldn't find \"${llvm_target_lib}\" in list of targets")
                endif()
                if ("${position}" LESS "${targets_to_return_last_index}")
                    math(EXPR position_plus_one "${position} + 1")
                    foreach (index RANGE ${position_plus_one} ${targets_to_return_last_index})
                        # Get the target for this index
                        list(GET targets_to_return ${index} target_for_index)
                        # DEBUG: message(STATUS "${llvm_target_libs} depends on ${target_for_index}")
                        set_property(TARGET "${llvm_target_lib}" APPEND PROPERTY
                                INTERFACE_LINK_LIBRARIES "${target_for_index}"
                                )
                    endforeach()
                endif()
                # Now finally add the system library dependencies. These must be last.
                set_property(TARGET "${target_name}" APPEND PROPERTY
                        INTERFACE_LINK_LIBRARIES "${_system_libs_list}"
                        )
            endforeach()
        endif()

        set(${OUTPUT_VAR} ${targets_to_return} PARENT_SCOPE)
    endfunction()
endif()

# Filter out `-DNEBUG` from LLVM_DEFINITIONS.  The caller can use
# `LLVM_ENABLE_ASSERTIONS` to decide how to set their defines.
set(_new_llvm_definitions "")
foreach (llvm_define ${LLVM_DEFINITIONS})
    if ("${llvm_define}" STREQUAL "-DNDEBUG")
        # Skip
    else()
        list(APPEND _new_llvm_definitions "${llvm_define}")
    endif()
endforeach()
set(LLVM_DEFINITIONS "${_new_llvm_definitions}")
unset(_new_llvm_definitions)