# One big CMakeLists.txt because otherwise clutter spreads too easily. cmake_minimum_required(VERSION 3.28) project(Dawn VERSION 1.0.0 DESCRIPTION "Colour-managed image browser" HOMEPAGE_URL "https://git.janouch.name/p/dawn" LANGUAGES C CXX ) if (APPLE) enable_language(OBJCXX) endif() set(DAWN_NAMESPACE "name.janouch.Dawn") set(dawn_packaging "${PROJECT_SOURCE_DIR}/packaging") set(CPACK_PACKAGE_VENDOR "The Dawn Authors") set(CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch ") set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # A plain build-root path would collide with CMake-produced subdirs. set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") if (WIN32) # Installation still won't work because it's tailored to msys2-configure.sh. set(DAWN_MSYS2_PREFIX "$ENV{MSYSTEM_PREFIX}" CACHE PATH "MSYS2 prefix") endif() if (WIN32 AND CMAKE_CROSSCOMPILING) set(DAWN_MSYS2_PREFIX "${PROJECT_BINARY_DIR}/ucrt64") list(APPEND CMAKE_PREFIX_PATH "${DAWN_MSYS2_PREFIX}") list(APPEND CMAKE_FIND_ROOT_PATH "${DAWN_MSYS2_PREFIX}") set(CMAKE_CROSSCOMPILING_EMULATOR "${CMAKE_COMMAND}" -E env "WINEPATH=${DAWN_MSYS2_PREFIX}/bin" wine) # The host's libstdc++ may export __cxa_thread_atexit, MSYS2's doesn't. # Take it from the CRT, as dn.exe already does through Qt's entry point. link_libraries(mingw32) # Relativize prefixes, and bar pkg-config from looking up host libraries set(ENV{PKG_CONFIG_SYSROOT_DIR} "${PROJECT_BINARY_DIR}") set(DAWN_MSYS2_PCPATH "${DAWN_MSYS2_PREFIX}/share/pkgconfig:${DAWN_MSYS2_PREFIX}/lib/pkgconfig") set(ENV{PKG_CONFIG_PATH} "${DAWN_MSYS2_PCPATH}") set(ENV{PKG_CONFIG_LIBDIR} "${DAWN_MSYS2_PCPATH}") # Qt tries to find qtpaths without an .exe file extension # (see Qt6CoreMacros.cmake). This ultimately means broken windeployqt. set(QT_NO_QTPATHS_DEPLOYMENT_WARNING ON) endif() if (APPLE) # Homebrew targets the host system. With LSMinimumSystemVersion, # Launch Services explains the refusal; dyld would not. if (NOT CMAKE_OSX_DEPLOYMENT_TARGET) execute_process(COMMAND sw_vers -productVersion OUTPUT_VARIABLE dawn_macos_version OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT dawn_macos_version MATCHES "^([0-9]+)") message(FATAL_ERROR "Cannot tell what macOS version this is") endif() set(CMAKE_OSX_DEPLOYMENT_TARGET "${CMAKE_MATCH_1}.0") endif() # Build tree: bin/dn.app/Contents/MacOS vs bin/libdn.dylib. # Install: MacOS/dn, Frameworks/libdn.dylib (macdeployqt leaves # @rpath/libdn.dylib; Homebrew dylibs become @executable_path). set(CMAKE_INSTALL_RPATH "@loader_path;@loader_path/../Frameworks") else() set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) endif() set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" "${PROJECT_SOURCE_DIR}/submodules/liberty/cmake" ) # --- Dependencies ------------------------------------------------------------- find_package(PkgConfig REQUIRED) find_package(Vulkan REQUIRED) find_package(Intl REQUIRED) # Maybe we need a newer version, just not to build. find_package(Qt6 6.8 REQUIRED COMPONENTS Gui OPTIONAL_COMPONENTS WaylandClient) find_program(AWK NAMES awk REQUIRED) include(lxdr-generate) include(embed-shader) set(DN_WITH_WAYLAND "${Qt6WaylandClient_FOUND}") if (DN_WITH_WAYLAND) pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET wayland-client) # For QPlatformNativeInterface: Qt exposes no other way to the toplevel. # Debian's qt6-base-private-dev adds Qt6::GuiPrivate while loading Qt6Gui, # but deliberately ships no standalone Qt6GuiPrivateConfig.cmake. if (NOT TARGET Qt6::GuiPrivate) find_package(Qt6 6.8 REQUIRED COMPONENTS GuiPrivate) endif() endif() pkg_check_modules(JPEG REQUIRED IMPORTED_TARGET libjpeg) pkg_check_modules(TURBOJPEG REQUIRED IMPORTED_TARGET libturbojpeg>=3.0) pkg_check_modules(WEBP REQUIRED IMPORTED_TARGET libwebp) pkg_check_modules(WEBPDEMUX REQUIRED IMPORTED_TARGET libwebpdemux) pkg_check_modules(WEBPMUX REQUIRED IMPORTED_TARGET libwebpmux) pkg_check_modules(LIBPNG IMPORTED_TARGET libpng) # Qt6 SVG cannot render our icons correctly, so we hard depend on resvg. pkg_check_modules(RESVG REQUIRED IMPORTED_TARGET resvg) macro(dawn_optional_pkg stem doc module) pkg_check_modules(${stem} IMPORTED_TARGET ${module}) option(DAWN_WITH_${stem} "${doc}" ${${stem}_FOUND}) if(DAWN_WITH_${stem} AND NOT ${stem}_FOUND) message(FATAL_ERROR "DAWN_WITH_${stem}=ON but ${module} was not found") endif() endmacro() dawn_optional_pkg(LIBHEIF "HEIF/AVIF support" libheif) dawn_optional_pkg(LIBJXL "JPEG XL support" libjxl) dawn_optional_pkg(LIBRAW "LibRaw support" libraw_r) dawn_optional_pkg(LIBRSVG "SVG via librsvg" librsvg-2.0) dawn_optional_pkg(LIBTIFF "TIFF support" libtiff-4) dawn_optional_pkg(LIBWMF "WMF support" libwmf) dawn_optional_pkg(OPENJPEG "JPEG 2000 support" libopenjp2) dawn_optional_pkg(POPPLER "PDF via Poppler" poppler-cpp) dawn_optional_pkg(XCURSOR "Xcursor support" xcursor) if (LIBRAW_FOUND) get_property(options TARGET PkgConfig::LIBRAW PROPERTY INTERFACE_LINK_OPTIONS) # "Libs:" include -fopenmp, which Clang links as a second OpenMP runtime # (-lomp) although we don't use it when linked dynamically. # Homebrew spells it "-Xpreprocessor -fopenmp", and a lone -Xpreprocessor # would swallow whatever follows, such as -o. list(REMOVE_ITEM options -Xpreprocessor -fopenmp) set_property(TARGET PkgConfig::LIBRAW PROPERTY INTERFACE_LINK_OPTIONS "${options}") endif() find_program(CARGO_EXECUTABLE cargo) if (CARGO_EXECUTABLE) set(CARGO_FOUND TRUE) endif() option(DAWN_WITH_DNRS "Rust image loader" ${CARGO_FOUND}) if (DAWN_WITH_DNRS AND NOT CARGO_FOUND) message(FATAL_ERROR "DAWN_WITH_DNRS=ON but cargo was not found") endif() # jxrlib ships no pkg-config file, and its headers include one another # by bare name, so the whole package directory has to go on the include path. find_path(JXRLIB_INCLUDE_DIR JXRGlue.h PATH_SUFFIXES jxrlib) find_library(JXRLIB_GLUE_LIBRARY jxrglue) find_library(JXRLIB_CORE_LIBRARY jpegxr) if (JXRLIB_INCLUDE_DIR AND JXRLIB_GLUE_LIBRARY AND JXRLIB_CORE_LIBRARY) set(JXRLIB_FOUND TRUE) endif() option(DAWN_WITH_JXRLIB "JPEG XR support" ${JXRLIB_FOUND}) if (DAWN_WITH_JXRLIB AND NOT JXRLIB_FOUND) message(FATAL_ERROR "DAWN_WITH_JXRLIB=ON but jxrlib was not found") endif() if (APPLE) # These don't make much sense to disable, but it's more regular with them. option(DAWN_WITH_IMAGEIO "ImageIO loader" ON) option(DAWN_WITH_CGPDF "Core Graphics PDF page rendering" ON) endif() include(FetchContent) include(CTest) # The GPL fast float plugin improves performance a ton, # but distributions usually omit it. option(DAWN_BUNDLED_LCMS2 "Use a fast bundled lcms2 instead of the system one" ON) set(DAWN_WITH_LCMS2_FAST_FLOAT FALSE) set(DAWN_WITH_LCMS2_THREADED FALSE) add_library(dawn-lcms2 INTERFACE) if (NOT DAWN_BUNDLED_LCMS2) pkg_check_modules(LCMS2 REQUIRED IMPORTED_TARGET lcms2) target_link_libraries(dawn-lcms2 INTERFACE PkgConfig::LCMS2) # Either lcms2 has been built to include the plugin, or it hasn't. find_path(LCMS2_FAST_FLOAT_INCLUDE_DIR lcms2_fast_float.h HINTS ${LCMS2_INCLUDE_DIRS} NO_DEFAULT_PATH) find_library(LCMS2_FAST_FLOAT_LIB NAMES lcms2_fast_float HINTS ${LCMS2_LIBRARY_DIRS} NO_DEFAULT_PATH) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Lcms2FastFloat REQUIRED_VARS LCMS2_FAST_FLOAT_LIB LCMS2_FAST_FLOAT_INCLUDE_DIR) if (Lcms2FastFloat_FOUND) set(DAWN_WITH_LCMS2_FAST_FLOAT TRUE) target_link_libraries(dawn-lcms2 INTERFACE "${LCMS2_FAST_FLOAT_LIB}") target_include_directories(dawn-lcms2 INTERFACE "${LCMS2_FAST_FLOAT_INCLUDE_DIR}") endif() else() set(LCMS2_BUILD_SHARED OFF CACHE BOOL "" FORCE) set(LCMS2_BUILD_STATIC ON CACHE BOOL "" FORCE) set(LCMS2_BUILD_TOOLS OFF CACHE BOOL "" FORCE) set(LCMS2_WITH_FASTFLOAT ON CACHE BOOL "" FORCE) set(LCMS2_WITH_THREADED_PLUGIN ON CACHE BOOL "" FORCE) set(LCMS2_BUILD_TESTS "${BUILD_TESTING}" CACHE BOOL "Build tests (testbed)") # Bundled static lib; do not ship headers/.a with the app package. FetchContent_Declare(lcms2 URL https://github.com/mm2/Little-CMS/archive/refs/tags/lcms2.19.1.tar.gz URL_HASH SHA256=267705e278e2f7c2fb886c259dadcbaeb2be52748bcbc71c79f08aacacb7a709 DOWNLOAD_EXTRACT_TIMESTAMP TRUE EXCLUDE_FROM_ALL ) FetchContent_MakeAvailable(lcms2) set(DAWN_WITH_LCMS2_FAST_FLOAT TRUE) set(DAWN_WITH_LCMS2_THREADED TRUE) set_target_properties(lcms2 lcms2_fast_float lcms2_threaded PROPERTIES POSITION_INDEPENDENT_CODE ON) # Upstream keeps plugin includes PRIVATE. target_include_directories(lcms2_fast_float PUBLIC $) target_include_directories(lcms2_threaded PUBLIC $) # FIXME: LibRaw may transitively link to system lcms2. # What gives? Clear in LINK_INTERFACE_LIBRARIES? target_link_libraries(dawn-lcms2 INTERFACE lcms2 lcms2_fast_float lcms2_threaded) # Have CTest not fail. if (TARGET testcms) set_target_properties(testcms PROPERTIES EXCLUDE_FROM_ALL FALSE) endif() endif() option(DAWN_WITH_JPEG_QS "Bundled JPEG Quant Smooth" ON) if (DAWN_WITH_JPEG_QS) FetchContent_Declare(jpegqs URL https://github.com/ilyakurdyukov/jpeg-quantsmooth/archive/refs/tags/1.20260122.tar.gz URL_HASH SHA256=7dcbaa7d994511a03dba845aa218a25f9c8e70c0bcb0800ff61ae3d92dd50c3d DOWNLOAD_EXTRACT_TIMESTAMP TRUE PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/cmake/jpegqs/CMakeLists.txt" /CMakeLists.txt COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/cmake/jpegqs/include" /include ) FetchContent_MakeAvailable(jpegqs) if (NOT TARGET jpegqs) message(FATAL_ERROR "Bundled jpegqs did not create target 'jpegqs'") endif() endif() option(DAWN_WITH_WIP "Expose unfinished subapplications" OFF) # Qt states its features in a generated header rather than in its CMake package. include(CheckCXXSourceCompiles) block() set(CMAKE_REQUIRED_LIBRARIES Qt6::Gui) check_cxx_source_compiles([[ #include #if !QT_CONFIG(accessibility) #error Qt has no accessibility support #endif #if defined Q_OS_UNIX && !defined Q_OS_MACOS #if !QT_CONFIG(accessibility_atspi_bridge) #error Qt has no AT-SPI bridge #endif #endif int main() { return 0; } ]] dawn_qt_accessibility) check_cxx_source_compiles([[ #include #if !QT_CONFIG(xcb) #error Qt has no X11 support #endif int main() { return 0; } ]] dawn_qt_xcb) endblock() option(DN_WITH_ACCESSIBILITY "Expose the UI to assistive technologies" "${dawn_qt_accessibility}") if (DN_WITH_ACCESSIBILITY AND NOT dawn_qt_accessibility) message(FATAL_ERROR "DN_WITH_ACCESSIBILITY=ON but this Qt build lacks " "accessibility support (on Linux it also needs the AT-SPI bridge)") elseif (NOT DN_WITH_ACCESSIBILITY) message(WARNING "Building without accessibility: screen readers and " "external UI tests will see nothing of dn's interface") endif() # --- Build configuration ------------------------------------------------------ # CMAKE_INSTALL_FULL_LOCALEDIR goes into dawn-config.h. include(GNUInstallDirs) configure_file( "${PROJECT_SOURCE_DIR}/dawn-config.h.in" "${PROJECT_BINARY_DIR}/dawn-config.h") include_directories("${PROJECT_SOURCE_DIR}") # Keep diagnostics after bundled dependencies so that they don't apply to them. if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_compile_options(-Wall -Wextra -Wpedantic -Wconversion -Wformat=2 -Wundef -Wcast-align -Wno-unused-parameter -Wno-missing-field-initializers) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-Wshadow=local) else() add_compile_options(-Wshadow-uncaptured-local) endif() add_compile_options( $<$:-Wextra-semi> $<$:-Woverloaded-virtual> $<$:-Wnon-virtual-dtor>) endif() # --- libdn -------------------------------------------------------------------- add_library(libdn SHARED libdn/cmm.cpp libdn/gettext.cpp libdn/ipc.cpp libdn/ipc-instance.cpp libdn/ipc-rpc.cpp libdn/libdn.cpp libdn/load-icns.cpp libdn/load-jpeg.cpp libdn/load-ora.cpp libdn/load-psd.cpp libdn/load-resvg.cpp libdn/load-tiff-ep.cpp libdn/load-webp.cpp libdn/load-wuffs.cpp libdn/scale-engine.cpp libdn/scale-scaler.cpp libdn/thumb-scaler.cpp libdn/vk-device.cpp ) find_package(Threads REQUIRED) target_link_libraries(libdn PUBLIC dawn-lcms2 Intl::Intl Vulkan::Vulkan PRIVATE Threads::Threads PkgConfig::JPEG PkgConfig::TURBOJPEG PkgConfig::RESVG PkgConfig::WEBP PkgConfig::WEBPDEMUX PkgConfig::WEBPMUX ) if (WIN32) target_sources(libdn PRIVATE libdn/config-windows.cpp libdn/ipc-loop-windows.cpp libdn/ipc-shm-windows.cpp libdn/ipc-windows.cpp ) target_link_libraries(libdn PRIVATE advapi32) elseif (APPLE) target_sources(libdn PRIVATE libdn/config-macos.mm libdn/ipc-loop-unix.cpp libdn/ipc-shm-unix.cpp libdn/ipc-unix.cpp ) else() target_sources(libdn PRIVATE libdn/config-unix.cpp libdn/ipc-loop-unix.cpp libdn/ipc-shm-unix.cpp libdn/ipc-unix.cpp ) endif() macro(dawn_loader stem suffix) if (DAWN_WITH_${stem}) target_sources(libdn PRIVATE libdn/load-${suffix}.cpp) target_link_libraries(libdn PRIVATE PkgConfig::${stem}) endif() endmacro() dawn_loader(LIBHEIF heif) dawn_loader(LIBJXL jxl) dawn_loader(LIBRAW libraw) dawn_loader(LIBRSVG librsvg) dawn_loader(LIBTIFF tiff) dawn_loader(LIBWMF libwmf) dawn_loader(OPENJPEG openjpeg) dawn_loader(POPPLER poppler) dawn_loader(XCURSOR xcursor) if (DAWN_WITH_JPEG_QS) target_link_libraries(libdn PRIVATE jpegqs) endif() if (DAWN_WITH_JXRLIB) target_sources(libdn PRIVATE libdn/load-jxr.cpp) target_include_directories(libdn SYSTEM PRIVATE "${JXRLIB_INCLUDE_DIR}") target_link_libraries(libdn PRIVATE "${JXRLIB_GLUE_LIBRARY}" "${JXRLIB_CORE_LIBRARY}") endif() if (DAWN_WITH_DNRS) add_subdirectory(libdnrs) target_sources(libdn PRIVATE libdn/load-dnrs.cpp) target_link_libraries(libdn PRIVATE libdnrs) # Rust would otherwise export rust_eh_personality, which resvg exports # as well, and ELF interposition would cross the two runtimes. set(exclude "${CMAKE_STATIC_LIBRARY_PREFIX}dnrs${CMAKE_STATIC_LIBRARY_SUFFIX}") set(exclude "LINKER:--exclude-libs,${exclude}") include(CheckLinkerFlag) check_linker_flag(CXX "${exclude}" DAWN_HAVE_EXCLUDE_LIBS) if (DAWN_HAVE_EXCLUDE_LIBS) target_link_options(libdn PRIVATE "${exclude}") endif() endif() if (APPLE) find_library(DAWN_COREFOUNDATION_FRAMEWORK CoreFoundation REQUIRED) find_library(DAWN_COREGRAPHICS_FRAMEWORK CoreGraphics REQUIRED) find_library(DAWN_IMAGEIO_FRAMEWORK ImageIO REQUIRED) target_link_libraries(libdn PRIVATE "${DAWN_COREFOUNDATION_FRAMEWORK}" "${DAWN_COREGRAPHICS_FRAMEWORK}") if (DAWN_WITH_IMAGEIO) target_sources(libdn PRIVATE libdn/load-imageio.mm) target_link_libraries(libdn PRIVATE "${DAWN_IMAGEIO_FRAMEWORK}") endif() if (DAWN_WITH_CGPDF) target_sources(libdn PRIVATE libdn/load-cgpdf.mm) endif() endif() add_custom_command( OUTPUT "${PROJECT_BINARY_DIR}/tiff-tables.h" COMMAND ${AWK} -f "${PROJECT_SOURCE_DIR}/libdn/tiff-tables.awk" "${PROJECT_SOURCE_DIR}/libdn/tiff-tables.db" > "${PROJECT_BINARY_DIR}/tiff-tables.h" DEPENDS "${PROJECT_SOURCE_DIR}/libdn/tiff-tables.awk" "${PROJECT_SOURCE_DIR}/libdn/tiff-tables.db" VERBATIM ) target_sources(libdn PRIVATE "${PROJECT_BINARY_DIR}/tiff-tables.h") # Generated LXDR headers; ipc-instance.cpp binds instance to the RPC core, # and tests/test-lxdr.cpp checks them against Encoder/Decoder. The types # every service shares are generated once, into the enclosing namespace. lxdr_generate( ${PROJECT_BINARY_DIR}/generated/ipc/common.lxdr.hpp dawn::ipc Common ${PROJECT_SOURCE_DIR}/ipc/common.lxdr ) target_sources(libdn PRIVATE "${PROJECT_BINARY_DIR}/generated/ipc/common.lxdr.hpp") foreach (ipc Instance Imaged Thumbd) string(TOLOWER "${ipc}" lower) lxdr_generate( ${PROJECT_BINARY_DIR}/generated/ipc/${lower}.lxdr.hpp dawn::ipc::${lower} ${ipc} ${PROJECT_SOURCE_DIR}/ipc/${lower}.lxdr EXTERN ${PROJECT_SOURCE_DIR}/ipc/common.lxdr ) target_sources(libdn PRIVATE "${PROJECT_BINARY_DIR}/generated/ipc/${lower}.lxdr.hpp") endforeach() target_include_directories(libdn PUBLIC $ $ PRIVATE ${PROJECT_SOURCE_DIR}/submodules/wuffs-mirror-release-c/release/c ) # Vulkan scale shaders dawn_embed_shader(libdn fullscreen.vert fullscreen_vert) dawn_embed_shader(libdn scale-h.frag scale_h_bilinear -DDN_FILTER=1) dawn_embed_shader(libdn scale-v.frag scale_v_bilinear -DDN_FILTER=1) dawn_embed_shader(libdn scale-2d.frag scale_2d_nearest -DDN_FILTER=0) dawn_embed_shader(libdn scale-2d.frag scale_2d_bilinear -DDN_FILTER=1) dawn_embed_shader(libdn scale-2d.frag scale_2d_nohalo -DDN_FILTER=2) dawn_embed_shader(libdn thumb-scale-h.comp thumb_scale_h) dawn_embed_shader(libdn thumb-scale-v.comp thumb_scale_v) dawn_embed_shader(libdn thumb-reduce.comp thumb_reduce) set_target_properties(libdn PROPERTIES OUTPUT_NAME dn SOVERSION 0 ) # --- Other targets ------------------------------------------------------------ if (LIBPNG_FOUND) foreach (target dnimage dnthumb) add_executable(${target} tools/${target}.cpp tools/png-io.cpp) target_link_libraries(${target} PRIVATE libdn PkgConfig::LIBPNG) endforeach() endif() add_subdirectory(po) add_executable(dnimaged dnimaged/main.cpp) target_link_libraries(dnimaged PRIVATE libdn) add_executable(dnthumbd dnthumbd/main.cpp) target_link_libraries(dnthumbd PRIVATE libdn) qt_add_executable(dn dn/accessible.cpp dn/action.cpp dn/app.cpp dn/display-profile.cpp dn/gpu.cpp dn/instance.cpp dn/kit-browser.cpp dn/kit-chrome.cpp dn/kit-cie-diagram.cpp dn/kit-files.cpp dn/kit-commander.cpp dn/kit.cpp dn/kit-crop-jpeg.cpp dn/kit-viewer.cpp dn/main.cpp dn/overlay.cpp dn/renderer.cpp dn/thumbnail-cache.cpp dn/thumbnailer.cpp dn/url.cpp dn/window.cpp dn/xdg.cpp icons/icons.qrc ) set_target_properties(dn PROPERTIES AUTORCC ON) target_link_libraries(dn PRIVATE libdn Qt6::Gui PkgConfig::WEBP PkgConfig::WEBPDEMUX PkgConfig::WEBPMUX PkgConfig::RESVG Vulkan::Vulkan Threads::Threads ) file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/generated") add_custom_command( OUTPUT "${PROJECT_BINARY_DIR}/generated/cmf-cie1931-2deg-1nm.h" COMMAND ${AWK} -f "${PROJECT_SOURCE_DIR}/dn/cmf-to-header.awk" "${PROJECT_SOURCE_DIR}/dn/ciexyz31_1.csv" "${PROJECT_BINARY_DIR}/generated/cmf-cie1931-2deg-1nm.h" DEPENDS "${PROJECT_SOURCE_DIR}/dn/cmf-to-header.awk" "${PROJECT_SOURCE_DIR}/dn/ciexyz31_1.csv" COMMENT "Generate CIE 1931 2° 1 nm CMF header" VERBATIM ) target_sources(dn PRIVATE "${PROJECT_BINARY_DIR}/generated/cmf-cie1931-2deg-1nm.h") dawn_embed_shader(dn dn-overlay.vert dn_overlay_vert) dawn_embed_shader(dn dn-overlay.frag dn_overlay_frag) dawn_embed_shader(dn dn-thumb.frag dn_thumb_frag) dawn_embed_shader(dn dn-present.frag dn_present_frag) # The native text backend, which test-text builds as well. add_library(dn-text INTERFACE) target_sources(dn-text INTERFACE dn/text.cpp) target_link_libraries(dn-text INTERFACE Qt6::Gui) target_link_libraries(dn PRIVATE dn-text) if (WIN32) target_sources(dn-text INTERFACE dn/text-windows.cpp) target_link_libraries(dn-text INTERFACE dwrite) target_sources(dn PRIVATE dn/display-profile-windows.cpp dn/assoc-windows.cpp dn/volumes-windows.cpp dn/window-appearance-windows.cpp ) target_link_libraries(dn PRIVATE dwmapi dxgi gdi32 shell32 shlwapi ole32 advapi32 user32) set_target_properties(dn PROPERTIES WIN32_EXECUTABLE $>) elseif (APPLE) find_library(DN_APPKIT_FRAMEWORK AppKit REQUIRED) find_library(DN_CORETEXT_FRAMEWORK CoreText REQUIRED) find_library(DN_COREGRAPHICS_FRAMEWORK CoreGraphics REQUIRED) find_library(DN_CORESERVICES_FRAMEWORK CoreServices REQUIRED) find_library(DN_UNIFORMTYPES_FRAMEWORK UniformTypeIdentifiers REQUIRED) find_library(DN_IOKIT_FRAMEWORK IOKit REQUIRED) target_sources(dn-text INTERFACE dn/text-macos.mm) target_link_libraries(dn-text INTERFACE "${DN_APPKIT_FRAMEWORK}" "${DN_COREGRAPHICS_FRAMEWORK}" "${DN_CORETEXT_FRAMEWORK}") target_sources(dn PRIVATE dn/display-profile-macos.mm dn/assoc-macos.mm dn/app-menu-macos.mm dn/window-appearance-macos.mm dn/volumes-macos.mm ) target_link_libraries(dn PRIVATE "${DN_CORESERVICES_FRAMEWORK}" "${DN_UNIFORMTYPES_FRAMEWORK}" "${DN_IOKIT_FRAMEWORK}") else() pkg_check_modules(COLORD REQUIRED IMPORTED_TARGET colord) pkg_check_modules(PANGOFT2 REQUIRED IMPORTED_TARGET pangoft2>=1.48 pangofc>=1.48 freetype2>=2.11) target_sources(dn-text INTERFACE dn/text-linux.cpp) target_link_libraries(dn-text INTERFACE PkgConfig::PANGOFT2) target_sources(dn PRIVATE dn/display-profile-linux.cpp dn/assoc-unix.cpp dn/volumes-unix.cpp ) target_link_libraries(dn PRIVATE PkgConfig::COLORD) if (dawn_qt_xcb) pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb xcb-randr) target_link_libraries(dn PRIVATE PkgConfig::XCB) endif() endif() # Application icon: SVG → PNG/ICO (xT) on non-Apple; Swift squircle ICNS (xM) # on Darwin. Qt looks up IDI_ICON1 and nothing else. set(dn_icon_svg "${dawn_packaging}/${DAWN_NAMESPACE}.svg") if (APPLE) find_program(DN_SWIFT_EXECUTABLE swift REQUIRED) set(MACOSX_BUNDLE_ICON_FILE dn.icns) set(dn_icon_icns "${PROJECT_BINARY_DIR}/${MACOSX_BUNDLE_ICON_FILE}") add_custom_command(OUTPUT "${dn_icon_icns}" COMMAND "${DN_SWIFT_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/dn/gen-icon.swift" "${dn_icon_icns}" DEPENDS "${PROJECT_SOURCE_DIR}/dn/gen-icon.swift" COMMENT "Generating dn application icon" VERBATIM) set_source_files_properties("${dn_icon_icns}" PROPERTIES MACOSX_PACKAGE_LOCATION Resources) target_sources(dn PRIVATE "${dn_icon_icns}") # Mirrors supported_media_types(), restricted to macOS types. # See /System/Library/CoreServices/CoreTypes.bundle/Contents/Info.plist set(utis com.apple.icns com.compuserve.gif com.microsoft.bmp com.truevision.tga-image org.webmproject.webp public.jpeg public.png public.svg-image ) if (DAWN_WITH_LIBTIFF) list(APPEND utis public.tiff) endif() if (DAWN_WITH_LIBHEIF) list(APPEND utis public.avif public.heic public.heif) endif() if (DAWN_WITH_LIBJXL) list(APPEND utis public.jpeg-xl) endif() if (DAWN_WITH_OPENJPEG) list(APPEND utis public.jpeg-2000) endif() if (DAWN_WITH_CGPDF OR DAWN_WITH_POPPLER) list(APPEND utis com.adobe.pdf) endif() if (DAWN_WITH_LIBRAW) # The abstract type the vendor formats conform to. list(APPEND utis public.camera-raw-image) endif() if (DAWN_WITH_DNRS) # The rest of what it adds, ImageIO knows about as well. list(APPEND utis com.microsoft.ico public.xbitmap-image ) endif() if (DAWN_WITH_IMAGEIO) # Ask ImageIO through the very call the loader uses. # Hopefully it will match the system the program is run on as well. execute_process( COMMAND "${DN_SWIFT_EXECUTABLE}" -e [=[ import ImageIO for type in CGImageSourceCopyTypeIdentifiers() as! [String] { print(type) } ]=] OUTPUT_VARIABLE imageio_utis COMMAND_ERROR_IS_FATAL ANY ) separate_arguments(imageio_utis UNIX_COMMAND "${imageio_utis}") list(APPEND utis ${imageio_utis}) endif() list(REMOVE_DUPLICATES utis) list(SORT utis) set(DN_MACOS_CONTENT_TYPES "") foreach (uti IN LISTS utis) string(APPEND DN_MACOS_CONTENT_TYPES "\n\t\t\t\t${uti}") endforeach() # AppKit only speaks the languages a bundle declares, whatever gettext finds. set(DN_MACOS_LOCALIZATIONS "") foreach (lang IN ITEMS en ${DAWN_LINGUAS}) string(APPEND DN_MACOS_LOCALIZATIONS "\n\t\t${lang}") endforeach() # Still a template afterwards: CMake fills ${MACOSX_BUNDLE_*} at generate # time, and Qt copies it once more in between. set(plist "${PROJECT_BINARY_DIR}/Info.plist.in") configure_file(dn/Info.plist.in "${plist}" @ONLY) # XXX: I actually want to name the binary dn but the bundle Dawn. set_target_properties(dn PROPERTIES RUNTIME_OUTPUT_NAME "${PROJECT_NAME}" MACOSX_BUNDLE ON MACOSX_BUNDLE_GUI_IDENTIFIER "${DAWN_NAMESPACE}" MACOSX_BUNDLE_ICON_FILE "${MACOSX_BUNDLE_ICON_FILE}" MACOSX_BUNDLE_INFO_PLIST "${plist}" MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}" MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}" MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}" MACOSX_BUNDLE_COPYRIGHT "${CPACK_PACKAGE_VENDOR}" ) else() include(IconUtils) set(dn_icon_base "${PROJECT_BINARY_DIR}/icons") set(dn_icon_png_list) if (WIN32) set(dn_icon_name dn) else() set(dn_icon_name "${DAWN_NAMESPACE}") endif() foreach (dn_icon_size 16 32 48 256) icon_to_png("${dn_icon_name}" "${dn_icon_svg}" ${dn_icon_size} "${dn_icon_base}" dn_icon_png) list(APPEND dn_icon_png_list "${dn_icon_png}") endforeach() if (WIN32) list(REMOVE_ITEM dn_icon_png_list "${dn_icon_png}") set(dn_icon_ico "${PROJECT_BINARY_DIR}/dn.ico") icon_for_win32("${dn_icon_ico}" "${dn_icon_png_list}" "${dn_icon_png}") set(dn_manifest "${PROJECT_SOURCE_DIR}/dn/dn.manifest") set(dn_rc "${PROJECT_BINARY_DIR}/dn.rc") configure_file("${PROJECT_SOURCE_DIR}/dn/dn.rc.in" "${dn_rc}" @ONLY) set_property(SOURCE "${dn_rc}" APPEND PROPERTY OBJECT_DEPENDS "${dn_icon_ico}" "${dn_manifest}") target_sources(dn PRIVATE "${dn_rc}") else() add_custom_target(icons ALL DEPENDS ${dn_icon_png_list}) install(FILES "${dn_icon_svg}" DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps") install(DIRECTORY "${dn_icon_base}" DESTINATION "${CMAKE_INSTALL_DATADIR}") # MimeType= follows the loader table compiled into dn, so ask it. # A cross build can only ask through an emulator; without one, # the checked-in list is installed as it is. set(desktop "${dawn_packaging}/${DAWN_NAMESPACE}.desktop") if (NOT CMAKE_CROSSCOMPILING OR CMAKE_CROSSCOMPILING_EMULATOR) set(generated "${PROJECT_BINARY_DIR}/${DAWN_NAMESPACE}.mime.desktop") add_custom_command(OUTPUT "${generated}" COMMAND ${CMAKE_COMMAND} "-DEMULATOR=${CMAKE_CROSSCOMPILING_EMULATOR}" -DDN=$ "-DINPUT=${desktop}" "-DOUTPUT=${generated}" -P "${PROJECT_SOURCE_DIR}/cmake/desktop-media-types.cmake" DEPENDS dn "${desktop}" COMMENT "Filling in desktop file media types" VERBATIM) set(desktop "${generated}") endif() set(desktops "${desktop}" "${dawn_packaging}/${DAWN_NAMESPACE}.Browse.desktop" "${dawn_packaging}/${DAWN_NAMESPACE}.CropJpeg.desktop") if (DAWN_WITH_WIP) list(APPEND desktops "${dawn_packaging}/${DAWN_NAMESPACE}.Commander.desktop") endif() # What the launcher says is translated from the same catalogue as the # application, which is why the names in it are never invented here. set(entries) file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/applications") foreach (template IN LISTS desktops) get_filename_component(entry "${template}" NAME) string(REPLACE ".mime." "." entry "${entry}") set(output "${PROJECT_BINARY_DIR}/applications/${entry}") add_custom_command(OUTPUT "${output}" COMMAND "${MSGFMT_EXECUTABLE}" --desktop --keyword=Name --keyword=GenericName --keyword=X-GNOME-FullName "--template=${template}" -d "${PROJECT_SOURCE_DIR}/po" -o "${output}" DEPENDS "${template}" ${DAWN_TRANSLATIONS} COMMENT "Translating ${entry}" VERBATIM) list(APPEND entries "${output}") endforeach() add_custom_target(desktop-entries ALL DEPENDS ${entries}) install(FILES ${entries} DESTINATION "${CMAKE_INSTALL_DATADIR}/applications") endif() endif() if (DN_WITH_WAYLAND) pkg_get_variable(dn_wayland_protocols_dir wayland-protocols pkgdatadir) if (NOT dn_wayland_protocols_dir) message(FATAL_ERROR "wayland-protocols pkgdatadir not found") endif() pkg_get_variable(dn_wayland_scanner wayland-scanner wayland_scanner) if (NOT dn_wayland_scanner) find_program(dn_wayland_scanner wayland-scanner REQUIRED) endif() set(dn_protocol_dir "${PROJECT_BINARY_DIR}/protocols") file(MAKE_DIRECTORY "${dn_protocol_dir}") set(dn_color_xml "${dn_wayland_protocols_dir}/staging/color-management/color-management-v1.xml") set(dn_color_header "${dn_protocol_dir}/color-management-v1-client-protocol.h") set(dn_color_source "${dn_protocol_dir}/color-management-v1-protocol.c") set(dn_xdg_xml "${dn_wayland_protocols_dir}/stable/xdg-shell/xdg-shell.xml") set(dn_xdg_header "${dn_protocol_dir}/xdg-shell-client-protocol.h") add_custom_command( OUTPUT "${dn_color_header}" "${dn_color_source}" "${dn_xdg_header}" COMMAND "${dn_wayland_scanner}" client-header "${dn_color_xml}" "${dn_color_header}" COMMAND "${dn_wayland_scanner}" private-code "${dn_color_xml}" "${dn_color_source}" COMMAND "${dn_wayland_scanner}" client-header "${dn_xdg_xml}" "${dn_xdg_header}" DEPENDS "${dn_color_xml}" "${dn_xdg_xml}" COMMENT "wayland-scanner protocols for dn" VERBATIM) target_sources(dn PRIVATE dn/wayland-color-bridge.cpp dn/wayland-window.cpp "${dn_color_header}" "${dn_color_source}" "${dn_xdg_header}" ) target_include_directories(dn PRIVATE "${dn_protocol_dir}") target_link_libraries(dn PRIVATE PkgConfig::WAYLAND Qt6::GuiPrivate) endif() # User guide: build tree at share/docs/dawn (../share from bin/), # installed next to the application on each platform. set(DAWN_HELP_DIR "${PROJECT_BINARY_DIR}/share/doc/dawn") file(MAKE_DIRECTORY "${DAWN_HELP_DIR}") foreach (file dn.html dn.cs.html) configure_file( "${PROJECT_SOURCE_DIR}/docs/${file}.in" "${DAWN_HELP_DIR}/${file}" @ONLY) list(APPEND dawn_help_files "${DAWN_HELP_DIR}/${file}") endforeach() foreach (file stylesheet.css dn.webp p3-srgb-ramps.png) configure_file( "${PROJECT_SOURCE_DIR}/docs/${file}" "${DAWN_HELP_DIR}/${file}" COPYONLY) list(APPEND dawn_help_files "${DAWN_HELP_DIR}/${file}") endforeach() if (APPLE) list(APPEND dawn_help_files "${PROJECT_SOURCE_DIR}/LICENSE") set_source_files_properties(${dawn_help_files} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/share/doc/dawn") target_sources(dn PRIVATE ${dawn_help_files}) elseif (NOT WIN32) # This manual page generator should be good enough. set(ASCIIMAN "${PROJECT_SOURCE_DIR}/submodules/liberty/tools/asciiman.awk") foreach (page dn) set(page_output "${PROJECT_BINARY_DIR}/${page}.1") list(APPEND project_MAN_PAGES "${page_output}") add_custom_command(OUTPUT ${page_output} COMMAND env LC_ALL=C asciidoc-release-version=${PROJECT_VERSION} ${AWK} -f ${ASCIIMAN} "${PROJECT_SOURCE_DIR}/docs/${page}.adoc" > ${page_output} DEPENDS "${PROJECT_SOURCE_DIR}/docs/${page}.adoc" ${ASCIIMAN} COMMENT "Generating man page for ${page}" VERBATIM) endforeach() add_custom_target(docs ALL DEPENDS ${project_MAN_PAGES}) endif() # Message catalogues. The bundle's Resources have to be complete already # in the build tree; the installation copies the bundle as it finds it. add_dependencies(dn catalogues) if (APPLE AND DAWN_CATALOGUES) foreach (mo IN LISTS DAWN_CATALOGUES) file(RELATIVE_PATH rel "${DAWN_LOCALE_DIR}" "${mo}") get_filename_component(rel "${rel}" DIRECTORY) set_source_files_properties("${mo}" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/share/locale/${rel}") endforeach() target_sources(dn PRIVATE ${DAWN_CATALOGUES}) endif() # AppKit-only identities inside Dawn.app, which Launch Services registers # along with it. Compile with swiftc directly so that this also works # with Makefile generators, without enabling CMake's Swift language. if (APPLE) find_program(DN_SWIFTC_EXECUTABLE swiftc REQUIRED) find_program(DN_LIPO_EXECUTABLE lipo REQUIRED) find_program(DN_CODESIGN_EXECUTABLE codesign REQUIRED) set(dn_launcher_arches ${CMAKE_OSX_ARCHITECTURES}) if (NOT dn_launcher_arches) set(dn_launcher_arches "${CMAKE_SYSTEM_PROCESSOR}") endif() set(dn_launcher_sdk "${CMAKE_OSX_SYSROOT}") if (NOT IS_DIRECTORY "${dn_launcher_sdk}") execute_process(COMMAND xcrun --sdk macosx --show-sdk-path OUTPUT_VARIABLE dn_launcher_sdk OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY) endif() set(dn_launcher_slices) foreach (arch IN LISTS dn_launcher_arches) set(slice "${PROJECT_BINARY_DIR}/dn-launcher-${arch}") add_custom_command(OUTPUT "${slice}" COMMAND "${DN_SWIFTC_EXECUTABLE}" -swift-version 5 -O -target "${arch}-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" -sdk "${dn_launcher_sdk}" -framework AppKit "${PROJECT_SOURCE_DIR}/dn/launcher.swift" -o "${slice}" DEPENDS "${PROJECT_SOURCE_DIR}/dn/launcher.swift" VERBATIM) list(APPEND dn_launcher_slices "${slice}") endforeach() set(dn_launcher "${PROJECT_BINARY_DIR}/dn-launcher") add_custom_command(OUTPUT "${dn_launcher}" COMMAND "${DN_LIPO_EXECUTABLE}" -create ${dn_launcher_slices} -output "${dn_launcher}" DEPENDS ${dn_launcher_slices} VERBATIM) set(dn_launch_ids CropJpeg) if (DAWN_WITH_WIP) list(APPEND dn_launch_ids Commander) endif() foreach (DN_LAUNCH_ID IN LISTS dn_launch_ids) if (DN_LAUNCH_ID STREQUAL CropJpeg) set(DN_LAUNCH_NAME "Dawn Lossless JPEG Cropper") set(DN_LAUNCH_MODE cropjpeg) set(DN_LAUNCH_TYPE_NAME JPEG) set(DN_LAUNCH_UTI public.jpeg) set(DN_LAUNCH_ROLE Editor) else() set(DN_LAUNCH_NAME "Dawn Commander") set(DN_LAUNCH_MODE commander) set(DN_LAUNCH_TYPE_NAME Folder) set(DN_LAUNCH_UTI public.folder) set(DN_LAUNCH_ROLE Viewer) endif() set(bundle "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.app") set(bundle "${bundle}/Contents/Applications/${DN_LAUNCH_NAME}.app") file(MAKE_DIRECTORY "${bundle}/Contents/MacOS" "${bundle}/Contents/Resources") configure_file("${PROJECT_SOURCE_DIR}/dn/launcher.Info.plist.in" "${bundle}/Contents/Info.plist" @ONLY) # Dawn's own signature can only seal a bundle that has one already. add_custom_command(OUTPUT "${bundle}/Contents/MacOS/dn-launcher" "${bundle}/Contents/Resources/dn.icns" "${bundle}/Contents/_CodeSignature/CodeResources" COMMAND "${CMAKE_COMMAND}" -E copy "${dn_launcher}" "${bundle}/Contents/MacOS/dn-launcher" COMMAND "${CMAKE_COMMAND}" -E copy "${dn_icon_icns}" "${bundle}/Contents/Resources/dn.icns" COMMAND "${DN_CODESIGN_EXECUTABLE}" --force --sign - "${bundle}" DEPENDS "${dn_launcher}" "${dn_icon_icns}" "${bundle}/Contents/Info.plist" VERBATIM) add_custom_target(dn-launcher-${DN_LAUNCH_ID} ALL DEPENDS "${bundle}/Contents/_CodeSignature/CodeResources") add_dependencies(dn-launcher-${DN_LAUNCH_ID} dn) list(APPEND dn_launcher_apps "${DN_LAUNCH_NAME}.app") endforeach() endif() # Software centres want this, and Flatpak bundles only carry an icon # when an AppStream component points at one. Every platform configures it, # so that the validation test always has a file to look at. set(dawn_metainfo "${PROJECT_BINARY_DIR}/${DAWN_NAMESPACE}.metainfo.xml") configure_file("${dawn_packaging}/${DAWN_NAMESPACE}.metainfo.xml.in" "${dawn_metainfo}" @ONLY) # --- Tests -------------------------------------------------------------------- if (BUILD_TESTING) # This spawns a dialog box on Windows. if (NOT WIN32) add_test(NAME dn-help COMMAND dn --help) endif() add_subdirectory(tests) endif() # --- Installation ------------------------------------------------------------- if (WIN32) install(TARGETS dn libdn RUNTIME DESTINATION .) install(DIRECTORY "${DAWN_MSYS2_PREFIX}/bin/" DESTINATION . FILES_MATCHING PATTERN "*.dll" PATTERN "vulkan-1.dll" EXCLUDE) install(IMPORTED_RUNTIME_ARTIFACTS Qt6::QWindowsIntegrationPlugin RUNTIME DESTINATION platforms LIBRARY DESTINATION platforms) install(FILES ${dawn_help_files} DESTINATION share/doc/dawn) install(FILES "${DAWN_MSYS2_PREFIX}/share/mime/globs2" "${DAWN_MSYS2_PREFIX}/share/mime/subclasses" DESTINATION share/mime) install(FILES "${DAWN_MSYS2_PREFIX}/bin/vk_swiftshader_icd.json" DESTINATION .) # This installation is pretty big, with a ton of files, thus it's optional. install(FILES "${DAWN_MSYS2_PREFIX}/bin/wperl.exe" "${DAWN_MSYS2_PREFIX}/bin/exiftool" DESTINATION . OPTIONAL) install(DIRECTORY "${DAWN_MSYS2_PREFIX}/lib/perl5/" DESTINATION lib/perl5 OPTIONAL) # Prune unused libraries. install(SCRIPT "${PROJECT_SOURCE_DIR}/cmake/msys2-cleanup.cmake") elseif (APPLE) # macOS app bundle: Qt Frameworks/PlugIns + fiv-like Resources/share. # MACOSX_BUNDLE is set on dn next to qt_add_executable. set(dn_bundle "$") install(TARGETS dn BUNDLE DESTINATION .) # The launchers directly depend on the bundle, but nesting them makes # them undiscoverable. Symlinks can at least be launched. foreach (link IN LISTS dn_launcher_apps) install(CODE "file(CREATE_LINK \"${dn_bundle}/Contents/Applications/${link}\" \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${link}\" SYMBOLIC)") endforeach() #install(TARGETS dnimaged dnthumbd # RUNTIME DESTINATION "${dn_bundle}/Contents/MacOS") install(TARGETS libdn LIBRARY DESTINATION "${dn_bundle}/Contents/Frameworks") set(dn_mime_dir) pkg_get_variable(dn_smi_datadir shared-mime-info datadir) if (dn_smi_datadir AND EXISTS "${dn_smi_datadir}/mime/globs2" AND EXISTS "${dn_smi_datadir}/mime/subclasses") set(dn_mime_dir "${dn_smi_datadir}/mime") endif() if (NOT dn_mime_dir) foreach (dn_root IN ITEMS "$ENV{HOMEBREW_PREFIX}" /opt/homebrew /usr/local) if(dn_root AND EXISTS "${dn_root}/share/mime/globs2" AND EXISTS "${dn_root}/share/mime/subclasses") set(dn_mime_dir "${dn_root}/share/mime") break() endif() endforeach() endif() install(FILES "${dn_mime_dir}/globs2" "${dn_mime_dir}/subclasses" DESTINATION "${dn_bundle}/Contents/Resources/share/mime") # macdeployqt otherwise copies imageformats/virtualkeyboard/styles and # pulls QtSvg, QtPdf, QtQuick, QtWidgets, QtNetwork with them. install(IMPORTED_RUNTIME_ARTIFACTS Qt6::QCocoaIntegrationPlugin LIBRARY DESTINATION "${dn_bundle}/Contents/PlugIns/platforms" RUNTIME DESTINATION "${dn_bundle}/Contents/PlugIns/platforms") # MoltenVK is an ICD; macdeployqt only copies the linked loader. # Homebrew lib/libMoltenVK.dylib is a Cellar symlink; install(FILES) would # copy the link and the relocated bundle would not find MoltenVK. find_library(DN_MOLTENVK_LIBRARY NAMES MoltenVK REQUIRED HINTS "$ENV{HOMEBREW_PREFIX}/lib" /opt/homebrew/lib /usr/local/lib) get_filename_component( DN_MOLTENVK_LIBRARY "${DN_MOLTENVK_LIBRARY}" REALPATH) install(FILES "${DN_MOLTENVK_LIBRARY}" DESTINATION "${dn_bundle}/Contents/Frameworks") file(WRITE "${PROJECT_BINARY_DIR}/MoltenVK_icd.json" [[{ "file_format_version": "1.0.0", "ICD": { "library_path": "../../../Frameworks/libMoltenVK.dylib", "api_version": "1.4.0", "is_portability_driver": true } } ]]) install(FILES "${PROJECT_BINARY_DIR}/MoltenVK_icd.json" DESTINATION "${dn_bundle}/Contents/Resources/vulkan/icd.d") # Qt's own catalogues would stay behind in its prefix. find_program(DN_QTPATHS_EXECUTABLE NAMES qtpaths6 REQUIRED) execute_process( COMMAND "${DN_QTPATHS_EXECUTABLE}" --query QT_INSTALL_TRANSLATIONS OUTPUT_VARIABLE dn_qt_translations OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY) foreach (lang IN LISTS DAWN_LINGUAS) install(FILES "${dn_qt_translations}/qtbase_${lang}.qm" DESTINATION "${dn_bundle}/Contents/Resources/translations" OPTIONAL) endforeach() file(WRITE "${PROJECT_BINARY_DIR}/qt.conf" "[Paths]\nPlugins = PlugIns\nTranslations = Resources/translations\n") install(FILES "${PROJECT_BINARY_DIR}/qt.conf" DESTINATION "${dn_bundle}/Contents/Resources") unset(dn_bundle) qt_generate_deploy_app_script(TARGET dn OUTPUT_SCRIPT dn_deploy NO_PLUGINS) install(SCRIPT "${dn_deploy}") # Homebrew Qt QTBUG-127075: run macdeployqt twice. install(SCRIPT "${dn_deploy}") else() install(TARGETS dn libdn RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES ${dawn_help_files} DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/dawn") foreach (page ${project_MAN_PAGES}) string(REGEX MATCH "\\.([0-9])$" manpage_suffix "${page}") install(FILES "${page}" DESTINATION "${CMAKE_INSTALL_MANDIR}/man${CMAKE_MATCH_1}") endforeach() install(FILES "${dawn_metainfo}" DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo") endif() # --- Packaging ---------------------------------------------------------------- set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") set(CPACK_GENERATOR ZIP) set(CPACK_PACKAGE_DIRECTORY "${PROJECT_BINARY_DIR}") set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") if (APPLE) # The disk image gets an /Applications link to drag the bundle onto. list(APPEND CPACK_GENERATOR DragNDrop) # Debug maps name the object files of the build tree. set(CPACK_STRIP_FILES ON) set(CPACK_DMG_FORMAT ULMO) set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME}") # This enables showing hidden files inside, and is of questionable value. #set(CPACK_PACKAGE_ICON "${dn_icon_icns}") elseif (UNIX) set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) set(CPACK_DEBIAN_PACKAGE_SECTION graphics) # Everything else dn needs is a library, and comes out of the ELF files. set(CPACK_DEBIAN_PACKAGE_DEPENDS shared-mime-info) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) # libdn is ours, and no installed package provides it to resolve against. set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") # Directory ownership is left as it comes, and will overlap other packages. set(CPACK_RPM_FILE_NAME RPM-DEFAULT) set(CPACK_RPM_PACKAGE_LICENSE MPL-2.0) set(CPACK_RPM_PACKAGE_REQUIRES shared-mime-info) set(CPACK_RPM_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") endif() include(CPack) # CPack's WIX generator needs the real WiX toolset (Windows or wine). # We've already figured out msitools. if (WIN32 AND CMAKE_CROSSCOMPILING) set(DAWN_ICON_ICO "${dn_icon_ico}") set(DAWN_MSI_WIP OFF) if (DAWN_WITH_WIP) set(DAWN_MSI_WIP ON) endif() set(dawn_wxs "${PROJECT_BINARY_DIR}/dawn.wxs") configure_file("${dawn_packaging}/Dawn.wxs.in" "${dawn_wxs}" @ONLY) set(dawn_msi "${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_PROCESSOR}.msi") add_custom_target(msi COMMAND "${PROJECT_SOURCE_DIR}/msys2-package-msi.sh" "${CMAKE_SYSTEM_PROCESSOR}" "${dawn_msi}" "${dawn_wxs}" "${PROJECT_DESCRIPTION}" "${DAWN_MSI_WIP}" WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" VERBATIM) endif()