# AUTOGENERATED COPYRIGHT HEADER START # Copyright (C) 2017-2023 Michael Fabian 'Xaymar' Dirks # Copyright (C) 2019 Cat Stevens # Copyright (C) 2020 Brandon Edens # Copyright (C) 2021 Christopher P Yarger # Copyright (C) 2022 Carsten Braun # Copyright (C) 2022 Romain Vigier # Copyright (C) 2023 Isaac Nudelman # AUTOGENERATED COPYRIGHT HEADER END # ToDo: # - Explore RESOURCE property: https://cmake.org/cmake/help/latest/prop_tgt/RESOURCE.html # - Explore PUBLIC_HEADER property: https://cmake.org/cmake/help/latest/prop_tgt/PUBLIC_HEADER.html # CMake Setup cmake_minimum_required(VERSION 3.26) list(APPEND CMAKE_MESSAGE_INDENT "[StreamFX] ") project(StreamFX) ################################################################################ # Modules ################################################################################ # Search Paths set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/clang" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/version" "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ) # Include required Modules # - Stock include("CheckIPOSupported") # - Custom include("util") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/version/version.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version/version.cmake") else() message(FATAL_ERROR "Please ensure you've cloned recursively and try again. Thank you!") endif() # Include optional Modules if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/clang/Clang.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/clang/Clang.cmake") endif() ################################################################################ # Bootstrapping ################################################################################ # MacOSX: Require at least MacOSX 10.15 for C++17 support. if(APPLE) if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.15) CacheSet(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") endif() endif() # Check if this is standalone or bundled. set(STANDALONE ON) if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}") set(GROUPED OFF) set(PREFIX "") else() set(GROUPED ON) set(PREFIX "StreamFX_") if(GROUPED AND ((TARGET libobs) OR (TARGET OBS::libobs))) set(STANDALONE OFF) endif() endif() # Operating System if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set(D_PLATFORM_OS "windows") set(D_PLATFORM_WINDOWS 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(D_PLATFORM_OS "linux") set(D_PLATFORM_LINUX 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(D_PLATFORM_OS "macos") set(D_PLATFORM_MAC 1) else() set(D_PLATFORM_OS "unknown") set(D_PLATFORM_UNKNOWN 1) message(WARNING "The operating system '${CMAKE_SYSTEM_NAME}' is unknown to to this script, continue at your own risk.") endif() # Architecture set(ARCH_INSTR_32 "i386;i686;x86;arm;ARM") set(ARCH_INSTR_64 "x86_64;AMD64;IA64;arm64;ARM64") set(ARCH_INSTR_X86 "i386;i686;x86;x86_64;AMD64") set(ARCH_INSTR_ARM "arm;ARM;arm64;ARM64") set(ARCH_INSTR_ITANIUM "IA64") set(ARCH_BITS 0) set(ARCH_BITS_POINTER 0) set(ARCH_INST "") # - Bitness list(FIND ARCH_INSTR_32 "${CMAKE_SYSTEM_PROCESSOR}" FOUND) if(FOUND GREATER -1) set(ARCH_BITS 32) endif() list(FIND ARCH_INSTR_64 "${CMAKE_SYSTEM_PROCESSOR}" FOUND) if(FOUND GREATER -1) set(ARCH_BITS 64) endif() set(D_PLATFORM_BITS ${ARCH_BITS}) # - Pointer Size (bits) math(EXPR ARCH_BITS_POINTER "8*${CMAKE_SIZEOF_VOID_P}") set(D_PLATFORM_BITS_PTR ${ARCH_BITS_POINTER}) # - Basic Instruction Set list(FIND ARCH_INSTR_X86 "${CMAKE_SYSTEM_PROCESSOR}" FOUND) if(FOUND GREATER -1) list(APPEND ARCH_INST "x86") set(D_PLATFORM_INSTR_X86 ON) set(D_PLATFORM_ARCH_X86 ON) endif() list(FIND ARCH_INSTR_ARM "${CMAKE_SYSTEM_PROCESSOR}" FOUND) if(FOUND GREATER -1) list(APPEND ARCH_INST "ARM") set(D_PLATFORM_INSTR_ARM ON) set(D_PLATFORM_ARCH_ARM ON) endif() list(FIND ARCH_INSTR_ITANIUM "${CMAKE_SYSTEM_PROCESSOR}" FOUND) if(FOUND GREATER -1) list(APPEND ARCH_INST "Itanium") set(D_PLATFORM_INSTR_ITANIUM ON) set(D_PLATFORM_ARCH_ITANIUM ON) endif() # Interprocedural Optimization check_ipo_supported(RESULT D_HAS_IPO) set(D_PLATFORM_INSTR ${ARCH_INST}) set(D_PLATFORM_ARCH ${ARCH_INST}) message(STATUS "Target is ${D_PLATFORM_BITS}bit ${ARCH_INST} with a pointer size of ${D_PLATFORM_BITS_PTR}bit.") ################################################################################ # Versioning ################################################################################ version(GENERATE _VERSION COMPRESSED MAJOR 0 MINOR 0 PATCH 0 TWEAK 0 REQUIRE "PATCH;") version(PARSE _VERSION "${_VERSION}" REQUIRE "PATCH;TWEAK") # If possible, automatically generate versions from git. if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") find_program(GIT NAMES git git.exe ) if(EXISTS "${GIT}") # Try and calculate the exist version using git. execute_process(COMMAND "${GIT}" describe --tags --long --abbrev=8 HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET) if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL "")) # Result will be MAJOR.MINOR.PATCH-TWEAK-gHASH string(REPLACE "-" ";" GIT_OUTPUT "${GIT_OUTPUT}") string(REPLACE "." ";" GIT_OUTPUT "${GIT_OUTPUT}") # Split into components list(GET GIT_OUTPUT 0 GIT_OUTPUT_MAJOR) list(GET GIT_OUTPUT 1 GIT_OUTPUT_MINOR) list(GET GIT_OUTPUT 2 GIT_OUTPUT_PATCH) list(GET GIT_OUTPUT 3 GIT_OUTPUT_TWEAK) list(GET GIT_OUTPUT 4 GIT_OUTPUT_BUILD) # Special case: Tag contains prerelease if(GIT_OUTPUT_PATCH MATCHES "([0-9]+)([a-zA-Z]+)([0-9]*)") # Patch requires special parsing. set(GIT_OUTPUT_PATCH "${CMAKE_MATCH_1}") if(CMAKE_MATCH_3 GREATER 0) set(GIT_OUTPUT_PRERELEASE "${CMAKE_MATCH_2}") else() set(GIT_OUTPUT_PRERELEASE "a") endif() MATH(EXPR GIT_OUTPUT_TWEAK "${GIT_OUTPUT_TWEAK} + ${CMAKE_MATCH_3}") # Modify the global version version(MODIFY _VERSION "${_VERSION}" COMPRESS MAJOR "${GIT_OUTPUT_MAJOR}" MINOR "${GIT_OUTPUT_MINOR}" PATCH "${GIT_OUTPUT_PATCH}" TWEAK "${GIT_OUTPUT_TWEAK}" BUILD "${GIT_OUTPUT_BUILD}" PRERELEASE "${GIT_OUTPUT_PRERELEASE}" REQUIRE "PATCH;TWEAK" ) else() # Modify the global version version(MODIFY _VERSION "${_VERSION}" COMPRESS MAJOR "${GIT_OUTPUT_MAJOR}" MINOR "${GIT_OUTPUT_MINOR}" PATCH "${GIT_OUTPUT_PATCH}" TWEAK "${GIT_OUTPUT_TWEAK}" BUILD "${GIT_OUTPUT_BUILD}" PRERELEASE "a" REQUIRE "PATCH;TWEAK" ) endif() else() execute_process(COMMAND "${GIT}" rev-list --count HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET) if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL "")) version(MODIFY _VERSION "${_VERSION}" COMPRESS TWEAK "${GIT_OUTPUT}" PRERELEASE "a" REQUIRE "PATCH;TWEAK" ) # Determine the build using git. execute_process(COMMAND "${GIT}" log -1 "--pretty=format:g%h" --abbrev=8 HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET) if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL "")) version(MODIFY _VERSION "${_VERSION}" COMPRESS BUILD "${GIT_OUTPUT}" REQUIRE "PATCH;TWEAK" ) else() message(WARNING "Failed to detect build version with 'git'.") endif() else() message(WARNING "Failed to automatically detect version with 'git'.") endif() endif() else() message(WARNING "'git' not found, automatic version detection disabled.") endif() else() message(STATUS "Not a git repository, automatic version detection disabled.") endif() version(PARSE _VERSION "${_VERSION}" REQUIRE "PATCH;TWEAK") # Allow manual overrides of the detected version. if(NOT ("${${PREFIX}VERSION}" STREQUAL "")) version(PARSE _VERSION_CFG "${${PREFIX}VERSION}" REQUIRE "PATCH;TWEAK") if("${_VERSION_CFG_BUILD}" STREQUAL "") set(_VERSION_CFG_BUILD "${_VERSION_BUILD}") endif() version(GENERATE _VERSION COMPRESS MAJOR "${_VERSION_CFG_MAJOR}" MINOR "${_VERSION_CFG_MINOR}" PATCH "${_VERSION_CFG_PATCH}" TWEAK "${_VERSION_CFG_TWEAK}" PRERELEASE "${_VERSION_CFG_PRERELEASE}" BUILD "${_VERSION_CFG_BUILD}" ) endif() set(_VERSION_THIN "${_VERSION_MAJOR}.${_VERSION_MINOR}.${_VERSION_PATCH}") if(NOT (_VERSION_PRERELEASE STREQUAL "")) set(_VERSION_THIN "${_VERSION_THIN}${_VERSION_PRERELEASE}${_VERSION_TWEAK}") endif() if(NOT (VERSION_COMMIT STREQUAL "")) set(_VERSION_THIN "${_VERSION_THIN}-${_VERSION_BUILD}") endif() # Parse & Log the detected version. message(STATUS "Version ${_VERSION_THIN}") ################################################################################ # Options ################################################################################ # Don't enable stability affecting features by default. if(_VERSION_PRERELEASE STREQUAL "a") set(FEATURE_DEPRECATED ON) set(FEATURE_EXPERIMENTAL ON) set(FEATURE_UNSTABLE ON) set(FEATURE_STABLE ON) elseif(_VERSION_PRERELEASE STREQUAL "b") set(FEATURE_DEPRECATED ON) set(FEATURE_EXPERIMENTAL OFF) set(FEATURE_UNSTABLE ON) set(FEATURE_STABLE ON) elseif(_VERSION_PRERELEASE STREQUAL "c") set(FEATURE_DEPRECATED ON) set(FEATURE_EXPERIMENTAL OFF) set(FEATURE_UNSTABLE OFF) set(FEATURE_STABLE ON) else() set(FEATURE_DEPRECATED ON) set(FEATURE_EXPERIMENTAL OFF) set(FEATURE_UNSTABLE OFF) set(FEATURE_STABLE ON) endif() # Version override set(${PREFIX}VERSION "" CACHE STRING "Specify an override for the automatically detected version. Accepts a mixture of SemVer 2.0 and CMake Version.") # Compile/Link Related set(${PREFIX}ENABLE_FASTMATH ON CACHE BOOL "Enable fast math optimizations, which sacrifice precision and stability.") if(D_PLATFORM_ARCH_X86) set(${PREFIX}TARGET_X86_64_V4 OFF CACHE BOOL "Target x86-64-v4 (x86-64-v3, AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL).") set(${PREFIX}TARGET_X86_64_V3 OFF CACHE BOOL "Target x86-64-v3 (x86-64-v2, AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, OSXSAVE).") set(${PREFIX}TARGET_X86_64_V2_EX ON CACHE BOOL "Target extended x86-64-v2 (x86-64-v2, AVX, BMI1, BMI2, F16C, FMA, MOVBE).") set(${PREFIX}TARGET_X86_64_V2 ON CACHE BOOL "Target x86-64-v2 (x86-64, CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3).") set(${PREFIX}TARGET_X86_64 ON CACHE BOOL "Target x86-64 (CMOV, CX8, FPU, FXSR, MMX, OSFXSR, SCE, SSE, SSE2).") endif() if(NOT MSVC) set(${PREFIX}TARGET_NATIVE OFF CACHE BOOL "Target the native CPU architecture. Enable it for development or personal builds, but disable it for distribution.") endif() # Installation / Packaging if(STANDALONE) if(D_PLATFORM_LINUX) set(STRUCTURE_PACKAGEMANAGER CACHE BOOL "Install for use in a Package Manager (system-wide installation)") endif() set(PACKAGE_PREFIX "${CMAKE_BINARY_DIR}" CACHE PATH "Where to place the packages?") set(PACKAGE_NAME "StreamFX" CACHE STRING "What should the package be called?") set(PACKAGE_SUFFIX "" CACHE STRING "Any suffix for the package name? (Defaults to the current version string)") endif() # Dependencies if(STANDALONE) set(libobs_DIR "" CACHE PATH "Path to libobs and obs-frontend-api") set(Qt_DIR "" CACHE PATH "Path to Qt6 or Qt5") set(CURL_DIR "" CACHE PATH "Path to CURL") set(FFmpeg_DIR "" CACHE PATH "Path to FFmpeg") endif() set(AOM_DIR "" CACHE PATH "Path to AOM library") ################################################################################ # Project ################################################################################ # Metadata version(GENERATE PROJECT_VERSION MAJOR "${_VERSION_MAJOR}" MINOR "${_VERSION_MINOR}" PATCH "${_VERSION_PATCH}" TWEAK "${_VERSION_TWEAK}" REQUIRE "PATCH;TWEAK" ) project( StreamFX VERSION ${PROJECT_VERSION} DESCRIPTION "Additional sources, filters, transitions and encoders for OBS Studio." HOMEPAGE_URL "https://streamfx.xaymar.com/" ) set(PROJECT_IDENTIFER "com.xaymar.StreamFX.obs") set(PROJECT_TITLE "StreamFX (for OBS Studio)") set(PROJECT_AUTHORS "Michael Fabian 'Xaymar' Dirks ") set(PROJECT_COPYRIGHT "2017 - 2022, Michael Fabian Dirks. All Rights Reserved") set(PROJECT_TRADEMARKS "") list(APPEND PROJECT_TRADEMARKS "AMD is a trademark of Advanced Micro Devices Inc." "NVIDIA is a trademark of Nvidia Corporation" ) # Component search paths list(APPEND CMAKE_PREFIX_PATH "${${PREFIX}AOM_PATH}" "${${PREFIX}OBSDEPS_PATH}" "${${PREFIX}QT_PATH}" "${AOM_DIR}" "${CURL_DIR}" "${DepsPath}" "${FFmpeg_DIR}" "${libobs_DIR}" "${Qt_DIR}" "${Qt5_DIR}" "${Qt6_DIR}" "${QTDIR}" ) if(D_PLATFORM_MAC) list(APPEND CMAKE_PREFIX_PATH "${${PREFIX}AOM_PATH}/Frameworks" "${${PREFIX}OBSDEPS_PATH}/Frameworks" "${${PREFIX}QT_PATH}/Frameworks" "${AOM_DIR}/Frameworks" "${CURL_DIR}/Frameworks" "${DepsPath}/Frameworks" "${FFmpeg_DIR}/Frameworks" "${libobs_DIR}/Frameworks" "${Qt_DIR}/Frameworks" "${Qt5_DIR}/Frameworks" "${Qt6_DIR}/Frameworks" "${QTDIR}/Frameworks" ) endif() set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP "User-Interface Files/Generated") ################################################################################ # Dependencies ################################################################################ #- OBS: Library if(STANDALONE) find_package("libobs" REQUIRED CONFIG) elseif(NOT TARGET OBS::libobs) # Add the missing OBS::libobs alias target if possible. add_library(OBS::libobs ALIAS libobs) endif() if(D_PLATFORM_MAC) # We may need to reconstruct the include directories on MacOS. get_target_property(libobs_INTERFACE_INCLUDE_DIRECTORIES OBS::libobs INTERFACE_INCLUDE_DIRECTORIES) if("${libobs_INTERFACE_INCLUDE_DIRECTORIES}" STREQUAL "libobs_INTERFACE_INCLUDE_DIRECTORIES-NOTFOUND") get_target_property(libobs_IMPORTED_LOCATION_RELEASE OBS::libobs IMPORTED_LOCATION_RELEASE) get_filename_component(libobs_IMPORTED_LOCATION_RELEASE "${libobs_IMPORTED_LOCATION_RELEASE}" DIRECTORY) get_filename_component(libobs_IMPORTED_LOCATION_RELEASE "${libobs_IMPORTED_LOCATION_RELEASE}" DIRECTORY) get_filename_component(libobs_IMPORTED_LOCATION_RELEASE "${libobs_IMPORTED_LOCATION_RELEASE}" DIRECTORY) set(libobs_IMPORTED_LOCATION_RELEASE "${libobs_IMPORTED_LOCATION_RELEASE}/Headers") set_target_properties(OBS::libobs PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${libobs_IMPORTED_LOCATION_RELEASE}" ) endif() endif() #- OBS: Front-End API if(STANDALONE) find_package("obs-frontend-api" CONFIG) set(obs-frontend-api_FOUND "${obs-frontend-api_FOUND}") elseif(TARGET obs-frontend-api) set(obs-frontend-api_FOUND ON) endif() if(NOT TARGET OBS::obs-frontend-api) add_library(OBS::obs-frontend-api ALIAS obs-frontend-api) endif() #- CURL find_package("CURL") #- JSON if(NOT EXISTS "${PROJECT_SOURCE_DIR}/third-party/nlohmann-json/single_include") message(FATAL_ERROR "Please make sure to update git submodules to their latest supported version.") return() else() set(JSON_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/third-party/nlohmann-json/single_include") endif() #- Qt 6 find_package("Qt6" COMPONENTS Core Gui Widgets CONFIG ) ################################################################################ # Helpers ################################################################################ define_property(TARGET PROPERTY COMPONENT_LABEL) define_property(TARGET PROPERTY COMPONENT_NAME) define_property(TARGET PROPERTY COMPONENT_OPTION) define_property(TARGET PROPERTY COMPONENT_RESOLVER) define_property(TARGET PROPERTY COMPONENT_DEPENDS) function(streamfx_add_library TARGET_NAME TARGET_TYPE) add_library(${TARGET_NAME} ${TARGET_TYPE}) set_target_properties(${TARGET_NAME} PROPERTIES # Always generate position independent code. POSITION_INDEPENDENT_CODE ON # Set C++ Standard and Extensions C_STANDARD 17 C_STANDARD_REQUIRED ON CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF # Remove prefix from generated files. PREFIX "" IMPORT_PREFIX "" ) if(D_PLATFORM_MAC) set_target_properties(${TARGET_NAME} PROPERTIES # @rpath in installed binaries INSTALL_RPATH "@executable_path/../Frameworks/;@loader_path/../Frameworks/;@loader_path/../Resources/" ) if(STANDALONE) set_target_properties(${TARGET_NAME} PROPERTIES # @rpath in built binaries BUILD_WITH_INSTALL_RPATH ON ) endif() endif() target_compile_definitions(${TARGET_NAME} PRIVATE __STDC_WANT_LIB_EXT1__=1 ) if(D_PLATFORM_WINDOWS) target_compile_definitions(${TARGET_NAME} PUBLIC # Microsoft Visual C++ _CRT_SECURE_NO_WARNINGS _ENABLE_EXTENDED_ALIGNED_STORAGE PUBLIC # windows.h # - Disable MIN/MAX macro as this breaks a lot of code. NOMINMAX # - Disable IN/OUT macro as this breaks a lot of code. NOINOUT ) endif() # C/C++ Compiler Adjustments if(D_PLATFORM_WINDOWS AND ((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))) # MSVC/ClangCL # - Dynamically link Microsoft C/C++ Redistributable. target_compile_options(${TARGET_NAME} PRIVATE $<$:/MD> $<$:/MDd> $<$:/MD> $<$:/MD> $<$:/MD> ) # - Treat Warnings as Errors target_compile_options(${TARGET_NAME} PRIVATE "/Wall") # - Explicitly disable treating all Warnings as Errors target_compile_options(${TARGET_NAME} PRIVATE "/WX-") # - Disable useless warnings set(DISABLED_WARNINGS # Don't warn about unused variables, parameters, labels, functions, or typedefs. "4100" "4101" "4102" "4505" "4514" "5245" "5264" # Don't warn about unreferenced variables or parameters which are assigned/initialized. "4189" # Don't warn about not-explicitly-handled enumeration identifiers "4061" # Ignore automatic padding warnings. "4820" # Ignore assignment/move/copy being implicit '= delete;'. "4623" "4625" "4626" "5026" "5027" # Relative include paths are fine. "4464" # Buggy warning: printf expects string literal "4774" # Buggy warning: subobject initialization should be wrapped in braces "5246" # Ignore undefined, unused or unreferenced pre-processor macros "4688" # Ignore non-inlined functions "4710" # Ignore Spectre mitigation insertions "5045" # Ignore inserted padding. "4324" # Ignore non-standard nameless struct/union. "4201" # Intended behavior: Replace undefined macros with 0 "4668" ) foreach(WARN ${DISABLED_WARNINGS}) target_compile_options(${TARGET_NAME} PRIVATE "/wd${WARN}") endforeach() # - Require enabled instruction sets. if(D_PLATFORM_ARCH_X86) if(${PREFIX}TARGET_X86_64_V4) target_compile_options(${TARGET_NAME} PRIVATE "/arch:AVX512") elseif(${PREFIX}TARGET_X86_64_V3) target_compile_options(${TARGET_NAME} PRIVATE "/arch:AVX2") elseif(${PREFIX}TARGET_X86_64_V2_EX) target_compile_options(${TARGET_NAME} PRIVATE "/arch:AVX") elseif(${PREFIX}TARGET_X86_64_V2) target_compile_options(${TARGET_NAME} PRIVATE "/d2archSSE42") elseif(${PREFIX}TARGET_X86_64) #target_compile_options(${TARGET_NAME} PRIVATE "/arch:SSE2") endif() endif() # - Use fast unordered math if possible. if(${PREFIX}ENABLE_FASTMATH) target_compile_options(${TARGET_NAME} PRIVATE "/fp:fast") else() target_compile_options(${TARGET_NAME} PRIVATE "/fp:precise") if(MSVC_VERSION GREATER 1930) # Keep original behavior in VS2022 and up. target_compile_options(${TARGET_NAME} PRIVATE "/fp:contract") endif() endif() # - Disable incremental builds target_compile_options(${TARGET_NAME} PRIVATE "/INCREMENTAL:NO") # - Enable C++ exceptions with SEH exceptions. target_compile_options(${TARGET_NAME} PRIVATE "/EHa") # - Enable multi-processor compiling. target_compile_options(${TARGET_NAME} PRIVATE "/MP") # - Enable updated __cplusplus macro target_compile_options(${TARGET_NAME} PRIVATE "/Zc:__cplusplus") elseif(D_PLATFORM_LINUX AND ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))) # GCC/Clang # - Enable all warnings. target_compile_options(${TARGET_NAME} PRIVATE "-Wall") target_compile_options(${TARGET_NAME} PRIVATE "-Wextra") # - Disable useless warnings set(DISABLED_WARNINGS # Don't warn about unused variables, parameters, labels, functions, or typedefs. "unused-function" "unused-label" "unused-local-typedefs" "unused-parameter" "unused-result" "unused-const-variable" "unused-variable" "unused-value" # Don't warn about unreferenced variables or parameters which are assigned/initialized. "unused-but-set-parameter" "unused-but-set-variable" # Don't warn about not-explicitly-handled enumeration identifiers "switch" # Ignore automatic padding warnings. "padded" # Ignore implicit '= delete;'. # Ignore extra arguments for printf "format-extra-args" # Ignore undefined, unused or unreferenced pre-processor macros "unused-macros" ) foreach(WARN ${DISABLED_WARNINGS}) target_compile_options(${TARGET_NAME} PRIVATE "-Wno-${WARN}") endforeach() # - Require enabled instruction sets. if(${PREFIX}TARGET_NATIVE) target_compile_options(${TARGET_NAME} PRIVATE "-march=native" ) elseif(D_PLATFORM_ARCH_X86) if(${PREFIX}TARGET_X86_64_V4) target_compile_options(${TARGET_NAME} PRIVATE "-march=x86-64-v4" ) elseif(${PREFIX}TARGET_X86_64_V3) target_compile_options(${TARGET_NAME} PRIVATE "-march=x86-64-v3" ) elseif(${PREFIX}TARGET_X86_64_V2_EX) target_compile_options(${TARGET_NAME} PRIVATE "-march=x86-64-v2" "-mavx" "-mbmi2" "-mbmi" "-mfma" "-mf16c" "-mmovbe" "-mpclmul" "-mpopcnt" ) elseif(${PREFIX}TARGET_X86_64_V2) target_compile_options(${TARGET_NAME} PRIVATE "-march=x86-64-v2" ) elseif(${PREFIX}TARGET_X86_64) target_compile_options(${TARGET_NAME} PRIVATE "-march=x86-64" ) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(${TARGET_NAME} PRIVATE "-mtune=generic" ) else() target_compile_options(${TARGET_NAME} PRIVATE "-mtune=x86-64" ) endif() endif() # - Use fast unordered math if possible. if(${PREFIX}ENABLE_FASTMATH) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(${TARGET_NAME} PRIVATE "-ffast-math" ) else() target_compile_options(${TARGET_NAME} PRIVATE "-ffp-model=fast" ) endif() else() target_compile_options(${TARGET_NAME} PRIVATE "-ffp-model=precise" ) endif() # - Don't export by default, require attributes. # add_compile_options("-fvisibility=hidden") elseif(D_PLATFORM_MAC AND (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) # AppleClang # - Enable most useful warnings. target_compile_options(${TARGET_NAME} PRIVATE "-Wall") target_compile_options(${TARGET_NAME} PRIVATE "-Wextra") # - Disable useless warnings set(DISABLED_WARNINGS # Don't warn about unused variables, parameters, labels, functions, or typedefs. "unused-function" "unused-label" "unused-local-typedefs" "unused-parameter" "unused-result" "unused-const-variable" "unused-variable" "unused-value" # Don't warn about unreferenced variables or parameters which are assigned/initialized. "unused-but-set-parameter" "unused-but-set-variable" # Don't warn about not-explicitly-handled enumeration identifiers "switch" # Ignore automatic padding warnings. "padded" # Ignore implicit '= delete;'. # Ignore extra arguments for printf "format-extra-args" # Ignore undefined, unused or unreferenced pre-processor macros "unused-macros" ) foreach(WARN ${DISABLED_WARNINGS}) target_compile_options(${TARGET_NAME} PRIVATE "-Wno-${WARN}") endforeach() # - Require enabled instruction sets. if(${PREFIX}TARGET_NATIVE) target_compile_options(${TARGET_NAME} PRIVATE "-march=native" ) message(WARNING "Targeting native architecture. Binaries will not be distributable to other systems!") endif() # - Use fast unordered math if possible. # FIXME: Appears to not be supported. # - Don't export by default, require attributes. # add_compile_options("-fvisibility=hidden") endif() endfunction() function(streamfx_sanitize_name TEXT _OUTPUT_NAME _OUTPUT_TARGET _OUTPUT_OPTION) string(REGEX REPLACE "^[ \t]+" "" TEXT "${TEXT}") string(REGEX REPLACE "[ \t]+$" "" TEXT "${TEXT}") string(REGEX REPLACE "[^a-zA-Z0-9_-]+" "_" TEXT2 "${TEXT}") string(REGEX REPLACE "_[_]+" "_" TEXT2 "${TEXT2}") string(TOUPPER "${TEXT2}" TEXT3) set(${_OUTPUT_NAME} "${TEXT}" PARENT_SCOPE) set(${_OUTPUT_TARGET} "${TEXT2}" PARENT_SCOPE) set(${_OUTPUT_OPTION} "${TEXT3}" PARENT_SCOPE) endfunction() function(streamfx_add_component COMPONENT_NAME) cmake_parse_arguments(PARSE_ARGV 1 _ARG "" "RESOLVER" "" ) # Sanitize name for further use. streamfx_sanitize_name("${COMPONENT_NAME}" COMPONENT_NAME COMPONENT_SANITIZED_NAME COMPONENT_OPTION_NAME) set(COMPONENT_OPTION "${PREFIX}COMPONENT_${COMPONENT_OPTION_NAME}") set(COMPONENT_OPTION "${COMPONENT_OPTION}" PARENT_SCOPE) # Define the necessary targets. set(COMPONENT_TARGET "StreamFX_${COMPONENT_SANITIZED_NAME}") set(COMPONENT_TARGET "${COMPONENT_TARGET}" PARENT_SCOPE) set(COMPONENT_ALIAS "StreamFX::${COMPONENT_SANITIZED_NAME}") set(COMPONENT_ALIAS "${COMPONENT_ALIAS}" PARENT_SCOPE) streamfx_add_library(${COMPONENT_TARGET} STATIC EXCLUDE_FROM_ALL) add_library(${COMPONENT_ALIAS} ALIAS ${COMPONENT_TARGET}) set_target_properties(${COMPONENT_TARGET} PROPERTIES COMPONENT_LABEL "${COMPONENT_NAME}" COMPONENT_NAME "${COMPONENT_ALIAS}" COMPONENT_OPTION "${COMPONENT_OPTION}" ) # Always depend on StreamFX::Core target_link_libraries(${COMPONENT_TARGET} PUBLIC $) # Register the component globally. get_target_property(_DEPENDS StreamFX COMPONENT_DEPENDS) if(_DEPENDS) list(APPEND _DEPENDS "${COMPONENT_SANITIZED_NAME}") else() set(_DEPENDS "${COMPONENT_SANITIZED_NAME}") endif() set_target_properties(StreamFX PROPERTIES COMPONENT_DEPENDS "${_DEPENDS}" ) # If there is a resolver function, register it. if(_ARG_RESOLVER) set_target_properties(${COMPONENT_TARGET} PROPERTIES COMPONENT_RESOLVER "${_ARG_RESOLVER}" ) endif() # Allow disabling this component. set(${COMPONENT_OPTION} ON CACHE BOOL "Enable the ${COMPONENT_NAME} component?") set(${COMPONENT_OPTION}_DISABLED OFF CACHE INTERNAL "Disable the ${_NAME} component?" FORCE) # Add common include directories target_include_directories(${COMPONENT_TARGET} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/source" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) # Add files in common places. file(GLOB_RECURSE TEMPLATES FOLLOW_SYMLINKS CONFIGURE_DEPENDS "templates/*") source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/templates" PREFIX "Templates" FILES ${TEMPLATES}) file(GLOB_RECURSE DATA FOLLOW_SYMLINKS CONFIGURE_DEPENDS "data/*") source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/data" PREFIX "Data" FILES ${DATA}) file(GLOB_RECURSE SOURCE_PRIVATE FOLLOW_SYMLINKS CONFIGURE_DEPENDS "ui/*") source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/ui" PREFIX "User-Interface Files" FILES ${USERINTERFACE}) file(GLOB_RECURSE SOURCE_PRIVATE FOLLOW_SYMLINKS CONFIGURE_DEPENDS "source/*") source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/source" PREFIX "Private Files" FILES ${SOURCE_PRIVATE}) file(GLOB_RECURSE UI_PRIVATE FOLLOW_SYMLINKS CONFIGURE_DEPENDS "source/ui/*") file(GLOB_RECURSE GENERATED_PRIVATE FOLLOW_SYMLINKS CONFIGURE_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/source/*") source_group(TREE "${CMAKE_CURRENT_BINARY_DIR}/source" PREFIX "Private Files/Generated" FILES ${GENERATED_PRIVATE}) file(GLOB_RECURSE SOURCE_PUBLIC FOLLOW_SYMLINKS CONFIGURE_DEPENDS "include/*") source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/include" PREFIX "Public Files" FILES ${SOURCE_PUBLIC}) file(GLOB_RECURSE UI_PUBLIC FOLLOW_SYMLINKS CONFIGURE_DEPENDS "include/ui/*") file(GLOB_RECURSE GENERATED_PUBLIC FOLLOW_SYMLINKS CONFIGURE_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/*") source_group(TREE "${CMAKE_CURRENT_BINARY_DIR}/include" PREFIX "Public Files/Generated" FILES ${GENERATED_PUBLIC}) target_sources(${COMPONENT_TARGET} PRIVATE ${DATA} ${TEMPLATES} ${USERINTERFACE} ${SOURCE_PRIVATE} ${GENERATED_PRIVATE} ${SOURCE_PUBLIC} ${GENERATED_PUBLIC} ) # Ignore data only files. set_source_files_properties( ${DATA} ${TEMPLATES} ${USERINTERFACE} PROPERTIES HEADER_FILE_ONLY ON ) # Enable Qt if needed set_target_properties(${TARGET_NAME} PROPERTIES AUTOUIC ON AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_SOURCE_DIR}/ui" AUTOMOC ON AUTORCC ON AUTOGEN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" ) set_source_files_properties( ${TEMPLATES} ${SOURCE_PRIVATE} ${GENERATED_PRIVATE} ${SOURCE_PUBLIC} ${GENERATED_PUBLIC} PROPERTIES SKIP_AUTOGEN ON SKIP_AUTOMOC ON SKIP_AUTORCC ON SKIP_AUTOUIC ON ) set_source_files_properties( ${UI_PRIVATE} ${UI_PUBLIC} PROPERTIES SKIP_AUTOGEN OFF SKIP_AUTOMOC OFF SKIP_AUTORCC OFF SKIP_AUTOUIC OFF ) endfunction() function(streamfx_has_component _NAME _OUTPUT) streamfx_sanitize_name("${_NAME}" _NAME _TARGET _OPTION) set(${_OUTPUT} OFF PARENT_SCOPE) if(TARGET "StreamFX_${_TARGET}") set(${_OUTPUT} ON PARENT_SCOPE) endif() endfunction() function(streamfx_enabled_component _NAME _OUTPUT) streamfx_sanitize_name("${_NAME}" _NAME _TARGET _OPTION) if(NOT TARGET "StreamFX_${_TARGET}") message(FATAL_ERROR "Unknown component '${_NAME}'.") endif() get_target_property(_OPTION "StreamFX_${_TARGET}" COMPONENT_OPTION) set(${_OUTPUT} OFF PARENT_SCOPE) if(${_OPTION} AND NOT (${_OPTION}_DISABLED)) set(${_OUTPUT} ON PARENT_SCOPE) endif() endfunction() # Use this to add a dependency on another component. function(streamfx_add_component_dependency _NAME) cmake_parse_arguments(PARSE_ARGV 1 _ARG "OPTIONAL" "" "" ) streamfx_sanitize_name("${_NAME}" _NAME _TARGET _OPTION) set(DEPENDENCY "${_TARGET}") if(_ARG_OPTIONAL) list(APPEND DEPENDENCY "OPTIONAL") endif() string(REPLACE ";" "\\;" DEPENDENCY "${DEPENDENCY}") get_target_property(DEPENDS "${COMPONENT_TARGET}" COMPONENT_DEPENDS) if(DEPENDS) list(APPEND DEPENDS "${DEPENDENCY}") else() set(DEPENDS "${DEPENDENCY}") endif() set_target_properties("${COMPONENT_TARGET}" PROPERTIES COMPONENT_DEPENDS "${DEPENDS}") endfunction() # Use this to disable a component via script. function(streamfx_disable_component _NAME) cmake_parse_arguments(PARSE_ARGV 1 _ARG "" "REASON" "" ) streamfx_sanitize_name("${_NAME}" _NAME _TARGET _OPTION) if(NOT (_TARGET MATCHES "^StreamFX_")) set(_TARGET "StreamFX_${_TARGET}") endif() # If the component doesn't exist, skip it. if(NOT TARGET ${_TARGET}) message(WARNING "Not disabling invalid component '${_NAME}'.") return() endif() get_target_property(_LABEL ${_TARGET} COMPONENT_LABEL) get_target_property(_OPTION ${_TARGET} COMPONENT_OPTION) CacheSet(${_OPTION}_DISABLED ON) if(_ARG_REASON) message(STATUS "[${_LABEL}] Disabled due to: ${_ARG_REASON}") endif() endfunction() ################################################################################ # Register Library ################################################################################ streamfx_add_library(StreamFX MODULE) # We are a module for libOBS. # Ensure there is at least one file. if(D_PLATFORM_WINDOWS) target_sources(StreamFX PRIVATE "source/windll.cpp" ) else() target_sources(StreamFX PRIVATE "/dev/null" ) endif() # version.rc (can't be part of a static library) if(D_PLATFORM_WINDOWS) # Windows-exclusive # Version Resource set(PROJECT_PRODUCT_NAME "${PROJECT_FULL_NAME}") set(PROJECT_COMPANY_NAME "${PROJECT_AUTHORS}") set(PROJECT_COPYRIGHT "${PROJECT_AUTHORS} © ${PROJECT_COPYRIGHT_YEARS}") set(PROJECT_LEGAL_TRADEMARKS_1 "") set(PROJECT_LEGAL_TRADEMARKS_2 "") configure_file( "templates/windows/version.rc.in" "version.rc" ) target_sources(StreamFX PRIVATE "${PROJECT_BINARY_DIR}/version.rc" ) endif() set_target_properties(StreamFX PROPERTIES # Version MACHO_COMPATIBILITY_VERSION ${_VERSION_MAJOR}.${_VERSION_MINOR} SOVERSION ${_VERSION_MAJOR}.${_VERSION_MINOR} MACHO_CURRENT_VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION} ) ################################################################################ # Add Core ################################################################################ streamfx_add_library(StreamFX_Core STATIC EXCLUDE_FROM_ALL) add_library(StreamFX::Core ALIAS StreamFX_Core) # Enable Qt set_target_properties(StreamFX_Core PROPERTIES AUTOUIC ON AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_SOURCE_DIR}/ui" AUTOMOC ON AUTORCC ON AUTOGEN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/qt" ) # Default Links target_link_libraries(StreamFX_Core PUBLIC CURL::libcurl Qt6::Core Qt6::Gui Qt6::Widgets OBS::obs-frontend-api OBS::libobs ) # Default Include Dirs target_include_directories(StreamFX_Core PRIVATE "${PROJECT_SOURCE_DIR}/source/ui" "${PROJECT_BINARY_DIR}/qt" PUBLIC "${PROJECT_SOURCE_DIR}/source" "${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/generated" ${JSON_INCLUDE_DIR} ) # Templates file(GLOB_RECURSE PROJECT_TEMPLATES FOLLOW_SYMLINKS CONFIGURE_DEPENDS "templates/*") source_group(TREE "${PROJECT_SOURCE_DIR}/templates" PREFIX "Templates" FILES ${PROJECT_TEMPLATES}) set_source_files_properties(${PROJECT_TEMPLATES} PROPERTIES HEADER_FILE_ONLY ON SKIP_AUTOGEN ON SKIP_AUTOMOC ON SKIP_AUTORCC ON SKIP_AUTOUIC ON ) target_sources(StreamFX_Core PRIVATE ${PROJECT_TEMPLATES}) # Generated #- config.hpp configure_file( "templates/config.hpp.in" "generated/config.hpp" ) #- version.hpp configure_file( "templates/version.hpp.in" "generated/version.hpp" ) #- module.cpp configure_file( "templates/module.cpp.in" "generated/module.cpp" ) #- Add all generated files file(GLOB_RECURSE PROJECT_PRIVATE_GENERATED FOLLOW_SYMLINKS CONFIGURE_DEPENDS "${PROJECT_BINARY_DIR}/generated/*") source_group(TREE "${PROJECT_BINARY_DIR}/generated" PREFIX "Private Files/Generated" FILES ${PROJECT_PRIVATE_GENERATED}) set_source_files_properties(${PROJECT_PRIVATE_GENERATED} PROPERTIES SKIP_AUTOGEN ON SKIP_AUTOMOC ON SKIP_AUTORCC ON SKIP_AUTOUIC ON ) target_sources(StreamFX_Core PRIVATE ${PROJECT_PRIVATE_GENERATED}) # Source file(GLOB_RECURSE PROJECT_PRIVATE FOLLOW_SYMLINKS CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/source/*") list(REMOVE_ITEM PROJECT_PRIVATE "${PROJECT_SOURCE_DIR}/source/windll.cpp") # Only used by StreamFX, not part of StreamFX::Core source_group(TREE "${PROJECT_SOURCE_DIR}/source" PREFIX "Private Files" FILES ${PROJECT_PRIVATE}) set_source_files_properties(${PROJECT_PRIVATE} PROPERTIES SKIP_AUTOGEN ON SKIP_AUTOMOC ON SKIP_AUTORCC ON SKIP_AUTOUIC ON ) target_sources(StreamFX_Core PRIVATE ${PROJECT_PRIVATE}) # User-Interface Source file(GLOB_RECURSE PROJECT_PRIVATE_UI FOLLOW_SYMLINKS CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/source/ui/*") list(APPEND PROJECT_PRIVATE_UI "${PROJECT_SOURCE_DIR}/source/obs/browser/obs-browser-panel.hpp") set_source_files_properties(${PROJECT_PRIVATE_UI} PROPERTIES SKIP_AUTOGEN OFF SKIP_AUTOMOC OFF SKIP_AUTORCC OFF SKIP_AUTOUIC OFF ) # User-Interface Definitions file(GLOB_RECURSE PROJECT_UI FOLLOW_SYMLINKS CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/ui/*") source_group(TREE "${PROJECT_SOURCE_DIR}/ui" PREFIX "User-Interface Files" FILES ${PROJECT_UI}) set_source_files_properties(${PROJECT_UI} PROPERTIES HEADER_FILE_ONLY ON ) target_sources(StreamFX_Core PRIVATE ${PROJECT_UI}) # Windows if(D_PLATFORM_WINDOWS) # Disable/Enable a ton of things. target_compile_definitions(StreamFX_Core PRIVATE # Microsoft Visual C++ _CRT_SECURE_NO_WARNINGS _ENABLE_EXTENDED_ALIGNED_STORAGE # windows.h WIN32_LEAN_AND_MEAN NOGPICAPMASKS NOVIRTUALKEYCODES #NOWINMESSAGES NOWINSTYLES NOSYSMETRICS NOMENUS NOICONS NOKEYSTATES NOSYSCOMMANDS NORASTEROPS #NOSHOWWINDOW NOATOM NOCLIPBOARD NOCOLOR #NOCTLMGR NODRAWTEXT #NOGDI NOKERNEL #NOUSER #NONLS NOMB NOMEMMGR NOMETAFILE NOMINMAX #NOMSG NOOPENFILE NOSCROLL NOSERVICE NOSOUND #NOTEXTMETRIC NOWH NOWINOFFSETS NOCOMM NOKANJI #NOHELP NOPROFILER NODEFERWINDOWPOS NOMCX NOIME NOMDI NOINOUT ) endif() # OpenGL via GLAD if(NOT TARGET StreamFX::GLAD) add_library(StreamFX_GLAD STATIC "${PROJECT_SOURCE_DIR}/third-party/khronos/glad/src/gl.c" ) target_include_directories(StreamFX_GLAD PUBLIC "${PROJECT_SOURCE_DIR}/third-party/khronos/glad/include" ) if(D_PLATFORM_WINDOWS) target_sources(StreamFX_GLAD PRIVATE "${PROJECT_SOURCE_DIR}/third-party/khronos/glad/src/wgl.c" ) elseif(D_PLATFORM_LINUX) target_sources(StreamFX_GLAD PRIVATE "${PROJECT_SOURCE_DIR}/third-party/khronos/glad/src/glx.c" ) endif() # Requires for shared/static mixing. set_property(TARGET StreamFX_GLAD PROPERTY POSITION_INDEPENDENT_CODE ON) add_library(StreamFX::GLAD ALIAS StreamFX_GLAD) endif() target_link_libraries(StreamFX_Core PUBLIC StreamFX::GLAD) ################################################################################ # Components ################################################################################ # As the above monolithic approach started to show its weaknesses, it was time # for an improved system which suffers under less issues. This new component # system should address the main necessary parts, #- Registration file(GLOB COMPONENTS RELATIVE ${PROJECT_SOURCE_DIR} CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/components/*) foreach(COMPONENT ${COMPONENTS}) # Ignore directories with no CMakeLists.txt if(EXISTS "${PROJECT_SOURCE_DIR}/${COMPONENT}/CMakeLists.txt") add_subdirectory(${COMPONENT} EXCLUDE_FROM_ALL) endif() endforeach() get_target_property(_UNRESOLVED StreamFX COMPONENT_DEPENDS) set(_RESOLVED "") set(_DISABLED "") #- Cleanup list(REMOVE_DUPLICATES _UNRESOLVED) foreach(_ENTITY ${_UNRESOLVED}) # Remove any invalid entries. if(NOT TARGET "StreamFX_${_ENTITY}") message(WARNING "Encountered invalid component '${_ENTITY}', removing...") list(REMOVE_ITEM _UNRESOLVED "${_ENTITY}") continue() endif() endforeach() #- Resolve Dependencies in a loop set(_UNRESOLVED_LOOP 0) while(_UNRESOLVED) MATH(EXPR _UNRESOLVED_LOOP "${_UNRESOLVED_LOOP}+1") if(_UNRESOLVED_LOOP GREATER_EQUAL 10) message(FATAL_ERROR "Infinite loop while resolving components: ${_UNRESOLVED}") endif() # Attempt to resolve while there are still unresolved entries. foreach(_ENTITY ${_UNRESOLVED}) set(RENTITY "StreamFX_${_ENTITY}") get_target_property(_ALIAS "${RENTITY}" COMPONENT_NAME) get_target_property(_LABEL "${RENTITY}" COMPONENT_LABEL) get_target_property(_OPTION "${RENTITY}" COMPONENT_OPTION) get_target_property(_DEPENDS "${RENTITY}" COMPONENT_DEPENDS) get_target_property(_RESOLVER "${RENTITY}" COMPONENT_RESOLVER) # Remove any that have been disabled. if(NOT ${_OPTION}) message(STATUS "[${_LABEL}] Disabled by user.") list(REMOVE_ITEM _UNRESOLVED "${_ENTITY}") list(APPEND _DISABLED "${_ENTITY}") continue() elseif(${_OPTION}_DISABLED) # Test for pre-resolve disabling. message(STATUS "[${_LABEL}] Disabled by build script.") list(REMOVE_ITEM _UNRESOLVED "${_ENTITY}") list(APPEND _DISABLED "${_ENTITY}") continue() endif() # Check if all dependencies are resolved. set(_HAS_UNRESOLVED_DEPENDS OFF) set(_HAS_DISABLED_DEPENDS OFF) set(_HAS_UNRESOLVED_OPTIONAL_DEPENDS OFF) if(_DEPENDS) foreach(_DEPEND ${_DEPENDS}) list(GET _DEPEND 0 _ENTITY2) set(RENTITY2 "StreamFX_${_ENTITY2}") get_target_property(_ENTITY2_LABEL "${RENTITY2}" COMPONENT_LABEL) if(NOT ("OPTIONAL" IN_LIST _DEPEND)) if("${_ENTITY2}" IN_LIST _DISABLED) message("[${_LABEL}] Required dependency '${_ENTITY2_LABEL}' is disabled.") set(_HAS_DISABLED_DEPENDS ON) endif() if("${_ENTITY2}" IN_LIST _UNRESOLVED) set(_HAS_UNRESOLVED_DEPENDS ON) endif() else() if("${_ENTITY2}" IN_LIST _UNRESOLVED) set(_HAS_UNRESOLVED_OPTIONAL_DEPENDS ON) endif() endif() endforeach() list(JOIN _DEPENDS ", " _DEPENDS) else() set(_DEPENDS "") endif() if(_HAS_DISABLED_DEPENDS) # A required dependency is disabled, so disable this entry. message(STATUS "[${_LABEL}] Disabled by dependency.") list(REMOVE_ITEM _UNRESOLVED "${_ENTITY}") list(APPEND _DISABLED "${_ENTITY}") streamfx_disable_component(${_ENTITY}) continue() elseif(_HAS_UNRESOLVED_DEPENDS) continue() elseif(_HAS_UNRESOLVED_OPTIONAL_DEPENDS AND (_UNRESOLVED_LOOP LESS 8)) # Temporarily skip this element while there are still remaining loops. continue() endif() # Call Resolver function if(_RESOLVER) set(COMPONENT_TARGET "${RENTITY}") set(COMPONENT_ALIAS "${_ALIAS}") set(COMPONENT_OPTION "${_OPTION}") cmake_language(CALL ${_RESOLVER}) endif() if(${_OPTION}_DISABLED) # Test for resolve disabling. message(STATUS "[${_LABEL}] Disabled by resolver.") list(REMOVE_ITEM _UNRESOLVED "${_ENTITY}") list(APPEND _DISABLED "${_ENTITY}") continue() endif() # Finally, if everything went well, we now have a resolved entity. list(REMOVE_ITEM _UNRESOLVED "${_ENTITY}") list(APPEND _RESOLVED "${_ENTITY}") message(STATUS "[${_LABEL}] Enabled. Depends: ${_DEPENDS}") endforeach() endwhile() #- Linking foreach(_ENTITY ${_RESOLVED}) set(RENTITY "StreamFX_${_ENTITY}") get_target_property(_DEPENDS "${RENTITY}" COMPONENT_DEPENDS) # Finally if everything is correct, do things. if(_DEPENDS) foreach(_DEPEND ${_DEPENDS}) list(GET _DEPEND 0 _ENTITY2) set(RENTITY2 "StreamFX_${_ENTITY2}") streamfx_enabled_component(${_ENTITY2} T_CHECK) if(T_CHECK) target_link_libraries(${RENTITY} PUBLIC $) endif() endforeach() endif() target_link_libraries(StreamFX PUBLIC $) endforeach() target_link_libraries(StreamFX PUBLIC $) ################################################################################ # Resources ################################################################################ # This needs to be handled separately, as CMake does not propagate Resources # between dependencies. This feels like a bug in CMake, but it is better than # not doing anything at all. Might eventually need to revise this when we start # generating data files. # Data file(GLOB_RECURSE PROJECT_DATA "data/*") source_group(TREE "${PROJECT_SOURCE_DIR}/data" PREFIX "Data" FILES ${PROJECT_DATA}) if(D_PLATFORM_MAC) foreach(FILE IN LISTS PROJECT_DATA) # Data location cmake_path(ABSOLUTE_PATH FILE OUTPUT_VARIABLE FILE_PATH) cmake_path(RELATIVE_PATH FILE_PATH BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/data/") cmake_path(GET FILE_PATH PARENT_PATH FILE_PATH) set_source_files_properties("${FILE}" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/${FILE_PATH}" ) endforeach() endif() # Media file(GLOB_RECURSE PROJECT_MEDIA "media/*") source_group(TREE "${PROJECT_SOURCE_DIR}/media" PREFIX "Media" FILES ${PROJECT_MEDIA}) if(D_PLATFORM_MAC) #- Bundle Icon location set_source_files_properties("media/icon.png" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources" ) endif() # Add all Resources to StreamFX set_source_files_properties(${PROJECT_DATA} ${PROJECT_MEDIA} PROPERTIES HEADER_FILE_ONLY ON ) target_sources(StreamFX PRIVATE ${PROJECT_DATA} ${PROJECT_MEDIA}) ################################################################################ # Installation ################################################################################ if(STANDALONE) if(D_PLATFORM_WINDOWS) install( TARGETS StreamFX RUNTIME DESTINATION "obs-plugins/${D_PLATFORM_BITS}bit/" COMPONENT StreamFX LIBRARY DESTINATION "obs-plugins/${D_PLATFORM_BITS}bit/" COMPONENT StreamFX ) install( DIRECTORY "data/" DESTINATION "data/obs-plugins/StreamFX/" ) if(MSVC) install( FILES $ DESTINATION "obs-plugins/${D_PLATFORM_BITS}bit/" OPTIONAL ) endif() elseif(D_PLATFORM_LINUX) if(STRUCTURE_PACKAGEMANAGER) install( TARGETS StreamFX RUNTIME DESTINATION "lib/obs-plugins/" COMPONENT StreamFX LIBRARY DESTINATION "lib/obs-plugins/" COMPONENT StreamFX PERMISSIONS WORLD_EXECUTE;WORLD_READ;OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;GROUP_EXECUTE;GROUP_READ;GROUP_WRITE ) install( DIRECTORY "data/" DESTINATION "share/obs/obs-plugins/StreamFX" COMPONENT StreamFX FILE_PERMISSIONS WORLD_EXECUTE;WORLD_READ;OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;GROUP_EXECUTE;GROUP_READ;GROUP_WRITE DIRECTORY_PERMISSIONS WORLD_EXECUTE;WORLD_READ;OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;GROUP_EXECUTE;GROUP_READ;GROUP_WRITE ) else() install( TARGETS StreamFX RUNTIME DESTINATION "plugins/StreamFX/bin/${D_PLATFORM_BITS}bit/" COMPONENT StreamFX LIBRARY DESTINATION "plugins/StreamFX/bin/${D_PLATFORM_BITS}bit/" COMPONENT StreamFX PERMISSIONS WORLD_EXECUTE;WORLD_READ;OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;GROUP_EXECUTE;GROUP_READ;GROUP_WRITE ) install( DIRECTORY "data/" DESTINATION "plugins/StreamFX/data/" COMPONENT StreamFX FILE_PERMISSIONS WORLD_EXECUTE;WORLD_READ;OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;GROUP_EXECUTE;GROUP_READ;GROUP_WRITE DIRECTORY_PERMISSIONS WORLD_EXECUTE;WORLD_READ;OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;GROUP_EXECUTE;GROUP_READ;GROUP_WRITE ) endif() elseif(D_PLATFORM_MAC) # Bundle Information set(MACOSX_BUNDLE_BUNDLE_NAME "StreamFX") set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}") set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT}") set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_IDENTIFER}") set(MACOSX_BUNDLE_ICON_FILE "icon.png") set(MACOSX_BUNDLE_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${_VERSION}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") set_target_properties(StreamFX PROPERTIES BUNDLE ON BUNDLE_EXTENSION "plugin" OUTPUT_NAME StreamFX MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/templates/macos/Info.plist.in" XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${MACOSX_BUNDLE_GUI_IDENTIFIER}" ) install( TARGETS StreamFX RUNTIME DESTINATION "." COMPONENT StreamFX LIBRARY DESTINATION "." COMPONENT StreamFX BUNDLE DESTINATION "." COMPONENT StreamFX PERMISSIONS WORLD_EXECUTE;WORLD_READ;OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;GROUP_EXECUTE;GROUP_READ;GROUP_WRITE ) endif() else() if(COMMAND setup_plugin_target) setup_plugin_target(StreamFX) # Seems like we lost the ability to customize which directoy resources are in, and instead are forced to use '/data'. elseif(COMMAND install_obs_plugin_with_data) install_obs_plugin_with_data(StreamFX data) endif() endif() ################################################################################ # Packaging ################################################################################ if(STANDALONE) # Packaging if(NOT PACKAGE_SUFFIX) set(_PACKAGE_SUFFIX_OVERRIDE "${_VERSION_THIN}") else() set(_PACKAGE_SUFFIX_OVERRIDE "${PACKAGE_SUFFIX}") endif() set(_PACKAGE_FULL_NAME "${PACKAGE_PREFIX}/${PACKAGE_NAME}-${_PACKAGE_SUFFIX_OVERRIDE}") if(D_PLATFORM_WINDOWS) ## Installer (InnoSetup) get_filename_component(ISS_FILES_DIR "${CMAKE_INSTALL_PREFIX}" ABSOLUTE) file(TO_NATIVE_PATH "${ISS_FILES_DIR}" ISS_FILES_DIR) get_filename_component(ISS_PACKAGE_DIR "${PACKAGE_PREFIX}" ABSOLUTE) file(TO_NATIVE_PATH "${ISS_PACKAGE_DIR}" ISS_PACKAGE_DIR) get_filename_component(ISS_SOURCE_DIR "${PROJECT_SOURCE_DIR}" ABSOLUTE) file(TO_NATIVE_PATH "${ISS_SOURCE_DIR}" ISS_SOURCE_DIR) get_filename_component(ISS_MSVCHELPER_PATH "${msvc-redist-helper_BUILD_DIR}" ABSOLUTE) file(TO_NATIVE_PATH "${ISS_MSVCHELPER_PATH}" ISS_MSVCHELPER_PATH) configure_file( "templates/windows/installer.iss.in" "installer.iss" ) elseif(D_PLATFORM_MAC) # .pkg Installer set(PACKAGES_PATH_NAME "StreamFX") set(PACKAGES_PATH_NAME "${PACKAGES_PATH_NAME}.plugin") configure_file( "templates/macos/installer.pkgproj.in" "installer.pkgproj" ) elseif(D_PLATFORM_LINUX) add_custom_target( PACKAGE ${CMAKE_COMMAND} -E tar cfv "${_PACKAGE_FULL_NAME}.7z" --format=7zip -- "." WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}" ) endif() endif()