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    # Build dev_ext_trampoline.c and unknown_ext_chain.c with /O2 to allow tail-call optimization.
36    # Setup two CMake targets (loader-norm and loader-opt) for the different compilation flags.
37    # ~~~
38    set(MODIFIED_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG})
39
40    string(REPLACE "/Od" "/O2" MODIFIED_C_FLAGS_DEBUG ${MODIFIED_C_FLAGS_DEBUG})
41    string(REPLACE "/Ob0" "/Ob2" MODIFIED_C_FLAGS_DEBUG ${MODIFIED_C_FLAGS_DEBUG})
42    string(REGEX REPLACE "/RTC." "" MODIFIED_C_FLAGS_DEBUG ${MODIFIED_C_FLAGS_DEBUG})  #remove run-time error checks
43
44    separate_arguments(MODIFIED_C_FLAGS_DEBUG WINDOWS_COMMAND ${MODIFIED_C_FLAGS_DEBUG})
45
46    # ~~~
47    # Only generate the loader.rc file with CMake if BUILD_DLL_VERSIONINFO was set.
48    # This feature is for the Vulkan Runtime build
49    # Otherwise rely on the checked in loader.rc from the python script
50    # ~~~
51    if (NOT "$CACHE{BUILD_DLL_VERSIONINFO}" STREQUAL "")
52        string(TIMESTAMP CURRENT_YEAR "%Y")
53        set(LOADER_CUR_COPYRIGHT_YEAR "${CURRENT_YEAR}")
54        set(LOADER_RC_VERSION "$CACHE{BUILD_DLL_VERSIONINFO}")
55        set(LOADER_VER_FILE_VERSION_STR "\"${LOADER_RC_VERSION}\"")
56        set(LOADER_VER_FILE_DESCRIPTION_STR "\"Vulkan Loader\"")
57
58        # RC file wants the value of FILEVERSION to separated by commas
59        string(REPLACE "." ", " LOADER_VER_FILE_VERSION "${LOADER_RC_VERSION}")
60
61        # Configure the file to include the versioning info
62        # Place it in the build directory - the GN build will use the checked in file
63        configure_file(loader.rc.in ${CMAKE_CURRENT_BINARY_DIR}/loader.rc)
64    endif()
65else()
66    # Used to make alloca() and secure_getenv() available
67    target_compile_definitions(loader_specific_options INTERFACE _GNU_SOURCE)
68    if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
69        target_compile_definitions(loader_specific_options INTERFACE __BSD_VISIBLE=1)
70    endif()
71    check_include_file("alloca.h" HAVE_ALLOCA_H)
72    if(HAVE_ALLOCA_H)
73        target_compile_definitions(loader_specific_options INTERFACE HAVE_ALLOCA_H)
74    endif()
75endif()
76
77set(NORMAL_LOADER_SRCS
78    allocation.c
79    allocation.h
80    cJSON.c
81    cJSON.h
82    debug_utils.c
83    debug_utils.h
84    extension_manual.c
85    extension_manual.h
86    loader_environment.c
87    loader_environment.h
88    gpa_helper.c
89    gpa_helper.h
90    loader.c
91    loader.h
92    log.c
93    log.h
94    settings.c
95    settings.h
96    terminator.c
97    trampoline.c
98    unknown_function_handling.c
99    unknown_function_handling.h
100    wsi.c
101    wsi.h
102    )
103
104if(WIN32)
105    list(APPEND NORMAL_LOADER_SRCS loader_windows.c dirent_on_windows.c)
106elseif(CMAKE_SYSTEM_NAME MATCHES "Linux|BSD|DragonFly|GNU")
107    list(APPEND NORMAL_LOADER_SRCS loader_linux.c)
108    target_compile_definitions(loader_specific_options INTERFACE LOADER_ENABLE_LINUX_SORT)
109endif()
110
111if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES ";")
112    set(APPLE_UNIVERSAL_BINARY ON)
113
114    # When building a universal binary we cannot enable USE_GAS
115    # Since USE_GAS assumes only 1 architecture (arm64, x64, etc).
116    set(USE_GAS OFF)
117endif()
118
119set(OPT_LOADER_SRCS dev_ext_trampoline.c phys_dev_ext.c)
120
121# Check for assembler support
122set(ASM_FAILURE_MSG "The build will fall back on building with C code\n")
123set(ASM_FAILURE_MSG "${ASM_FAILURE_MSG}Note that this may be unsafe, as the C code requires tail-call optimizations to remove")
124set(ASM_FAILURE_MSG "${ASM_FAILURE_MSG} the stack frame for certain calls. If the compiler does not do this, then unknown device")
125set(ASM_FAILURE_MSG "${ASM_FAILURE_MSG} extensions will suffer from a corrupted stack.")
126
127if (APPLE_UNIVERSAL_BINARY)
128    set(USE_ASSEMBLY_FALLBACK ON)
129elseif(WIN32)
130    option(USE_MASM "Use MASM" ON)
131    if(USE_MASM AND MINGW)
132        find_program(JWASM_FOUND NAMES jwasm uasm)
133        if (JWASM_FOUND)
134            set(CMAKE_ASM_MASM_COMPILER ${JWASM_FOUND})
135            execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine OUTPUT_VARIABLE COMPILER_VERSION_OUTPUT)
136            if (COMPILER_VERSION_OUTPUT)
137                if (COMPILER_VERSION_OUTPUT MATCHES "x86_64")
138                    set(JWASM_FLAGS -win64)
139                else()
140                    # jwasm requires setting the cpu to at least 386 for setting a flat model
141                    set(JWASM_FLAGS -3 -coff)
142                endif()
143            endif()
144        endif()
145    endif()
146    if (USE_MASM)
147        enable_language(ASM_MASM)
148    endif()
149    # Test if the detected compiler actually works.
150    # Unfortunately, CMake's detection of ASM_MASM is not reliable, so we need to do this ourselves.
151    if (CMAKE_SIZEOF_VOID_P EQUAL 4)
152        file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[
153.model flat
154.code
155extrn _start:near
156    xor eax, eax
157    ret
158end
159]=])
160    else()
161        file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[
162.code
163extrn start:near
164    xor rax, rax
165    ret
166end
167]=])
168    endif ()
169    if(MINGW)
170        set(CMAKE_ASM_MASM_FLAGS ${CMAKE_ASM_MASM_FLAGS} ${JWASM_FLAGS})
171    elseif(NOT CMAKE_CL_64 AND NOT JWASM_FOUND)
172        set(CMAKE_ASM_MASM_FLAGS ${CMAKE_ASM_MASM_FLAGS} /safeseh)
173    endif()
174    # try_compile does not work here due to the same reasons as static above.
175    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
176        RESULT_VARIABLE CMAKE_ASM_MASM_COMPILER_WORKS
177        OUTPUT_QUIET ERROR_QUIET)
178    # Convert the return code to a boolean
179    if(CMAKE_ASM_MASM_COMPILER_WORKS EQUAL 0)
180        set(CMAKE_ASM_MASM_COMPILER_WORKS true)
181    else()
182        set(CMAKE_ASM_MASM_COMPILER_WORKS false)
183    endif()
184    if(CMAKE_ASM_MASM_COMPILER_WORKS)
185        add_executable(asm_offset asm_offset.c)
186        target_link_libraries(asm_offset PRIVATE loader_specific_options)
187        # If am emulator is provided (Like Wine), or running on native, run asm_offset to generate gen_defines.asm
188        if (CMAKE_CROSSCOMPILING_EMULATOR OR NOT CMAKE_CROSSCOMPILING)
189            add_custom_command(OUTPUT gen_defines.asm DEPENDS asm_offset COMMAND asm_offset MASM)
190        else()
191            # Forces compiler to write the intermediate asm file, needed so that we can get sizeof/offset of info out of it.
192            target_compile_options(asm_offset PRIVATE "/Fa$<TARGET_FILE_DIR:asm_offset>/asm_offset.asm" /FA)
193            # Force off optimization so that the output assembly includes all the necessary info - optimizer would get rid of it otherwise.
194            target_compile_options(asm_offset PRIVATE /Od)
195
196            find_package(Python3 REQUIRED QUIET)
197            # Run parse_asm_values.py on asm_offset's assembly file to generate the gen_defines.asm, which the asm code depends on
198            add_custom_command(TARGET asm_offset POST_BUILD
199                COMMAND Python3::Interpreter ${PROJECT_SOURCE_DIR}/scripts/parse_asm_values.py "${CMAKE_CURRENT_BINARY_DIR}/gen_defines.asm"
200                    "$<TARGET_FILE_DIR:asm_offset>/asm_offset.asm" "MASM" "${CMAKE_C_COMPILER_ID}" "${CMAKE_SYSTEM_PROCESSOR}"
201                BYPRODUCTS gen_defines.asm
202            )
203        endif()
204        add_custom_target(loader_asm_gen_files DEPENDS gen_defines.asm)
205        set_target_properties(loader_asm_gen_files PROPERTIES FOLDER ${LOADER_HELPER_FOLDER})
206
207        add_library(loader-unknown-chain OBJECT unknown_ext_chain_masm.asm)
208        target_link_libraries(loader-unknown-chain Vulkan::Headers)
209        target_include_directories(loader-unknown-chain PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
210        add_dependencies(loader-unknown-chain loader_asm_gen_files)
211    else()
212        set(USE_ASSEMBLY_FALLBACK ON)
213        message(WARNING "Could not find working MASM assembler\n${ASM_FAILURE_MSG}")
214    endif()
215elseif(UNIX) # i.e.: Linux & Apple
216
217    option(USE_GAS "Use GAS" ON)
218    if(USE_GAS)
219        if (APPLE_UNIVERSAL_BINARY)
220            message(FATAL_ERROR "USE_GAS cannot be used when compiling a universal binary!")
221        endif()
222
223        enable_language(ASM)
224
225        set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}")
226        set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
227
228        if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64|arm64")
229            try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_aarch64.S)
230            if(ASSEMBLER_WORKS)
231                set(OPT_LOADER_SRCS ${OPT_LOADER_SRCS} unknown_ext_chain_gas_aarch64.S)
232            endif()
233        # Covers x86_64, amd64, x86, i386, i686, I386, I686
234        elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "amd64|86")
235            check_include_file("cet.h" HAVE_CET_H)
236            if(HAVE_CET_H)
237                target_compile_definitions(loader_specific_options INTERFACE HAVE_CET_H)
238            endif()
239
240            try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_x86.S)
241            if(ASSEMBLER_WORKS)
242                set(OPT_LOADER_SRCS ${OPT_LOADER_SRCS} unknown_ext_chain_gas_x86.S)
243            endif()
244        endif()
245    endif()
246
247    # When compiling for x86 on x64, we can't use CMAKE_SYSTEM_PROCESSOR to determine which architecture to use,
248    # Instead, check the size of void* and if its 4, set ASM_OFFSET_SYSTEM_PROCESSOR to x86
249    # Note - there is no 32 bit arm assembly code, so this only applies to x86 currently.
250    if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
251        set(ASM_OFFSET_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) # x86_64 or aarch64/arm64
252    else()
253        set(ASM_OFFSET_SYSTEM_PROCESSOR "x86")
254    endif()
255
256    if(ASSEMBLER_WORKS)
257        add_executable(asm_offset asm_offset.c)
258        target_link_libraries(asm_offset loader_specific_options)
259        # If not cross compiling, run asm_offset to generage gen_defines.asm
260        if (NOT CMAKE_CROSSCOMPILING)
261            add_custom_command(OUTPUT gen_defines.asm DEPENDS asm_offset COMMAND asm_offset GAS)
262        else()
263            # Forces compiler to write the intermediate asm file, needed so that we can get sizeof/offset of info out of it.
264            target_compile_options(asm_offset PRIVATE -save-temps=obj)
265            if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
266                set(ASM_OFFSET_EXECUTABLE_LOCATION "$<TARGET_FILE_DIR:asm_offset>/gen_defines.asm")
267                set(ASM_OFFSET_INTERMEDIATE_LOCATION "$<TARGET_FILE_DIR:asm_offset>/CMakeFiles/asm_offset.dir/asm_offset.c.s")
268            elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang")
269                set(ASM_OFFSET_EXECUTABLE_LOCATION "$<TARGET_FILE_DIR:asm_offset>/gen_defines.asm")
270                set(ASM_OFFSET_INTERMEDIATE_LOCATION "$<TARGET_FILE_DIR:asm_offset>/CMakeFiles/asm_offset.dir/asm_offset.s")
271            elseif(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
272            # Need to use the current binary dir since the asm_offset.s file is in that folder rather than the bundle
273                set(ASM_OFFSET_EXECUTABLE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/gen_defines.asm")
274                set(ASM_OFFSET_INTERMEDIATE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/asm_offset.dir/asm_offset.s")
275            else()
276                message(FATAL_ERROR "C_COMPILER_ID not supported!")
277            endif()
278
279            find_package(Python3 REQUIRED QUIET)
280            # Run parse_asm_values.py on asm_offset's assembly file to generate the gen_defines.asm, which the asm code depends on
281            add_custom_command(TARGET asm_offset POST_BUILD
282                COMMAND Python3::Interpreter ${PROJECT_SOURCE_DIR}/scripts/parse_asm_values.py "${ASM_OFFSET_EXECUTABLE_LOCATION}"
283                    "${ASM_OFFSET_INTERMEDIATE_LOCATION}" "GAS" "${CMAKE_C_COMPILER_ID}" "${ASM_OFFSET_SYSTEM_PROCESSOR}"
284                BYPRODUCTS gen_defines.asm
285            )
286        endif()
287        add_custom_target(loader_asm_gen_files DEPENDS gen_defines.asm)
288
289        if (APPLE)
290            set(MODIFY_UNKNOWN_FUNCTION_DECLS ON)
291        endif()
292    else()
293        set(USE_ASSEMBLY_FALLBACK ON)
294        if(USE_GAS)
295            message(WARNING "Could not find working ${ASM_OFFSET_SYSTEM_PROCESSOR} GAS assembler\n${ASM_FAILURE_MSG}")
296        else()
297            message(WARNING "Assembly sources have been disabled\n${ASM_FAILURE_MSG}")
298        endif()
299    endif()
300endif()
301
302if(USE_ASSEMBLY_FALLBACK)
303    add_custom_target(loader_asm_gen_files)
304    if (MSVC)
305        add_library(loader-unknown-chain OBJECT unknown_ext_chain.c)
306        target_link_libraries(loader-unknown-chain loader_specific_options)
307        set_target_properties(loader-unknown-chain PROPERTIES CMAKE_C_FLAGS_DEBUG "${MODIFIED_C_FLAGS_DEBUG}")
308    else()
309        set(OPT_LOADER_SRCS ${OPT_LOADER_SRCS} unknown_ext_chain.c)
310        set_source_files_properties(${OPT_LOADER_SRCS} PROPERTIES COMPILE_FLAGS -O)
311    endif()
312endif()
313
314if(WIN32)
315    add_library(loader-opt STATIC ${OPT_LOADER_SRCS})
316    target_link_libraries(loader-opt PUBLIC loader_specific_options)
317    add_dependencies(loader-opt loader_asm_gen_files)
318    set_target_properties(loader-opt PROPERTIES CMAKE_C_FLAGS_DEBUG "${MODIFIED_C_FLAGS_DEBUG}")
319
320    # If BUILD_DLL_VERSIONINFO was set, use the loader.rc in the build dir, otherwise use the checked in file
321    set(RC_FILE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/loader.rc)
322    if (NOT "$CACHE{BUILD_DLL_VERSIONINFO}" STREQUAL "")
323        set(RC_FILE_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/loader.rc)
324    endif()
325
326    set(LOADER_UNKNOWN_CHAIN_LIBRARY $<$<TARGET_EXISTS:loader-unknown-chain>:$<TARGET_OBJECTS:loader-unknown-chain>>)
327
328    add_library(vulkan
329                SHARED
330                ${NORMAL_LOADER_SRCS}
331                ${LOADER_UNKNOWN_CHAIN_LIBRARY}
332                ${CMAKE_CURRENT_SOURCE_DIR}/${API_TYPE}-1.def
333                ${RC_FILE_LOCATION})
334
335    target_link_libraries(vulkan PRIVATE loader_specific_options loader-opt)
336
337    # when adding the suffix the import and runtime library names must be consistent
338    # mingw: libvulkan-1.dll.a / vulkan-1.dll
339    # msvc: vulkan-1.lib / vulkan-1.dll
340    set_target_properties(vulkan
341                          PROPERTIES
342                          OUTPUT_NAME ${API_TYPE}-1)
343    if(MINGW)
344        # generate the same DLL with mingw
345        set_target_properties(vulkan
346                              PROPERTIES
347                              PREFIX "")
348    endif()
349
350    if(MSVC AND ENABLE_WIN10_ONECORE)
351        target_link_libraries(vulkan PRIVATE OneCoreUAP.lib LIBCMT.LIB LIBCMTD.LIB LIBVCRUNTIME.LIB LIBUCRT.LIB)
352        set_target_properties(vulkan PROPERTIES LINK_FLAGS "/NODEFAULTLIB")
353    else()
354       target_link_libraries(vulkan PRIVATE cfgmgr32)
355    endif()
356
357    add_dependencies(vulkan loader_asm_gen_files)
358
359else()
360    if(APPLE)
361        option(APPLE_STATIC_LOADER "Build a loader that can be statically linked. Intended for Chromium usage/testing.")
362        mark_as_advanced(APPLE_STATIC_LOADER)
363    endif()
364
365    if(APPLE_STATIC_LOADER)
366        add_library(vulkan STATIC)
367        target_compile_definitions(vulkan PRIVATE APPLE_STATIC_LOADER)
368
369        message(WARNING "The APPLE_STATIC_LOADER option has been set. Note that this will only work on MacOS and is not supported "
370                "or tested as part of the loader. Use it at your own risk.")
371    else()
372        add_library(vulkan SHARED)
373    endif()
374
375    target_sources(vulkan PRIVATE ${NORMAL_LOADER_SRCS} ${OPT_LOADER_SRCS})
376
377    add_dependencies(vulkan loader_asm_gen_files)
378
379    set_target_properties(vulkan PROPERTIES
380        SOVERSION "1"
381        VERSION "${VULKAN_LOADER_VERSION}"
382    )
383
384    set(THREADS_PREFER_PTHREAD_FLAG ON)
385    find_package(Threads REQUIRED)
386
387    target_link_libraries(vulkan PRIVATE ${CMAKE_DL_LIBS} m Threads::Threads)
388
389    set_target_properties(vulkan PROPERTIES OUTPUT_NAME ${API_TYPE})
390
391    if (LOADER_ENABLE_ADDRESS_SANITIZER)
392        target_compile_options(vulkan PUBLIC -fsanitize=address)
393        target_link_options(vulkan PUBLIC -fsanitize=address)
394    endif()
395    if (LOADER_ENABLE_THREAD_SANITIZER)
396        target_compile_options(vulkan PUBLIC -fsanitize=thread)
397        target_link_options(vulkan PUBLIC -fsanitize=thread)
398    endif()
399    if (LOADER_ENABLE_UNDEFINED_BEHAVIOR_SANITIZER)
400        target_compile_options(vulkan PUBLIC -fsanitize=undefined)
401        target_link_options(vulkan PUBLIC -fsanitize=undefined)
402    endif()
403
404    if(APPLE)
405        find_library(COREFOUNDATION_LIBRARY NAMES CoreFoundation)
406        target_link_libraries(vulkan PRIVATE "-framework CoreFoundation")
407
408        # Build vulkan.framework
409        # Use GLOB_RECURSE to find all the header files and populate the vulkan.framework headers with them
410        # Use CONFIGURE_DEPENDS to ensure that if the header files are updated, this list is also updated
411        get_target_property(VulkanHeaders_INCLUDE_DIRS Vulkan::Headers INTERFACE_INCLUDE_DIRECTORIES)
412        file(GLOB_RECURSE CONFIGURE_DEPENDS FRAMEWORK_HEADERS ${VulkanHeaders_INCLUDE_DIRS})
413
414        add_library(vulkan-framework SHARED)
415        target_sources(vulkan-framework PRIVATE ${NORMAL_LOADER_SRCS} ${OPT_LOADER_SRCS} ${FRAMEWORK_HEADERS})
416
417        add_dependencies(vulkan-framework loader_asm_gen_files)
418        target_link_libraries(vulkan-framework ${CMAKE_DL_LIBS} Threads::Threads -lm "-framework CoreFoundation")
419        target_link_libraries(vulkan-framework loader_specific_options)
420
421        if (MODIFY_UNKNOWN_FUNCTION_DECLS)
422            # Modifies the names of functions as they appearin the assembly code so that the
423            # unknown function handling will work
424            target_compile_definitions(vulkan PRIVATE MODIFY_UNKNOWN_FUNCTION_DECLS)
425            target_compile_definitions(vulkan-framework PRIVATE MODIFY_UNKNOWN_FUNCTION_DECLS)
426        endif()
427
428        # The FRAMEWORK_VERSION needs to be "A" here so that Xcode code-signing works when a user adds their framework to an Xcode
429        # project and does "Sign on Copy". It would have been nicer to use "1" to denote Vulkan 1. Although Apple docs say that a
430        # framework version does not have to be "A", this part of the Apple toolchain expects it.
431        # https://forums.developer.apple.com/thread/65963
432
433        set_target_properties(vulkan-framework PROPERTIES
434            OUTPUT_NAME vulkan
435            FRAMEWORK TRUE
436            FRAMEWORK_VERSION A
437            VERSION "${VULKAN_LOADER_VERSION}"
438            SOVERSION "1.0.0"
439            MACOSX_FRAMEWORK_IDENTIFIER com.lunarg.vulkanFramework
440            PUBLIC_HEADER "${FRAMEWORK_HEADERS}"
441        )
442
443        # Workaround linker warning: https://github.com/KhronosGroup/Vulkan-Loader/issues/1332
444        #
445        # MACHO_CURRENT_VERSION specifically applies to the -current_version linker option which is the
446        # linker warning we are trying to address.
447        set(APPLE_VULKAN_LOADER_VERSION "${VULKAN_LOADER_VERSION_MAJOR}.${VULKAN_LOADER_VERSION_MINOR}.0")
448        set_target_properties(vulkan PROPERTIES MACHO_CURRENT_VERSION "${APPLE_VULKAN_LOADER_VERSION}")
449        set_target_properties(vulkan-framework PROPERTIES MACHO_CURRENT_VERSION "${APPLE_VULKAN_LOADER_VERSION}")
450
451        install(TARGETS vulkan-framework
452            PUBLIC_HEADER DESTINATION vulkan
453            FRAMEWORK DESTINATION loader
454        )
455    endif()
456endif()
457
458option(LOADER_USE_UNSAFE_FILE_SEARCH "Allows the loader to search in unsafe locations")
459if (LOADER_USE_UNSAFE_FILE_SEARCH)
460    target_compile_definitions(vulkan PRIVATE LOADER_USE_UNSAFE_FILE_SEARCH)
461endif()
462
463# common attributes of the vulkan library
464target_link_libraries(vulkan PRIVATE loader_specific_options)
465
466target_link_libraries(vulkan PRIVATE Vulkan::Headers)
467add_library(Vulkan::Loader ALIAS vulkan)
468
469if (APPLE_STATIC_LOADER)
470    # TLDR: This feature only exists at the request of Google for Chromium. No other project should use this!
471    message(NOTICE "Apple STATIC lib: it will be built but not installed, and vulkan.pc and VulkanLoaderConfig.cmake won't be generated!")
472    return()
473endif()
474
475# Generate CMake Configuration File (IE: VulkanLoaderConfig.cmake)
476install(TARGETS vulkan EXPORT VulkanLoaderConfig)
477set_target_properties(vulkan PROPERTIES EXPORT_NAME "Loader")
478install(EXPORT VulkanLoaderConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VulkanLoader NAMESPACE Vulkan::)
479
480# Generate CMake Version File (IE: VulkanLoaderConfigVersion.cmake)
481include(CMakePackageConfigHelpers)
482
483set(version_config "${CMAKE_CURRENT_BINARY_DIR}/generated/VulkanLoaderConfigVersion.cmake")
484write_basic_package_version_file("${version_config}" COMPATIBILITY SameMajorVersion)
485install(FILES "${version_config}" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VulkanLoader)
486
487# Generate PkgConfig File (IE: vulkan.pc)
488# NOTE: Hopefully in the future CMake can generate .pc files natively.
489# https://gitlab.kitware.com/cmake/cmake/-/issues/22621
490find_package(PkgConfig)
491if (PKG_CONFIG_FOUND)
492    if(WIN32)
493        if(MINGW)
494            set(VULKAN_LIB_SUFFIX "-1.dll")
495        else()
496            set(VULKAN_LIB_SUFFIX "-1")
497        endif()
498    endif()
499
500    # BUG: The following code will NOT work well with `cmake --install ... --prefix <dir>`
501    # due to this code relying on CMAKE_INSTALL_PREFIX being defined at configure time.
502    #
503    # NOTE: vulkan.pc essentially cover both Vulkan-Loader and Vulkan-Headers for legacy reasons.
504    if ("${CMAKE_INSTALL_PREFIX}" STREQUAL "")
505        set(CMAKE_INSTALL_LIBDIR_PC ${CMAKE_INSTALL_FULL_LIBDIR})
506        set(CMAKE_INSTALL_INCLUDEDIR_PC ${CMAKE_INSTALL_FULL_INCLUDEDIR})
507    else()
508        file(RELATIVE_PATH CMAKE_INSTALL_LIBDIR_PC ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_FULL_LIBDIR})
509        file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR_PC ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_FULL_INCLUDEDIR})
510    endif()
511    configure_file("vulkan.pc.in" "vulkan.pc" @ONLY)
512    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/vulkan.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" RENAME "${API_TYPE}.pc")
513endif()
514