• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ~~~
2# Copyright (c) 2014-2023 The Khronos Group Inc.
3# Copyright (c) 2014-2023 Valve Corporation
4# Copyright (c) 2014-2023 LunarG, Inc.
5# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
6# Copyright (c) 2023-2023 RasterGrid Kft.
7#
8# Licensed under the Apache License, Version 2.0 (the "License");
9# you may not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12#     http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS,
16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17# See the License for the specific language governing permissions and
18# limitations under the License.
19# ~~~
20include(CheckIncludeFile)
21
22add_library(loader_specific_options INTERFACE)
23target_link_libraries(loader_specific_options INTERFACE loader_common_options Vulkan::Headers)
24target_include_directories(loader_specific_options INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/generated ${CMAKE_CURRENT_BINARY_DIR})
25
26if(WIN32)
27
28    if(ENABLE_WIN10_ONECORE)
29        # Note: When linking your app or driver to OneCore.lib, be sure to remove any links to non-umbrella libs (such as
30        # kernel32.lib).
31        set(CMAKE_C_STANDARD_LIBRARIES " ") # space is intentional
32    endif()
33
34    # ~~~
35    # Only generate the loader.rc file with CMake if BUILD_DLL_VERSIONINFO was set.
36    # This feature is for the Vulkan Runtime build
37    # Otherwise rely on the checked in loader.rc from the python script
38    # ~~~
39    if (NOT "$CACHE{BUILD_DLL_VERSIONINFO}" STREQUAL "")
40        string(TIMESTAMP CURRENT_YEAR "%Y")
41        set(LOADER_CUR_COPYRIGHT_YEAR "${CURRENT_YEAR}")
42        set(LOADER_RC_VERSION "$CACHE{BUILD_DLL_VERSIONINFO}")
43        set(LOADER_VER_FILE_VERSION_STR "\"${LOADER_RC_VERSION}\"")
44        set(LOADER_VER_FILE_DESCRIPTION_STR "\"Vulkan Loader\"")
45
46        # RC file wants the value of FILEVERSION to separated by commas
47        string(REPLACE "." ", " LOADER_VER_FILE_VERSION "${LOADER_RC_VERSION}")
48
49        # Configure the file to include the versioning info
50        # Place it in the build directory - the GN build will use the checked in file
51        configure_file(loader.rc.in ${CMAKE_CURRENT_BINARY_DIR}/loader.rc)
52    endif()
53else()
54    # Used to make alloca() and secure_getenv() available
55    target_compile_definitions(loader_specific_options INTERFACE _GNU_SOURCE)
56    if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
57        target_compile_definitions(loader_specific_options INTERFACE __BSD_VISIBLE=1)
58    endif()
59    check_include_file("alloca.h" HAVE_ALLOCA_H)
60    if(HAVE_ALLOCA_H)
61        target_compile_definitions(loader_specific_options INTERFACE HAVE_ALLOCA_H)
62    endif()
63
64    set(THREADS_PREFER_PTHREAD_FLAG ON)
65    find_package(Threads REQUIRED)
66endif()
67
68set(NORMAL_LOADER_SRCS
69    allocation.c
70    allocation.h
71    cJSON.c
72    cJSON.h
73    debug_utils.c
74    debug_utils.h
75    extension_manual.c
76    extension_manual.h
77    loader_environment.c
78    loader_environment.h
79    gpa_helper.c
80    gpa_helper.h
81    loader.c
82    loader.h
83    log.c
84    log.h
85    loader_json.c
86    loader_json.h
87    settings.c
88    settings.h
89    terminator.c
90    trampoline.c
91    unknown_function_handling.c
92    unknown_function_handling.h
93    wsi.c
94    wsi.h
95    )
96
97if(WIN32)
98    list(APPEND NORMAL_LOADER_SRCS loader_windows.c dirent_on_windows.c)
99elseif(CMAKE_SYSTEM_NAME MATCHES "Linux|BSD|DragonFly|GNU")
100    list(APPEND NORMAL_LOADER_SRCS loader_linux.c)
101    target_compile_definitions(loader_specific_options INTERFACE LOADER_ENABLE_LINUX_SORT)
102endif()
103
104if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES ";")
105    set(APPLE_UNIVERSAL_BINARY ON)
106
107    # When building a universal binary we cannot enable USE_GAS
108    # Since USE_GAS assumes only 1 architecture (arm64, x64, etc).
109    set(USE_GAS OFF)
110endif()
111
112set(OPT_LOADER_SRCS dev_ext_trampoline.c phys_dev_ext.c)
113
114set(ASM_FAILURE_MSG "Support for unknown physical device and device functions is disabled due to missing the required assembly support code. \
115To support unknown functions, assembly must be added for the platform.\n")
116set(ARMASM_CMAKE_FAILURE_MSG "Support for unknown physical device and device functions is disabled due to the CMake version ${CMAKE_VERSION} \
117being older than 3.26. Please update CMake to version 3.26 or newer.\n")
118
119# Check for assembler support
120if(WIN32 AND NOT USE_GAS)
121    option(USE_MASM "Use MASM" ON)
122    if(USE_MASM AND MINGW)
123        find_program(JWASM_FOUND NAMES jwasm uasm)
124        if (JWASM_FOUND)
125            set(CMAKE_ASM_MASM_COMPILER ${JWASM_FOUND})
126            execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine OUTPUT_VARIABLE COMPILER_VERSION_OUTPUT)
127            if (COMPILER_VERSION_OUTPUT)
128                if (COMPILER_VERSION_OUTPUT MATCHES "x86_64")
129                    set(JWASM_FLAGS -win64)
130                else()
131                    # jwasm requires setting the cpu to at least 386 for setting a flat model
132                    set(JWASM_FLAGS -3 -coff)
133                endif()
134            endif()
135        endif()
136    endif()
137    if (USE_MASM)
138        if(SYSTEM_PROCESSOR MATCHES "arm")
139            if(CMAKE_VERSION VERSION_LESS "3.26.0")
140                set(ASM_FAILURE_MSG ${ARMASM_CMAKE_FAILURE_MSG})
141            else()
142                enable_language(ASM_MARMASM)
143                set(LOADER_ASM_DIALECT "MARMASM")
144            endif()
145        else()
146            enable_language(ASM_MASM)
147            set(LOADER_ASM_DIALECT "MASM")
148        endif()
149    endif()
150
151    # Test if the detected compiler actually works.
152    # Unfortunately, CMake's detection of ASM_MASM is not reliable, so we need to do this ourselves.
153    if(SYSTEM_PROCESSOR MATCHES "arm")
154        if (CMAKE_SIZEOF_VOID_P EQUAL 4)
155            file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[
156test_func FUNCTION
157    mov r0, #0
158    bx lr
159    ENDFUNC
160    END
161]=])
162        else()
163            file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[
164test_func FUNCTION
165    mov x1, 26
166    ldr x0, [x0, x1]
167    ENDFUNC
168    END
169]=])
170        endif()
171    else()
172        if (CMAKE_SIZEOF_VOID_P EQUAL 4)
173            file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[
174.model flat
175.code
176extrn _start:near
177    xor eax, eax
178    ret
179end
180]=])
181        else()
182            file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[
183.code
184extrn start:near
185    xor rax, rax
186    ret
187end
188]=])
189        endif()
190    endif ()
191
192    if(MINGW)
193        set(CMAKE_ASM_MASM_FLAGS ${CMAKE_ASM_MASM_FLAGS} ${JWASM_FLAGS})
194        set(CMAKE_ASM_MARMASM_FLAGS ${CMAKE_ASM_MARMASM_FLAGS} ${JWASM_FLAGS})
195    elseif(NOT CMAKE_CL_64 AND NOT JWASM_FOUND AND CMAKE_SIZEOF_VOID_P EQUAL 4)
196        set(CMAKE_ASM_MASM_FLAGS ${CMAKE_ASM_MASM_FLAGS} /safeseh) # /safeseh is only needed in x86
197    endif()
198
199    # try_compile does not work here due to the same reasons as static above.
200    if(SYSTEM_PROCESSOR MATCHES "arm")
201        execute_process(COMMAND ${CMAKE_ASM_MARMASM_COMPILER} ${CMAKE_ASM_MARMASM_FLAGS} ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm
202            RESULT_VARIABLE ASM_COMPILER_WORKS
203            OUTPUT_QUIET ERROR_QUIET)
204    else()
205        execute_process(COMMAND ${CMAKE_ASM_MASM_COMPILER} ${CMAKE_ASM_MASM_FLAGS} -c -Fo ${CMAKE_CURRENT_BINARY_DIR}/masm_check.obj ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm
206            RESULT_VARIABLE ASM_COMPILER_WORKS
207            OUTPUT_QUIET ERROR_QUIET)
208    endif()
209
210    # Convert the return code to a boolean
211    if(ASM_COMPILER_WORKS EQUAL 0)
212        set(ASM_COMPILER_WORKS true)
213    else()
214        set(ASM_COMPILER_WORKS false)
215    endif()
216
217    if(ASM_COMPILER_WORKS)
218        add_executable(asm_offset asm_offset.c)
219        target_link_libraries(asm_offset PRIVATE loader_specific_options)
220        # If am emulator is provided (Like Wine), or running on native, run asm_offset to generate gen_defines.asm
221        if (CMAKE_CROSSCOMPILING_EMULATOR OR NOT CMAKE_CROSSCOMPILING)
222            add_custom_command(OUTPUT gen_defines.asm DEPENDS asm_offset COMMAND asm_offset ${LOADER_ASM_DIALECT})
223        else()
224            # Forces compiler to write the intermediate asm file, needed so that we can get sizeof/offset of info out of it.
225            target_compile_options(asm_offset PRIVATE "/Fa$<TARGET_FILE_DIR:asm_offset>/asm_offset.asm" /FA)
226            # Force off optimization so that the output assembly includes all the necessary info - optimizer would get rid of it otherwise.
227            target_compile_options(asm_offset PRIVATE /Od)
228
229            find_package(Python3 REQUIRED QUIET)
230            # Run parse_asm_values.py on asm_offset's assembly file to generate the gen_defines.asm, which the asm code depends on
231            add_custom_command(TARGET asm_offset POST_BUILD
232                COMMAND Python3::Interpreter ${PROJECT_SOURCE_DIR}/scripts/parse_asm_values.py "${CMAKE_CURRENT_BINARY_DIR}/gen_defines.asm"
233                    "$<TARGET_FILE_DIR:asm_offset>/asm_offset.asm" "${LOADER_ASM_DIALECT}" "${CMAKE_C_COMPILER_ID}" "${SYSTEM_PROCESSOR}"
234                BYPRODUCTS gen_defines.asm
235            )
236        endif()
237        add_custom_target(loader_asm_gen_files DEPENDS gen_defines.asm)
238        set_target_properties(loader_asm_gen_files PROPERTIES FOLDER ${LOADER_HELPER_FOLDER})
239
240        if(SYSTEM_PROCESSOR MATCHES "arm")
241            list(APPEND OPT_LOADER_SRCS unknown_ext_chain_marmasm.asm)
242        else()
243            list(APPEND OPT_LOADER_SRCS unknown_ext_chain_masm.asm)
244        endif()
245        set(UNKNOWN_FUNCTIONS_SUPPORTED ON)
246    else()
247        message(WARNING "Could not find working ${} assembler\n${ASM_FAILURE_MSG}")
248    endif()
249elseif(UNIX OR MINGW OR (WIN32 AND USE_GAS)) # i.e.: Linux & Apple & MinGW & Windows using Clang-CL
250
251    option(USE_GAS "Use GAS" ON)
252    if(USE_GAS)
253        if (APPLE_UNIVERSAL_BINARY)
254            message(FATAL_ERROR "USE_GAS cannot be used when compiling a universal binary!")
255        endif()
256
257        enable_language(ASM)
258
259        set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}")
260        set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
261
262        if(WIN32 AND ${SYSTEM_PROCESSOR} MATCHES "arm64")
263            # In this particular case, we are using the same approach as for NASM
264            # This specifically avoids CMake issue #18889
265            execute_process(COMMAND ${CMAKE_ASM_COMPILER} ${CMAKE_ASM_FLAGS} -c -o ${CMAKE_CURRENT_BINARY_DIR}/asm_test_aarch64.obj ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_aarch64.S
266                RESULT_VARIABLE WIN_ARM64_ASSEMBLER_WORKS
267                OUTPUT_QUIET ERROR_QUIET)
268            if(WIN_ARM64_ASSEMBLER_WORKS EQUAL 0)
269                set(ASSEMBLER_WORKS true)
270            endif()
271
272            if(ASSEMBLER_WORKS)
273                list(APPEND OPT_LOADER_SRCS unknown_ext_chain_gas_aarch.S)
274            endif()
275        elseif (${SYSTEM_PROCESSOR} MATCHES "aarch64|arm64")
276            try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_aarch64.S OUTPUT_VARIABLE TRY_COMPILE_OUTPUT)
277            if(ASSEMBLER_WORKS)
278                list(APPEND OPT_LOADER_SRCS unknown_ext_chain_gas_aarch.S)
279            endif()
280        elseif (${SYSTEM_PROCESSOR} MATCHES "aarch32|armhf|armv7l|armv8l")
281            try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_aarch32.S OUTPUT_VARIABLE TRY_COMPILE_OUTPUT)
282            if(ASSEMBLER_WORKS)
283                list(APPEND OPT_LOADER_SRCS unknown_ext_chain_gas_aarch.S)
284            endif()
285        # Covers x86_64, amd64, x86, i386, i686, I386, I686
286        elseif(${SYSTEM_PROCESSOR} MATCHES "amd64|86")
287            check_include_file("cet.h" HAVE_CET_H)
288            if(HAVE_CET_H)
289                target_compile_definitions(loader_specific_options INTERFACE HAVE_CET_H)
290            endif()
291
292            try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_x86.S)
293            if(ASSEMBLER_WORKS)
294                list(APPEND OPT_LOADER_SRCS unknown_ext_chain_gas_x86.S)
295            endif()
296        endif()
297    endif()
298
299    # When compiling for x86 on x64, we can't use CMAKE_SYSTEM_PROCESSOR to determine which architecture to use,
300    # Instead, check the size of void* and if its 4, set ASM_OFFSET_SYSTEM_PROCESSOR to x86 if we aren't on arm
301    if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
302        set(ASM_OFFSET_SYSTEM_PROCESSOR ${SYSTEM_PROCESSOR}) # x86_64 or aarch64/arm64
303        string(REPLACE amd64 x86_64 ASM_OFFSET_SYSTEM_PROCESSOR "${ASM_OFFSET_SYSTEM_PROCESSOR}")
304    else()
305        if(${SYSTEM_PROCESSOR} MATCHES "86")
306            set(ASM_OFFSET_SYSTEM_PROCESSOR "x86")
307        else()
308            set(ASM_OFFSET_SYSTEM_PROCESSOR ${SYSTEM_PROCESSOR})
309        endif()
310    endif()
311
312    if(ASSEMBLER_WORKS)
313        add_executable(asm_offset asm_offset.c)
314        target_link_libraries(asm_offset loader_specific_options)
315        # If not cross compiling, run asm_offset to generage gen_defines.asm
316        if (NOT CMAKE_CROSSCOMPILING)
317            add_custom_command(OUTPUT gen_defines.asm DEPENDS asm_offset COMMAND asm_offset GAS)
318        else()
319            # Forces compiler to write the intermediate asm file, needed so that we can get sizeof/offset of info out of it.
320            # If with lto, compiler will output IR instead of asm, so we need to explicitly disable lto here.
321            if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
322                target_compile_options(asm_offset PRIVATE -save-temps=obj -fno-lto)
323            elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
324                target_compile_options(asm_offset PRIVATE -save-temps=obj -fno-lto -fno-whole-program-vtables -fno-virtual-function-elimination)
325            else()
326                target_compile_options(asm_offset PRIVATE -save-temps=obj)
327            endif()
328            if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
329                set(ASM_OFFSET_EXECUTABLE_LOCATION "$<TARGET_FILE_DIR:asm_offset>/gen_defines.asm")
330                set(ASM_OFFSET_INTERMEDIATE_LOCATION "$<TARGET_FILE_DIR:asm_offset>/CMakeFiles/asm_offset.dir/asm_offset.c.s")
331            elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang")
332                set(ASM_OFFSET_EXECUTABLE_LOCATION "$<TARGET_FILE_DIR:asm_offset>/gen_defines.asm")
333                set(ASM_OFFSET_INTERMEDIATE_LOCATION "$<TARGET_FILE_DIR:asm_offset>/CMakeFiles/asm_offset.dir/asm_offset.s")
334            elseif(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
335            # Need to use the current binary dir since the asm_offset.s file is in that folder rather than the bundle
336                set(ASM_OFFSET_EXECUTABLE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/gen_defines.asm")
337                set(ASM_OFFSET_INTERMEDIATE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/asm_offset.dir/asm_offset.s")
338            else()
339                message(FATAL_ERROR "C_COMPILER_ID not supported!")
340            endif()
341            message(STATUS "CMAKE_CROSSCOMPILING FALSE")
342
343            find_package(Python3 REQUIRED QUIET)
344            # Run parse_asm_values.py on asm_offset's assembly file to generate the gen_defines.asm, which the asm code depends on
345            add_custom_command(TARGET asm_offset POST_BUILD
346                COMMAND Python3::Interpreter ${PROJECT_SOURCE_DIR}/scripts/parse_asm_values.py "${ASM_OFFSET_EXECUTABLE_LOCATION}"
347                    "${ASM_OFFSET_INTERMEDIATE_LOCATION}" "GAS" "${CMAKE_C_COMPILER_ID}" "${ASM_OFFSET_SYSTEM_PROCESSOR}"
348                BYPRODUCTS gen_defines.asm
349            )
350        endif()
351        add_custom_target(loader_asm_gen_files DEPENDS gen_defines.asm)
352
353        if (APPLE)
354            set(MODIFY_UNKNOWN_FUNCTION_DECLS ON)
355        endif()
356        set(UNKNOWN_FUNCTIONS_SUPPORTED ON)
357    else()
358        if(USE_GAS)
359            message(WARNING "Could not find working ${ASM_OFFSET_SYSTEM_PROCESSOR} GAS assembler\n${ASM_FAILURE_MSG}")
360        else()
361            message(WARNING "Assembly sources have been disabled\n${ASM_FAILURE_MSG}")
362        endif()
363    endif()
364endif()
365
366if(UNKNOWN_FUNCTIONS_SUPPORTED)
367    list(APPEND NORMAL_LOADER_SRCS ${OPT_LOADER_SRCS})
368endif()
369
370if(WIN32)
371    # If BUILD_DLL_VERSIONINFO was set, use the loader.rc in the build dir, otherwise use the checked in file
372    set(RC_FILE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/loader.rc)
373    if (NOT "$CACHE{BUILD_DLL_VERSIONINFO}" STREQUAL "")
374        set(RC_FILE_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/loader.rc)
375    endif()
376
377    add_library(vulkan
378                SHARED
379                ${NORMAL_LOADER_SRCS}
380                ${CMAKE_CURRENT_SOURCE_DIR}/${API_TYPE}-1.def
381                ${RC_FILE_LOCATION})
382
383    target_link_libraries(vulkan PRIVATE loader_specific_options)
384
385    # when adding the suffix the import and runtime library names must be consistent
386    # mingw: libvulkan-1.dll.a / vulkan-1.dll
387    # msvc: vulkan-1.lib / vulkan-1.dll
388    set_target_properties(vulkan
389                          PROPERTIES
390                          OUTPUT_NAME ${API_TYPE}-1)
391    if(MINGW)
392        # generate the same DLL with mingw
393        set_target_properties(vulkan
394                              PROPERTIES
395                              PREFIX "")
396    endif()
397
398    if(MSVC AND ENABLE_WIN10_ONECORE)
399        target_link_libraries(vulkan PRIVATE OneCoreUAP.lib LIBCMT.LIB LIBCMTD.LIB LIBVCRUNTIME.LIB LIBUCRT.LIB)
400        set_target_properties(vulkan PROPERTIES LINK_FLAGS "/NODEFAULTLIB")
401    else()
402       target_link_libraries(vulkan PRIVATE cfgmgr32)
403    endif()
404
405else()
406    if(APPLE)
407        option(APPLE_STATIC_LOADER "Build a loader that can be statically linked. Intended for Chromium usage/testing.")
408        mark_as_advanced(APPLE_STATIC_LOADER)
409    endif()
410
411    if(APPLE_STATIC_LOADER)
412        add_library(vulkan STATIC)
413        target_compile_definitions(vulkan PRIVATE APPLE_STATIC_LOADER)
414
415        message(WARNING "The APPLE_STATIC_LOADER option has been set. Note that this will only work on MacOS and is not supported "
416                "or tested as part of the loader. Use it at your own risk.")
417    else()
418        add_library(vulkan SHARED)
419    endif()
420
421    target_sources(vulkan PRIVATE ${NORMAL_LOADER_SRCS})
422
423    set_target_properties(vulkan PROPERTIES
424        SOVERSION "1"
425        VERSION "${VULKAN_LOADER_VERSION}"
426    )
427
428    target_link_libraries(vulkan PRIVATE ${CMAKE_DL_LIBS} m Threads::Threads)
429
430    set_target_properties(vulkan PROPERTIES OUTPUT_NAME ${API_TYPE})
431
432    if (LOADER_ENABLE_ADDRESS_SANITIZER)
433        target_compile_options(vulkan PUBLIC -fsanitize=address)
434        target_link_options(vulkan PUBLIC -fsanitize=address)
435    endif()
436    if (LOADER_ENABLE_THREAD_SANITIZER)
437        target_compile_options(vulkan PUBLIC -fsanitize=thread)
438        target_link_options(vulkan PUBLIC -fsanitize=thread)
439    endif()
440    if (LOADER_ENABLE_UNDEFINED_BEHAVIOR_SANITIZER)
441        target_compile_options(vulkan PUBLIC -fsanitize=undefined)
442        target_link_options(vulkan PUBLIC -fsanitize=undefined)
443    endif()
444
445    if(APPLE)
446        find_library(COREFOUNDATION_LIBRARY NAMES CoreFoundation)
447        target_link_libraries(vulkan PRIVATE "-framework CoreFoundation")
448
449        # Build vulkan.framework
450        # Use GLOB_RECURSE to find all the header files and populate the vulkan.framework headers with them
451        # Use CONFIGURE_DEPENDS to ensure that if the header files are updated, this list is also updated
452        get_target_property(VulkanHeaders_INCLUDE_DIRS Vulkan::Headers INTERFACE_INCLUDE_DIRECTORIES)
453        file(GLOB_RECURSE CONFIGURE_DEPENDS FRAMEWORK_HEADERS ${VulkanHeaders_INCLUDE_DIRS})
454
455        add_library(vulkan-framework SHARED)
456        target_sources(vulkan-framework PRIVATE ${NORMAL_LOADER_SRCS} ${FRAMEWORK_HEADERS})
457
458        if (UNKNOWN_FUNCTIONS_SUPPORTED)
459            add_dependencies(vulkan-framework loader_asm_gen_files)
460        endif()
461
462        target_link_libraries(vulkan-framework ${CMAKE_DL_LIBS} Threads::Threads -lm "-framework CoreFoundation")
463        target_link_libraries(vulkan-framework loader_specific_options)
464
465        if (MODIFY_UNKNOWN_FUNCTION_DECLS)
466            # Modifies the names of functions as they appearin the assembly code so that the
467            # unknown function handling will work
468            target_compile_definitions(vulkan PRIVATE MODIFY_UNKNOWN_FUNCTION_DECLS)
469            target_compile_definitions(vulkan-framework PRIVATE MODIFY_UNKNOWN_FUNCTION_DECLS)
470        endif()
471
472        # The FRAMEWORK_VERSION needs to be "A" here so that Xcode code-signing works when a user adds their framework to an Xcode
473        # project and does "Sign on Copy". It would have been nicer to use "1" to denote Vulkan 1. Although Apple docs say that a
474        # framework version does not have to be "A", this part of the Apple toolchain expects it.
475        # https://forums.developer.apple.com/thread/65963
476
477        set_target_properties(vulkan-framework PROPERTIES
478            OUTPUT_NAME vulkan
479            FRAMEWORK TRUE
480            FRAMEWORK_VERSION A
481            VERSION "${VULKAN_LOADER_VERSION}"
482            MACOSX_FRAMEWORK_BUNDLE_VERSION "${VULKAN_LOADER_VERSION}"
483            MACOSX_FRAMEWORK_SHORT_VERSION_STRING "${VULKAN_LOADER_VERSION}"
484            SOVERSION "1.0.0"
485            MACOSX_FRAMEWORK_IDENTIFIER com.lunarg.vulkanFramework
486            PUBLIC_HEADER "${FRAMEWORK_HEADERS}"
487        )
488
489        # Workaround linker warning: https://github.com/KhronosGroup/Vulkan-Loader/issues/1332
490        #
491        # MACHO_CURRENT_VERSION specifically applies to the -current_version linker option which is the
492        # linker warning we are trying to address.
493        set(APPLE_VULKAN_LOADER_VERSION "${VULKAN_LOADER_VERSION_MAJOR}.${VULKAN_LOADER_VERSION_MINOR}.0")
494        set_target_properties(vulkan PROPERTIES MACHO_CURRENT_VERSION "${APPLE_VULKAN_LOADER_VERSION}")
495        set_target_properties(vulkan-framework PROPERTIES MACHO_CURRENT_VERSION "${APPLE_VULKAN_LOADER_VERSION}")
496
497        install(TARGETS vulkan-framework
498            PUBLIC_HEADER DESTINATION vulkan
499            FRAMEWORK DESTINATION loader
500        )
501    endif()
502endif()
503
504option(LOADER_USE_UNSAFE_FILE_SEARCH "Allows the loader to search in unsafe locations")
505if (LOADER_USE_UNSAFE_FILE_SEARCH)
506    target_compile_definitions(vulkan PRIVATE LOADER_USE_UNSAFE_FILE_SEARCH)
507endif()
508
509# common attributes of the vulkan library
510target_link_libraries(vulkan PRIVATE loader_specific_options)
511
512target_link_libraries(vulkan PRIVATE Vulkan::Headers)
513add_library(Vulkan::Loader ALIAS vulkan)
514
515if (UNKNOWN_FUNCTIONS_SUPPORTED)
516    target_compile_definitions(vulkan PRIVATE UNKNOWN_FUNCTIONS_SUPPORTED)
517    add_dependencies(vulkan loader_asm_gen_files)
518endif()
519
520if (BUILD_TESTS)
521    target_compile_definitions(vulkan PRIVATE SHOULD_EXPORT_TEST_FUNCTIONS)
522endif()
523
524if (APPLE_STATIC_LOADER)
525    # TLDR: This feature only exists at the request of Google for Chromium. No other project should use this!
526    message(NOTICE "Apple STATIC lib: it will be built but not installed, and vulkan.pc and VulkanLoaderConfig.cmake won't be generated!")
527    return()
528endif()
529
530# Generate CMake Configuration File (IE: VulkanLoaderConfig.cmake)
531install(TARGETS vulkan EXPORT VulkanLoaderConfig)
532set_target_properties(vulkan PROPERTIES EXPORT_NAME "Loader")
533install(EXPORT VulkanLoaderConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VulkanLoader NAMESPACE Vulkan::)
534
535# Generate CMake Version File (IE: VulkanLoaderConfigVersion.cmake)
536include(CMakePackageConfigHelpers)
537
538set(version_config "${CMAKE_CURRENT_BINARY_DIR}/generated/VulkanLoaderConfigVersion.cmake")
539write_basic_package_version_file("${version_config}" COMPATIBILITY SameMajorVersion)
540install(FILES "${version_config}" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VulkanLoader)
541
542# Generate PkgConfig File (IE: vulkan.pc)
543# NOTE: Hopefully in the future CMake can generate .pc files natively.
544# https://gitlab.kitware.com/cmake/cmake/-/issues/22621
545find_package(PkgConfig)
546if (PKG_CONFIG_FOUND)
547    if(WIN32)
548        if(MINGW)
549            set(VULKAN_LIB_SUFFIX "-1.dll")
550        else()
551            set(VULKAN_LIB_SUFFIX "-1")
552        endif()
553    endif()
554
555    # BUG: The following code will NOT work well with `cmake --install ... --prefix <dir>`
556    # due to this code relying on CMAKE_INSTALL_PREFIX being defined at configure time.
557    #
558    # NOTE: vulkan.pc essentially cover both Vulkan-Loader and Vulkan-Headers for legacy reasons.
559    if ("${CMAKE_INSTALL_PREFIX}" STREQUAL "")
560        set(CMAKE_INSTALL_LIBDIR_PC ${CMAKE_INSTALL_FULL_LIBDIR})
561        set(CMAKE_INSTALL_INCLUDEDIR_PC ${CMAKE_INSTALL_FULL_INCLUDEDIR})
562    else()
563        file(RELATIVE_PATH CMAKE_INSTALL_LIBDIR_PC ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_FULL_LIBDIR})
564        file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR_PC ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_FULL_INCLUDEDIR})
565    endif()
566    configure_file("vulkan.pc.in" "vulkan.pc" @ONLY)
567    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/vulkan.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" RENAME "${API_TYPE}.pc")
568endif()
569
570if (CODE_COVERAGE)
571    target_code_coverage(vulkan AUTO ALL)
572endif()
573