From 2edad2f31c40a49e9a94ec4091884447b4478f92 Mon Sep 17 00:00:00 2001 From: Matt Jolly 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 --- 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 " message and properly set _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 - $ - $ - PRIVATE - $ -) +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 - $<$:/MP /WX /W4> - $<$>:-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 + $ + $ + PRIVATE + $ + ) + + target_link_libraries(${_target} + PUBLIC + Qt::Core + Qt::Widgets + Qt::Svg + ) + + target_compile_options(${_target} + PRIVATE + $<$:/MP /WX /W4> + $<$>:-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