From 67bf3d1381b1faf59e87001d6156ba4e21cada14 Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Mon, 29 Dec 2025 21:22:36 -0500
Subject: [PATCH] [cmake] refactor: shared/static handling

This significantly redoes the way shared and static libraries are
handled. Now, it's controlled by two options: `SPIRV_TOOLS_BUILD_STATIC`
and `SPIRV_TOOLS_BUILD_SHARED`.

The default configuration (no `BUILD_SHARED_LIBS` set, options left at
default) is to build shared ONLY if this is the master project, or
static ONLY if this is a subproject (e.g. FetchContent, CPM.cmake). Also
I should note that static-only (i.e. no shared) is now a supported
target, this is done because projects including it as a submodule e.g.
on Android or Windows may prefer this.

Now the shared/static handling:
- static ON, shared OFF: Only generates `.a` libraries.
- static ON, shared ON: Generates `.a` libraries, but also
  `libSPIRV-Tools.so`
- static OFF, shared ON: Only generates `.so` libraries.

Notable TODOs:
- SPIRV-Tools-shared.pc seems redundant--how should we handle which one
  to use in the case of distributions that distribute both types (MSYS2
  for instance)?
  * *Note: pkgconfig sucks at this and usually just leaves it up to the
    user, so the optimal solution may indeed be doing absolutely
    nothing.* CMake is unaffected :)
- use namespaces in the CMake config files pleaaaaase

This is going to change things a good bit for package maintainers, but
cest la vie. It's for the greater good, I promise.

Signed-off-by: crueter <crueter@eden-emu.dev>
---
 CMakeLists.txt        | 108 +++++++++++++++++++++++++-----------------
 source/CMakeLists.txt |  62 ++++++++++++------------
 2 files changed, 94 insertions(+), 76 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4d843b4d2f..07201f690f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,15 @@
 
 cmake_minimum_required(VERSION 3.22.1)
 
+# master project detection--useful for FetchContent/submodule inclusion
+set(master_project OFF)
+set(subproject ON)
+
+if (NOT DEFINED PROJECT_NAME)
+  set(master_project ON)
+  set(subproject OFF)
+endif()
+
 project(spirv-tools)
 
 # Avoid a bug in CMake 3.22.1. By default it will set -std=c++11 for
@@ -135,46 +144,49 @@ if (DEFINED SPIRV_TOOLS_EXTRA_DEFINITIONS)
   add_definitions(${SPIRV_TOOLS_EXTRA_DEFINITIONS})
 endif()
 
-# Library build setting definitions:
-#
-# * SPIRV_TOOLS_BUILD_STATIC - ON or OFF - Defaults to ON.
-#   If enabled the following targets will be created:
-#     ${SPIRV_TOOLS}-static - STATIC library.
-#                             Has full public symbol visibility.
-#     ${SPIRV_TOOLS}-shared - SHARED library.
-#                             Has default-hidden symbol visibility.
-#     ${SPIRV_TOOLS}        - will alias to one of above, based on BUILD_SHARED_LIBS.
-#   If disabled the following targets will be created:
-#     ${SPIRV_TOOLS}        - either STATIC or SHARED based on SPIRV_TOOLS_LIBRARY_TYPE.
-#                             Has full public symbol visibility.
-#     ${SPIRV_TOOLS}-shared - SHARED library.
-#                             Has default-hidden symbol visibility.
-#
-# * SPIRV_TOOLS_LIBRARY_TYPE - SHARED or STATIC.
-#   Specifies the library type used for building SPIRV-Tools libraries.
-#   Defaults to SHARED when BUILD_SHARED_LIBS=1, otherwise STATIC.
-#
-# * SPIRV_TOOLS_FULL_VISIBILITY - "${SPIRV_TOOLS}-static" or "${SPIRV_TOOLS}"
-#   Evaluates to the SPIRV_TOOLS target library name that has no hidden symbols.
-#   This is used by internal targets for accessing symbols that are non-public.
-#   Note this target provides no API stability guarantees.
-#
-# Ideally, all of these will go away - see https://github.com/KhronosGroup/SPIRV-Tools/issues/3909.
-option(ENABLE_EXCEPTIONS_ON_MSVC "Build SPIRV-TOOLS with c++ exceptions enabled in MSVC" ON)
-option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS}-static target. ${SPIRV_TOOLS} will alias to ${SPIRV_TOOLS}-static or ${SPIRV_TOOLS}-shared based on BUILD_SHARED_LIBS" ON)
-if(SPIRV_TOOLS_BUILD_STATIC)
-  set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static)
+# If BUILD_SHARED_LIBS is undefined, set it based on whether we are
+# the master project or a subproject
+if (NOT DEFINED BUILD_SHARED_LIBS)
+  set(BUILD_SHARED_LIBS ${master_project})
+endif()
+
+if (BUILD_SHARED_LIBS)
+  set(static_default OFF)
+else()
+  set(static_default ON)
+endif()
+
+option(SPIRV_TOOLS_BUILD_SHARED "Build ${SPIRV_TOOLS} as a shared library"
+       ${BUILD_SHARED_LIBS})
+option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS} as a static library"
+       ${static_default})
+
+# Avoid conflict between the dll import library and
+# the static library (thanks microsoft)
+if(CMAKE_STATIC_LIBRARY_PREFIX STREQUAL "" AND
+   CMAKE_STATIC_LIBRARY_SUFFIX STREQUAL ".lib")
+    set(SPIRV_TOOLS_STATIC_LIBNAME "${SPIRV_TOOLS}-static")
+else()
+    set(SPIRV_TOOLS_STATIC_LIBNAME "${SPIRV_TOOLS}")
+endif()
+
+if (SPIRV_TOOLS_BUILD_STATIC)
+  # If building a static library at all, always build other libraries as static,
+  # and link to the static SPIRV-Tools library.
   set(SPIRV_TOOLS_LIBRARY_TYPE "STATIC")
-else(SPIRV_TOOLS_BUILD_STATIC)
-  set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS})
-  if (NOT DEFINED SPIRV_TOOLS_LIBRARY_TYPE)
-      if(BUILD_SHARED_LIBS)
-        set(SPIRV_TOOLS_LIBRARY_TYPE "SHARED")
-      else()
-        set(SPIRV_TOOLS_LIBRARY_TYPE "STATIC")
-      endif()
-  endif()
-endif(SPIRV_TOOLS_BUILD_STATIC)
+  set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static)
+elseif (SPIRV_TOOLS_BUILD_SHARED)
+  # If only building a shared library, link other libraries to the
+  # shared library. Also, other libraries should be shared
+  set(SPIRV_TOOLS_LIBRARY_TYPE "SHARED")
+  set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-shared)
+else()
+  message(FATAL_ERROR "You must set one of "
+    "SPIRV_TOOLS_BUILD_STATIC or SPIRV_TOOLS_BUILD_SHARED!")
+endif()
+
+option(ENABLE_EXCEPTIONS_ON_MSVC
+    "Build SPIRV-TOOLS with C++ exceptions enabled in MSVC" ON)
 
 function(spvtools_default_compile_options TARGET)
   target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS})
@@ -372,7 +384,7 @@ if (NOT "${SPIRV_SKIP_TESTS}")
 endif()
 
 set(SPIRV_LIBRARIES "-lSPIRV-Tools-opt -lSPIRV-Tools -lSPIRV-Tools-link")
-set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared")
+set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools")
 
 # Build pkg-config file
 # Use a first-class target so it's regenerated when relevant files are updated.
@@ -388,7 +400,12 @@ add_custom_command(
                       -DSPIRV_LIBRARIES=${SPIRV_LIBRARIES}
                       -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake
         DEPENDS "CHANGES" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake")
-add_custom_command(
+
+set(pc_files ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc)
+
+# TODO(crueter): remove?
+if (SPIRV_TOOLS_BUILD_SHARED)
+  add_custom_command(
         OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc
         COMMAND ${CMAKE_COMMAND}
                       -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES
@@ -400,9 +417,12 @@ add_custom_command(
                       -DSPIRV_SHARED_LIBRARIES=${SPIRV_SHARED_LIBRARIES}
                       -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake
         DEPENDS "CHANGES" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake")
-add_custom_target(spirv-tools-pkg-config 
-        ALL 
-        DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc)
+  set(pc_files ${pc_files} ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc)
+endif()
+
+add_custom_target(spirv-tools-pkg-config
+        ALL
+        DEPENDS ${pc_files})
 
 # Install pkg-config file
 if (ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index bfa1e661bc..fd3712c70c 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -337,49 +337,44 @@ function(spirv_tools_default_target_options target)
   )
   set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools libraries")
   spvtools_check_symbol_exports(${target})
-  add_dependencies(${target} spirv-tools-build-version core_tables extinst_tables)
+  add_dependencies(${target}
+    spirv-tools-build-version core_tables extinst_tables)
 endfunction()
 
-# Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and
-# unlike the other SPIRV_TOOLS target, defaults to hidden symbol visibility.
-add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
-if (SPIRV_TOOLS_USE_MIMALLOC)
-  target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static)
+if (SPIRV_TOOLS_BUILD_SHARED)
+  add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
+  if (SPIRV_TOOLS_USE_MIMALLOC)
+    target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static)
+  endif()
+
+  set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES
+    OUTPUT_NAME "${SPIRV_TOOLS}")
+  spirv_tools_default_target_options(${SPIRV_TOOLS}-shared)
+
+  target_compile_definitions(${SPIRV_TOOLS}-shared
+    PRIVATE SPIRV_TOOLS_IMPLEMENTATION
+    PUBLIC SPIRV_TOOLS_SHAREDLIB)
+
+  list(APPEND SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-shared)
 endif()
-spirv_tools_default_target_options(${SPIRV_TOOLS}-shared)
-set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES CXX_VISIBILITY_PRESET hidden)
-target_compile_definitions(${SPIRV_TOOLS}-shared
-  PRIVATE SPIRV_TOOLS_IMPLEMENTATION
-  PUBLIC SPIRV_TOOLS_SHAREDLIB
-)
 
 if(SPIRV_TOOLS_BUILD_STATIC)
   add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES})
   if (SPIRV_TOOLS_USE_MIMALLOC AND SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)
     target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static)
   endif()
+
   spirv_tools_default_target_options(${SPIRV_TOOLS}-static)
-  # The static target does not have the '-static' suffix.
-  set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}")
-
-  # Create the "${SPIRV_TOOLS}" target as an alias to either "${SPIRV_TOOLS}-static"
-  # or "${SPIRV_TOOLS}-shared" depending on the value of BUILD_SHARED_LIBS.
-  if(BUILD_SHARED_LIBS)
-    add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-shared)
-  else()
-    add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static)
-  endif()
+  set_target_properties(${SPIRV_TOOLS}-static PROPERTIES
+    OUTPUT_NAME "${SPIRV_TOOLS_STATIC_LIBNAME}")
 
-  set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared)
-else()
-  add_library(${SPIRV_TOOLS} ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_SOURCES})
-  if (SPIRV_TOOLS_USE_MIMALLOC)
-    target_link_libraries(${SPIRV_TOOLS} PRIVATE mimalloc-static)
-  endif()
-  spirv_tools_default_target_options(${SPIRV_TOOLS})
-  set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared)
+  list(APPEND SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static)
 endif()
 
+# Create the "SPIRV-Tools" target as an alias to either "SPIRV-Tools-static"
+# or "SPIRV-Tools-shared" depending on the value of SPIRV_TOOLS_BUILD_SHARED.
+add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS_FULL_VISIBILITY})
+
 if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
   find_library(LIBRT rt)
   if(LIBRT)
@@ -390,14 +385,17 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
 endif()
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+  if (SPIRV_TOOLS_USE_MIMALLOC AND
+    (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
     list(APPEND SPIRV_TOOLS_TARGETS mimalloc-static)
   endif()
   install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets)
   export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake)
 
   spvtools_config_package_dir(${SPIRV_TOOLS} PACKAGE_DIR)
-  install(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake DESTINATION ${PACKAGE_DIR})
+  install(EXPORT ${SPIRV_TOOLS}Targets
+    FILE ${SPIRV_TOOLS}Target.cmake
+    DESTINATION ${PACKAGE_DIR})
 
   # Special config file for root library compared to other libs.
   file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake
