cmake_minimum_required(VERSION 3.22) project(looper VERSION 1.0.0 LANGUAGES C CXX) option(SHARED_LIB "Enables building as a shared library." OFF) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten") set(EMSCRIPTEN ON) message("Building for WASM.") endif() option(TESTS "Enables unit testing" OFF) find_package(Threads) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) include(SanitizerBuildTypes) sanitizer_build_types(ASAN AddressSanitizer LSAN LeakSanitizer MSAN MemorySanitizer TSAN ThreadSanitizer UBSAN UndefinedBehaviorSanitizer) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(SDL_MIXER_X_STATIC ON CACHE BOOL "") set(SDL_MIXER_X_SHARED OFF CACHE BOOL "") set(DOWNLOAD_AUDIO_CODECS_DEPENDENCY OFF CACHE BOOL "") set(AUDIO_CODECS_BUILD_LOCAL_SDL2 OFF CACHE BOOL "" FORCE) set(MIXERX_LGPL ON CACHE BOOL "" FORCE) set(USE_MIDI ON CACHE BOOL "" FORCE) set(USE_MIDI_NATIVE_ALT OFF CACHE BOOL "" FORCE) set(USE_MIDI_NATIVE OFF CACHE BOOL "" FORCE) set(USE_MIDI_TIMIDITY OFF CACHE BOOL "" FORCE) set(USE_MIDI_FLUIDLITE OFF CACHE BOOL "" FORCE) set(USE_MIDI_OPNMIDI OFF CACHE BOOL "" FORCE) set(USE_MIDI_ADLMIDI OFF CACHE BOOL "" FORCE) set(USE_MIDI_FLUIDSYNTH ON CACHE BOOL "" FORCE) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(BUILD_FB2K OFF CACHE BOOL "" FORCE) set(BUILD_CLI OFF CACHE BOOL "" FORCE) set(BUILD_WINAMP OFF CACHE BOOL "" FORCE) set(BUILD_XMPLAY OFF CACHE BOOL "" FORCE) set(BUILD_AUDACIOUS OFF CACHE BOOL "" FORCE) set(BUILD_V123 OFF CACHE BOOL "" FORCE) set(JSONCPP_WITH_TESTS OFF CACHE BOOL "" FORCE) set(BUILD_STATIC_LIBS ON CACHE BOOL "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(JSONCPP_WITH_PKGCONFIG_SUPPORT OFF CACHE BOOL "" FORCE) set(JSONCPP_WITH_CMAKE_PACKAGE OFF CACHE BOOL "" FORCE) if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) option(USE_PORTALS "Enables libportal" ON) option(ENABLE_DBUS "Enables DBus support" ON) endif() option(BUILD_LIBFMT "Builds libfmt" OFF) if(HAIKU) set(USE_CELT OFF CACHE BOOL "" FORCE) set(USE_SPEEX OFF CACHE BOOL "" FORCE) endif() if (DEFINED EMSCRIPTEN) option(FOR_WASMER "Enables support for wasmer" OFF) set(BUILD_STATIC ON CACHE BOOL "" FORCE) set(ENABLE_DBUS OFF CACHE BOOL "" FORCE) set(DOWNLOAD_AUDIO_CODECS_DEPENDENCY ON CACHE BOOL "" FORCE) set(SDL_MIXER_X_SHARED OFF CACHE BOOL "" FORCE) set(SDL_MIXER_X_STATIC ON CACHE BOOL "" FORCE) set(USE_OGG_VORBIS_STB ON CACHE BOOL "" FORCE) set(USE_OPUS OFF CACHE BOOL "" FORCE) set(USE_MODPLUG OFF CACHE BOOL "" FORCE) set(USE_GME OFF CACHE BOOL "" FORCE) set(USE_WAVPACK OFF CACHE BOOL "" FORCE) set(USE_XMP OFF CACHE BOOL "" FORCE) set(USE_MIDI_EDMIDI OFF CACHE BOOL "" FORCE) set(USE_PORTALS OFF CACHE BOOL "" FORCE) set(USE_SYSTEM_SDL2 ON CACHE BOOL "" FORCE) set(USE_MPEG OFF CACHE BOOL "" FORCE) set(USE_CELT OFF CACHE BOOL "" FORCE) set(USE_ATRAC9 OFF CACHE BOOL "" FORCE) set(USE_SPEEX OFF CACHE BOOL "" FORCE) set(BUILD_PROTOC OFF CACHE BOOL "" FORCE) # set(BUILD_LIBUPB OFF CACHE BOOL "" FORCE) set(USE_G719 OFF CACHE BOOL "" FORCE) set(EXTRA_FLAGS "-sUSE_VORBIS -sUSE_MPG123=1 -sUSE_ZLIB -sUSE_OGG=1 -sUSE_MODPLUG=1 -sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sNO_DISABLE_EXCEPTION_CATCHING") set(DEBUG_INFO ${CMAKE_BUILD_TYPE} STREQUAL Debug OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo) set(RELASE_OPTS ${CMAKE_BUILD_TYPE} STREQUAL Release OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo) set(PROFILE_ENABLED ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo) set(EXTRA_LINKER_FLAGS "-sALLOW_MEMORY_GROWTH=1 -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8,lengthBytesUTF8 -sEXPORTED_FUNCTIONS=_malloc,_main,_memset,_write_log,_write_logln") if (NOT FOR_WASMER) set(EXTRA_LINKER_FLAGS "${EXTRA_LINKER_FLAGS} -sASYNCIFY_IMPORTS=read_file,read_storage,write_storage,remove_storage,find_keys") set(EXTRA_FLAGS "${EXTRA_FLAGS} --shell-file=${CMAKE_CURRENT_SOURCE_DIR}/web/shell.html --js-library=${CMAKE_CURRENT_SOURCE_DIR}/web/api.js") endif() set(OPENMP OFF CACHE BOOL "" FORCE) set(SOUNDSTRETCH OFF CACHE BOOL "" FORCE) set(SOUNDTOUCH_DLL OFF CACHE BOOL "" FORCE) if(DEBUG_INFO) option(STACK_OVERFLOW_CHECK "Enables extra stack overflow checks" OFF) if(${STACK_OVERFLOW_CHECK}) set(EXTRA_LINKER_FLAGS "${EXTRA_LINKER_FLAGS} -sSTACK_OVERFLOW_CHECK=2") endif() set(EXTRA_FLAGS "${EXTRA_FLAGS} -g -gsource-map") set(EXTRA_LINKER_FLAGS "${EXTRA_LINKER_FLAGS} --emit-symbol-map -sASSERTIONS=1") endif() if(RELEASE_OPTS) set(EXTRA_FLAGS "${EXTRA_FLAGS} -flto") set(EXTRA_LINKER_FLAGS "${EXTRA_LINKER_FLAGS} --closure 1") endif() if(PROFILE_ENABLED) set(EXTRA_LINKER_FLAGS "${EXTRA_LINKER_FLAGS} --profiling --profiling-funcs") endif() set(CMAKE_FIND_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/web/cmake" ${CMAKE_FIND_ROOT_PATH}) set(SDL2_DIR ${CMAKE_CURRENT_SOURCE_DIR}/web/cmake) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_FLAGS}") set(EXTRA_LINKER_FLAGS "${EXTRA_LINKER_FLAGS} ${EXTRA_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EXTRA_LINKER_FLAGS}") endif() option(BUILD_SDL "Enables built-in SDL" OFF) option(BUILD_SDL_IMAGE "Enables built-in SDL_image" ${BUILD_SDL}) if(DEFINED ANDROID_NDK) set(USE_SYSTEM_SDL2 ON CACHE BOOL "" FORCE) set(AUDIOCODECS_BUILD_TIMIDITYSDL OFF CACHE BOOL "" FORCE) set(AUDIOCODECS_BUILD_OPUS OFF CACHE BOOL "" FORCE) set(USE_OGG_VORBIS_STB ON CACHE BOOL "" FORCE) set(AUDIOCODECS_BUILD_WAVPACK OFF CACHE BOOL "" FORCE) set(USE_WAVPACK OFF CACHE BOOL "" FORCE) set(USE_MODPLUG OFF CACHE BOOL "" FORCE) set(USE_XMP OFF CACHE BOOL "" FORCE) set(USE_GME OFF CACHE BOOL "" FORCE) set(USE_MIDI_EDMIDI OFF CACHE BOOL "" FORCE) set(USE_OPUS OFF CACHE BOOL "" FORCE) set(USE_MPEG OFF CACHE BOOL "" FORCE) set(USE_CELT OFF CACHE BOOL "" FORCE) set(USE_ATRAC9 OFF CACHE BOOL "" FORCE) set(USE_SPEEX OFF CACHE BOOL "" FORCE) set(USE_G719 OFF CACHE BOOL "" FORCE) set(USE_VORBIS OFF CACHE BOOL "" FORCE) set(BUILD_FMT ON CACHE BOOL "" FORCE) set(protobuf_INSTALL OFF CACHE BOOL "" FORCE) set(protobuf_BUILD_PROTOBUF_BINARIES ON CACHE BOOL "" FORE) set(protobuf_BUILD_PROTOC_BINARIES OFF CACHE BOOL "" FORCE) set(utf8_range_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) set(protobuf_BUILD_TESTS OFF CACHE BOOL "" FORCE) endif() if (BUILD_SDL) set(SDL_SHARED OFF CACHE BOOL "" FORCE) set(SDL_STATIC ON CACHE BOOL "" FORCE) set(SDL_TEST OFF CACHE BOOL "" FORCE) set(USE_SYSTEM_SDL2 ON CACHE BOOL "" FORCE) set(SDL_MIXER_X_STATIC ON CACHE BOOL "" FORCE) add_subdirectory(${CMAKE_SOURCE_DIR}/subprojects/SDL) install(TARGETS SDL2-static SDL2main EXPORT SDL_EXPORTS) install(EXPORT SDL_EXPORTS DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) export(TARGETS SDL2-static sdl-build-options SDL2main FILE ${CMAKE_CURRENT_BINARY_DIR}/SDL2Config.cmake NAMESPACE SDL2) set(SDL2_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake/built_sdl) endif() if (BUILD_SDL_IMAGE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(SDL2IMAGE_SAMPLES OFF CACHE BOOL "" FORCE) set(SDL2IMAGE_TESTS OFF CACHE BOOL "" FORCE) add_subdirectory(${CMAKE_SOURCE_DIR}/subprojects/SDL_image) set(SDL2_image_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake/built_sdl_image) endif() find_package(PkgConfig) #add_subdirectory(subprojects/jsoncpp) include(GNUInstallDirs) add_subdirectory(subprojects/SDL-Mixer-X) add_subdirectory(subprojects/vgmstream) if (DEFINED EMSCRIPTEN) set(EXTRA_LIBS ) else() if (BUILD_STATIC) set(EXTRA_LIBS libvgmstream) else() set(EXTRA_LIBS libvgmstream_shared) endif() endif() if(SDL_MIXER_X_STATIC) set(SDL_MIXER_X_TARGET SDL2_mixer_ext_Static) else() set(SDL_MIXER_X_TARGET SDL2_mixer_ext) set(EXTRA_LIBS ${EXTRA_LIBS} ${SDL_MIXER_X_TARGET}) endif() add_custom_target(looper_assets COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/assets/update_assets.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) find_package(Git) if (Git_FOUND) execute_process(COMMAND ./version.sh OUTPUT_VARIABLE TAG WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) message("" ${TAG}) else() set(TAG "unknown") endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(DEBUG ON) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(log) #execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/meson2cmake_cfg.py ${CMAKE_CURRENT_SOURCE_DIR}/config.meson.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.cmake.h.in) set(DEBUG_MODE_VALUE ${DEBUG}) configure_file(config.cmake.h.in config.h) if (BUILD_FMT) add_subdirectory(subprojects/fmt) else() find_package(fmt) endif() macro(target_pkgconfig) push_fnstack("target_pkgconfig") cmake_parse_arguments(PARSED_ARGS "OPTIONAL;PRIVATE;PUBLIC;INTERFACE" "PREFIX" "TARGETS;LIBRARIES" ${ARGN}) if (NOT ${PARSED_ARGS_PREFIX}) warning(MESSAGE "No prefix specified for reading thie PKGConfig variable to. Attempting to guess it from the library specification...") if (NOT ${PARSED_ARGS_LIBRARIES}) fatal(MESSAGE "... But it turns out no library specification was specified. Giving up.") else() set(PARSED_ARGS_PREFIX ${PARSED_ARGS_LIBRARIES}) endif() else() if(NOT ${PARSED_ARGS_NAME}) warning(MESSAGE "No library name was specified. Assuming it is the same as the prefix.") set(PARSED_ARGS_LIBSPEC ${PARSED_ARGS_LIBRARIES}) endif() endif() pkg_check_modules(${PARSED_ARGS_PREFIX} IMPORTED_TARGET ${PARSED_ARGS_LIBRARIES}) set(PARSED_ARGS_PUBLICITY PRIVATE) if(${PARSED_ARGS_PRIVATE}) set(PARSED_ARGS_PUBLICITY PRIVATE) elseif(${PARSED_ARGS_PUBLIC}) set(PARSED_ARGS_PUBLICITY PUBLIC) elseif(${PARSED_ARGS_INTERFACE}) set(PARSED_ARGS_PUBLICITY INTERFACE) endif() foreach(TARGET IN ITEMS ${PARSED_ARGS_TARGETS}) target_link_libraries(${TARGET} ${PARSED_ARGS_PUBLICITY} PkgConfig::${PARSED_ARGS_PREFIX}) endforeach() pop_fnstack() endmacro() set(INC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} subprojects/vgmstream/src subprojects/vgmstream/src/base) set(UI_BACKENDS "") list(POP_FRONT UI_BACKENDS) macro(prefix_all) set(ARGS ${ARGV}) list(POP_FRONT ARGS OUT_VAR PREFIX) set(${OUT_VAR} ) foreach(ARG IN ITEMS ${ARGS}) list(APPEND ${OUT_VAR} ${PREFIX}${ARG}) endforeach() endmacro() include(FetchContent) #execute_process(COMMAND patch -u -Np1 -i ${CMAKE_SOURCE_DIR}/protobuf_c_optional-1.patch WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/subprojects/protobuf-c/) #set(BUILD_PROTOC ON CACHE BOOL "" FORCE) #set(BUILD_TESTS OFF CACHE BOOL "" FORCE) #add_subdirectory(${CMAKE_SOURCE_DIR}/subprojects/protobuf-c/build-cmake) if(BUILD_PROTOBUF) set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) else() set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) endif() find_program(_PROTOBUF_PROTOC protoc) macro(run_protoc) cmake_parse_arguments(PROTOC "" "OUTDIR;SOURCE;OUTVAR" "INCPATH" ${ARGV}) get_filename_component(_run_protoc_src_stem "${PROTOC_SOURCE}" NAME_WE) make_directory(${PROTOC_OUTDIR}) set(_run_protoc_dst_base ${PROTOC_OUTDIR}/${_run_protoc_src_stem}.pb) set(_run_protoc_incpath_expanded) foreach (INCPATH_IDX ${PROTOC_INCPATH}) list(APPEND _run_protoc_incpath_expanded -I${INCPATH_IDX}) endforeach() if (HAIKU) list(APPEND _run_protoc_incpath_expanded -I/boot/system/develop/headers) endif() set(_run_protoc_output ${_run_protoc_dst_base}.cc ${_run_protoc_dst_base}.h)# ${_run_protoc_dst_base}-c.c ${_run_protoc_dst_base}-c.h) unset(_run_protoc_dst_base) add_custom_command(OUTPUT ${_run_protoc_output} COMMAND ${_PROTOBUF_PROTOC} --cpp_out ${PROTOC_OUTDIR} ${_run_protoc_incpath_expanded} ${PROTOC_SOURCE})# --plugin=${CMAKE_BINARY_DIR}/subprojects/protobuf-c/build-cmake/protoc-gen-c --c_out ${PROTOC_OUTDIR}) unset(_run_protoc_incpath_expanded) unset(_run_protoc_src_stem) set(${PROTOC_OUTVAR} ${_run_protoc_output}) unset(_run_protoc_output) endmacro() function(grpc_proto) cmake_parse_arguments(GRPC_PROTO "" "TARGET" "SOURCES" ${ARGV}) foreach (GRPC_PROTO_SOURCE ${GRPC_PROTO_SOURCES}) get_filename_component(src_stem "${GRPC_PROTO_SOURCE}" NAME_WE) get_filename_component(src_path "${GRPC_PROTO_SOURCE}" DIRECTORY) cmake_path(RELATIVE_PATH src_path BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE src_rel_path) set(src_out ${CMAKE_CURRENT_BINARY_DIR}/${src_rel_path}) run_protoc(OUTDIR ${src_out} SOURCE ${GRPC_PROTO_SOURCE} INCPATH ${src_path} OUTVAR GRPC_PROTO_SRCS) target_sources(${GRPC_PROTO_TARGET} PRIVATE ${GRPC_PROTO_SRCS}) target_include_directories(${GRPC_PROTO_TARGET} PRIVATE ${src_out}) endforeach() target_include_directories(${GRPC_PROTO_TARGET} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) if (BUILD_PROTOBUF) target_include_directories(${GRPC_PROTO_TARGET} PUBLIC ${CMAKE_SOURCE_DIR}/subprojects/protobuf/src ${CMAKE_SOURCE_DIR}/subprojects/protobuf/third_party/abseil-cpp) endif() target_link_libraries(${GRPC_PROTO_TARGET} PUBLIC ${_PROTOBUF_LIBPROTOBUF}) endfunction() prefix_all(LIBRARY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ config.hpp backend.hpp backend.cpp options.cpp options.hpp playback.cpp playback.h util.cpp util.hpp log.cpp log.hpp dbus.cpp dbus.hpp file_backend.cpp file_backend.hpp playback_backend.cpp playback_backend.hpp translation.cpp translation.hpp playback_process.cpp playback_process.hpp base85.cpp base85.h ) run_protoc(OUTDIR ${CMAKE_BINARY_DIR}/google/protobuf SOURCE google/protobuf/any.proto OUTVAR _src) add_library(liblooper STATIC ${LIBRARY_SOURCES}) if(FOR_WASMER) target_compile_definitions(liblooper PUBLIC FOR_WASMER) endif() grpc_proto(TARGET liblooper SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ipc/common.proto) grpc_proto(TARGET liblooper SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ipc/internal.proto) target_include_directories(liblooper PUBLIC ${INC}) target_include_directories(liblooper PUBLIC) set(JSONCPP_TARGET PkgConfig::jsoncpp) set(SOUNDTOUCH_TARGET PkgConfig::SoundTouch) function(set_to_value_of_condition var) if (${ARGN}) set(${var} ON PARENT_SCOPE) else() set(${var} OFF PARENT_SCOPE) endif() endfunction() set_to_value_of_condition(BUILD_THIRDPARTY DEFINED EMSCRIPTEN OR DEFINED ANDROID_NDK) find_package(Intl) set_to_value_of_condition(_USE_LIBINTL_LITE NOT Intl_FOUND) option(BUILD_JSONCPP "Builds JsonCpp instead of using the system package." ${BUILD_THIRDPARTY}) option(BUILD_SOUNDTOUCH "Builds SoundTouch instead of using the system package" ${BUILD_THIRDPARTY}) option(BUILD_PROTOBUF "Builds protobuf instead of using the system package" ${BUILD_THIRDPARTY}) option(USE_LIBINTL_LITE "Set to ON to build and use libintl-lite" ${_USE_LIBINTL_LITE}) set(LIBINTL_LIBRARY) set(LIBINTL_INCDIRS) if(BUILD_JSONCPP) add_subdirectory(subprojects/jsoncpp) set(JSONCPP_TARGET jsoncpp_static) endif() if(BUILD_SOUNDTOUCH) set(SOUNDSTRETCH OFF CACHE BOOL "" FORCE) set(INTEGER_SAMPLES OFF CACHE BOOL "") add_subdirectory(subprojects/soundtouch) set(SOUNDTOUCH_TARGET SoundTouch) endif() if(USE_LIBINTL_LITE) add_subdirectory(subprojects/libintl-lite) set(LIBINTL_LIBRARY intl CACHE INTERNAL "") set(LIBINTL_INCDIRS "subprojects/libintl-lite" CACHE INTERNAL "") else() find_package(Intl REQUIRED) set(LIBINTL_LIBRARY Intl::Intl CACHE INTERNAL "") set(LIBINTL_INCDIRS ${Intl_INCLUDE_DIRS} CACHE INTERNAL "") endif() if (DEFINED ANDROID_NDK) add_subdirectory(subprojects/oboe) target_link_libraries(liblooper PUBLIC oboe) endif() pkg_check_modules(libxspf IMPORTED_TARGET libxspf) if (NOT libxspf_FOUND) include(ExternalProject) set(XSPF ${CMAKE_BINARY_DIR}/libxspf-prefix/src/libxspf-build/.libs/${CMAKE_STATIC_LIBRARY_PREFIX}xspf${CMAKE_STATIC_LIBRARY_SUFFIX}) ExternalProject_Add(libxspf URL https://ftp.osuosl.org/pub/xiph/releases/xspf/libxspf-1.2.0.tar.lzma PATCH_COMMAND cd && patch -Np1 -i ${CMAKE_SOURCE_DIR}/01-xspf-no-examples.patch && patch -Np1 -i ${CMAKE_SOURCE_DIR}/02-xspf-haiku.patch CONFIGURE_COMMAND /configure --disable-test --enable-static --disable-shared BUILD_COMMAND make INSTALL_COMMAND make install DESTDIR= TEST_COMMAND "" BUILD_BYPRODUCTS ${XSPF}) ExternalProject_Get_Property(libxspf install_dir) include_directories(${install_dir}/include) add_dependencies(liblooper libxspf) else() set(XSPF PkgConfig::libxspf) endif() target_link_libraries(liblooper PUBLIC fmt::fmt ${XSPF}) target_include_directories(liblooper PUBLIC ${LIBINTL_INCDIRS}) target_link_libraries(liblooper PUBLIC ${LIBINTL_LIBRARY}) if(DEFINED EMSCRIPTEN) target_link_libraries(liblooper PUBLIC ${SDL_MIXER_X_TARGET} ${SOUNDTOUCH_TARGET} libvgmstream ${JSONCPP_TARGET}) target_compile_options(liblooper PUBLIC "-sUSE_SDL=2") else() if(NOT BUILD_SOUNDTOUCH) pkg_check_modules(SoundTouch IMPORTED_TARGET soundtouch) endif() if (NOT BUILD_JSONCPP) pkg_check_modules(jsoncpp IMPORTED_TARGET jsoncpp) endif() if (NOT BUILD_SDL) if (CMAKE_SYSTEM_NAME STREQUAL "Haiku") pkg_check_modules(sdl2 sdl2) list(REMOVE_ITEM SDL2main) add_library(SDL2::SDL2 INTERFACE IMPORTED) target_include_directories(SDL2::SDL2 INTERFACE /boot/system/develop/headers/SDL2 ${sdl2_INCLUDE_DIRS}) target_link_libraries(SDL2::SDL2 INTERFACE ${sdl2_LIBRARIES}) else() find_package(SDL2 REQUIRED) endif() endif() if (NOT BUILD_SDL_IMAGE) if (CMAKE_SYSTEM_NAME STREQUAL "Haiku") pkg_check_modules(sdl2_image SDL2_image) add_library(SDL2_image::SDL2_image INTERFACE IMPORTED) target_include_directories(SDL2_image::SDL2_image INTERFACE ${sdl2_image_INCLUDE_DIRS}) target_link_libraries(SDL2_image::SDL2_image INTERFACE ${sdl2_image_LIBRARIES}) endif() endif() if (ENABLE_DBUS) find_package(sdbus-c++) if(NOT ${sdbus-c++_FOUND}) set(ENABLE_DBUS OFF) message("Warning: Dbus support not found - Not enabling DBus") endif() endif() set(SDL2_TARGET SDL2::SDL2) if (TARGET SDL2-static) set(SDL2_TARGET SDL2-static) endif() target_link_libraries(liblooper PUBLIC ${SDL2_TARGET} ${SDL_MIXER_X_TARGET} ${SOUNDTOUCH_TARGET} libvgmstream ${JSONCPP_TARGET}) endif() if(BUILD_PROTOBUF) add_subdirectory(subprojects/protobuf) else() if (CMAKE_SYSTEM_NAME STREQUAL "Haiku") find_package(Protobuf REQUIRED) else() find_package(protobuf REQUIRED) find_package(absl CONFIG REQUIRED) endif() endif() if (${ENABLE_DBUS}) target_link_libraries(liblooper PUBLIC SDBusCpp::sdbus-c++) target_compile_definitions(liblooper PUBLIC DBUS_ENABLED) endif() macro(add_ui_backend) set(ARGS ${ARGV}) list(POP_FRONT ARGS target) add_library(${target} STATIC ${ARGS}) message("Enabling UI backend '" ${target} "'...") list(APPEND UI_BACKENDS ${target}) set(UI_BACKENDS ${UI_BACKENDS} PARENT_SCOPE) add_dependencies(${target} looper_assets) target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${INC}) target_link_libraries(${target} PRIVATE liblooper) if(${USE_PORTALS}) target_pkgconfig(TARGETS ${target} PRIVATE PREFIX libPortal LIBRARIES libportal) if (NOT ${libPortal_FOUND} EQUAL "1") set(USE_PORTALS OFF) else() info("Enabling libportal support...") target_compile_definitions(${target} PRIVATE "PORTALS") endif() endif() endmacro() macro(ui_backend_subdir) cmake_parse_arguments(UI_OPTS "DEFAULT_OFF" "SUBDIR;NAME;READABLE_NAME" "" ${ARGN} ) message("Backend ${UI_OPTS_READABLE_NAME} defined...") set(UI_DISABLE_OPT DISABLE_${UI_OPTS_NAME}_UI) option(${UI_DISABLE_OPT} "Disables the ${UI_OPTS_READABLE_NAME} UI" ${UI_OPTS_DEFAULT_OFF}) if (NOT ${${UI_DISABLE_OPT}}) cmake_path(GET UI_OPTS_SUBDIR STEM UI_OPTS_DIRNAME) set(BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${UI_OPTS_SUBDIR}) add_subdirectory(${UI_OPTS_SUBDIR}) list(APPEND ENABLED_UIS "${UI_OPTS_DIRNAME}") message("Enabled backend ${UI_OPTS_READABLE_NAME}.") else() message("Disabled backend ${UI_OPTS_READABLE_NAME}") endif() endmacro() macro(add_playback_backend) set(ARGS ${ARGV}) list(POP_FRONT ARGS target) add_library(${target} STATIC ${ARGS}) message("Enabling playback backend '" ${target} "'...") list(APPEND PLAYBACK_BACKENDS ${target}) set(PLAYBACK_BACKENDS ${PLAYBACK_BACKENDS} PARENT_SCOPE) add_dependencies(${target} looper_assets) target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${INC}) target_link_libraries(${target} PRIVATE liblooper) endmacro() macro(playback_backend_subdir) cmake_parse_arguments(PLAYBACK_OPTS "" "SUBDIR;NAME;READABLE_NAME" "" ${ARGN} ) message("Backend ${PLAYBACK_OPTS_READABLE_NAME} defined...") set(PLAYBACK_DISABLE_OPT DISABLE_${PLAYBACK_OPTS_NAME}_PLAYBACK_BACKEND) option(${PLAYBACK_DISABLE_OPT} "Disables the ${PLAYBACK_OPTS_READABLE_NAME} playback backend" OFF) if (NOT ${${PLAYBACK_DISABLE_OPT}}) cmake_path(GET PLAYBACK_OPTS_SUBDIR STEM PLAYBACK_OPTS_DIRNAME) set(BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${PLAYBACK_OPTS_SUBDIR}) add_subdirectory(${PLAYBACK_OPTS_SUBDIR}) list(APPEND ENABLED_PLAYBACK_BACKENDS "${PLAYBACK_OPTS_DIRNAME}") message("Enabled backend ${PLAYBACK_OPTS_READABLE_NAME}.") else() message("Disabled backend ${PLAYBACK_OPTS_READABLE_NAME}") endif() endmacro() set(ENABLED_UIS ) set(ENABLED_PLAYBACK_BACKENDS ) ui_backend_subdir(NAME "IMGUI" READABLE_NAME "Dear ImGui" SUBDIR backends/ui/imgui) ui_backend_subdir(NAME "QT" READABLE_NAME "Qt" SUBDIR backends/ui/qt) if(CMAKE_SYSTEM_NAME STREQUAL "Haiku") ui_backend_subdir(NAME "HAIKU" READABLE_NAME "Haiku Native" SUBDIR backends/ui/haiku) endif() if (NOT (DEFINED EMSCRIPTEN OR DEFINED ANDROID_NDK)) ui_backend_subdir(DEFAULT_OFF NAME "GTK" READABLE_NAME "GTK4" SUBDIR backends/ui/gtk) endif() playback_backend_subdir(NAME "VGMSTREAM" READABLE_NAME "VgmStream" SUBDIR backends/playback/vgmstream) playback_backend_subdir(NAME "SDL_MIXER_X" READABLE_NAME "SDL Mixer X" SUBDIR backends/playback/sdl_mixer_x) playback_backend_subdir(NAME "ZSM" READABLE_NAME "ZSM" SUBDIR backends/playback/zsm) execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen_ui_backend_inc.py ${CMAKE_CURRENT_BINARY_DIR} --ui ${ENABLED_UIS} --playback ${ENABLED_PLAYBACK_BACKENDS}) prefix_all(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ main.cpp daemon_backend.cpp daemon_backend.hpp proxy_backend.cpp proxy_backend.hpp) list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/backend_glue.cpp) if(DEFINED EMSCRIPTEN) set(CMAKE_EXECUTABLE_SUFFIX ".html") endif() set(TARGET_NAME looper) if(DEFINED EMSCRIPTEN) set(TARGET_NAME index) endif() function(copy_to_bindir src dst) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${src}" "$/${dst}") endfunction() if (DEFINED ANDROID_NDK OR SHARED_LIB) add_library(${TARGET_NAME} SHARED ${SOURCES}) else() if(HAIKU) set(HAIKU_RES ${CMAKE_SOURCE_DIR}/haiku-res.rdef) foreach (asset haiku-icon slider-mode text-mode media-playback-start media-playback-stop media-playback-pause view-refresh folder document-open configure application-exit help-contents help-about) list(APPEND HAIKU_RES ${CMAKE_SOURCE_DIR}/assets/${asset}.rdef) endforeach() list(APPEND SOURCES ${CMAKE_BINARY_DIR}/haiku-res.rsrc) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/haiku-res.rsrc COMMAND rc -o ${CMAKE_BINARY_DIR}/haiku-res.rsrc ${HAIKU_RES} DEPENDS ${HAIKU_RES} COMMENT Compiling resources) endif() add_executable(${TARGET_NAME} ${SOURCES}) if(HAIKU) # add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND resattr -o ${TARGET_NAME} ${CMAKE_BINARY_DIR}/haiku-res.rsrc DEPENDS ${TARGET_NAME} ${CMAKE_BINARY_DIR}/haiku-res.rsrc COMMENT Adding resources to target attributes) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND xres -o ${TARGET_NAME} ${CMAKE_BINARY_DIR}/haiku-res.rsrc DEPENDS ${TARGET_NAME} ${CMAKE_BINARY_DIR}/haiku-res.rsrc COMMENT Adding resources to target) endif() endif() add_dependencies(${TARGET_NAME} looper_assets ${UI_BACKENDS}) if(DEFINED EMSCRIPTEN) copy_to_bindir(assets/icon.svg icon.svg) copy_to_bindir(assets/icon.png icon.png) copy_to_bindir(web/shell.js shell.js) copy_to_bindir(web/style.css style.css) copy_to_bindir(web/serviceWorker.js serviceWorker.js) copy_to_bindir(web/manifest.json manifest.json) copy_to_bindir(assets/ForkAwesome/fonts/forkawesome-webfont.ttf forkawesome-webfont.ttf) copy_to_bindir(assets/ForkAwesome/css/fork-awesome.min.css fork-awesome.min.css) copy_to_bindir(assets/ForkAwesome/css/fork-awesome.min.css.map fork-awesome.min.css.map) endif() target_link_libraries(${TARGET_NAME} PUBLIC liblooper ${UI_BACKENDS} ${PLAYBACK_BACKENDS}) install(TARGETS ${TARGET_NAME} ${EXTRA_LIBS}) if (${BUILD_SDL2}) install(EXPORT SDL2-static SDL2main) endif() if (NOT DEFINED EMSCRIPTEN) install(FILES assets/icon.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps/) install(FILES assets/com.complecwaft.Looper.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) install(FILES assets/com.complecwaft.Looper.metainfo.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) install(DIRECTORY assets/translations/ TYPE LOCALE PATTERN "*" EXCLUDE PATTERN "looper.pot") endif() if (SHARED_LIB) target_compile_definitions(${TARGET_NAME} PRIVATE "LIBRARY_MODE") endif() if(TESTS) include(GoogleTest) include(CTest) set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) add_subdirectory(subprojects/googletest) add_executable(looper_tests tests.cpp ${CMAKE_CURRENT_BINARY_DIR}/backend_glue.cpp) add_dependencies(looper_tests looper_assets) target_link_libraries(looper_tests PRIVATE ${PLAYBACK_BACKENDS} ${UI_BACKENDS}) set(TESTS "FileBackend.Seeking" "FileBackend.Reading" "FileBackend.ReadingSeeking" "PlaybackBackend.Looping") foreach (test ${TESTS}) get_filename_component(test_cat ${test} NAME_WE) get_filename_component(test_name ${test} EXT) string(SUBSTRING ${test_name} 1 -1 test_name) add_test(NAME ${test_name} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/looper_tests --gtest_filter=${test} PROPERTY LABELS ${test_cat}) endforeach() target_include_directories(liblooper SYSTEM PUBLIC subprojects/googletest/googletest/include) target_link_libraries(looper_tests PRIVATE liblooper) target_link_libraries(liblooper PUBLIC gtest_main) target_compile_definitions(liblooper PUBLIC TESTS) endif()