From 2edad2f31c40a49e9a94ec4091884447b4478f92 Mon Sep 17 00:00:00 2001
From: Matt Jolly <kangie@gentoo.org>
Date: Wed, 27 Aug 2025 18:39:54 +1000
Subject: [PATCH 1/3] Enable building as a shared library, add pkg-config

Downstream distributions often discourage bundling code, and build in
sandboxed environments that can make fetching sources to build static
libraries that are not vendored problematic.

Provide a shared library option as well as pkg-config files for `UNIX`
OSes so that systems that don't have CMake (e.g. binary distributions)
are able to link against the shared library.

`-DQLEMENTINE_BUILD_STATIC` can be used to build the static library
alongside the shared library, while it is built by default if
`-DBUILD_SHARED_LIBS` is not set.

Signed-off-by: Matt Jolly <kangie@gentoo.org>
---
 CMakeLists.txt         |   3 +
 CMakePresets.json      |  17 ++++
 cmake/config.cmake.in  |  10 +++
 cmake/qlementine.pc.in |  12 +++
 docs/usage.md          |  41 ++++++++-
 lib/CMakeLists.txt     | 198 +++++++++++++++++++++++++++++++++--------
 6 files changed, 240 insertions(+), 41 deletions(-)
 create mode 100644 cmake/qlementine.pc.in

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e26cf38..81d0528 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,6 +36,9 @@ if(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
 endif()
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
 
+# Include GNUInstallDirs for standard install directories
+include(GNUInstallDirs)
+
 # Find Qt.
 find_package(Qt6 REQUIRED COMPONENTS Core Widgets Svg)
 qt_standard_project_setup(
diff --git a/CMakePresets.json b/CMakePresets.json
index d7ce017..b9b8b9a 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -56,6 +56,23 @@
         "lhs": "${hostSystemName}",
         "rhs": "Linux"
       }
+    },
+    {
+      "name": "linux-shared",
+      "displayName": "Linux (Shared Library)",
+      "description": "Makefile for Linux with shared library",
+      "generator": "Unix Makefiles",
+      "binaryDir": "${sourceDir}/_build_shared",
+      "cacheVariables": {
+        "BUILD_SHARED_LIBS": true,
+        "QLEMENTINE_SANDBOX": true,
+        "QLEMENTINE_SHOWCASE": true
+      },
+      "condition": {
+        "type": "equals",
+        "lhs": "${hostSystemName}",
+        "rhs": "Linux"
+      }
     }
   ],
   "buildPresets": [
diff --git a/cmake/config.cmake.in b/cmake/config.cmake.in
index 9425122..901aed7 100644
--- a/cmake/config.cmake.in
+++ b/cmake/config.cmake.in
@@ -6,3 +6,13 @@ include(CMakeFindDependencyMacro)
 find_dependency(Qt6 REQUIRED COMPONENTS Core Widgets Svg)
 
 check_required_components(@PROJECT_NAME@)
+
+# Set version and config path for find_package_handle_standard_args.
+# These are normally set by find_package() after sourcing the config file,
+# but we need them available now since we call FPHSA from within.
+set(@PROJECT_NAME@_VERSION "@PROJECT_VERSION@")
+set(@PROJECT_NAME@_CONFIG "${CMAKE_CURRENT_LIST_FILE}")
+
+# Produce the standard "Found <pkg>" message and properly set <pkg>_FOUND.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(@PROJECT_NAME@ CONFIG_MODE)
diff --git a/cmake/qlementine.pc.in b/cmake/qlementine.pc.in
new file mode 100644
index 0000000..e4832cd
--- /dev/null
+++ b/cmake/qlementine.pc.in
@@ -0,0 +1,12 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+
+Name: @PROJECT_NAME@
+Description: @PROJECT_DESCRIPTION@
+Version: @PROJECT_VERSION@
+URL: https://github.com/oclero/qlementine
+Requires: Qt6Core Qt6Widgets Qt6Svg
+Libs: -L${libdir} -l@PROJECT_NAME@
+Cflags: -I${includedir}
diff --git a/docs/usage.md b/docs/usage.md
index 34f10b4..eba0824 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -2,20 +2,55 @@
 
 ## Installation
 
-1. Add the library as a dependency. Here is an example with CMake FetchContent. You may add it with another way such as vcpkg or from a regular installation.
+### Option 1: FetchContent (Build from Source)
 
-   ```bash
+1. Add the library as a dependency using CMake FetchContent:
+
+   ```cmake
    include(FetchContent)
    FetchContent_Declare(Qlementine GIT_REPOSITORY "https://github.com/oclero/qlementine.git")
    FetchContent_MakeAvailable(Qlementine)
    ```
 
-2. Link with the library in CMake.
+2. Link with the library:
 
    ```cmake
    target_link_libraries(your_project qlementine)
    ```
 
+### Option 2: Find Installed Library
+
+If Qlementine is already installed on your system (via package manager, vcpkg, or manual installation):
+
+#### CMake
+
+```cmake
+find_package(qlementine REQUIRED)
+target_link_libraries(your_project qlementine::qlementine)
+```
+
+#### Meson
+
+```meson
+qlementine_dep = dependency('qlementine')
+executable('your_project',
+  sources: ['main.cpp'],
+  dependencies: [qlementine_dep]
+)
+```
+
+### Option 3: vcpkg
+
+```bash
+vcpkg install qlementine
+```
+
+Then in CMake:
+```cmake
+find_package(qlementine CONFIG REQUIRED)
+target_link_libraries(your_project PRIVATE qlementine::qlementine)
+```
+
 ## Usage in code
 
 Define the `QStyle` on your `QApplication`.
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 2508fb9..7d16007 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -112,48 +112,154 @@ set(RESOURCES
   resources/qlementine_font_roboto.qrc
 )
 
-# Create target.
-qt_add_library(${PROJECT_NAME} STATIC
-  ${HEADERS}
-  ${SOURCES}
-  ${RESOURCES}
-)
-include(CMakePackageConfigHelpers)
+option(BUILD_SHARED_LIBS "Build shared libraries instead of static" OFF)
+option(QLEMENTINE_BUILD_STATIC "Build static library alongside shared (when BUILD_SHARED_LIBS=ON)" OFF)
 
-target_include_directories(${PROJECT_NAME}
-  PUBLIC
-    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
-    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
-  PRIVATE
-    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
-)
+set(_build_shared ${BUILD_SHARED_LIBS})
+set(_build_static ON)  # Always build static unless only shared is requested
 
-target_link_libraries(${PROJECT_NAME}
-  PUBLIC
-    Qt::Core
-    Qt::Widgets
-    Qt::Svg
-)
+if(BUILD_SHARED_LIBS AND NOT QLEMENTINE_BUILD_STATIC)
+  set(_build_static OFF)  # Only shared
+endif()
 
-set_target_properties(${PROJECT_NAME}
-  PROPERTIES
-    OUTPUT_NAME ${PROJECT_NAME}
-    PROJECT_LABEL ${PROJECT_NAME}
-    FOLDER lib
-    SOVERSION ${PROJECT_VERSION_MAJOR}
-    VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
-    DEBUG_POSTFIX _debug
-    CMAKE_AUTORCC ON
-    CMAKE_AUTOMOC ON
-    CMAKE_AUTOUIC ON
-)
+# Initialize target variables
+set(_targets_to_build)
+set(_main_target_name ${PROJECT_NAME})
+
+# Create shared library target
+if(_build_shared)
+  set(_shared_target "${PROJECT_NAME}_shared")
+  if(NOT _build_static)
+    # If only shared, use the main project name
+    set(_shared_target ${PROJECT_NAME})
+  endif()
+
+  qt_add_library(${_shared_target} SHARED
+    ${HEADERS}
+    ${SOURCES}
+    ${RESOURCES}
+  )
+
+  # Set shared library specific properties
+  set_target_properties(${_shared_target}
+    PROPERTIES
+      OUTPUT_NAME ${PROJECT_NAME}
+      PROJECT_LABEL "${PROJECT_NAME}_Shared"
+      FOLDER lib
+      SOVERSION ${PROJECT_VERSION_MAJOR}
+      VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+      DEBUG_POSTFIX _debug
+      CMAKE_AUTORCC ON
+      CMAKE_AUTOMOC ON
+      CMAKE_AUTOUIC ON
+      # Use C++ visibility attributes for symbol export
+      CXX_VISIBILITY_PRESET default
+      VISIBILITY_INLINES_HIDDEN ON
+  )
 
-target_compile_options(${PROJECT_NAME}
-  PRIVATE
-    $<$<CXX_COMPILER_ID:MSVC>:/MP /WX /W4>
-    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror>
+  list(APPEND _targets_to_build ${_shared_target})
+
+  # Create alias for shared library
+  if(_build_static)
+    add_library(${PROJECT_NAME}::shared ALIAS ${_shared_target})
+  else()
+    # If only shared, this is the main target
+    add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${_shared_target})
+    set(_main_target_name ${_shared_target})
+  endif()
+endif()
+
+# Create static library target
+if(_build_static)
+  set(_static_target "${PROJECT_NAME}_static")
+  if(NOT _build_shared)
+    # If only static, use the main project name
+    set(_static_target ${PROJECT_NAME})
+  endif()
+
+  qt_add_library(${_static_target} STATIC
+    ${HEADERS}
+    ${SOURCES}
+    ${RESOURCES}
+  )
+
+  # Set static library specific properties
+  set_target_properties(${_static_target}
+    PROPERTIES
+      OUTPUT_NAME ${PROJECT_NAME}
+      PROJECT_LABEL "${PROJECT_NAME}_Static"
+      FOLDER lib
+      DEBUG_POSTFIX _debug
+      CMAKE_AUTORCC ON
+      CMAKE_AUTOMOC ON
+      CMAKE_AUTOUIC ON
+  )
+
+  list(APPEND _targets_to_build ${_static_target})
+
+  # Create alias for static library
+  if(_build_shared)
+    add_library(${PROJECT_NAME}::static ALIAS ${_static_target})
+  else()
+    # If only static, this is the main target
+    add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${_static_target})
+    set(_main_target_name ${_static_target})
+  endif()
+endif()
+
+# Create the main alias that apps will link against
+# This always points to the "preferred" target (shared if available, otherwise static)
+if(_build_shared)
+  if(NOT TARGET ${PROJECT_NAME})
+    # Create an alias from the plain name to the shared target
+    add_library(${PROJECT_NAME} ALIAS ${_shared_target})
+  endif()
+else()
+  # For static-only builds, the plain name already exists as the target
+  if(NOT TARGET ${PROJECT_NAME})
+    add_library(${PROJECT_NAME} ALIAS ${_static_target})
+  endif()
+endif()
+# Configure all targets (shared and/or static)
+foreach(_target IN LISTS _targets_to_build)
+  if(TARGET ${_target})
+    message(STATUS "Configuring target: ${_target}")
+
+    target_include_directories(${_target}
+      PUBLIC
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+      PRIVATE
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+    )
+
+    target_link_libraries(${_target}
+      PUBLIC
+        Qt::Core
+        Qt::Widgets
+        Qt::Svg
+    )
+
+    target_compile_options(${_target}
+      PRIVATE
+        $<$<CXX_COMPILER_ID:MSVC>:/MP /WX /W4>
+        $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror>
+    )
+  endif()
+endforeach()
+
+# Create source groups.
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}
+  FILES
+    ${HEADERS}
+    ${SOURCES}
 )
 
+# Select correct startup project in Visual Studio.
+if(WIN32)
+  set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
+endif()
+
 # Create source groups.
 source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}
   FILES
@@ -166,7 +272,8 @@ if(WIN32)
   set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
 endif()
 
-# Install target
+# CMake package configuration
+include(CMakePackageConfigHelpers)
 configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/config.cmake.in"
   "${CMAKE_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake"
   INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
@@ -175,7 +282,22 @@ write_basic_package_version_file("${CMAKE_BINARY_DIR}/cmake/${PROJECT_NAME}Confi
   VERSION "${PROJECT_VERSION}"
   COMPATIBILITY AnyNewerVersion)
 
-install(TARGETS ${PROJECT_NAME}
+# Generate pkgconfig file on supported platforms
+if(UNIX OR MINGW)
+  configure_file(
+    "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/qlementine.pc.in"
+    "${CMAKE_CURRENT_BINARY_DIR}/qlementine.pc"
+    @ONLY
+  )
+
+  install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qlementine.pc"
+    DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
+    COMPONENT Development
+  )
+endif()
+
+# Install targets
+install(TARGETS ${_targets_to_build}
   EXPORT "${PROJECT_NAME}Targets"
   LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
   ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
-- 
2.52.0

