From 7a295d27217bb510b5c39778f306ff117b5d3bf6 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Tue, 13 Oct 2026 23:58:47 +1300 Subject: [PATCH 01/14] CMake build system for the core and apps libs/openFrameworksCompiled/project/CMake builds the openFrameworks core and apps on macOS, iOS, tvOS, Emscripten, Linux (linux64, linuxaarch64, linuxarmv7l, linuxarmv6l), Visual Studio and MSYS2, mirroring the makefiles and IDE projects: the same sources, defines, flags and third-party libraries per platform, apps built into bin/, and the core in libs/openFrameworksCompiled/lib/ with the names the existing builds use. Android keeps its own CMake for now. - openFrameworks.cmake: entry point for apps and the core project - CMakeLists.txt: the core library project - OFCore.cmake: core sources, third-party libs, the openFrameworks and openFrameworks::deps targets; apps link the prebuilt core, or build it first if it isn't there (OF_BUILD_CORE=ON forces that) - OFAddons.cmake: addons from their folders and addon_config.mk like the makefiles, or from explicit lists (of_target_addon_files, for the Project Generator) - OFApp.cmake: of_add_app() - platform/*.cmake: per platform excludes, defines, flags, libraries and app output (.app bundles with Info.plist and icon, bin/em/ for Emscripten, xcframeworks and bin/data in the bundle on iOS / tvOS) scripts/templates/cmake/CMakeLists.txt is the app template; generated CMakeLists.txt in examples are ignored like the Makefiles. --- examples/.gitignore | 2 + .../project/CMake/CMakeLists.txt | 15 + .../project/CMake/OFAddons.cmake | 225 +++++++++++++++ .../project/CMake/OFApp.cmake | 54 ++++ .../project/CMake/OFCore.cmake | 264 ++++++++++++++++++ .../project/CMake/OFPlatform.cmake | 88 ++++++ .../project/CMake/README.md | 118 ++++++++ .../project/CMake/openFrameworks.cmake | 48 ++++ .../project/CMake/platform/apple.cmake | 62 ++++ .../project/CMake/platform/emscripten.cmake | 147 ++++++++++ .../project/CMake/platform/ios.cmake | 176 ++++++++++++ .../project/CMake/platform/linux.cmake | 119 ++++++++ .../project/CMake/platform/macos.cmake | 84 ++++++ .../project/CMake/platform/msys2.cmake | 73 +++++ .../project/CMake/platform/vs.cmake | 88 ++++++ scripts/templates/cmake/CMakeLists.txt | 23 ++ 16 files changed, 1586 insertions(+) create mode 100644 libs/openFrameworksCompiled/project/CMake/CMakeLists.txt create mode 100644 libs/openFrameworksCompiled/project/CMake/OFAddons.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/OFApp.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/OFCore.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/OFPlatform.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/README.md create mode 100644 libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/platform/apple.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/platform/ios.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/platform/linux.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/platform/macos.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/platform/msys2.cmake create mode 100644 libs/openFrameworksCompiled/project/CMake/platform/vs.cmake create mode 100644 scripts/templates/cmake/CMakeLists.txt diff --git a/examples/.gitignore b/examples/.gitignore index d8e7da5d0f4..8db860daf1a 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -51,6 +51,8 @@ # Linux /*/*/Makefile /*/*/config.make +# CMake (scripts/templates/cmake, generated by the Project Generator) +/*/*/CMakeLists.txt # Leave Android files in until project generation works !/android/*/Makefile !/android/*/config.make diff --git a/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt b/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt new file mode 100644 index 00000000000..cf741e7cdf1 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt @@ -0,0 +1,15 @@ +# openFrameworks core library +# +# Builds libs/openFrameworksCompiled/lib/[/]/. +# Apps include openFrameworks.cmake and link it with of_add_app(); they build +# this project themselves if the library isn't there yet. +# +# cmake -S libs/openFrameworksCompiled/project/CMake -B build/core -DCMAKE_BUILD_TYPE=Release +# cmake --build build/core + +cmake_minimum_required(VERSION 3.22) +project(openFrameworks LANGUAGES C CXX) + +include("${CMAKE_CURRENT_LIST_DIR}/openFrameworks.cmake") + +of_add_core_library() diff --git a/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake new file mode 100644 index 00000000000..3650961c1ef --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake @@ -0,0 +1,225 @@ +# openFrameworks addons +# +# of_target_addons( ...) +# Adds addons the way the makefiles do: sources, include paths and libs +# from the addon's folders, adjusted by its addon_config.mk (common: and +# : sections), plus its ADDON_DEPENDENCIES. is a name in +# OF_ROOT/addons, or a path (relative to the project) for local addons. +# +# of_target_addon_files( NAME +# [SOURCES ...] [INCLUDES ...] [LIBS ...] [DEFINES ...] +# [CFLAGS ...] [LDFLAGS ...] [FRAMEWORKS ...] [PKG_CONFIG ...]) +# Adds an addon from explicit lists (absolute paths), e.g. as generated +# by the Project Generator. + +include_guard(GLOBAL) + +# addon_config.mk section names that apply to this platform +set(OF_ADDON_CONFIG_SECTIONS common ${OF_PLATFORM}) +if(OF_PLATFORM STREQUAL "macos") + list(APPEND OF_ADDON_CONFIG_SECTIONS osx) +endif() +if(OF_ARCH) + list(APPEND OF_ADDON_CONFIG_SECTIONS "${OF_PLATFORM}/${OF_ARCH}") +endif() + +set(OF_ADDON_CONFIG_VARS + ADDON_DEPENDENCIES ADDON_INCLUDES ADDON_INCLUDES_EXCLUDE ADDON_SOURCES ADDON_SOURCES_EXCLUDE + ADDON_LIBS ADDON_LIBS_EXCLUDE ADDON_DEFINES ADDON_CFLAGS ADDON_CPPFLAGS ADDON_LDFLAGS + ADDON_FRAMEWORKS ADDON_PKG_CONFIG_LIBRARIES ADDON_DATA) + +# make's filter-out pattern (one % matches anything) -> regex +function(of_make_pattern_regex out_var pattern) + string(REGEX REPLACE "([][+.*()^$?{}|\\\\])" "\\\\\\1" escaped "${pattern}") + string(REPLACE "%" ".*" escaped "${escaped}") + set(${out_var} "^${escaped}$" PARENT_SCOPE) +endfunction() + +# removes items matching any of the make patterns (relative to ) +function(of_filter_make_patterns out_var base patterns) + set(regexes) + foreach(pattern IN LISTS patterns) + if(NOT IS_ABSOLUTE "${pattern}") + set(pattern "${base}/${pattern}") + endif() + of_make_pattern_regex(regex "${pattern}") + list(APPEND regexes "${regex}") + endforeach() + of_filter_out(result "${regexes}" ${ARGN}) + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +# expands addon paths relative to ; a % works like the makefiles' wildcard +function(of_expand_addon_paths out_var base) + set(result) + foreach(item IN LISTS ARGN) + if(NOT IS_ABSOLUTE "${item}") + set(item "${base}/${item}") + endif() + if(item MATCHES "%") + string(REPLACE "%" "*" glob "${item}") + file(GLOB matches LIST_DIRECTORIES true "${glob}") + list(APPEND result ${matches}) + else() + list(APPEND result "${item}") + endif() + endforeach() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +# reads addon_config.mk into _ for the sections that apply here. +# Variables already set (the defaults) are replaced by = and extended by +=. +macro(of_read_addon_config file prefix) + file(STRINGS "${file}" _of_lines) + set(_of_section "") + set(_of_pending "") + foreach(_of_line IN LISTS _of_lines) + string(REGEX REPLACE "#.*$" "" _of_line "${_of_line}") + if(_of_pending) + string(PREPEND _of_line "${_of_pending} ") + set(_of_pending "") + endif() + if(_of_line MATCHES "\\\\[ \t]*$") + string(REGEX REPLACE "\\\\[ \t]*$" "" _of_pending "${_of_line}") + continue() + endif() + string(STRIP "${_of_line}" _of_line) + if(_of_line STREQUAL "") + continue() + endif() + if(_of_line MATCHES "^([A-Za-z0-9_/.-]+)[ \t]*:$") + set(_of_section "${CMAKE_MATCH_1}") + continue() + endif() + if(NOT _of_section IN_LIST OF_ADDON_CONFIG_SECTIONS) + continue() + endif() + if(_of_line MATCHES "^([A-Z_]+)[ \t]*(\\+=|:=|=)[ \t]*(.*)$") + set(_of_var "${CMAKE_MATCH_1}") + set(_of_op "${CMAKE_MATCH_2}") + separate_arguments(_of_values UNIX_COMMAND "${CMAKE_MATCH_3}") + if(_of_var IN_LIST OF_ADDON_CONFIG_VARS) + if(_of_op STREQUAL "+=") + list(APPEND ${prefix}_${_of_var} ${_of_values}) + else() + set(${prefix}_${_of_var} ${_of_values}) + endif() + endif() + endif() + endforeach() +endmacro() + +function(of_find_addon_dir out_var addon) + if(IS_ABSOLUTE "${addon}" AND IS_DIRECTORY "${addon}") + set(dir "${addon}") + elseif(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${addon}" AND addon MATCHES "/") + set(dir "${CMAKE_CURRENT_SOURCE_DIR}/${addon}") + elseif(IS_DIRECTORY "${OF_ADDONS_DIR}/${addon}") + set(dir "${OF_ADDONS_DIR}/${addon}") + elseif(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${addon}") + set(dir "${CMAKE_CURRENT_SOURCE_DIR}/${addon}") + else() + message(FATAL_ERROR "openFrameworks: addon '${addon}' not found in ${OF_ADDONS_DIR} or the project") + endif() + get_filename_component(dir "${dir}" ABSOLUTE) + set(${out_var} "${dir}" PARENT_SCOPE) +endfunction() + +function(of_target_addon_files target) + cmake_parse_arguments(A "" "NAME" "SOURCES;INCLUDES;LIBS;DEFINES;CFLAGS;LDFLAGS;FRAMEWORKS;PKG_CONFIG" ${ARGN}) + if(A_SOURCES) + target_sources(${target} PRIVATE ${A_SOURCES}) + source_group(TREE "${OF_ROOT}" PREFIX "" FILES ${A_SOURCES}) + endif() + target_include_directories(${target} PRIVATE ${A_INCLUDES}) + target_compile_definitions(${target} PRIVATE ${A_DEFINES}) + target_compile_options(${target} PRIVATE ${A_CFLAGS}) + target_link_options(${target} PRIVATE ${A_LDFLAGS}) + of_link_static_libs(${target} PRIVATE ${A_LIBS}) + foreach(framework IN LISTS A_FRAMEWORKS) + target_link_libraries(${target} PRIVATE "-framework ${framework}") + endforeach() + if(A_PKG_CONFIG) + find_package(PkgConfig REQUIRED) + string(MAKE_C_IDENTIFIER "${A_NAME}" id) + pkg_check_modules(OF_ADDON_PC_${id} REQUIRED IMPORTED_TARGET ${A_PKG_CONFIG}) + target_link_libraries(${target} PRIVATE PkgConfig::OF_ADDON_PC_${id}) + endif() +endfunction() + +function(of_target_addon target addon) + of_find_addon_dir(dir "${addon}") + get_filename_component(name "${dir}" NAME) + get_target_property(done ${target} OF_ADDONS) + if(done AND name IN_LIST done) + return() + endif() + set_property(TARGET ${target} APPEND PROPERTY OF_ADDONS ${name}) + + # defaults, as the makefiles build them before reading addon_config.mk + of_glob_sources(src_sources "${dir}/src") + of_glob_sources(lib_sources "${dir}/libs") + set(cfg_ADDON_SOURCES ${src_sources} ${lib_sources}) + of_dir_and_subdirs(src_dirs "${dir}/src") + of_dir_and_subdirs(lib_dirs "${dir}/libs") + set(cfg_ADDON_INCLUDES ${src_dirs} ${lib_dirs}) + set(cfg_ADDON_LIBS) + file(GLOB addon_lib_roots LIST_DIRECTORIES true "${dir}/libs/*") + foreach(lib_root IN LISTS addon_lib_roots) + of_static_libs_in(libs "${lib_root}/lib/${OF_PLATFORM}") + list(APPEND cfg_ADDON_LIBS ${libs}) + endforeach() + + if(EXISTS "${dir}/addon_config.mk") + of_read_addon_config("${dir}/addon_config.mk" cfg) + endif() + + foreach(dependency IN LISTS cfg_ADDON_DEPENDENCIES) + of_target_addon(${target} "${dependency}") + endforeach() + + of_expand_addon_paths(sources "${dir}" ${cfg_ADDON_SOURCES}) + of_filter_make_patterns(sources "${dir}" "${cfg_ADDON_SOURCES_EXCLUDE}" ${sources}) + of_filter_out(sources "${OF_PLATFORM_CORE_EXCLUDES}" ${sources}) + of_expand_addon_paths(includes "${dir}" ${cfg_ADDON_INCLUDES}) + of_filter_make_patterns(includes "${dir}" "${cfg_ADDON_INCLUDES_EXCLUDE}" ${includes}) + of_expand_addon_paths(libs "${dir}" ${cfg_ADDON_LIBS}) + of_filter_make_patterns(libs "${dir}" "${cfg_ADDON_LIBS_EXCLUDE}" ${libs}) + list(FILTER libs INCLUDE REGEX "\\.(a|lib|so|dylib|tbd)$|^-") + + of_target_addon_files(${target} NAME ${name} + SOURCES ${sources} + INCLUDES ${includes} + LIBS ${libs} + DEFINES ${cfg_ADDON_DEFINES} + CFLAGS ${cfg_ADDON_CFLAGS} ${cfg_ADDON_CPPFLAGS} + LDFLAGS ${cfg_ADDON_LDFLAGS} + FRAMEWORKS ${cfg_ADDON_FRAMEWORKS} + PKG_CONFIG ${cfg_ADDON_PKG_CONFIG_LIBRARIES}) + + list(LENGTH sources n_sources) + list(LENGTH libs n_libs) + message(STATUS "openFrameworks: addon ${name} (${n_sources} sources, ${n_libs} libs)") +endfunction() + +function(of_target_addons target) + foreach(addon IN LISTS ARGN) + of_target_addon(${target} "${addon}") + endforeach() +endfunction() + +# addon names from a project's addons.make (one per line, # comments) +function(of_read_addons_make out_var file) + set(result) + if(EXISTS "${file}") + file(STRINGS "${file}" lines) + foreach(line IN LISTS lines) + string(REGEX REPLACE "#.*$" "" line "${line}") + string(STRIP "${line}" line) + if(line) + list(APPEND result "${line}") + endif() + endforeach() + endif() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/OFApp.cmake b/libs/openFrameworksCompiled/project/CMake/OFApp.cmake new file mode 100644 index 00000000000..8090a12b26e --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/OFApp.cmake @@ -0,0 +1,54 @@ +# openFrameworks apps +# +# of_add_app( [SOURCES ...] [ADDONS ...] [NO_ADDONS_MAKE]) +# +# Creates the app target from the project's src/ folder (or SOURCES), links the +# openFrameworks core, and adds ADDONS (or the project's addons.make unless +# NO_ADDONS_MAKE). The app is built into the project's bin/ folder like the +# makefiles and IDE projects: for Release, _debug for Debug. + +include_guard(GLOBAL) + +function(of_add_app name) + cmake_parse_arguments(A "NO_ADDONS_MAKE" "" "SOURCES;ADDONS" ${ARGN}) + + if(A_SOURCES) + set(sources ${A_SOURCES}) + else() + of_glob_sources(sources "${CMAKE_CURRENT_SOURCE_DIR}/src") + file(GLOB_RECURSE headers CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp") + list(APPEND sources ${headers}) + endif() + if(NOT sources) + message(FATAL_ERROR "openFrameworks: no sources for ${name} (looked in ${CMAKE_CURRENT_SOURCE_DIR}/src)") + endif() + + add_executable(${name} ${sources}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources}) + of_dir_and_subdirs(src_dirs "${CMAKE_CURRENT_SOURCE_DIR}/src") + target_include_directories(${name} PRIVATE ${src_dirs}) + + of_use_core() + target_link_libraries(${name} PRIVATE openFrameworks::openFrameworks) + + set(bin_dir "${CMAKE_CURRENT_SOURCE_DIR}/bin") + set_target_properties(${name} PROPERTIES + OUTPUT_NAME ${name} + OUTPUT_NAME_DEBUG ${name}_debug + RUNTIME_OUTPUT_DIRECTORY "${bin_dir}" + VS_DEBUGGER_WORKING_DIRECTORY "${bin_dir}" + XCODE_SCHEME_WORKING_DIRECTORY "${bin_dir}") + foreach(config Debug Release RelWithDebInfo MinSizeRel) + string(TOUPPER ${config} upper) + set_target_properties(${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${upper} "${bin_dir}") + endforeach() + + set(addons ${A_ADDONS}) + if(NOT A_ADDONS AND NOT A_NO_ADDONS_MAKE) + of_read_addons_make(addons "${CMAKE_CURRENT_SOURCE_DIR}/addons.make") + endif() + of_target_addons(${name} ${OF_PLATFORM_REQUIRED_ADDONS} ${addons}) + + of_platform_setup_app(${name}) +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/OFCore.cmake b/libs/openFrameworksCompiled/project/CMake/OFCore.cmake new file mode 100644 index 00000000000..85842575a12 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/OFCore.cmake @@ -0,0 +1,264 @@ +# openFrameworks core: sources, third-party libs, and the targets +# +# openFrameworks::deps INTERFACE target with everything needed to compile +# and link against the core (includes, defines, flags, +# third-party and system libs) +# openFrameworks the core library: prebuilt from +# libs/openFrameworksCompiled/lib/[/], +# or built in this build (of_use_core) +# +# Each platform/.cmake sets: +# OF_PLATFORM_CORE_EXCLUDES regexes matched against core source paths +# OF_PLATFORM_SOURCE_EXTENSIONS source file extensions to compile +# OF_PLATFORM_DEFINES compile definitions +# OF_PLATFORM_COMPILE_OPTIONS compile options +# OF_PLATFORM_LINK_OPTIONS link options +# OF_PLATFORM_LINK_LIBRARIES system libraries / frameworks +# OF_PLATFORM_THIRDPARTY_EXCLUDE libs/ folders not to use +# OF_PLATFORM_INCLUDE_DIRS extra include paths for the core +# OF_PLATFORM_CORE_EXTRA_SOURCE_DIRS extra folders compiled into the core (ofxiOS on iOS) +# OF_PLATFORM_REQUIRED_ADDONS addons every app on this platform needs +# OF_PLATFORM_LIB_EXTENSIONS static library extensions +# OF_PLATFORM_LINK_GROUP ON to wrap static libs in --start-group/--end-group +# OF_CORE_LIB_NAME / OF_CORE_LIB_NAME_DEBUG / OF_CORE_LIB_PREFIX / OF_CORE_LIB_SUFFIX +# and defines of_platform_lib_dirs( ) and of_platform_setup_app(). + +include_guard(GLOBAL) + +# filters a list of paths, removing entries that match any regex in the exclude list +function(of_filter_out out_var excludes) + set(result) + foreach(item IN LISTS ARGN) + set(keep ON) + foreach(pattern IN LISTS excludes) + if(item MATCHES "${pattern}") + set(keep OFF) + break() + endif() + endforeach() + if(keep) + list(APPEND result "${item}") + endif() + endforeach() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +# all source files under with the platform's source extensions +function(of_glob_sources out_var dir) + set(patterns) + foreach(ext IN LISTS OF_PLATFORM_SOURCE_EXTENSIONS) + list(APPEND patterns "${dir}/*.${ext}") + endforeach() + file(GLOB_RECURSE files CONFIGURE_DEPENDS ${patterns}) + list(FILTER files EXCLUDE REGEX "/\\.[^/]*") + list(SORT files) + set(${out_var} ${files} PARENT_SCOPE) +endfunction() + +# plus all of its subdirectories (like the makefiles' header search paths) +function(of_dir_and_subdirs out_var dir) + set(result) + if(IS_DIRECTORY "${dir}") + list(APPEND result "${dir}") + file(GLOB_RECURSE children LIST_DIRECTORIES true "${dir}/*") + foreach(child IN LISTS children) + if(IS_DIRECTORY "${child}" AND NOT child MATCHES "/\\.[^/]*") + list(APPEND result "${child}") + endif() + endforeach() + endif() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +function(of_core_sources out_var) + of_glob_sources(sources "${OF_CORE_SRC_DIR}") + foreach(dir IN LISTS OF_PLATFORM_CORE_EXTRA_SOURCE_DIRS) + of_glob_sources(extra "${dir}") + list(APPEND sources ${extra}) + endforeach() + of_filter_out(sources "${OF_PLATFORM_CORE_EXCLUDES}" ${sources}) + set(${out_var} ${sources} PARENT_SCOPE) +endfunction() + +function(of_core_include_dirs out_var) + set(dirs "${OF_CORE_SRC_DIR}") + file(GLOB children LIST_DIRECTORIES true "${OF_CORE_SRC_DIR}/*") + foreach(child IN LISTS children) + if(IS_DIRECTORY "${child}") + list(APPEND dirs "${child}") + endif() + endforeach() + set(${out_var} ${dirs} PARENT_SCOPE) +endfunction() + +# libs/ folders the core uses on this platform +function(of_thirdparty_roots out_var) + file(GLOB roots LIST_DIRECTORIES true "${OF_LIBS_DIR}/*") + set(result) + foreach(root IN LISTS roots) + get_filename_component(name "${root}" NAME) + if(NOT IS_DIRECTORY "${root}" OR name MATCHES "^(openFrameworks|openFrameworksCompiled|download|\\..*)$") + continue() + endif() + if(name IN_LIST OF_PLATFORM_THIRDPARTY_EXCLUDE) + continue() + endif() + list(APPEND result "${root}") + endforeach() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +# libs//include roots, like the Xcode and Visual Studio projects. (The +# makefiles also add every subfolder, which lets e.g. glm/include/glm/simd shadow +# the SDK's headers.) +function(of_thirdparty_include_dirs out_var) + of_thirdparty_roots(roots) + set(result) + foreach(root IN LISTS roots) + if(IS_DIRECTORY "${root}/include") + list(APPEND result "${root}/include") + endif() + endforeach() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +# static libraries in (e.g. libs/curl/lib/macos), in the platform's +# layout (plain folder, arch subfolder, xcframework slice, ...) +function(of_static_libs_in out_var lib_folder) + if(COMMAND of_platform_static_libs_in) + of_platform_static_libs_in(result "${lib_folder}") + set(${out_var} ${result} PARENT_SCOPE) + return() + endif() + of_platform_lib_dirs("${lib_folder}" dirs) + set(result) + foreach(dir IN LISTS dirs) + foreach(ext IN LISTS OF_PLATFORM_LIB_EXTENSIONS) + file(GLOB libs "${dir}/*.${ext}") + list(SORT libs) + list(APPEND result ${libs}) + endforeach() + endforeach() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +function(of_thirdparty_libs out_var) + of_thirdparty_roots(roots) + set(result) + foreach(root IN LISTS roots) + of_static_libs_in(libs "${root}/lib/${OF_PLATFORM}") + list(APPEND result ${libs}) + endforeach() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +# links static libraries, grouped for single-pass linkers so their order doesn't matter +function(of_link_static_libs target scope) + if(NOT ARGN) + return() + endif() + if(OF_PLATFORM_LINK_GROUP) + target_link_libraries(${target} ${scope} "-Wl,--start-group" ${ARGN} "-Wl,--end-group") + else() + target_link_libraries(${target} ${scope} ${ARGN}) + endif() +endfunction() + +function(of_setup_deps) + if(TARGET openFrameworks_deps) + return() + endif() + add_library(openFrameworks_deps INTERFACE) + add_library(openFrameworks::deps ALIAS openFrameworks_deps) + + of_core_include_dirs(core_dirs) + of_thirdparty_include_dirs(thirdparty_dirs) + target_include_directories(openFrameworks_deps INTERFACE ${core_dirs} ${thirdparty_dirs} ${OF_PLATFORM_INCLUDE_DIRS}) + + target_compile_definitions(openFrameworks_deps INTERFACE + GLM_FORCE_CTOR_INIT + GLM_ENABLE_EXPERIMENTAL + $<$:DEBUG> + $<$>:NDEBUG> + ${OF_PLATFORM_DEFINES}) + target_compile_options(openFrameworks_deps INTERFACE ${OF_PLATFORM_COMPILE_OPTIONS}) + target_link_options(openFrameworks_deps INTERFACE ${OF_PLATFORM_LINK_OPTIONS}) + + of_thirdparty_libs(thirdparty_libs) + of_link_static_libs(openFrameworks_deps INTERFACE ${thirdparty_libs}) + target_link_libraries(openFrameworks_deps INTERFACE ${OF_PLATFORM_LINK_LIBRARIES}) + + list(LENGTH thirdparty_libs n) + message(STATUS "openFrameworks: ${n} third-party libraries from libs/*/lib/${OF_PLATFORM}") +endfunction() + +function(of_core_lib_path out_var config) + if(config STREQUAL "Debug") + set(name "${OF_CORE_LIB_NAME_DEBUG}") + else() + set(name "${OF_CORE_LIB_NAME}") + endif() + set(${out_var} "${OF_CORE_LIB_DIR}/${OF_CORE_LIB_PREFIX}${name}${OF_CORE_LIB_SUFFIX}" PARENT_SCOPE) +endfunction() + +# defines the openFrameworks core library target, built from source +function(of_add_core_library) + of_setup_deps() + of_core_sources(sources) + add_library(openFrameworks STATIC ${sources}) + target_link_libraries(openFrameworks PUBLIC openFrameworks::deps) + set_target_properties(openFrameworks PROPERTIES + OUTPUT_NAME "${OF_CORE_LIB_NAME}" + OUTPUT_NAME_DEBUG "${OF_CORE_LIB_NAME_DEBUG}" + PREFIX "${OF_CORE_LIB_PREFIX}" + ARCHIVE_OUTPUT_DIRECTORY "${OF_CORE_LIB_DIR}") + foreach(config Debug Release RelWithDebInfo MinSizeRel) + string(TOUPPER ${config} upper) + set_target_properties(openFrameworks PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_${upper} "${OF_CORE_LIB_DIR}") + endforeach() + if(COMMAND of_platform_setup_core) + of_platform_setup_core(openFrameworks) + endif() + add_library(openFrameworks::openFrameworks ALIAS openFrameworks) + list(LENGTH sources n) + message(STATUS "openFrameworks: building the core (${n} sources) into ${OF_CORE_LIB_DIR}") +endfunction() + +# makes the openFrameworks core available to this build: the prebuilt library +# if it exists for the configurations being built, otherwise builds it here +function(of_use_core) + if(TARGET openFrameworks) + return() + endif() + of_setup_deps() + + if(CMAKE_CONFIGURATION_TYPES) + set(configs Debug Release) + else() + set(configs ${CMAKE_BUILD_TYPE}) + endif() + set(prebuilt ON) + foreach(config IN LISTS configs) + of_core_lib_path(path ${config}) + if(NOT EXISTS "${path}") + set(prebuilt OFF) + endif() + endforeach() + + if(prebuilt AND NOT OF_BUILD_CORE) + add_library(openFrameworks STATIC IMPORTED GLOBAL) + of_core_lib_path(release Release) + of_core_lib_path(debug Debug) + set_target_properties(openFrameworks PROPERTIES + IMPORTED_LOCATION "${release}" + IMPORTED_LOCATION_RELEASE "${release}" + IMPORTED_LOCATION_RELWITHDEBINFO "${release}" + IMPORTED_LOCATION_MINSIZEREL "${release}" + IMPORTED_LOCATION_DEBUG "${debug}" + INTERFACE_LINK_LIBRARIES openFrameworks::deps) + add_library(openFrameworks::openFrameworks ALIAS openFrameworks) + message(STATUS "openFrameworks: using prebuilt core from ${OF_CORE_LIB_DIR}") + else() + add_subdirectory("${OF_CMAKE_DIR}" "${CMAKE_BINARY_DIR}/openFrameworks") + endif() +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/OFPlatform.cmake b/libs/openFrameworksCompiled/project/CMake/OFPlatform.cmake new file mode 100644 index 00000000000..b2401c84497 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/OFPlatform.cmake @@ -0,0 +1,88 @@ +# Detects the openFrameworks platform from the toolchain. +# +# OF_PLATFORM libs/*/lib/ folder name +# (macos ios tvos emscripten linux64 linuxaarch64 linuxarmv7l linuxarmv6l vs msys2) +# OF_PLATFORM_FAMILY platform/.cmake to load (macos ios emscripten linux vs msys2) +# OF_ARCH architecture subfolder under libs/*/lib/, empty if none + +include_guard(GLOBAL) + +string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _of_proc) + +if(NOT OF_PLATFORM) + if(EMSCRIPTEN) + set(OF_PLATFORM emscripten) + elseif(ANDROID) + set(OF_PLATFORM android) + elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") + set(OF_PLATFORM ios) + elseif(CMAKE_SYSTEM_NAME STREQUAL "tvOS") + set(OF_PLATFORM tvos) + elseif(APPLE) + set(OF_PLATFORM macos) + elseif(MSVC) + set(OF_PLATFORM vs) + elseif(MINGW OR MSYS) + set(OF_PLATFORM msys2) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(_of_proc MATCHES "^(x86_64|amd64)$") + set(OF_PLATFORM linux64) + elseif(_of_proc MATCHES "^(aarch64|arm64)$") + set(OF_PLATFORM linuxaarch64) + elseif(_of_proc MATCHES "^armv7") + set(OF_PLATFORM linuxarmv7l) + elseif(_of_proc MATCHES "^armv6") + set(OF_PLATFORM linuxarmv6l) + else() + message(FATAL_ERROR "openFrameworks: unsupported Linux processor '${CMAKE_SYSTEM_PROCESSOR}'") + endif() + else() + message(FATAL_ERROR "openFrameworks: unsupported platform '${CMAKE_SYSTEM_NAME}'") + endif() +endif() + +if(OF_PLATFORM STREQUAL "android") + message(FATAL_ERROR "openFrameworks: Android uses its own CMake (libs/openFrameworksCompiled/project/android)") +endif() + +if(OF_PLATFORM MATCHES "^linux") + set(OF_PLATFORM_FAMILY linux) +elseif(OF_PLATFORM STREQUAL "tvos") + set(OF_PLATFORM_FAMILY ios) +else() + set(OF_PLATFORM_FAMILY ${OF_PLATFORM}) +endif() + +if(NOT DEFINED OF_ARCH) + set(OF_ARCH "") + if(OF_PLATFORM STREQUAL "vs") + # Visual Studio generators pass the platform with -A, Ninja uses the compiler's target + if(CMAKE_GENERATOR_PLATFORM) + set(_of_vs_arch "${CMAKE_GENERATOR_PLATFORM}") + elseif(CMAKE_VS_PLATFORM_NAME) + set(_of_vs_arch "${CMAKE_VS_PLATFORM_NAME}") + else() + set(_of_vs_arch "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}") + endif() + string(TOUPPER "${_of_vs_arch}" _of_vs_arch_upper) + if(_of_vs_arch_upper STREQUAL "ARM64EC") + set(OF_ARCH ARM64EC) + elseif(_of_vs_arch_upper MATCHES "^ARM64") + set(OF_ARCH ARM64) + else() + set(OF_ARCH x64) + endif() + endif() +endif() + +# msys2 environment (clang64 / ucrt64 / mingw64 / clangarm64); the core and libs +# live in lib/msys2, some addons have per-environment subfolders +if(OF_PLATFORM STREQUAL "msys2" AND NOT DEFINED OF_MSYSTEM) + set(OF_MSYSTEM "") + if(DEFINED ENV{MSYSTEM}) + string(TOLOWER "$ENV{MSYSTEM}" OF_MSYSTEM) + endif() +endif() + +message(STATUS "openFrameworks: platform ${OF_PLATFORM} ${OF_ARCH}" + " (${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR}, ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION})") diff --git a/libs/openFrameworksCompiled/project/CMake/README.md b/libs/openFrameworksCompiled/project/CMake/README.md new file mode 100644 index 00000000000..cb9b75cef42 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/README.md @@ -0,0 +1,118 @@ +# openFrameworks CMake + +CMake build for the openFrameworks core and apps on macOS, iOS, tvOS, Emscripten, +Linux (linux64, linuxaarch64, linuxarmv7l, linuxarmv6l) and Windows (Visual Studio, +MSYS2). Android has its own CMake in `project/android` for now. + +It mirrors the makefiles and IDE projects: the same sources, defines, flags and +third-party libraries per platform, apps built into the project's `bin/`, and +the core in `libs/openFrameworksCompiled/lib/`. + +## App + +Copy `scripts/templates/cmake/CMakeLists.txt` into a project (`apps/myApps/myApp`): + +```sh +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +Sources come from `src/`, addons from `addons.make`. The core is linked from +`libs/openFrameworksCompiled/lib/` if it's there for the configuration +being built, otherwise it's built in this build first (and written there for the +next app). `-DOF_BUILD_CORE=ON` always builds it here (e.g. after changing the +core sources). + +| Platform | Configure | Output | +|---|---|---| +| macOS | `cmake -S . -B build -G Ninja` (or `-G Xcode`) | `bin/.app` | +| Linux | `cmake -S . -B build -G Ninja` | `bin/` | +| Visual Studio | `cmake -S . -B build -G "Visual Studio 17 2022" -A x64` (`ARM64`, `ARM64EC`) | `bin/.exe` | +| MSYS2 | `cmake -S . -B build -G Ninja` (from the MSYS2 shell) | `bin/.exe` | +| Emscripten | `emcmake cmake -S . -B build -G Ninja`, then `emrun bin/em/` | `bin/em//index.html` | +| iOS | `cmake -S . -B build -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator` | `bin/.app` | +| tvOS | `cmake -S . -B build -G Xcode -DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_OSX_SYSROOT=appletvsimulator` | `bin/.app` | + +Debug builds are named `_debug`. For an iOS / tvOS device use +`-DCMAKE_OSX_SYSROOT=iphoneos` (`appletvos`) and `-DOF_APPLE_DEVELOPMENT_TEAM=`. + +In `CMakeLists.txt`: + +```cmake +of_add_app(myApp) # src/ + addons.make +of_add_app(myApp ADDONS ofxGui ofxOsc) # explicit addons +of_add_app(myApp SOURCES src/main.cpp src/ofApp.cpp NO_ADDONS_MAKE) +target_compile_definitions(myApp PRIVATE MY_DEFINE) # it's a normal CMake target +``` + +## Core only + +```sh +cmake -S libs/openFrameworksCompiled/project/CMake -B build/core -DCMAKE_BUILD_TYPE=Release +cmake --build build/core +``` + +| Platform | Core library (same name and place as the existing builds) | +|---|---| +| macOS | `lib/macos/openFrameworks.a`, `openFrameworksDebug.a` (Xcode's) | +| iOS / tvOS | `lib/ios//openFrameworksiOS.a`, `lib/tvos//openFrameworkstvOS.a` | +| Linux | `lib//libopenFrameworks.a`, `libopenFrameworksDebug.a` (make's) | +| Visual Studio | `lib/vs//openframeworksLib.lib`, `openframeworksLib_debug.lib` (the VS project's) | +| MSYS2 | `lib/msys2/libopenFrameworks.a` (make's) | +| Emscripten | `lib/emscripten/libopenFrameworks.a` (make builds `libopenFrameworks.o` next to it) | + +## Options + +| Option | Default | | +|---|---|---| +| `OF_BUILD_CORE` | `OFF` | build the core in this build even if a prebuilt one exists | +| `OF_PLATFORM` | detected | `macos ios tvos emscripten linux64 linuxaarch64 linuxarmv7l linuxarmv6l vs msys2` | +| `OF_USE_DAWN`, `OF_USE_ANGLE` | `OFF` | link `libs/dawn`, `libs/metalangle` | +| `OF_EMSCRIPTEN_INITIAL_MEMORY` | `134217728` | | +| `OF_EMSCRIPTEN_EMBIND_AOT` | `ON` | | +| `OF_EMSCRIPTEN_TEMPLATE` | `project/emscripten/template.html` | HTML shell | +| `OF_EMSCRIPTEN_MEMORY64` | `OFF` | wasm64, libs from `lib/emscripten/WASM64` | +| `OF_WEBGL_VERSION` | `2` | `1` for a WebGL 1 build | +| `OF_APPLE_DEVELOPMENT_TEAM` | | signing team for iOS / tvOS devices | + +## Addons + +`of_target_addons( ...)` adds addons the way the makefiles do: +`src/` and `libs/*/src` sources, `src/`, `libs/` and `libs/*/include` include paths, +`libs/*/lib/` libraries, then `addon_config.mk`'s `common:` and +`:` sections (`=` replaces, `+=` appends, `%` is a wildcard), including +`ADDON_DEPENDENCIES`, `ADDON_*_EXCLUDE`, `ADDON_DEFINES`, `ADDON_CFLAGS`, +`ADDON_LDFLAGS`, `ADDON_FRAMEWORKS` and `ADDON_PKG_CONFIG_LIBRARIES`. An addon is a +name in `addons/`, or a path relative to the project for local addons. +`of_add_app()` calls it for `ADDONS` / `addons.make`. + +## Project Generator + +A generated project only needs the template `CMakeLists.txt` with the addons +listed (or an `addons.make`). If PG resolves the addons itself, it can write them +out explicitly instead: + +```cmake +of_add_app(myApp NO_ADDONS_MAKE) +of_target_addon_files(myApp NAME ofxOsc + SOURCES ${OF_ROOT}/addons/ofxOsc/src/ofxOscReceiver.cpp ... + INCLUDES ${OF_ROOT}/addons/ofxOsc/src ${OF_ROOT}/addons/ofxOsc/libs/oscpack/src ... + LIBS ... # static libraries, absolute paths + DEFINES ... + CFLAGS ... + LDFLAGS ... + FRAMEWORKS ... # Apple frameworks + PKG_CONFIG ...) # pkg-config packages (Linux, MSYS2) +``` + +## Layout + +| File | | +|---|---| +| `openFrameworks.cmake` | entry point, included by apps and `CMakeLists.txt` | +| `CMakeLists.txt` | the core library project | +| `OFPlatform.cmake` | platform / arch detection | +| `OFCore.cmake` | core sources, third-party libs, `openFrameworks` and `openFrameworks::deps` targets | +| `OFAddons.cmake` | addons and `addon_config.mk` | +| `OFApp.cmake` | `of_add_app()` | +| `platform/.cmake` | per platform: excludes, defines, flags, libraries, app bundle / output | diff --git a/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake b/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake new file mode 100644 index 00000000000..11574394544 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake @@ -0,0 +1,48 @@ +# openFrameworks CMake +# +# Shared entry point for the core library project (CMakeLists.txt in this +# folder) and for apps. An app's CMakeLists.txt does: +# +# cmake_minimum_required(VERSION 3.22) +# project(myApp LANGUAGES C CXX) +# set(OF_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../..") +# include("${OF_ROOT}/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake") +# of_add_app(myApp ADDONS ofxGui) +# +# Platforms (OF_PLATFORM, detected from the toolchain, or set it): +# macos ios tvos emscripten linux64 linuxaarch64 linuxarmv7l linuxarmv6l vs msys2 +# +# Options: +# OF_BUILD_CORE=ON always build the core in this build, even if a prebuilt +# libs/openFrameworksCompiled/lib/ library exists + +include_guard(GLOBAL) + +get_filename_component(OF_ROOT "${CMAKE_CURRENT_LIST_DIR}/../../../.." ABSOLUTE) +set(OF_ROOT "${OF_ROOT}" CACHE PATH "openFrameworks root" FORCE) +set(OF_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}") +set(OF_LIBS_DIR "${OF_ROOT}/libs") +set(OF_CORE_SRC_DIR "${OF_LIBS_DIR}/openFrameworks") +set(OF_ADDONS_DIR "${OF_ROOT}/addons") + +option(OF_BUILD_CORE "Build the openFrameworks core in this build instead of linking a prebuilt one" OFF) + +set(CMAKE_C_STANDARD 17) +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED OFF) +set(CMAKE_CXX_EXTENSIONS OFF) +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) +endif() + +include("${OF_CMAKE_DIR}/OFPlatform.cmake") +include("${OF_CMAKE_DIR}/OFCore.cmake") +include("${OF_CMAKE_DIR}/platform/${OF_PLATFORM_FAMILY}.cmake") +include("${OF_CMAKE_DIR}/OFAddons.cmake") +include("${OF_CMAKE_DIR}/OFApp.cmake") + +# libs/openFrameworksCompiled/lib/[/] +set(OF_CORE_LIB_DIR "${OF_LIBS_DIR}/openFrameworksCompiled/lib/${OF_PLATFORM}") +if(OF_ARCH) + string(APPEND OF_CORE_LIB_DIR "/${OF_ARCH}") +endif() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/apple.cmake b/libs/openFrameworksCompiled/project/CMake/platform/apple.cmake new file mode 100644 index 00000000000..7a3dcd902c9 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/apple.cmake @@ -0,0 +1,62 @@ +# Shared by macos.cmake and ios.cmake + +include_guard(GLOBAL) + +enable_language(OBJC) +enable_language(OBJCXX) +set(CMAKE_OBJC_STANDARD 11) +set(CMAKE_OBJCXX_STANDARD 23) + +# libs//lib//*.xcframework, except the combined openssl.xcframework +# (libssl / libcrypto have their own) +function(of_apple_xcframeworks out_var lib_folder) + file(GLOB xcframeworks LIST_DIRECTORIES true "${lib_folder}/*.xcframework") + list(FILTER xcframeworks EXCLUDE REGEX "/openssl\\.xcframework$") + list(SORT xcframeworks) + set(${out_var} ${xcframeworks} PARENT_SCOPE) +endfunction() + +# Writes the app's Info.plist from the project's plist (or if the +# project has none), filling in the Xcode-style $(VAR) / ${VAR} placeholders +# the way the makefiles do, and sets it on 's bundle. +function(of_apple_info_plist target project_plist fallback_plist) + get_target_property(name ${target} OUTPUT_NAME) + if(NOT name) + set(name ${target}) + endif() + set(plist_text "") + if(EXISTS "${project_plist}") + file(READ "${project_plist}" plist_text) + endif() + if(NOT plist_text MATCHES "CFBundleExecutable") + file(READ "${fallback_plist}" plist_text) + endif() + string(MAKE_C_IDENTIFIER "${name}" bundle_name) + string(REPLACE "_" "-" bundle_name "${bundle_name}") + set(identifier "cc.openFrameworks.${bundle_name}") + foreach(pair + "DEVELOPMENT_LANGUAGE=English" + "ICON_NAME=of.icns" + "PRODUCT_BUNDLE_IDENTIFIER=${identifier}" + "TARGET_NAME=${name}" + "PRODUCT_NAME=${name}" + "MARKETING_VERSION=1.0" + "CURRENT_PROJECT_VERSION=1" + "HIGH_RESOLUTION_CAPABLE=true" + "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") + string(REGEX REPLACE "=.*" "" key "${pair}") + string(REGEX REPLACE "^[^=]*=" "" value "${pair}") + string(REPLACE "\${${key}}" "${value}" plist_text "${plist_text}") + string(REPLACE "$(${key})" "${value}" plist_text "${plist_text}") + endforeach() + # the executable name differs per configuration (_debug), so CMake fills it in + string(REPLACE "\${EXECUTABLE_NAME}" "\${MACOSX_BUNDLE_EXECUTABLE_NAME}" plist_text "${plist_text}") + string(REPLACE "$(EXECUTABLE_NAME)" "\${MACOSX_BUNDLE_EXECUTABLE_NAME}" plist_text "${plist_text}") + set(generated "${CMAKE_CURRENT_BINARY_DIR}/${target}-Info.plist.in") + file(WRITE "${generated}" "${plist_text}") + set_target_properties(${target} PROPERTIES + MACOSX_BUNDLE ON + MACOSX_BUNDLE_INFO_PLIST "${generated}" + MACOSX_BUNDLE_GUI_IDENTIFIER "${identifier}" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${identifier}") +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake new file mode 100644 index 00000000000..c4be2cecbce --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake @@ -0,0 +1,147 @@ +# Emscripten (mirrors libs/openFrameworksCompiled/project/emscripten/config.emscripten.default.mk) +# +# emcmake cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +# cmake --build build +# emrun bin/em/ +# +# Project options (cache variables): +# OF_EMSCRIPTEN_INITIAL_MEMORY bytes (default 128 MB) +# OF_EMSCRIPTEN_EMBIND_AOT ON (default) / OFF +# OF_EMSCRIPTEN_TEMPLATE HTML shell file (default the one in project/emscripten) +# OF_WEBGL_VERSION 2 (default) or 1 +# OF_EMSCRIPTEN_MEMORY64 OFF (default) / ON: wasm64, libs from lib/emscripten/WASM64 + +include_guard(GLOBAL) + +set(OF_EMSCRIPTEN_INITIAL_MEMORY 134217728 CACHE STRING "Initial wasm memory in bytes") +option(OF_EMSCRIPTEN_EMBIND_AOT "Ahead-of-time embind (CSP safe)" ON) +set(OF_EMSCRIPTEN_TEMPLATE "${OF_LIBS_DIR}/openFrameworksCompiled/project/emscripten/template.html" + CACHE FILEPATH "HTML shell for the app") +set(OF_WEBGL_VERSION 2 CACHE STRING "WebGL version (1 or 2)") +option(OF_EMSCRIPTEN_MEMORY64 "64-bit wasm memory (MEMORY64)" OFF) + +set(OF_PLATFORM_SOURCE_EXTENSIONS cpp cc cxx c) + +set(OF_PLATFORM_CORE_EXCLUDES + "/graphics/ofGraphicsCairo\\.cpp$" + "/graphics/ofCairoRenderer\\.cpp$" + "/gl/ofGLRenderer\\.cpp$" + "/video/ofDirectShow" + "/video/ofMediaFoundation" + "/sound/ofMediaFoundation" + "/video/ofGst" + "/communication/" + "/sound/ofFmodSoundPlayer\\.cpp$" + "/sound/ofOpenALSoundPlayer\\.cpp$" + "/sound/ofRtAudioSoundStream\\.cpp$" + "/app/ofAppGlutWindow\\.cpp$" + "/app/ofAppGLFWWindow\\.cpp$" + "/app/ofAppEGLWindow\\.cpp$") + +set(OF_PLATFORM_DEFINES __EMSCRIPTEN__ URI_STATIC_BUILD TARGET_EMSCRIPTEN) +if(OF_WEBGL_VERSION STREQUAL "1") + list(APPEND OF_PLATFORM_DEFINES OF_WEBGL_VERSION=1) +endif() + +set(OF_PLATFORM_COMPILE_OPTIONS + -fPIC + -pthread + -matomics + -mbulk-memory + "$<$:-Wall;-Wno-warn-absolute-paths>") + +set(OF_PLATFORM_LINK_OPTIONS + --emrun + --bind + --profiling-funcs + --use-port=emdawnwebgpu + -sAUTO_NATIVE_LIBRARIES=1 + -sAUTO_JS_LIBRARIES=1 + -pthread + -matomics + -mbulk-memory + "SHELL:--js-library ${OF_ADDONS_DIR}/ofxEmscripten/libs/html5video/lib/emscripten/library_html5video.js" + -sMAIN_MODULE=1 + -sEXPORT_ALL=1 + -sNO_DYNAMIC_EXECUTION=1 + -sABORT_ON_WASM_EXCEPTIONS=0 + -sFILESYSTEM=1 + -sMINIFY_HTML=0 + -sUSE_GLFW=3 + -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS,UTF8ToString,stringToUTF8,lengthBytesUTF8 + -sERROR_ON_UNDEFINED_SYMBOLS=1 + -sINITIAL_MEMORY=${OF_EMSCRIPTEN_INITIAL_MEMORY} + -sALLOW_MEMORY_GROWTH=1 + -sWASM=1 + "SHELL:--shell-file ${OF_EMSCRIPTEN_TEMPLATE}" + "$<$:-sVERBOSE=1;-sGL_ASSERTIONS=1;-sASSERTIONS=2;-gsource-map>") +if(OF_WEBGL_VERSION STREQUAL "1") + list(APPEND OF_PLATFORM_LINK_OPTIONS -sMAX_WEBGL_VERSION=1 -sFULL_ES2) +else() + list(APPEND OF_PLATFORM_LINK_OPTIONS -sMAX_WEBGL_VERSION=2 -sWEBGL2_BACKWARDS_COMPATIBILITY_EMULATION=1 -sFULL_ES3) +endif() +if(OF_EMSCRIPTEN_EMBIND_AOT) + list(APPEND OF_PLATFORM_LINK_OPTIONS -sEMBIND_AOT=1 -sDYNAMIC_EXECUTION=0) +else() + list(APPEND OF_PLATFORM_LINK_OPTIONS -sDYNAMIC_EXECUTION=1) +endif() + +if(OF_EMSCRIPTEN_MEMORY64) + list(APPEND OF_PLATFORM_COMPILE_OPTIONS -sMEMORY64=1) + list(APPEND OF_PLATFORM_LINK_OPTIONS -sMEMORY64=1 -mwasm64) +endif() + +set(OF_PLATFORM_LINK_LIBRARIES glfw) + +set(OF_PLATFORM_THIRDPARTY_EXCLUDE glew videoInput fmod openssl curl rtAudio cairo pixman metalangle) +if(NOT OF_USE_DAWN) + list(APPEND OF_PLATFORM_THIRDPARTY_EXCLUDE dawn) +endif() + +set(OF_PLATFORM_INCLUDE_DIRS "${OF_ADDONS_DIR}/ofxEmscripten/src") + +set(OF_PLATFORM_LIB_EXTENSIONS a) +set(OF_PLATFORM_LINK_GROUP OFF) +set(OF_PLATFORM_REQUIRED_ADDONS ofxEmscripten) + +# the makefiles build the core as libopenFrameworks.o; CMake builds a static +# library next to it +set(OF_CORE_LIB_PREFIX lib) +set(OF_CORE_LIB_NAME openFrameworks) +set(OF_CORE_LIB_NAME_DEBUG openFrameworksDebug) +set(OF_CORE_LIB_SUFFIX .a) + +# libs//lib/emscripten/WASM (WASM64 with OF_EMSCRIPTEN_MEMORY64) +function(of_platform_lib_dirs lib_folder out_var) + if(OF_EMSCRIPTEN_MEMORY64) + set(${out_var} "${lib_folder}" "${lib_folder}/WASM64" PARENT_SCOPE) + else() + set(${out_var} "${lib_folder}" "${lib_folder}/WASM" PARENT_SCOPE) + endif() +endfunction() + +# bin/em//index.html (+ .js .wasm .data), with app.js / style.css / +# .htaccess and bin/data preloaded at /data, like the makefiles +function(of_platform_setup_app target) + set(out_dir "${CMAKE_CURRENT_SOURCE_DIR}/bin/em/${target}") + set_target_properties(${target} PROPERTIES + OUTPUT_NAME index + OUTPUT_NAME_DEBUG index + SUFFIX ".html" + RUNTIME_OUTPUT_DIRECTORY "${out_dir}") + foreach(config Debug Release RelWithDebInfo MinSizeRel) + string(TOUPPER ${config} upper) + set_target_properties(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${upper} "${out_dir}") + endforeach() + + file(GLOB data_files "${CMAKE_CURRENT_SOURCE_DIR}/bin/data/*") + if(data_files) + target_link_options(${target} PRIVATE "SHELL:--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/bin/data@data") + endif() + + set(project_dir "${OF_LIBS_DIR}/openFrameworksCompiled/project/emscripten") + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${project_dir}/app.js" "${project_dir}/style.css" "${project_dir}/.htaccess" "${out_dir}/" + VERBATIM) +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/ios.cmake b/libs/openFrameworksCompiled/project/CMake/platform/ios.cmake new file mode 100644 index 00000000000..fb9228b3008 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/ios.cmake @@ -0,0 +1,176 @@ +# iOS and tvOS (mirrors project/ios/CoreOF.xcconfig, project/tvOS/CoreOF.xcconfig and +# the scripts/templates/ios / tvOS app templates) +# +# cmake -S . -B build-ios -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator +# cmake --build build-ios --config Release +# +# Use -DCMAKE_SYSTEM_NAME=tvOS (sysroot appletvsimulator / appletvos) for tvOS, and +# -DCMAKE_OSX_SYSROOT=iphoneos -DOF_APPLE_DEVELOPMENT_TEAM= for a device. +# Needs CMake 3.28+ (links the libs' xcframeworks directly). + +include_guard(GLOBAL) + +cmake_minimum_required(VERSION 3.28) +include("${CMAKE_CURRENT_LIST_DIR}/apple.cmake") + +set(OF_APPLE_DEVELOPMENT_TEAM "" CACHE STRING "Apple development team id for signing device builds") + +if(OF_PLATFORM STREQUAL "tvos") + set(_of_ios_name tvOS) + if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET 18.0 CACHE STRING "Minimum tvOS version" FORCE) + endif() +else() + set(_of_ios_name iOS) + if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET 15.0 CACHE STRING "Minimum iOS version" FORCE) + endif() +endif() + +# device and simulator builds of the core go in separate folders: +# lib/ios/iphoneos, lib/ios/iphonesimulator, lib/tvos/appletvos, lib/tvos/appletvsimulator +string(TOLOWER "${CMAKE_OSX_SYSROOT}" _of_sysroot) +if(_of_sysroot MATCHES "simulator") + set(_of_simulator ON) +else() + set(_of_simulator OFF) +endif() +if(OF_PLATFORM STREQUAL "tvos") + set(OF_ARCH appletvos) + if(_of_simulator) + set(OF_ARCH appletvsimulator) + endif() +else() + set(OF_ARCH iphoneos) + if(_of_simulator) + set(OF_ARCH iphonesimulator) + endif() +endif() + +set(OF_PLATFORM_SOURCE_EXTENSIONS cpp cc cxx c mm m) + +# the iOS core library is libs/openFrameworks + addons/ofxiOS +set(OF_PLATFORM_CORE_EXTRA_SOURCE_DIRS "${OF_ADDONS_DIR}/ofxiOS/src") +of_dir_and_subdirs(OF_PLATFORM_INCLUDE_DIRS "${OF_ADDONS_DIR}/ofxiOS/src") + +set(OF_PLATFORM_CORE_EXCLUDES + "/libs/openFrameworks/app/ofAppGLFWWindow\\.cpp$" + "/libs/openFrameworks/app/ofAppEGLWindow\\.cpp$" + "/libs/openFrameworks/app/ofAppGlutWindow\\.cpp$" + "/libs/openFrameworks/app/ofAppNoWindow\\.cpp$" + "/libs/openFrameworks/communication/" + "/libs/openFrameworks/graphics/ofCairoRenderer\\.cpp$" + "/libs/openFrameworks/graphics/ofGraphicsCairo\\.cpp$" + "/libs/openFrameworks/sound/ofAVEngineSoundPlayer\\.mm$" + "/libs/openFrameworks/sound/ofOpenALSoundPlayer\\.cpp$" + "/libs/openFrameworks/sound/ofRtAudioSoundStream\\.cpp$" + "/libs/openFrameworks/sound/ofFmodSoundPlayer\\.cpp$" + "/libs/openFrameworks/sound/ofMediaFoundation" + "/libs/openFrameworks/video/ofAVFoundation" + "/libs/openFrameworks/video/ofDirectShow" + "/libs/openFrameworks/video/ofMediaFoundation" + "/libs/openFrameworks/video/ofGst") + +set(OF_PLATFORM_DEFINES GLES_SILENCE_DEPRECATION=1 $<$:__MACOSX_CORE__>) +set(OF_PLATFORM_COMPILE_OPTIONS -fobjc-arc -fobjc-weak -Werror=return-type) +set(OF_PLATFORM_LINK_OPTIONS -ObjC) + +set(_of_frameworks + AudioToolbox Accelerate AVFoundation CoreAudio CoreGraphics CoreLocation CoreMedia CoreVideo + Foundation GameController GLKit MapKit OpenAL OpenGLES UIKit Security CFNetwork QuartzCore Metal) +if(OF_PLATFORM STREQUAL "tvos") + list(APPEND _of_frameworks MetalKit) +else() + list(APPEND _of_frameworks CoreMotion CoreHaptics) +endif() +set(OF_PLATFORM_LINK_LIBRARIES) +foreach(framework IN LISTS _of_frameworks) + list(APPEND OF_PLATFORM_LINK_LIBRARIES "-framework ${framework}") +endforeach() + +set(OF_PLATFORM_THIRDPARTY_EXCLUDE dawn metalangle glew glfw rtAudio cairo pixman) + +set(OF_PLATFORM_LIB_EXTENSIONS a) +set(OF_PLATFORM_LINK_GROUP OFF) + +set(OF_CORE_LIB_PREFIX "") +set(OF_CORE_LIB_NAME openFrameworks${_of_ios_name}) +set(OF_CORE_LIB_NAME_DEBUG openFrameworks${_of_ios_name}Debug) +set(OF_CORE_LIB_SUFFIX .a) + +function(of_platform_lib_dirs lib_folder out_var) + set(${out_var} "${lib_folder}" PARENT_SCOPE) +endfunction() + +# whole xcframeworks (CMake / Xcode pick the device or simulator slice) from +# lib/ios (lib/tvos) or, like the Xcode core project, the multi-platform ones in +# lib/macos; only those that have a slice for this platform +function(of_platform_static_libs_in out_var lib_folder) + get_filename_component(lib_root "${lib_folder}" DIRECTORY) + if(OF_PLATFORM STREQUAL "tvos") + set(slice_prefix tvos-) + else() + set(slice_prefix ios-) + endif() + set(result) + foreach(folder "${lib_folder}" "${lib_root}/macos") + of_apple_xcframeworks(xcframeworks "${folder}") + foreach(xcf IN LISTS xcframeworks) + get_filename_component(xcf_name "${xcf}" NAME) + file(GLOB slices LIST_DIRECTORIES true "${xcf}/${slice_prefix}*") + if(slices AND NOT ";${result};" MATCHES ";[^;]*/${xcf_name};") + list(APPEND result "${xcf}") + endif() + endforeach() + endforeach() + file(GLOB libs "${lib_folder}/*.a") + list(APPEND result ${libs}) + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +function(of_platform_setup_core target) + if(OF_APPLE_DEVELOPMENT_TEAM) + set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${OF_APPLE_DEVELOPMENT_TEAM}") + endif() +endfunction() + +# bin/.app with bin/data in the bundle (like the template's rsync build +# phase) and, with the Xcode generator, the template's mediaAssets +function(of_platform_setup_app target) + if(OF_PLATFORM STREQUAL "tvos") + set(plist "${CMAKE_CURRENT_SOURCE_DIR}/ofxtvOS-Info.plist") + set(fallback "${OF_ROOT}/scripts/templates/tvOS/ofxtvOS-Info.plist") + set(assets_dir "${CMAKE_CURRENT_SOURCE_DIR}/mediaAssets") + set(families 3) + else() + set(plist "${CMAKE_CURRENT_SOURCE_DIR}/ofxiOS-Info.plist") + set(fallback "${OF_ROOT}/scripts/templates/ios/ofxiOS-Info.plist") + set(assets_dir "${CMAKE_CURRENT_SOURCE_DIR}/mediaAssets") + set(families "1,2") + endif() + of_apple_info_plist(${target} "${plist}" "${fallback}") + set_target_properties(${target} PROPERTIES + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "${families}" + XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AppIcon) + if(OF_APPLE_DEVELOPMENT_TEAM) + set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${OF_APPLE_DEVELOPMENT_TEAM}") + endif() + + if(NOT IS_DIRECTORY "${assets_dir}") + get_filename_component(template_dir "${fallback}" DIRECTORY) + set(assets_dir "${template_dir}/mediaAssets") + endif() + # storyboards and asset catalogs need Xcode's ibtool / actool + if(CMAKE_GENERATOR STREQUAL "Xcode" AND IS_DIRECTORY "${assets_dir}") + file(GLOB assets LIST_DIRECTORIES true "${assets_dir}/*.storyboard" "${assets_dir}/*.xcassets") + target_sources(${target} PRIVATE ${assets}) + set_source_files_properties(${assets} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + set_property(TARGET ${target} APPEND PROPERTY RESOURCE ${assets}) + endif() + + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/data") + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/bin/data" "$" + VERBATIM) + endif() +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/linux.cmake b/libs/openFrameworksCompiled/project/CMake/platform/linux.cmake new file mode 100644 index 00000000000..7d540fbece3 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/linux.cmake @@ -0,0 +1,119 @@ +# Linux: linux64 / linuxaarch64 / linuxarmv7l / linuxarmv6l +# (mirrors makefileCommon/config.linux.common.mk and /config..default.mk) +# +# Most dependencies come from the system through pkg-config; install them with +# scripts/linux//install_dependencies.sh. + +include_guard(GLOBAL) + +find_package(PkgConfig REQUIRED) + +set(_of_linux_arm OFF) +if(OF_PLATFORM MATCHES "^linuxarmv[67]l$") + set(_of_linux_arm ON) +endif() + +set(OF_PLATFORM_SOURCE_EXTENSIONS cpp cc cxx c) + +set(OF_PLATFORM_CORE_EXCLUDES + "/video/ofDirectShow" + "/video/ofMediaFoundation" + "/sound/ofMediaFoundation" + "/sound/ofFmodSoundPlayer\\.cpp$") +if(_of_linux_arm) + list(APPEND OF_PLATFORM_CORE_EXCLUDES "/app/ofAppGlutWindow\\.cpp$") +else() + list(APPEND OF_PLATFORM_CORE_EXCLUDES "/app/ofAppEGLWindow\\.cpp$") +endif() + +set(OF_PLATFORM_DEFINES) +set(OF_PLATFORM_COMPILE_OPTIONS -Wall -Werror=return-type) +set(OF_PLATFORM_LINK_OPTIONS "-Wl,-rpath=./libs:./bin/libs:./" -Wl,--as-needed -Wl,--gc-sections) + +if(OF_PLATFORM STREQUAL "linuxaarch64") + list(APPEND OF_PLATFORM_COMPILE_OPTIONS + -march=armv8-a -mcpu=cortex-a72 -mtune=cortex-a72 -fPIC -ftree-vectorize -Wno-psabi -pipe) + list(APPEND OF_PLATFORM_LINK_OPTIONS -no-pie) +elseif(OF_PLATFORM STREQUAL "linuxarmv7l") + list(APPEND OF_PLATFORM_COMPILE_OPTIONS + -march=armv7 -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard -fPIC -ftree-vectorize -Wno-psabi -pipe) +endif() + +set(OF_PLATFORM_LINK_LIBRARIES) + +# required pkg-config packages +set(_of_pkgs cairo zlib gstreamer-app-1.0 gstreamer-1.0 gstreamer-video-1.0 gstreamer-base-1.0 + libudev freetype2 fontconfig sndfile openal) +if(NOT _of_linux_arm) + list(APPEND _of_pkgs libpulse-simple alsa gl glu glew) +endif() +if(OF_PLATFORM MATCHES "^linux(aarch64|armv7l|armv6l)$") + list(APPEND _of_pkgs glesv1_cm glesv2 egl) +endif() +foreach(pkg IN LISTS _of_pkgs) + string(MAKE_C_IDENTIFIER "OF_PC_${pkg}" id) + pkg_check_modules(${id} REQUIRED IMPORTED_TARGET GLOBAL ${pkg}) + list(APPEND OF_PLATFORM_LINK_LIBRARIES PkgConfig::${id}) +endforeach() + +# optional pkg-config packages +foreach(pkg openssl libcurl libcurl4 glfw3 rtaudio) + string(MAKE_C_IDENTIFIER "OF_PC_${pkg}" id) + pkg_check_modules(${id} QUIET IMPORTED_TARGET GLOBAL ${pkg}) + if(${id}_FOUND) + list(APPEND OF_PLATFORM_LINK_LIBRARIES PkgConfig::${id}) + endif() +endforeach() +if(OF_PC_glfw3_FOUND) + list(APPEND OF_PLATFORM_LINK_LIBRARIES Xinerama) +endif() +if(NOT OF_PC_rtaudio_FOUND) + list(APPEND OF_PLATFORM_LINK_LIBRARIES rtaudio) +endif() + +pkg_check_modules(OF_PC_gtk3 QUIET IMPORTED_TARGET GLOBAL gtk+-3.0) +if(OF_PC_gtk3_FOUND) + list(APPEND OF_PLATFORM_LINK_LIBRARIES PkgConfig::OF_PC_gtk3) + list(APPEND OF_PLATFORM_DEFINES OF_USING_GTK) +else() + pkg_check_modules(OF_PC_gtk2 QUIET IMPORTED_TARGET GLOBAL gtk+-2.0) + if(OF_PC_gtk2_FOUND) + list(APPEND OF_PLATFORM_LINK_LIBRARIES PkgConfig::OF_PC_gtk2) + list(APPEND OF_PLATFORM_DEFINES OF_USING_GTK) + endif() +endif() +pkg_check_modules(OF_PC_mpg123 QUIET IMPORTED_TARGET GLOBAL libmpg123) +if(OF_PC_mpg123_FOUND) + list(APPEND OF_PLATFORM_LINK_LIBRARIES PkgConfig::OF_PC_mpg123) + list(APPEND OF_PLATFORM_DEFINES OF_USING_MPG123) +endif() + +# plain system libraries +if(NOT _of_linux_arm) + list(APPEND OF_PLATFORM_LINK_LIBRARIES glut) +endif() +if(NOT OF_PLATFORM STREQUAL "linuxarmv6l") + list(APPEND OF_PLATFORM_LINK_LIBRARIES X11 Xrandr Xxf86vm Xi Xcursor dl pthread) +endif() +list(APPEND OF_PLATFORM_LINK_LIBRARIES freeimage pugixml uriparser) + +# these come from the system on Linux, not libs/ +set(OF_PLATFORM_THIRDPARTY_EXCLUDE + glew cairo videoInput freetype FreeImage assimp rtAudio openssl glfw curl uriparser fmod + pixman dawn metalangle) + +set(OF_PLATFORM_LIB_EXTENSIONS a) +set(OF_PLATFORM_LINK_GROUP ON) + +set(OF_CORE_LIB_PREFIX lib) +set(OF_CORE_LIB_NAME openFrameworks) +set(OF_CORE_LIB_NAME_DEBUG openFrameworksDebug) +set(OF_CORE_LIB_SUFFIX .a) + +function(of_platform_lib_dirs lib_folder out_var) + set(${out_var} "${lib_folder}" PARENT_SCOPE) +endfunction() + +# bin/ (bin/_debug for Debug); nothing else to do +function(of_platform_setup_app target) +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake b/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake new file mode 100644 index 00000000000..5cd84723bc3 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake @@ -0,0 +1,84 @@ +# macOS (mirrors libs/openFrameworksCompiled/project/macos/config.macos.default.mk) + +include_guard(GLOBAL) + +include("${CMAKE_CURRENT_LIST_DIR}/apple.cmake") + +if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0 CACHE STRING "Minimum macOS version" FORCE) +endif() + +set(OF_PLATFORM_SOURCE_EXTENSIONS cpp cc cxx c mm m) + +set(OF_PLATFORM_CORE_EXCLUDES + "/app/ofAppGlutWindow\\.cpp$" + "/app/ofAppEGLWindow\\.cpp$" + "/video/ofDirectShow" + "/video/ofMediaFoundation" + "/sound/ofMediaFoundation" + "/video/ofGst" + "/sound/ofFmodSoundPlayer\\.cpp$") + +set(OF_PLATFORM_DEFINES __MACOSX_CORE__ USE_FMOD=0) + +set(OF_PLATFORM_COMPILE_OPTIONS + -Wall + -Werror=return-type + -fexceptions + -fobjc-arc + # like the makefiles, C++ is compiled as Objective-C++ so it can use Cocoa + "$<$:-xobjective-c++>") + +set(OF_PLATFORM_LINK_OPTIONS) + +set(OF_PLATFORM_LINK_LIBRARIES objc) +foreach(framework + Accelerate AppKit ApplicationServices AVFoundation AudioToolbox Cocoa + CoreVideo CoreAudio CoreMedia CoreFoundation CoreServices Metal Foundation + IOKit OpenGL QuartzCore Security CFNetwork SystemConfiguration) + list(APPEND OF_PLATFORM_LINK_LIBRARIES "-framework ${framework}") +endforeach() + +# Dawn / MetalANGLE are opt-in renderers, not core dependencies +set(OF_PLATFORM_THIRDPARTY_EXCLUDE) +if(NOT OF_USE_DAWN) + list(APPEND OF_PLATFORM_THIRDPARTY_EXCLUDE dawn) +endif() +if(NOT OF_USE_ANGLE) + list(APPEND OF_PLATFORM_THIRDPARTY_EXCLUDE metalangle) +endif() + +set(OF_PLATFORM_LIB_EXTENSIONS a) +set(OF_PLATFORM_LINK_GROUP OFF) + +set(OF_CORE_LIB_PREFIX "") # same as the Xcode core project: lib/macos/openFrameworks.a +set(OF_CORE_LIB_NAME openFrameworks) +set(OF_CORE_LIB_NAME_DEBUG openFrameworksDebug) +set(OF_CORE_LIB_SUFFIX .a) + +# libs//lib/macos holds .a files and/or xcframeworks; use each +# xcframework's macOS slice +function(of_platform_lib_dirs lib_folder out_var) + set(dirs "${lib_folder}") + of_apple_xcframeworks(xcframeworks "${lib_folder}") + foreach(xcf IN LISTS xcframeworks) + file(GLOB slices LIST_DIRECTORIES true "${xcf}/macos-*") + list(APPEND dirs ${slices}) + endforeach() + set(${out_var} ${dirs} PARENT_SCOPE) +endfunction() + +# turns the app into bin/.app (bin/_debug.app for Debug), with the +# Info.plist and icon the makefiles use +function(of_platform_setup_app target) + of_apple_info_plist(${target} + "${CMAKE_CURRENT_SOURCE_DIR}/openFrameworks-Info.plist" + "${OF_ROOT}/scripts/templates/osx/openFrameworks-Info.plist") + + set(icon "${CMAKE_CURRENT_SOURCE_DIR}/of.icns") + if(NOT EXISTS "${icon}") + set(icon "${OF_LIBS_DIR}/openFrameworksCompiled/project/osx/of.icns") + endif() + set_source_files_properties("${icon}" PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + target_sources(${target} PRIVATE "${icon}") +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/msys2.cmake b/libs/openFrameworksCompiled/project/CMake/platform/msys2.cmake new file mode 100644 index 00000000000..1a9827f66e0 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/msys2.cmake @@ -0,0 +1,73 @@ +# Windows, MSYS2 (clang64 / ucrt64 / mingw64 / clangarm64) +# (mirrors libs/openFrameworksCompiled/project/msys2/config.msys2.default.mk) +# +# From an MSYS2 shell, with the dependencies from scripts/msys2/install_dependencies.sh: +# cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release +# cmake --build build + +include_guard(GLOBAL) + +find_package(PkgConfig REQUIRED) +enable_language(RC) # windres, for the app icon + +set(OF_PLATFORM_SOURCE_EXTENSIONS cpp cc cxx c) + +set(OF_PLATFORM_CORE_EXCLUDES + "/sound/ofMediaFoundationSoundPlayer\\.cpp$" + "/video/ofMediaFoundationPlayer\\.cpp$" + "/video/ofGst" + "/app/ofAppEGLWindow\\.cpp$") + +set(OF_PLATFORM_DEFINES OF_VIDEO_PLAYER_DIRECTSHOW UNICODE _UNICODE) +set(OF_PLATFORM_COMPILE_OPTIONS -Wall -fexceptions) +set(OF_PLATFORM_LINK_OPTIONS) +if(OF_MSYSTEM STREQUAL "mingw64") + list(APPEND OF_PLATFORM_LINK_OPTIONS "-Wl,--disable-dynamicbase,--disable-high-entropy-va,--default-image-base-low") +endif() + +set(OF_PLATFORM_LINK_LIBRARIES) +foreach(pkg cairo zlib openssl freetype2 glew glfw3 libcurl liburiparser nlohmann_json openal pugixml rtaudio) + string(MAKE_C_IDENTIFIER "OF_PC_${pkg}" id) + pkg_check_modules(${id} REQUIRED IMPORTED_TARGET GLOBAL ${pkg}) + list(APPEND OF_PLATFORM_LINK_LIBRARIES PkgConfig::${id}) +endforeach() +pkg_check_modules(OF_PC_mpg123 QUIET IMPORTED_TARGET GLOBAL libmpg123) +if(OF_PC_mpg123_FOUND) + pkg_check_modules(OF_PC_sndfile REQUIRED IMPORTED_TARGET GLOBAL sndfile) + list(APPEND OF_PLATFORM_LINK_LIBRARIES PkgConfig::OF_PC_mpg123 PkgConfig::OF_PC_sndfile) + list(APPEND OF_PLATFORM_DEFINES OF_USING_MPG123) +endif() +list(APPEND OF_PLATFORM_LINK_LIBRARIES + ksuser opengl32 gdi32 msimg32 glu32 dsound winmm strmiids + uuid ole32 oleaut32 setupapi wsock32 ws2_32 Iphlpapi Comdlg32 + freeimage mf mfplat mfuuid mfreadwrite pthread) + +# these come from MSYS2 packages, not libs/ +set(OF_PLATFORM_THIRDPARTY_EXCLUDE + glew cairo freetype FreeImage glm json openssl glfw curl pugixml rtAudio uriparser utf8 + pixman dawn metalangle) + +set(OF_PLATFORM_LIB_EXTENSIONS a) +set(OF_PLATFORM_LINK_GROUP ON) + +set(OF_CORE_LIB_PREFIX lib) +set(OF_CORE_LIB_NAME openFrameworks) +set(OF_CORE_LIB_NAME_DEBUG openFrameworksDebug) +set(OF_CORE_LIB_SUFFIX .a) + +# libs//lib/msys2, and lib/msys2/ where a library has one +function(of_platform_lib_dirs lib_folder out_var) + set(dirs "${lib_folder}") + if(OF_MSYSTEM) + list(APPEND dirs "${lib_folder}/${OF_MSYSTEM}") + endif() + set(${out_var} ${dirs} PARENT_SCOPE) +endfunction() + +# bin/.exe with the openFrameworks icon (Debug: _debug.exe) +function(of_platform_setup_app target) + set(icon_rc "${CMAKE_CURRENT_SOURCE_DIR}/icon.rc") + if(EXISTS "${icon_rc}") + target_sources(${target} PRIVATE "${icon_rc}") + endif() +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake new file mode 100644 index 00000000000..70f18acea24 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake @@ -0,0 +1,88 @@ +# Windows, Visual Studio / MSVC (mirrors project/vs/openFrameworks{Common,Release,Debug}.props) +# +# cmake -S . -B build -G "Visual Studio 17 2022" -A x64 (or ARM64 / ARM64EC) +# cmake --build build --config Release +# +# Ninja from a Developer Command Prompt works too (the arch comes from the compiler). + +include_guard(GLOBAL) + +set(OF_PLATFORM_SOURCE_EXTENSIONS cpp cc cxx c) + +set(OF_PLATFORM_CORE_EXCLUDES + "/app/ofAppEGLWindow\\.cpp$" + "/sound/ofOpenALSoundPlayer\\.cpp$" + "/video/ofGst") + +set(OF_PLATFORM_DEFINES + WIN32 _CONSOLE POCO_STATIC CAIRO_WIN32_STATIC_BUILD DISABLE_SOME_FLOATING_POINT + URI_STATIC_BUILD CURL_STATICLIB FREEIMAGE_LIB OF_NO_FMOD + _HAS_STREAM_INSERTION_OPERATORS_DELETED_IN_CXX20 + $<$:_DEBUG>) + +set(OF_PLATFORM_COMPILE_OPTIONS /Zc:__cplusplus /MP /utf-8) + +set(OF_PLATFORM_LINK_OPTIONS + /NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:LIBC.lib + "$,/NODEFAULTLIB:msvcrt.lib;/NODEFAULTLIB:libcmt.lib;/NODEFAULTLIB:LIBCMTD.lib;/ignore:4099,/NODEFAULTLIB:LIBCMT.lib>") + +set(OF_PLATFORM_LINK_LIBRARIES + msimg32 OpenGL32 GLu32 kernel32 setupapi Vfw32 comctl32 dsound user32 gdi32 winspool + comdlg32 advapi32 shell32 ole32 oleaut32 uuid odbc32 odbccp32 wldap32 crypt32 bcrypt + Ws2_32 winmm iphlpapi mf mfplat mfuuid mfreadwrite d3d11 xaudio2) + +set(OF_PLATFORM_THIRDPARTY_EXCLUDE dawn metalangle) + +set(OF_PLATFORM_LIB_EXTENSIONS lib) +set(OF_PLATFORM_LINK_GROUP OFF) + +# same as project/vs/openframeworksLib.vcxproj: lib/vs//openframeworksLib[_debug].lib +set(OF_CORE_LIB_PREFIX "") +set(OF_CORE_LIB_NAME openframeworksLib) +set(OF_CORE_LIB_NAME_DEBUG openframeworksLib_debug) +set(OF_CORE_LIB_SUFFIX .lib) + +function(of_platform_lib_dirs lib_folder out_var) + set(${out_var} "${lib_folder}/${OF_ARCH}" PARENT_SCOPE) +endfunction() + +# libs//lib/vs/ holds Release and Debug builds side by side +# (rtAudio.lib / rtAudioD.lib, freetype.lib / freetyped.lib), or in +# Release / Debug subfolders; each is linked only in its configuration +function(of_platform_static_libs_in out_var lib_folder) + set(dir "${lib_folder}/${OF_ARCH}") + set(result) + if(IS_DIRECTORY "${dir}/Release" OR IS_DIRECTORY "${dir}/Debug") + file(GLOB release_libs "${dir}/Release/*.lib") + file(GLOB debug_libs "${dir}/Debug/*.lib") + foreach(lib IN LISTS release_libs) + list(APPEND result "$<$>:${lib}>") + endforeach() + foreach(lib IN LISTS debug_libs) + list(APPEND result "$<$:${lib}>") + endforeach() + endif() + file(GLOB libs "${dir}/*.lib") + list(SORT libs) + foreach(lib IN LISTS libs) + get_filename_component(name "${lib}" NAME_WE) + string(REGEX REPLACE "[dD]$" "" base "${name}") + if(NOT base STREQUAL name AND EXISTS "${dir}/${base}.lib") + list(APPEND result "$<$:${lib}>") + elseif(EXISTS "${dir}/${name}d.lib" OR EXISTS "${dir}/${name}D.lib") + list(APPEND result "$<$>:${lib}>") + else() + list(APPEND result "${lib}") + endif() + endforeach() + set(${out_var} ${result} PARENT_SCOPE) +endfunction() + +# console app in bin/, like the VS templates (Debug: _debug.exe) +function(of_platform_setup_app target) + set(icon_rc "${CMAKE_CURRENT_SOURCE_DIR}/icon.rc") + if(EXISTS "${icon_rc}") + target_sources(${target} PRIVATE "${icon_rc}") + endif() + set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY VS_STARTUP_PROJECT ${target}) +endfunction() diff --git a/scripts/templates/cmake/CMakeLists.txt b/scripts/templates/cmake/CMakeLists.txt new file mode 100644 index 00000000000..2867e97edff --- /dev/null +++ b/scripts/templates/cmake/CMakeLists.txt @@ -0,0 +1,23 @@ +# openFrameworks app (CMake) +# +# cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +# cmake --build build +# +# The app is built into bin/. The openFrameworks core is linked from +# libs/openFrameworksCompiled/lib, or built here first if it isn't there. + +cmake_minimum_required(VERSION 3.22) + +# project folder name is the app name by default +get_filename_component(OF_APP_NAME "${CMAKE_CURRENT_SOURCE_DIR}" NAME) +project(${OF_APP_NAME} LANGUAGES C CXX) + +# path to openFrameworks, relative to this project (apps/myApps/ by default) +if(NOT OF_ROOT) + set(OF_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../..") +endif() +include("${OF_ROOT}/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake") + +# sources come from src/, addons from addons.make; or list them: +# of_add_app(${OF_APP_NAME} ADDONS ofxGui ofxOsc) +of_add_app(${OF_APP_NAME}) From 71daeb753b6387031d4842642f4e5ca122e97983 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Wed, 14 Oct 2026 23:58:47 +1300 Subject: [PATCH 02/14] CMake Emscripten: use the makefiles' option names PROJECT_EMSCRIPTEN_WASM (32 / 64), PROJECT_EMSCRIPTEN_TOTAL_MEMORY, PROJECT_EMSCRIPTEN_EMBIND_AOT and PROJECT_EMSCRIPTEN_TEMPLATE, as in a project's config.make, so the Project Generator writes the same setting for both. wasm64 builds the core into lib/emscripten/WASM64 like the makefiles; EMSCRIPTEN_MEMORY64 still selects 64. --- .../project/CMake/README.md | 11 +++-- .../project/CMake/platform/emscripten.cmake | 47 ++++++++++++------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/libs/openFrameworksCompiled/project/CMake/README.md b/libs/openFrameworksCompiled/project/CMake/README.md index cb9b75cef42..b528207c741 100644 --- a/libs/openFrameworksCompiled/project/CMake/README.md +++ b/libs/openFrameworksCompiled/project/CMake/README.md @@ -63,15 +63,18 @@ cmake --build build/core ## Options +The Emscripten `PROJECT_EMSCRIPTEN_*` options have the same names and values as the +makefiles' `config.make` options. + | Option | Default | | |---|---|---| | `OF_BUILD_CORE` | `OFF` | build the core in this build even if a prebuilt one exists | | `OF_PLATFORM` | detected | `macos ios tvos emscripten linux64 linuxaarch64 linuxarmv7l linuxarmv6l vs msys2` | | `OF_USE_DAWN`, `OF_USE_ANGLE` | `OFF` | link `libs/dawn`, `libs/metalangle` | -| `OF_EMSCRIPTEN_INITIAL_MEMORY` | `134217728` | | -| `OF_EMSCRIPTEN_EMBIND_AOT` | `ON` | | -| `OF_EMSCRIPTEN_TEMPLATE` | `project/emscripten/template.html` | HTML shell | -| `OF_EMSCRIPTEN_MEMORY64` | `OFF` | wasm64, libs from `lib/emscripten/WASM64` | +| `PROJECT_EMSCRIPTEN_WASM` | `32` | `64` for wasm64 (MEMORY64): libs from `lib/emscripten/WASM64`, core in `lib/emscripten/WASM64` | +| `PROJECT_EMSCRIPTEN_TOTAL_MEMORY` | `134217728` | initial memory in bytes | +| `PROJECT_EMSCRIPTEN_EMBIND_AOT` | `ON` | | +| `PROJECT_EMSCRIPTEN_TEMPLATE` | `project/emscripten/template.html` | HTML shell | | `OF_WEBGL_VERSION` | `2` | `1` for a WebGL 1 build | | `OF_APPLE_DEVELOPMENT_TEAM` | | signing team for iOS / tvOS devices | diff --git a/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake index c4be2cecbce..3130527f6f4 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake @@ -4,21 +4,36 @@ # cmake --build build # emrun bin/em/ # -# Project options (cache variables): -# OF_EMSCRIPTEN_INITIAL_MEMORY bytes (default 128 MB) -# OF_EMSCRIPTEN_EMBIND_AOT ON (default) / OFF -# OF_EMSCRIPTEN_TEMPLATE HTML shell file (default the one in project/emscripten) -# OF_WEBGL_VERSION 2 (default) or 1 -# OF_EMSCRIPTEN_MEMORY64 OFF (default) / ON: wasm64, libs from lib/emscripten/WASM64 +# Project options (cache variables, same names as the makefiles' config.make options): +# PROJECT_EMSCRIPTEN_WASM 32 (default) or 64: wasm64 (MEMORY64), libs from +# lib/emscripten/WASM64, core in lib/emscripten/WASM64 +# PROJECT_EMSCRIPTEN_TOTAL_MEMORY initial memory in bytes (default 128 MB) +# PROJECT_EMSCRIPTEN_EMBIND_AOT ON (default) / OFF +# PROJECT_EMSCRIPTEN_TEMPLATE HTML shell file (default the one in project/emscripten) +# OF_WEBGL_VERSION 2 (default) or 1 include_guard(GLOBAL) -set(OF_EMSCRIPTEN_INITIAL_MEMORY 134217728 CACHE STRING "Initial wasm memory in bytes") -option(OF_EMSCRIPTEN_EMBIND_AOT "Ahead-of-time embind (CSP safe)" ON) -set(OF_EMSCRIPTEN_TEMPLATE "${OF_LIBS_DIR}/openFrameworksCompiled/project/emscripten/template.html" +if(EMSCRIPTEN_MEMORY64) + set(_of_wasm_default 64) +else() + set(_of_wasm_default 32) +endif() +set(PROJECT_EMSCRIPTEN_WASM ${_of_wasm_default} CACHE STRING "WebAssembly 32 or 64 (MEMORY64)") +set_property(CACHE PROJECT_EMSCRIPTEN_WASM PROPERTY STRINGS 32 64) +if(NOT PROJECT_EMSCRIPTEN_WASM MATCHES "^(32|64)$") + message(FATAL_ERROR "PROJECT_EMSCRIPTEN_WASM must be 32 or 64, not \"${PROJECT_EMSCRIPTEN_WASM}\"") +endif() +set(PROJECT_EMSCRIPTEN_TOTAL_MEMORY 134217728 CACHE STRING "Initial wasm memory in bytes") +option(PROJECT_EMSCRIPTEN_EMBIND_AOT "Ahead-of-time embind (CSP safe)" ON) +set(PROJECT_EMSCRIPTEN_TEMPLATE "${OF_LIBS_DIR}/openFrameworksCompiled/project/emscripten/template.html" CACHE FILEPATH "HTML shell for the app") set(OF_WEBGL_VERSION 2 CACHE STRING "WebGL version (1 or 2)") -option(OF_EMSCRIPTEN_MEMORY64 "64-bit wasm memory (MEMORY64)" OFF) + +# wasm64: the core goes in lib/emscripten/WASM64, like the makefiles +if(PROJECT_EMSCRIPTEN_WASM STREQUAL "64") + set(OF_ARCH WASM64) +endif() set(OF_PLATFORM_SOURCE_EXTENSIONS cpp cc cxx c) @@ -70,23 +85,23 @@ set(OF_PLATFORM_LINK_OPTIONS -sUSE_GLFW=3 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS,UTF8ToString,stringToUTF8,lengthBytesUTF8 -sERROR_ON_UNDEFINED_SYMBOLS=1 - -sINITIAL_MEMORY=${OF_EMSCRIPTEN_INITIAL_MEMORY} + -sINITIAL_MEMORY=${PROJECT_EMSCRIPTEN_TOTAL_MEMORY} -sALLOW_MEMORY_GROWTH=1 -sWASM=1 - "SHELL:--shell-file ${OF_EMSCRIPTEN_TEMPLATE}" + "SHELL:--shell-file ${PROJECT_EMSCRIPTEN_TEMPLATE}" "$<$:-sVERBOSE=1;-sGL_ASSERTIONS=1;-sASSERTIONS=2;-gsource-map>") if(OF_WEBGL_VERSION STREQUAL "1") list(APPEND OF_PLATFORM_LINK_OPTIONS -sMAX_WEBGL_VERSION=1 -sFULL_ES2) else() list(APPEND OF_PLATFORM_LINK_OPTIONS -sMAX_WEBGL_VERSION=2 -sWEBGL2_BACKWARDS_COMPATIBILITY_EMULATION=1 -sFULL_ES3) endif() -if(OF_EMSCRIPTEN_EMBIND_AOT) +if(PROJECT_EMSCRIPTEN_EMBIND_AOT) list(APPEND OF_PLATFORM_LINK_OPTIONS -sEMBIND_AOT=1 -sDYNAMIC_EXECUTION=0) else() list(APPEND OF_PLATFORM_LINK_OPTIONS -sDYNAMIC_EXECUTION=1) endif() -if(OF_EMSCRIPTEN_MEMORY64) +if(PROJECT_EMSCRIPTEN_WASM STREQUAL "64") list(APPEND OF_PLATFORM_COMPILE_OPTIONS -sMEMORY64=1) list(APPEND OF_PLATFORM_LINK_OPTIONS -sMEMORY64=1 -mwasm64) endif() @@ -111,9 +126,9 @@ set(OF_CORE_LIB_NAME openFrameworks) set(OF_CORE_LIB_NAME_DEBUG openFrameworksDebug) set(OF_CORE_LIB_SUFFIX .a) -# libs//lib/emscripten/WASM (WASM64 with OF_EMSCRIPTEN_MEMORY64) +# libs//lib/emscripten/WASM (WASM64 with PROJECT_EMSCRIPTEN_WASM=64) function(of_platform_lib_dirs lib_folder out_var) - if(OF_EMSCRIPTEN_MEMORY64) + if(PROJECT_EMSCRIPTEN_WASM STREQUAL "64") set(${out_var} "${lib_folder}" "${lib_folder}/WASM64" PARENT_SCOPE) else() set(${out_var} "${lib_folder}" "${lib_folder}/WASM" PARENT_SCOPE) From 02b91f0b27ac3a0b0ccadb7999c07bfcda85399e Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Fri, 16 Oct 2026 15:56:25 +1300 Subject: [PATCH 03/14] CMake: APP_VERSION and APP_BUILD for macOS / iOS / tvOS Info.plist Same names as the makefiles' config.make (#8592): APP_VERSION sets CFBundleShortVersionString (default: project(... VERSION), else 1.0) and APP_BUILD sets CFBundleVersion (default 1), also as Xcode's MARKETING_VERSION / CURRENT_PROJECT_VERSION. Based on #3394. Co-authored-by: Vincent Donnefort --- .../project/CMake/README.md | 2 ++ .../project/CMake/platform/apple.cmake | 22 ++++++++++++++++--- scripts/templates/cmake/CMakeLists.txt | 5 +++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/libs/openFrameworksCompiled/project/CMake/README.md b/libs/openFrameworksCompiled/project/CMake/README.md index b528207c741..99f0e85fcdd 100644 --- a/libs/openFrameworksCompiled/project/CMake/README.md +++ b/libs/openFrameworksCompiled/project/CMake/README.md @@ -76,6 +76,8 @@ makefiles' `config.make` options. | `PROJECT_EMSCRIPTEN_EMBIND_AOT` | `ON` | | | `PROJECT_EMSCRIPTEN_TEMPLATE` | `project/emscripten/template.html` | HTML shell | | `OF_WEBGL_VERSION` | `2` | `1` for a WebGL 1 build | +| `APP_VERSION` | `project()` version, else `1.0` | app version (macOS / iOS / tvOS `CFBundleShortVersionString`), same as the makefiles' `config.make` | +| `APP_BUILD` | `1` | build number (`CFBundleVersion`) | | `OF_APPLE_DEVELOPMENT_TEAM` | | signing team for iOS / tvOS devices | ## Addons diff --git a/libs/openFrameworksCompiled/project/CMake/platform/apple.cmake b/libs/openFrameworksCompiled/project/CMake/platform/apple.cmake index 7a3dcd902c9..d17fa18c76d 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/apple.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/apple.cmake @@ -31,6 +31,20 @@ function(of_apple_info_plist target project_plist fallback_plist) if(NOT plist_text MATCHES "CFBundleExecutable") file(READ "${fallback_plist}" plist_text) endif() + # APP_VERSION / APP_BUILD, like the makefiles' config.make; the version + # falls back to project(... VERSION x.y) + if(APP_VERSION) + set(version "${APP_VERSION}") + elseif(PROJECT_VERSION) + set(version "${PROJECT_VERSION}") + else() + set(version 1.0) + endif() + if(APP_BUILD) + set(build "${APP_BUILD}") + else() + set(build 1) + endif() string(MAKE_C_IDENTIFIER "${name}" bundle_name) string(REPLACE "_" "-" bundle_name "${bundle_name}") set(identifier "cc.openFrameworks.${bundle_name}") @@ -40,8 +54,8 @@ function(of_apple_info_plist target project_plist fallback_plist) "PRODUCT_BUNDLE_IDENTIFIER=${identifier}" "TARGET_NAME=${name}" "PRODUCT_NAME=${name}" - "MARKETING_VERSION=1.0" - "CURRENT_PROJECT_VERSION=1" + "MARKETING_VERSION=${version}" + "CURRENT_PROJECT_VERSION=${build}" "HIGH_RESOLUTION_CAPABLE=true" "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") string(REGEX REPLACE "=.*" "" key "${pair}") @@ -58,5 +72,7 @@ function(of_apple_info_plist target project_plist fallback_plist) MACOSX_BUNDLE ON MACOSX_BUNDLE_INFO_PLIST "${generated}" MACOSX_BUNDLE_GUI_IDENTIFIER "${identifier}" - XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${identifier}") + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${identifier}" + XCODE_ATTRIBUTE_MARKETING_VERSION "${version}" + XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${build}") endfunction() diff --git a/scripts/templates/cmake/CMakeLists.txt b/scripts/templates/cmake/CMakeLists.txt index 2867e97edff..268fa756588 100644 --- a/scripts/templates/cmake/CMakeLists.txt +++ b/scripts/templates/cmake/CMakeLists.txt @@ -18,6 +18,11 @@ if(NOT OF_ROOT) endif() include("${OF_ROOT}/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake") +# app version and build number (macOS / iOS / tvOS Info.plist), like APP_VERSION / +# APP_BUILD in a config.make; the version defaults to project(... VERSION), then 1.0 +# set(APP_VERSION 1.0) +# set(APP_BUILD 1) + # sources come from src/, addons from addons.make; or list them: # of_add_app(${OF_APP_NAME} ADDONS ofxGui ofxOsc) of_add_app(${OF_APP_NAME}) From 29731b4eaccf4e1650fe8e0579946feb390b3d7f Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Sun, 18 Oct 2026 17:28:57 +1300 Subject: [PATCH 04/14] CI: build-cmake job for linux64, macOS and VS x64 scripts/ci/cmake/build.sh builds the core (Release and Debug) with the CMake project, then emptyExample (Release and Debug), graphicsExample, guiExample and allAddonsExample. Each example's CMakeLists.txt comes from projectGenerator -t cmake when --pg is given (linux64 and VS use the nightly PG), otherwise from scripts/templates/cmake/CMakeLists.txt. A failed step fails the job with an ::error annotation, and each app must end up in bin/. --- .github/workflows/of.yml | 61 ++++++++++++++++++++++++++++++ scripts/ci/cmake/build.sh | 79 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100755 scripts/ci/cmake/build.sh diff --git a/.github/workflows/of.yml b/.github/workflows/of.yml index 58b7ba5ef3f..02c850ad97a 100644 --- a/.github/workflows/of.yml +++ b/.github/workflows/of.yml @@ -130,6 +130,67 @@ jobs: # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # CMake build (libs/openFrameworksCompiled/project/CMake): core + examples, with + # each example's CMakeLists.txt generated by projectGenerator -t cmake + build-cmake: + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - { name: linux64, runner: ubuntu-24.04 } + - { name: macos, runner: macos-15 } + - { name: vs-x64, runner: windows-2022 } + name: build-cmake (${{ matrix.name }}) + steps: + - uses: actions/checkout@v6 + - name: ccache + if: matrix.name != 'vs-x64' + uses: hendrikmuhs/ccache-action@v1.2.23 + with: + key: cmake-${{ matrix.name }} + + - name: Libs, dependencies and projectGenerator (linux64) + if: matrix.name == 'linux64' + run: | + ./scripts/linux/download_libs.sh -t $RELEASE -a 64gcc6 + ./scripts/ci/linux64/install.sh + sudo apt-get install -y ninja-build + mkdir -p ~/pg && cd ~/pg + curl -fsSL -o pg.tar.bz2 https://github.com/openframeworks/projectGenerator/releases/download/nightly/projectGenerator-linux.tar.bz2 + tar -xjf pg.tar.bz2 && rm pg.tar.bz2 + echo "PG=$(find ~/pg -type f -name 'projectGenerator*' -perm -u+x | head -1)" >> $GITHUB_ENV + - name: Build (linux64) + if: matrix.name == 'linux64' + run: scripts/ci/cmake/build.sh --pg "$PG" --platform linux64 -- -G Ninja + + - name: Libs (macos) + if: matrix.name == 'macos' + run: | + ./scripts/osx/download_libs.sh -t $RELEASE + brew install ninja || true + - name: Build (macos) + if: matrix.name == 'macos' + run: scripts/ci/cmake/build.sh -- -G Ninja + env: + DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" + + - uses: msys2/setup-msys2@v2 + if: matrix.name == 'vs-x64' + with: + update: true + install: >- + git + unzip + - name: Libs and projectGenerator (vs-x64) + if: matrix.name == 'vs-x64' + shell: msys2 {0} + run: ./scripts/ci/vs/install.sh -a x64 + - name: Build (vs-x64) + if: matrix.name == 'vs-x64' + shell: bash + run: scripts/ci/cmake/build.sh --pg "$PWD/projectGenerator/projectGenerator.exe" --platform vs -- -G "Visual Studio 17 2022" -A x64 + build-vs: runs-on: ${{ matrix.runner }} strategy: diff --git a/scripts/ci/cmake/build.sh b/scripts/ci/cmake/build.sh new file mode 100755 index 00000000000..dfbdb40cf79 --- /dev/null +++ b/scripts/ci/cmake/build.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# Builds the core and a few examples with CMake (libs/openFrameworksCompiled/project/CMake). +# Each example gets its CMakeLists.txt from projectGenerator (-t cmake) when --pg is +# given, otherwise from scripts/templates/cmake/CMakeLists.txt (the file PG copies). +# +# Usage: scripts/ci/cmake/build.sh [--pg ] [--platform ] [-- ] +# scripts/ci/cmake/build.sh --pg ~/pg/projectGenerator --platform linux64 -- -G Ninja +# scripts/ci/cmake/build.sh -- -G "Visual Studio 17 2022" -A x64 +ROOT="$( cd "$(dirname "$0")/../../.." ; pwd -P )" +PG="" +PG_PLATFORM="" +CONFIGURE_ARGS=() +while [[ $# -gt 0 ]]; do + case "$1" in + --pg) PG="$2"; shift 2 ;; + --platform) PG_PLATFORM="$2"; shift 2 ;; + --) shift; CONFIGURE_ARGS=("$@"); break ;; + *) echo "Unknown argument $1"; exit 1 ;; + esac +done + +# example path:configurations +EXAMPLES=( + "templates/emptyExample:Release Debug" + "graphics/graphicsExample:Release" + "gui/guiExample:Release" + "templates/allAddonsExample:Release" +) + +if command -v ccache >/dev/null 2>&1; then + CONFIGURE_ARGS+=(-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache) +fi + +fail(){ + echo "::error title=CMake build failed::$*" + exit 1 +} + +# single-config generators take the build type at configure time, multi-config ones at build time +configure_and_build(){ + local src="$1" dir="$2" config="$3" + cmake -S "$src" -B "$dir" "${CONFIGURE_ARGS[@]}" -DCMAKE_BUILD_TYPE="$config" || return 1 + cmake --build "$dir" --config "$config" --parallel || return 1 +} + +echo "##[group]core" +for config in Release Debug; do + configure_and_build "$ROOT/libs/openFrameworksCompiled/project/CMake" "$ROOT/build/cmake-core-$config" "$config" \ + || fail "core $config" +done +echo "##[endgroup]" + +for entry in "${EXAMPLES[@]}"; do + example="${entry%%:*}" + configs="${entry#*:}" + dir="$ROOT/examples/$example" + app="$(basename "$example")" + echo "##[group]$example" + rm -f "$dir/CMakeLists.txt" + if [[ -n "$PG" ]]; then + pg_args=(-o"$ROOT" -t"cmake") + [[ -n "$PG_PLATFORM" ]] && pg_args+=(-p"$PG_PLATFORM") + "$PG" "${pg_args[@]}" "$dir" || fail "projectGenerator -t cmake $example" + [[ -f "$dir/CMakeLists.txt" ]] || fail "projectGenerator wrote no CMakeLists.txt for $example" + else + cp "$ROOT/scripts/templates/cmake/CMakeLists.txt" "$dir/" || fail "copy template to $example" + fi + for config in $configs; do + configure_and_build "$dir" "$ROOT/build/cmake-$app-$config" "$config" || fail "$example $config" + name="$app"; [[ "$config" == "Debug" ]] && name="${app}_debug" + found="" + for out in "$dir/bin/$name" "$dir/bin/$name.exe" "$dir/bin/$name.app"; do + [[ -e "$out" ]] && found="$out" + done + [[ -n "$found" ]] || fail "$example $config: no bin/$name" + echo "built ${found#$ROOT/}" + done + echo "##[endgroup]" +done From 074af7ed90b19c725d4f7275feffb20aa478f118 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Sun, 18 Oct 2026 17:37:29 +1300 Subject: [PATCH 05/14] Match the Emscripten CMake link to the makefile Link the core archive with --whole-archive. Keep wasm32 and wasm64 libraries in separate folders, default embind AOT off, and drop MAIN_MODULE so audio worklets link. --- .../project/CMake/OFCore.cmake | 25 +++++++++- .../project/CMake/README.md | 4 +- .../project/CMake/platform/emscripten.cmake | 47 ++++++++++++++----- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/libs/openFrameworksCompiled/project/CMake/OFCore.cmake b/libs/openFrameworksCompiled/project/CMake/OFCore.cmake index 85842575a12..ebea0c804bf 100644 --- a/libs/openFrameworksCompiled/project/CMake/OFCore.cmake +++ b/libs/openFrameworksCompiled/project/CMake/OFCore.cmake @@ -20,6 +20,7 @@ # OF_PLATFORM_REQUIRED_ADDONS addons every app on this platform needs # OF_PLATFORM_LIB_EXTENSIONS static library extensions # OF_PLATFORM_LINK_GROUP ON to wrap static libs in --start-group/--end-group +# OF_PLATFORM_WHOLE_ARCHIVE_CORE ON to link every object in the core archive # OF_CORE_LIB_NAME / OF_CORE_LIB_NAME_DEBUG / OF_CORE_LIB_PREFIX / OF_CORE_LIB_SUFFIX # and defines of_platform_lib_dirs( ) and of_platform_setup_app(). @@ -201,6 +202,26 @@ function(of_core_lib_path out_var config) set(${out_var} "${OF_CORE_LIB_DIR}/${OF_CORE_LIB_PREFIX}${name}${OF_CORE_LIB_SUFFIX}" PARENT_SCOPE) endfunction() +# openFrameworks::openFrameworks is what apps link. With +# OF_PLATFORM_WHOLE_ARCHIVE_CORE the archive is wrapped in --whole-archive, +# which is what the Emscripten makefile gets from cc -r: every core object +# is in the link, including ones reached only from a static constructor. +function(of_expose_core) + if(TARGET openFrameworks::openFrameworks) + return() + endif() + if(OF_PLATFORM_WHOLE_ARCHIVE_CORE) + add_library(openFrameworks_link INTERFACE) + add_library(openFrameworks::openFrameworks ALIAS openFrameworks_link) + target_link_libraries(openFrameworks_link INTERFACE + "LINKER:--whole-archive,$,--no-whole-archive" + openFrameworks::deps) + add_dependencies(openFrameworks_link openFrameworks) + else() + add_library(openFrameworks::openFrameworks ALIAS openFrameworks) + endif() +endfunction() + # defines the openFrameworks core library target, built from source function(of_add_core_library) of_setup_deps() @@ -219,7 +240,7 @@ function(of_add_core_library) if(COMMAND of_platform_setup_core) of_platform_setup_core(openFrameworks) endif() - add_library(openFrameworks::openFrameworks ALIAS openFrameworks) + of_expose_core() list(LENGTH sources n) message(STATUS "openFrameworks: building the core (${n} sources) into ${OF_CORE_LIB_DIR}") endfunction() @@ -256,7 +277,7 @@ function(of_use_core) IMPORTED_LOCATION_MINSIZEREL "${release}" IMPORTED_LOCATION_DEBUG "${debug}" INTERFACE_LINK_LIBRARIES openFrameworks::deps) - add_library(openFrameworks::openFrameworks ALIAS openFrameworks) + of_expose_core() message(STATUS "openFrameworks: using prebuilt core from ${OF_CORE_LIB_DIR}") else() add_subdirectory("${OF_CMAKE_DIR}" "${CMAKE_BINARY_DIR}/openFrameworks") diff --git a/libs/openFrameworksCompiled/project/CMake/README.md b/libs/openFrameworksCompiled/project/CMake/README.md index 99f0e85fcdd..323ed0b07a3 100644 --- a/libs/openFrameworksCompiled/project/CMake/README.md +++ b/libs/openFrameworksCompiled/project/CMake/README.md @@ -71,9 +71,9 @@ makefiles' `config.make` options. | `OF_BUILD_CORE` | `OFF` | build the core in this build even if a prebuilt one exists | | `OF_PLATFORM` | detected | `macos ios tvos emscripten linux64 linuxaarch64 linuxarmv7l linuxarmv6l vs msys2` | | `OF_USE_DAWN`, `OF_USE_ANGLE` | `OFF` | link `libs/dawn`, `libs/metalangle` | -| `PROJECT_EMSCRIPTEN_WASM` | `32` | `64` for wasm64 (MEMORY64): libs from `lib/emscripten/WASM64`, core in `lib/emscripten/WASM64` | +| `PROJECT_EMSCRIPTEN_WASM` | `32` | `32`: libs from `lib/emscripten/WASM`, core in `lib/emscripten`. `64`: libs and core in `lib/emscripten/WASM64` | | `PROJECT_EMSCRIPTEN_TOTAL_MEMORY` | `134217728` | initial memory in bytes | -| `PROJECT_EMSCRIPTEN_EMBIND_AOT` | `ON` | | +| `PROJECT_EMSCRIPTEN_EMBIND_AOT` | `OFF` | ON needs an `EMSCRIPTEN_BINDINGS` block and breaks some addons | | `PROJECT_EMSCRIPTEN_TEMPLATE` | `project/emscripten/template.html` | HTML shell | | `OF_WEBGL_VERSION` | `2` | `1` for a WebGL 1 build | | `APP_VERSION` | `project()` version, else `1.0` | app version (macOS / iOS / tvOS `CFBundleShortVersionString`), same as the makefiles' `config.make` | diff --git a/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake index 3130527f6f4..181de7b7ef5 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake @@ -5,10 +5,12 @@ # emrun bin/em/ # # Project options (cache variables, same names as the makefiles' config.make options): -# PROJECT_EMSCRIPTEN_WASM 32 (default) or 64: wasm64 (MEMORY64), libs from -# lib/emscripten/WASM64, core in lib/emscripten/WASM64 +# PROJECT_EMSCRIPTEN_WASM 32 (default) or 64. wasm32 libs are +# lib/emscripten/WASM and the core stays in +# lib/emscripten. wasm64 libs and core are +# lib/emscripten/WASM64 # PROJECT_EMSCRIPTEN_TOTAL_MEMORY initial memory in bytes (default 128 MB) -# PROJECT_EMSCRIPTEN_EMBIND_AOT ON (default) / OFF +# PROJECT_EMSCRIPTEN_EMBIND_AOT OFF (default) / ON # PROJECT_EMSCRIPTEN_TEMPLATE HTML shell file (default the one in project/emscripten) # OF_WEBGL_VERSION 2 (default) or 1 @@ -25,7 +27,9 @@ if(NOT PROJECT_EMSCRIPTEN_WASM MATCHES "^(32|64)$") message(FATAL_ERROR "PROJECT_EMSCRIPTEN_WASM must be 32 or 64, not \"${PROJECT_EMSCRIPTEN_WASM}\"") endif() set(PROJECT_EMSCRIPTEN_TOTAL_MEMORY 134217728 CACHE STRING "Initial wasm memory in bytes") -option(PROJECT_EMSCRIPTEN_EMBIND_AOT "Ahead-of-time embind (CSP safe)" ON) +# Off: the core has no EMSCRIPTEN_BINDINGS block, so ON fails the link +# ("expected to find pattern <<< EMBIND_AOT_INVOKERS >>>") and breaks some addons. +option(PROJECT_EMSCRIPTEN_EMBIND_AOT "Ahead-of-time embind (CSP safe; breaks some addons)" OFF) set(PROJECT_EMSCRIPTEN_TEMPLATE "${OF_LIBS_DIR}/openFrameworksCompiled/project/emscripten/template.html" CACHE FILEPATH "HTML shell for the app") set(OF_WEBGL_VERSION 2 CACHE STRING "WebGL version (1 or 2)") @@ -63,7 +67,8 @@ set(OF_PLATFORM_COMPILE_OPTIONS -pthread -matomics -mbulk-memory - "$<$:-Wall;-Wno-warn-absolute-paths>") + "$<$:-Wall>" + "$<$:-Wno-warn-absolute-paths>") set(OF_PLATFORM_LINK_OPTIONS --emrun @@ -76,20 +81,30 @@ set(OF_PLATFORM_LINK_OPTIONS -matomics -mbulk-memory "SHELL:--js-library ${OF_ADDONS_DIR}/ofxEmscripten/libs/html5video/lib/emscripten/library_html5video.js" - -sMAIN_MODULE=1 + "SHELL:--pre-js ${OF_ADDONS_DIR}/ofxEmscripten/libs/html5video/lib/emscripten/recordVideo.js" + "SHELL:--pre-js ${OF_ADDONS_DIR}/ofxEmscripten/libs/html5video/lib/emscripten/fix-webm-duration.js" + "SHELL:--post-js ${OF_ADDONS_DIR}/ofxEmscripten/libs/html5audio/lib/emscripten/library_audioDeviceSelect.js" + "SHELL:--js-library ${OF_ADDONS_DIR}/ofxEmscripten/libs/html5audio/lib/emscripten/library_html5audio.js" + # No MAIN_MODULE: dynamic linking is not supported with WASM_WORKERS. + # EXPORT_ALL still exports every symbol for the js libraries. + -sAUDIO_WORKLET=1 + -sWASM_WORKERS=1 + "-sENVIRONMENT=web,worker" -sEXPORT_ALL=1 - -sNO_DYNAMIC_EXECUTION=1 -sABORT_ON_WASM_EXCEPTIONS=0 -sFILESYSTEM=1 -sMINIFY_HTML=0 -sUSE_GLFW=3 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS,UTF8ToString,stringToUTF8,lengthBytesUTF8 -sERROR_ON_UNDEFINED_SYMBOLS=1 + -sVERBOSE=1 -sINITIAL_MEMORY=${PROJECT_EMSCRIPTEN_TOTAL_MEMORY} -sALLOW_MEMORY_GROWTH=1 -sWASM=1 "SHELL:--shell-file ${PROJECT_EMSCRIPTEN_TEMPLATE}" - "$<$:-sVERBOSE=1;-sGL_ASSERTIONS=1;-sASSERTIONS=2;-gsource-map>") + "$<$:-sGL_ASSERTIONS=1>" + "$<$:-sASSERTIONS=2>" + "$<$:-gsource-map>") if(OF_WEBGL_VERSION STREQUAL "1") list(APPEND OF_PLATFORM_LINK_OPTIONS -sMAX_WEBGL_VERSION=1 -sFULL_ES2) else() @@ -117,21 +132,25 @@ set(OF_PLATFORM_INCLUDE_DIRS "${OF_ADDONS_DIR}/ofxEmscripten/src") set(OF_PLATFORM_LIB_EXTENSIONS a) set(OF_PLATFORM_LINK_GROUP OFF) +# The makefile partial-links the core with cc -r into one .o. A normal .a +# drops objects nothing references by name. --whole-archive keeps them. +set(OF_PLATFORM_WHOLE_ARCHIVE_CORE ON) set(OF_PLATFORM_REQUIRED_ADDONS ofxEmscripten) # the makefiles build the core as libopenFrameworks.o; CMake builds a static -# library next to it +# library next to it and links that archive whole set(OF_CORE_LIB_PREFIX lib) set(OF_CORE_LIB_NAME openFrameworks) set(OF_CORE_LIB_NAME_DEBUG openFrameworksDebug) set(OF_CORE_LIB_SUFFIX .a) -# libs//lib/emscripten/WASM (WASM64 with PROJECT_EMSCRIPTEN_WASM=64) +# Same folders as ABI_PATH in config.emscripten.default.mk. One ABI per +# directory, so a wasm64 link does not also pick up wasm32 archives. function(of_platform_lib_dirs lib_folder out_var) if(PROJECT_EMSCRIPTEN_WASM STREQUAL "64") - set(${out_var} "${lib_folder}" "${lib_folder}/WASM64" PARENT_SCOPE) + set(${out_var} "${lib_folder}/WASM64" PARENT_SCOPE) else() - set(${out_var} "${lib_folder}" "${lib_folder}/WASM" PARENT_SCOPE) + set(${out_var} "${lib_folder}/WASM" PARENT_SCOPE) endif() endfunction() @@ -149,7 +168,9 @@ function(of_platform_setup_app target) set_target_properties(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${upper} "${out_dir}") endforeach() - file(GLOB data_files "${CMAKE_CURRENT_SOURCE_DIR}/bin/data/*") + # Reconfigure when bin/data gains or loses entries, then emcc packs the + # directory as it is at link time. The makefile checks this every build. + file(GLOB data_files CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/bin/data/*") if(data_files) target_link_options(${target} PRIVATE "SHELL:--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/bin/data@data") endif() From 84ce89f10d1dd9f9dcf3cf0e83de4aaceefaaff8 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Sun, 18 Oct 2026 19:07:39 +1300 Subject: [PATCH 06/14] Define UNICODE for the Visual Studio CMake build The vcxproj sets CharacterSet to Unicode. Without UNICODE and _UNICODE, Win32 calls stay on the ANSI entry points and the wide paths in ofFileUtils and ofSystemUtils do not compile. --- libs/openFrameworksCompiled/project/CMake/platform/vs.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake index 70f18acea24..ef624b5f770 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake @@ -18,6 +18,10 @@ set(OF_PLATFORM_DEFINES WIN32 _CONSOLE POCO_STATIC CAIRO_WIN32_STATIC_BUILD DISABLE_SOME_FLOATING_POINT URI_STATIC_BUILD CURL_STATICLIB FREEIMAGE_LIB OF_NO_FMOD _HAS_STREAM_INSERTION_OPERATORS_DELETED_IN_CXX20 + # openframeworksLib.vcxproj sets CharacterSet to Unicode, which defines these. + # Without them GetFileAttributes, MessageBox and OPENFILENAME stay the ANSI + # entry points while the core passes wchar_t paths. + UNICODE _UNICODE $<$:_DEBUG>) set(OF_PLATFORM_COMPILE_OPTIONS /Zc:__cplusplus /MP /utf-8) From d4e4565154e2c03eff9c6d34d920d4d62e378817 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Mon, 19 Oct 2026 11:26:51 +1300 Subject: [PATCH 07/14] CMake VS: find the app icon from icon.rc icon.rc names icon.ico / icon_debug.ico, which live in project/vs; the VS template adds that folder to the resource compiler's include path, so do the same for the CMake app target. --- libs/openFrameworksCompiled/project/CMake/platform/vs.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake index ef624b5f770..a09ca871485 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake @@ -87,6 +87,9 @@ function(of_platform_setup_app target) set(icon_rc "${CMAKE_CURRENT_SOURCE_DIR}/icon.rc") if(EXISTS "${icon_rc}") target_sources(${target} PRIVATE "${icon_rc}") + # icon.rc names icon.ico / icon_debug.ico, which live in project/vs (the VS + # template's ResourceCompile AdditionalIncludeDirectories); rc gets _DEBUG in Debug + target_include_directories(${target} PRIVATE "${OF_LIBS_DIR}/openFrameworksCompiled/project/vs") endif() set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY VS_STARTUP_PROJECT ${target}) endfunction() From 15221231ab7f353da3dc2f4a887dbd8d7a3d0eac Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Mon, 19 Oct 2026 12:10:38 +1300 Subject: [PATCH 08/14] Keep config-specific addon libraries on the Visual Studio link Release and Debug libs are generator expressions ending in .lib>. The addon filter only kept paths ending in .lib, so ofxAssimpModelLoader and ofxOpenCv linked with no libraries. --- libs/openFrameworksCompiled/project/CMake/OFAddons.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake index 3650961c1ef..f7bbe8ed777 100644 --- a/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake +++ b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake @@ -185,7 +185,9 @@ function(of_target_addon target addon) of_filter_make_patterns(includes "${dir}" "${cfg_ADDON_INCLUDES_EXCLUDE}" ${includes}) of_expand_addon_paths(libs "${dir}" ${cfg_ADDON_LIBS}) of_filter_make_patterns(libs "${dir}" "${cfg_ADDON_LIBS_EXCLUDE}" ${libs}) - list(FILTER libs INCLUDE REGEX "\\.(a|lib|so|dylib|tbd)$|^-") + # Release/Debug libs are $<$:path.lib>. That trailing > must stay, + # or ofxAssimpModelLoader and ofxOpenCv link with no libraries on Visual Studio. + list(FILTER libs INCLUDE REGEX "\\.(a|lib|so|dylib|tbd|xcframework)(>)?$|^-") of_target_addon_files(${target} NAME ${name} SOURCES ${sources} From d310f1b91d20631ba29433c9203ae9e9608ac0d1 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Mon, 19 Oct 2026 12:11:42 +1300 Subject: [PATCH 09/14] CMake: preserve configuration-dependent addon library paths Keep platform-generated library expressions intact during addon path expansion and filtering. Verify split Debug/Release directories, flat paired libraries and shared libraries in the CMake CI entry point. --- .../project/CMake/OFAddons.cmake | 6 ++++ scripts/ci/cmake/build.sh | 8 +++++ .../tests/vs-addon-libraries/CMakeLists.txt | 36 +++++++++++++++++++ .../tests/vs-addon-libraries/verify.cmake | 20 +++++++++++ 4 files changed, 70 insertions(+) create mode 100644 scripts/ci/cmake/tests/vs-addon-libraries/CMakeLists.txt create mode 100644 scripts/ci/cmake/tests/vs-addon-libraries/verify.cmake diff --git a/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake index f7bbe8ed777..8610cc0f716 100644 --- a/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake +++ b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake @@ -53,6 +53,12 @@ endfunction() function(of_expand_addon_paths out_var base) set(result) foreach(item IN LISTS ARGN) + # Platform discovery can return configuration-dependent library expressions. + # They already contain absolute paths and must reach target_link_libraries intact. + if(item MATCHES "^\\$<") + list(APPEND result "${item}") + continue() + endif() if(NOT IS_ABSOLUTE "${item}") set(item "${base}/${item}") endif() diff --git a/scripts/ci/cmake/build.sh b/scripts/ci/cmake/build.sh index dfbdb40cf79..6c35b7a5d37 100755 --- a/scripts/ci/cmake/build.sh +++ b/scripts/ci/cmake/build.sh @@ -43,6 +43,14 @@ configure_and_build(){ cmake --build "$dir" --config "$config" --parallel || return 1 } +# Exercise MSVC addon layouts on every host; no Windows binaries are linked here. +echo "##[group]MSVC addon library selection" +for config in Release Debug; do + configure_and_build "$ROOT/scripts/ci/cmake/tests/vs-addon-libraries" "$ROOT/build/cmake-vs-addon-libraries-$config" "$config" \ + || fail "MSVC addon library selection $config" +done +echo "##[endgroup]" + echo "##[group]core" for config in Release Debug; do configure_and_build "$ROOT/libs/openFrameworksCompiled/project/CMake" "$ROOT/build/cmake-core-$config" "$config" \ diff --git a/scripts/ci/cmake/tests/vs-addon-libraries/CMakeLists.txt b/scripts/ci/cmake/tests/vs-addon-libraries/CMakeLists.txt new file mode 100644 index 00000000000..0a8d3a614d7 --- /dev/null +++ b/scripts/ci/cmake/tests/vs-addon-libraries/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.24) +project(of_vs_addon_library_selection LANGUAGES CXX) + +get_filename_component(source_root "${CMAKE_CURRENT_LIST_DIR}/../../../../.." ABSOLUTE) +set(OF_ROOT "${CMAKE_CURRENT_BINARY_DIR}/fixture") +set(OF_ADDONS_DIR "${OF_ROOT}/addons") +set(OF_PLATFORM vs) +set(OF_ARCH x64) +include("${source_root}/libs/openFrameworksCompiled/project/CMake/OFCore.cmake") +include("${source_root}/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake") +include("${source_root}/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake") + +# Match Apothecary's split-configuration and flat paired/shared library layouts. +# The files are discovery fixtures; the CI examples perform the native link. +foreach(addon ofxAssimpModelLoader ofxOpenCv ofxFlat) + file(MAKE_DIRECTORY "${OF_ADDONS_DIR}/${addon}/src") +endforeach() +foreach(config Release Debug) + file(WRITE "${OF_ADDONS_DIR}/ofxAssimpModelLoader/libs/assimp/lib/vs/x64/${config}/assimp.lib" "") + file(WRITE "${OF_ADDONS_DIR}/ofxOpenCv/libs/opencv/lib/vs/x64/${config}/opencv_world4140.lib" "") +endforeach() +foreach(name rtAudio rtAudioD shared) + file(WRITE "${OF_ADDONS_DIR}/ofxFlat/libs/audio/lib/vs/x64/${name}.lib" "") +endforeach() +# A different architecture must never leak into the selected libraries. +file(WRITE "${OF_ADDONS_DIR}/ofxFlat/libs/audio/lib/vs/ARM64/foreign.lib" "") +file(WRITE "${OF_ROOT}/main.cpp" "int main() { return 0; }\n") +add_executable(addon_fixture EXCLUDE_FROM_ALL "${OF_ROOT}/main.cpp") +of_target_addons(addon_fixture ofxAssimpModelLoader ofxOpenCv ofxFlat) +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/selected-$.txt" + CONTENT "$>,\n>\n") +add_custom_target(verify_selection ALL + COMMAND "${CMAKE_COMMAND}" "-DCONFIG=$" "-DFIXTURE_ROOT=${OF_ROOT}" + "-DSELECTED_FILE=${CMAKE_CURRENT_BINARY_DIR}/selected-$.txt" + -P "${CMAKE_CURRENT_LIST_DIR}/verify.cmake" + VERBATIM) diff --git a/scripts/ci/cmake/tests/vs-addon-libraries/verify.cmake b/scripts/ci/cmake/tests/vs-addon-libraries/verify.cmake new file mode 100644 index 00000000000..bc999c2a451 --- /dev/null +++ b/scripts/ci/cmake/tests/vs-addon-libraries/verify.cmake @@ -0,0 +1,20 @@ +file(STRINGS "${SELECTED_FILE}" actual) +if(CONFIG STREQUAL "Debug") + set(split Debug) + set(audio rtAudioD) +else() + set(split Release) + set(audio rtAudio) +endif() +set(expected + "${FIXTURE_ROOT}/addons/ofxAssimpModelLoader/libs/assimp/lib/vs/x64/${split}/assimp.lib" + "${FIXTURE_ROOT}/addons/ofxOpenCv/libs/opencv/lib/vs/x64/${split}/opencv_world4140.lib" + "${FIXTURE_ROOT}/addons/ofxFlat/libs/audio/lib/vs/x64/${audio}.lib" + "${FIXTURE_ROOT}/addons/ofxFlat/libs/audio/lib/vs/x64/shared.lib") +list(FILTER actual EXCLUDE REGEX "^$") +list(SORT actual) +list(SORT expected) +if(NOT actual STREQUAL expected) + message(FATAL_ERROR "${CONFIG} addon libraries differ:\n actual: ${actual}\n expected: ${expected}") +endif() +message(STATUS "${CONFIG}: split, paired and shared MSVC addon libraries selected correctly") From bc4ce3ef0ae89eb4d91b9e97f3f0d9ef17a22d03 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Mon, 19 Oct 2026 17:23:07 +1300 Subject: [PATCH 10/14] CI: build-cmake for linuxaarch64 and Emscripten linuxaarch64 (64-bit Raspberry Pi OS / arm64 Linux) builds natively on ubuntu-24.04-arm with the aarch64 libs and the nightly arm64 projectGenerator. Emscripten builds in the same emsdk image as build-emscripten, configured through emcmake, with emptyExample (Release, Debug), graphicsExample and guiExample. scripts/ci/cmake/build.sh takes CMAKE_WRAPPER (prefix for the configure step) and OF_CMAKE_EXAMPLES (replaces the example list), and accepts the Emscripten output bin/em//index.html. --- .github/workflows/of.yml | 32 +++++++++++++++++++++++++++++++- scripts/ci/cmake/build.sh | 11 +++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/.github/workflows/of.yml b/.github/workflows/of.yml index 02c850ad97a..2211911d3cc 100644 --- a/.github/workflows/of.yml +++ b/.github/workflows/of.yml @@ -141,11 +141,13 @@ jobs: - { name: linux64, runner: ubuntu-24.04 } - { name: macos, runner: macos-15 } - { name: vs-x64, runner: windows-2022 } + - { name: linuxaarch64, runner: ubuntu-24.04-arm } + - { name: emscripten, runner: ubuntu-24.04 } name: build-cmake (${{ matrix.name }}) steps: - uses: actions/checkout@v6 - name: ccache - if: matrix.name != 'vs-x64' + if: matrix.name != 'vs-x64' && matrix.name != 'emscripten' uses: hendrikmuhs/ccache-action@v1.2.23 with: key: cmake-${{ matrix.name }} @@ -164,6 +166,34 @@ jobs: if: matrix.name == 'linux64' run: scripts/ci/cmake/build.sh --pg "$PG" --platform linux64 -- -G Ninja + # 64-bit Raspberry Pi OS / arm64 Linux + - name: Libs, dependencies and projectGenerator (linuxaarch64) + if: matrix.name == 'linuxaarch64' + run: | + ./scripts/linux/download_libs.sh -t $RELEASE -a aarch64 + ./scripts/ci/linux64/install.sh + sudo apt-get install -y ninja-build + mkdir -p ~/pg && cd ~/pg + curl -fsSL -o pg.tar.bz2 https://github.com/openframeworks/projectGenerator/releases/download/nightly/projectGenerator-linux-arm64.tar.bz2 + tar -xjf pg.tar.bz2 && rm pg.tar.bz2 + echo "PG=$(find ~/pg -type f -name 'projectGenerator*' -perm -u+x | head -1)" >> $GITHUB_ENV + - name: Build (linuxaarch64) + if: matrix.name == 'linuxaarch64' + run: scripts/ci/cmake/build.sh --pg "$PG" --platform linuxaarch64 -- -G Ninja + + # same emsdk image as build-emscripten; emcmake sets the Emscripten toolchain + - name: Libs and emsdk (emscripten) + if: matrix.name == 'emscripten' + run: | + ./scripts/emscripten/download_libs.sh -t $RELEASE + docker run -di --name emscripten -v $PWD:/src emscripten/emsdk:6.0.11 bash + - name: Build (emscripten) + if: matrix.name == 'emscripten' + run: >- + docker exec -i -e CMAKE_WRAPPER=emcmake + -e OF_CMAKE_EXAMPLES="templates/emptyExample:Release,Debug graphics/graphicsExample:Release gui/guiExample:Release" + emscripten bash -c "cd /src && scripts/ci/cmake/build.sh" + - name: Libs (macos) if: matrix.name == 'macos' run: | diff --git a/scripts/ci/cmake/build.sh b/scripts/ci/cmake/build.sh index 6c35b7a5d37..90f36206d5d 100755 --- a/scripts/ci/cmake/build.sh +++ b/scripts/ci/cmake/build.sh @@ -6,6 +6,9 @@ # Usage: scripts/ci/cmake/build.sh [--pg ] [--platform ] [-- ] # scripts/ci/cmake/build.sh --pg ~/pg/projectGenerator --platform linux64 -- -G Ninja # scripts/ci/cmake/build.sh -- -G "Visual Studio 17 2022" -A x64 +# CMAKE_WRAPPER=emcmake OF_CMAKE_EXAMPLES="templates/emptyExample:Release" scripts/ci/cmake/build.sh +# CMAKE_WRAPPER prefixes the configure step (emcmake); OF_CMAKE_EXAMPLES replaces the +# example list (space-separated "path:Config[,Config]" entries). ROOT="$( cd "$(dirname "$0")/../../.." ; pwd -P )" PG="" PG_PLATFORM="" @@ -26,6 +29,9 @@ EXAMPLES=( "gui/guiExample:Release" "templates/allAddonsExample:Release" ) +if [[ -n "$OF_CMAKE_EXAMPLES" ]]; then + read -r -a EXAMPLES <<< "$OF_CMAKE_EXAMPLES" +fi if command -v ccache >/dev/null 2>&1; then CONFIGURE_ARGS+=(-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache) @@ -39,7 +45,7 @@ fail(){ # single-config generators take the build type at configure time, multi-config ones at build time configure_and_build(){ local src="$1" dir="$2" config="$3" - cmake -S "$src" -B "$dir" "${CONFIGURE_ARGS[@]}" -DCMAKE_BUILD_TYPE="$config" || return 1 + $CMAKE_WRAPPER cmake -S "$src" -B "$dir" "${CONFIGURE_ARGS[@]}" -DCMAKE_BUILD_TYPE="$config" || return 1 cmake --build "$dir" --config "$config" --parallel || return 1 } @@ -61,6 +67,7 @@ echo "##[endgroup]" for entry in "${EXAMPLES[@]}"; do example="${entry%%:*}" configs="${entry#*:}" + configs="${configs//,/ }" dir="$ROOT/examples/$example" app="$(basename "$example")" echo "##[group]$example" @@ -77,7 +84,7 @@ for entry in "${EXAMPLES[@]}"; do configure_and_build "$dir" "$ROOT/build/cmake-$app-$config" "$config" || fail "$example $config" name="$app"; [[ "$config" == "Debug" ]] && name="${app}_debug" found="" - for out in "$dir/bin/$name" "$dir/bin/$name.exe" "$dir/bin/$name.app"; do + for out in "$dir/bin/$name" "$dir/bin/$name.exe" "$dir/bin/$name.app" "$dir/bin/em/$app/index.html"; do [[ -e "$out" ]] && found="$out" done [[ -n "$found" ]] || fail "$example $config: no bin/$name" From 33362ed61b7ed3a0ac2fdf958bdc35abe001da50 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Wed, 21 Oct 2026 16:08:31 +1100 Subject: [PATCH 11/14] CMake Emscripten: link the core whole-archive as a link option LINKER: is only expanded in link options. In target_link_libraries it went to wasm-ld as -lLINKER:--whole-archive,... and the link failed. Pass it with target_link_options and add the archive to INTERFACE_LINK_DEPENDS so apps relink when the core changes. CI: install libblas-dev / liblapack-dev for build-cmake (linuxaarch64); ofxOpenCv links -lblas -llapack there, which rpi-build gets from multistrap. --- .github/workflows/of.yml | 3 ++- libs/openFrameworksCompiled/project/CMake/OFCore.cmake | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/of.yml b/.github/workflows/of.yml index 2211911d3cc..d6dfc906e96 100644 --- a/.github/workflows/of.yml +++ b/.github/workflows/of.yml @@ -172,7 +172,8 @@ jobs: run: | ./scripts/linux/download_libs.sh -t $RELEASE -a aarch64 ./scripts/ci/linux64/install.sh - sudo apt-get install -y ninja-build + # ofxOpenCv links -lblas -llapack on linuxaarch64 (rpi-build gets them from multistrap) + sudo apt-get install -y ninja-build libblas-dev liblapack-dev mkdir -p ~/pg && cd ~/pg curl -fsSL -o pg.tar.bz2 https://github.com/openframeworks/projectGenerator/releases/download/nightly/projectGenerator-linux-arm64.tar.bz2 tar -xjf pg.tar.bz2 && rm pg.tar.bz2 diff --git a/libs/openFrameworksCompiled/project/CMake/OFCore.cmake b/libs/openFrameworksCompiled/project/CMake/OFCore.cmake index ebea0c804bf..e381bf4ace9 100644 --- a/libs/openFrameworksCompiled/project/CMake/OFCore.cmake +++ b/libs/openFrameworksCompiled/project/CMake/OFCore.cmake @@ -213,9 +213,12 @@ function(of_expose_core) if(OF_PLATFORM_WHOLE_ARCHIVE_CORE) add_library(openFrameworks_link INTERFACE) add_library(openFrameworks::openFrameworks ALIAS openFrameworks_link) - target_link_libraries(openFrameworks_link INTERFACE - "LINKER:--whole-archive,$,--no-whole-archive" - openFrameworks::deps) + # LINKER: is only expanded in link options, not in link libraries + target_link_options(openFrameworks_link INTERFACE + "LINKER:--whole-archive,$,--no-whole-archive") + set_property(TARGET openFrameworks_link APPEND PROPERTY + INTERFACE_LINK_DEPENDS "$") + target_link_libraries(openFrameworks_link INTERFACE openFrameworks::deps) add_dependencies(openFrameworks_link openFrameworks) else() add_library(openFrameworks::openFrameworks ALIAS openFrameworks) From 4a2c50f3eac3a611d907db33a41a3cc761bc8aa6 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Wed, 21 Oct 2026 17:01:33 +1100 Subject: [PATCH 12/14] CMake: OF_BUILD_TESTS registers the unit tests with CTest -DOF_BUILD_TESTS=ON on the core build adds every test in tests/ (desktop platforms) as an app and registers it with add_test, so ctest runs them. Each test runs from its executable's folder like make RunDebug (bin/, or Contents/MacOS on macOS, which the fileUtils data path checks expect). of_add_app takes PROJECT_DIR so projects without a CMakeLists.txt (tests, examples) can be added; the addon and platform helpers read the project folder from OF_PROJECT_DIR. OFProjects.cmake walks // and skips groups for other platforms. jsonTest includes like ofJson.h; "json.hpp" only built with make, which adds every libs/*/include subfolder. CI: build-cmake (linux64, macos) builds the tests in Debug and runs ctest. Based on the CTest setup (define_test) in openframeworks/openFrameworks#7524. Co-authored-by: HerrNamenlos123 --- .github/workflows/of.yml | 11 +++ .../project/CMake/CMakeLists.txt | 15 ++++ .../project/CMake/OFAddons.cmake | 12 ++- .../project/CMake/OFApp.cmake | 27 ++++--- .../project/CMake/OFProjects.cmake | 80 +++++++++++++++++++ .../project/CMake/README.md | 18 +++++ .../project/CMake/platform/emscripten.cmake | 6 +- .../project/CMake/platform/ios.cmake | 12 +-- .../project/CMake/platform/macos.cmake | 4 +- .../project/CMake/platform/msys2.cmake | 2 +- .../project/CMake/platform/vs.cmake | 2 +- tests/libs/jsonTest/src/main.cpp | 2 +- 12 files changed, 164 insertions(+), 27 deletions(-) create mode 100644 libs/openFrameworksCompiled/project/CMake/OFProjects.cmake diff --git a/.github/workflows/of.yml b/.github/workflows/of.yml index d6dfc906e96..456cb79f110 100644 --- a/.github/workflows/of.yml +++ b/.github/workflows/of.yml @@ -206,6 +206,17 @@ jobs: env: DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" + # unit tests in tests/ through CTest (Debug, like the makefile CI) + - name: Tests (ctest) + if: matrix.name == 'linux64' || matrix.name == 'macos' + run: | + cmake -S libs/openFrameworksCompiled/project/CMake -B build/cmake-tests -G Ninja -DCMAKE_BUILD_TYPE=Debug -DOF_BUILD_TESTS=ON + cmake --build build/cmake-tests --parallel + if [ "$RUNNER_OS" = "Linux" ]; then RUN="xvfb-run -a"; fi + $RUN ctest --test-dir build/cmake-tests --output-on-failure --timeout 300 + env: + DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" + - uses: msys2/setup-msys2@v2 if: matrix.name == 'vs-x64' with: diff --git a/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt b/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt index cf741e7cdf1..986d7d733ca 100644 --- a/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt +++ b/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt @@ -6,10 +6,25 @@ # # cmake -S libs/openFrameworksCompiled/project/CMake -B build/core -DCMAKE_BUILD_TYPE=Release # cmake --build build/core +# +# -DOF_BUILD_TESTS=ON adds the unit tests in tests/ and registers them with CTest: +# cmake --build build/core && ctest --test-dir build/core --output-on-failure cmake_minimum_required(VERSION 3.22) project(openFrameworks LANGUAGES C CXX) include("${CMAKE_CURRENT_LIST_DIR}/openFrameworks.cmake") +option(OF_BUILD_TESTS "Build the unit tests in tests/ and register them with CTest" OFF) + of_add_core_library() + +if(OF_BUILD_TESTS) + if(OF_PLATFORM MATCHES "^(emscripten|ios|tvos)$") + message(WARNING "openFrameworks: OF_BUILD_TESTS is for desktop platforms; skipping on ${OF_PLATFORM}") + else() + enable_testing() + include("${CMAKE_CURRENT_LIST_DIR}/OFProjects.cmake") + of_add_projects(tests "${OF_ROOT}/tests" TESTS) + endif() +endif() diff --git a/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake index 8610cc0f716..8dd137bff5e 100644 --- a/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake +++ b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake @@ -116,14 +116,18 @@ macro(of_read_addon_config file prefix) endmacro() function(of_find_addon_dir out_var addon) + set(project_dir "${OF_PROJECT_DIR}") + if(NOT project_dir) + set(project_dir "${CMAKE_CURRENT_SOURCE_DIR}") + endif() if(IS_ABSOLUTE "${addon}" AND IS_DIRECTORY "${addon}") set(dir "${addon}") - elseif(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${addon}" AND addon MATCHES "/") - set(dir "${CMAKE_CURRENT_SOURCE_DIR}/${addon}") + elseif(IS_DIRECTORY "${project_dir}/${addon}" AND addon MATCHES "/") + set(dir "${project_dir}/${addon}") elseif(IS_DIRECTORY "${OF_ADDONS_DIR}/${addon}") set(dir "${OF_ADDONS_DIR}/${addon}") - elseif(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${addon}") - set(dir "${CMAKE_CURRENT_SOURCE_DIR}/${addon}") + elseif(IS_DIRECTORY "${project_dir}/${addon}") + set(dir "${project_dir}/${addon}") else() message(FATAL_ERROR "openFrameworks: addon '${addon}' not found in ${OF_ADDONS_DIR} or the project") endif() diff --git a/libs/openFrameworksCompiled/project/CMake/OFApp.cmake b/libs/openFrameworksCompiled/project/CMake/OFApp.cmake index 8090a12b26e..c81f94249a7 100644 --- a/libs/openFrameworksCompiled/project/CMake/OFApp.cmake +++ b/libs/openFrameworksCompiled/project/CMake/OFApp.cmake @@ -1,38 +1,47 @@ # openFrameworks apps # -# of_add_app( [SOURCES ...] [ADDONS ...] [NO_ADDONS_MAKE]) +# of_add_app( [SOURCES ...] [ADDONS ...] [NO_ADDONS_MAKE] +# [PROJECT_DIR ]) # # Creates the app target from the project's src/ folder (or SOURCES), links the # openFrameworks core, and adds ADDONS (or the project's addons.make unless # NO_ADDONS_MAKE). The app is built into the project's bin/ folder like the # makefiles and IDE projects: for Release, _debug for Debug. +# PROJECT_DIR is the project folder (default: the current CMakeLists.txt folder), +# so a project without its own CMakeLists.txt (tests, examples) can be added. include_guard(GLOBAL) function(of_add_app name) - cmake_parse_arguments(A "NO_ADDONS_MAKE" "" "SOURCES;ADDONS" ${ARGN}) + cmake_parse_arguments(A "NO_ADDONS_MAKE" "PROJECT_DIR" "SOURCES;ADDONS" ${ARGN}) + # the helpers called below read the project folder from OF_PROJECT_DIR + if(A_PROJECT_DIR) + get_filename_component(OF_PROJECT_DIR "${A_PROJECT_DIR}" ABSOLUTE) + else() + set(OF_PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + endif() if(A_SOURCES) set(sources ${A_SOURCES}) else() - of_glob_sources(sources "${CMAKE_CURRENT_SOURCE_DIR}/src") + of_glob_sources(sources "${OF_PROJECT_DIR}/src") file(GLOB_RECURSE headers CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp") + "${OF_PROJECT_DIR}/src/*.h" "${OF_PROJECT_DIR}/src/*.hpp") list(APPEND sources ${headers}) endif() if(NOT sources) - message(FATAL_ERROR "openFrameworks: no sources for ${name} (looked in ${CMAKE_CURRENT_SOURCE_DIR}/src)") + message(FATAL_ERROR "openFrameworks: no sources for ${name} (looked in ${OF_PROJECT_DIR}/src)") endif() add_executable(${name} ${sources}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources}) - of_dir_and_subdirs(src_dirs "${CMAKE_CURRENT_SOURCE_DIR}/src") + source_group(TREE "${OF_PROJECT_DIR}" FILES ${sources}) + of_dir_and_subdirs(src_dirs "${OF_PROJECT_DIR}/src") target_include_directories(${name} PRIVATE ${src_dirs}) of_use_core() target_link_libraries(${name} PRIVATE openFrameworks::openFrameworks) - set(bin_dir "${CMAKE_CURRENT_SOURCE_DIR}/bin") + set(bin_dir "${OF_PROJECT_DIR}/bin") set_target_properties(${name} PROPERTIES OUTPUT_NAME ${name} OUTPUT_NAME_DEBUG ${name}_debug @@ -46,7 +55,7 @@ function(of_add_app name) set(addons ${A_ADDONS}) if(NOT A_ADDONS AND NOT A_NO_ADDONS_MAKE) - of_read_addons_make(addons "${CMAKE_CURRENT_SOURCE_DIR}/addons.make") + of_read_addons_make(addons "${OF_PROJECT_DIR}/addons.make") endif() of_target_addons(${name} ${OF_PLATFORM_REQUIRED_ADDONS} ${addons}) diff --git a/libs/openFrameworksCompiled/project/CMake/OFProjects.cmake b/libs/openFrameworksCompiled/project/CMake/OFProjects.cmake new file mode 100644 index 00000000000..7a969d1c112 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/OFProjects.cmake @@ -0,0 +1,80 @@ +# openFrameworks tests and examples in the core build +# +# of_add_projects( [TESTS] [EXCLUDE_FROM_ALL]) +# +# Adds every // that has a src/ folder as an app (with +# of_add_app PROJECT_DIR, so the projects need no CMakeLists.txt), in the IDE +# folder /, plus a build_all_ target that builds them all. +# TESTS registers each one with CTest, run from the executable's folder like +# make Run (bin/, or .app/Contents/MacOS on macOS). Groups for other +# platforms (android, ios, ...) are skipped. +# +# Based on the tests and examples setup in openframeworks/openFrameworks#7524. + +include_guard(GLOBAL) + +# groups that only build on their own platform +function(of_project_group_skipped out_var group) + set(skip OFF) + if(group STREQUAL "android") + set(skip ON) + elseif(group STREQUAL "ios" AND NOT OF_PLATFORM STREQUAL "ios") + set(skip ON) + elseif(group STREQUAL "tvOS" AND NOT OF_PLATFORM STREQUAL "tvos") + set(skip ON) + elseif(group STREQUAL "emscripten" AND NOT OF_PLATFORM STREQUAL "emscripten") + set(skip ON) + elseif(group STREQUAL "gles" AND NOT OF_PLATFORM MATCHES "^linux(aarch64|armv7l|armv6l)$") + set(skip ON) + elseif(OF_PLATFORM MATCHES "^(ios|tvos)$" AND NOT group MATCHES "^(ios|tvOS)$") + set(skip ON) + endif() + set(${out_var} ${skip} PARENT_SCOPE) +endfunction() + +function(of_add_projects kind root) + cmake_parse_arguments(A "TESTS;EXCLUDE_FROM_ALL" "" "" ${ARGN}) + + add_custom_target(build_all_${kind}) + set_target_properties(build_all_${kind} PROPERTIES FOLDER "${kind}") + + set(count 0) + file(GLOB groups LIST_DIRECTORIES true "${root}/*") + foreach(group_dir IN LISTS groups) + get_filename_component(group "${group_dir}" NAME) + if(NOT IS_DIRECTORY "${group_dir}") + continue() + endif() + of_project_group_skipped(skip "${group}") + if(skip) + continue() + endif() + + file(GLOB projects LIST_DIRECTORIES true "${group_dir}/*") + foreach(project_dir IN LISTS projects) + if(NOT IS_DIRECTORY "${project_dir}/src") + continue() + endif() + get_filename_component(name "${project_dir}" NAME) + # the same project name in two groups (gl/ and ios/glRenderSmokeTest) + if(TARGET ${name}) + set(name "${group}_${name}") + endif() + + of_add_app(${name} PROJECT_DIR "${project_dir}") + set_target_properties(${name} PROPERTIES FOLDER "${kind}/${group}") + if(A_EXCLUDE_FROM_ALL) + set_target_properties(${name} PROPERTIES EXCLUDE_FROM_ALL ON) + endif() + add_dependencies(build_all_${kind} ${name}) + + if(A_TESTS) + add_test(NAME "${group}/${name}" + COMMAND $ + WORKING_DIRECTORY $) + endif() + math(EXPR count "${count} + 1") + endforeach() + endforeach() + message(STATUS "openFrameworks: ${count} ${kind} from ${root}") +endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/README.md b/libs/openFrameworksCompiled/project/CMake/README.md index 323ed0b07a3..0a5783b4e73 100644 --- a/libs/openFrameworksCompiled/project/CMake/README.md +++ b/libs/openFrameworksCompiled/project/CMake/README.md @@ -61,6 +61,23 @@ cmake --build build/core | MSYS2 | `lib/msys2/libopenFrameworks.a` (make's) | | Emscripten | `lib/emscripten/libopenFrameworks.a` (make builds `libopenFrameworks.o` next to it) | +## Tests + +`-DOF_BUILD_TESTS=ON` on the core build adds every test in `tests/` (desktop +platforms) and registers it with CTest. Each test runs from its executable's +folder, like `make RunDebug`: + +```sh +cmake -S libs/openFrameworksCompiled/project/CMake -B build/tests -DCMAKE_BUILD_TYPE=Debug -DOF_BUILD_TESTS=ON +cmake --build build/tests +ctest --test-dir build/tests --output-on-failure # Linux without a display: xvfb-run -a ctest ... +``` + +Multi-config generators (Visual Studio, Xcode) take `--config Debug` on the build +and `-C Debug` on `ctest`. The tests need no `CMakeLists.txt`: they're added with +`of_add_app(... PROJECT_DIR )` (`OFProjects.cmake`). This follows the CTest +setup in [#7524](https://github.com/openframeworks/openFrameworks/pull/7524). + ## Options The Emscripten `PROJECT_EMSCRIPTEN_*` options have the same names and values as the @@ -69,6 +86,7 @@ makefiles' `config.make` options. | Option | Default | | |---|---|---| | `OF_BUILD_CORE` | `OFF` | build the core in this build even if a prebuilt one exists | +| `OF_BUILD_TESTS` | `OFF` | core build: add `tests/` and register them with CTest | | `OF_PLATFORM` | detected | `macos ios tvos emscripten linux64 linuxaarch64 linuxarmv7l linuxarmv6l vs msys2` | | `OF_USE_DAWN`, `OF_USE_ANGLE` | `OFF` | link `libs/dawn`, `libs/metalangle` | | `PROJECT_EMSCRIPTEN_WASM` | `32` | `32`: libs from `lib/emscripten/WASM`, core in `lib/emscripten`. `64`: libs and core in `lib/emscripten/WASM64` | diff --git a/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake index 181de7b7ef5..e2894278bb6 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake @@ -157,7 +157,7 @@ endfunction() # bin/em//index.html (+ .js .wasm .data), with app.js / style.css / # .htaccess and bin/data preloaded at /data, like the makefiles function(of_platform_setup_app target) - set(out_dir "${CMAKE_CURRENT_SOURCE_DIR}/bin/em/${target}") + set(out_dir "${OF_PROJECT_DIR}/bin/em/${target}") set_target_properties(${target} PROPERTIES OUTPUT_NAME index OUTPUT_NAME_DEBUG index @@ -170,9 +170,9 @@ function(of_platform_setup_app target) # Reconfigure when bin/data gains or loses entries, then emcc packs the # directory as it is at link time. The makefile checks this every build. - file(GLOB data_files CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/bin/data/*") + file(GLOB data_files CONFIGURE_DEPENDS "${OF_PROJECT_DIR}/bin/data/*") if(data_files) - target_link_options(${target} PRIVATE "SHELL:--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/bin/data@data") + target_link_options(${target} PRIVATE "SHELL:--preload-file ${OF_PROJECT_DIR}/bin/data@data") endif() set(project_dir "${OF_LIBS_DIR}/openFrameworksCompiled/project/emscripten") diff --git a/libs/openFrameworksCompiled/project/CMake/platform/ios.cmake b/libs/openFrameworksCompiled/project/CMake/platform/ios.cmake index fb9228b3008..ed0bc403b00 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/ios.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/ios.cmake @@ -138,14 +138,14 @@ endfunction() # phase) and, with the Xcode generator, the template's mediaAssets function(of_platform_setup_app target) if(OF_PLATFORM STREQUAL "tvos") - set(plist "${CMAKE_CURRENT_SOURCE_DIR}/ofxtvOS-Info.plist") + set(plist "${OF_PROJECT_DIR}/ofxtvOS-Info.plist") set(fallback "${OF_ROOT}/scripts/templates/tvOS/ofxtvOS-Info.plist") - set(assets_dir "${CMAKE_CURRENT_SOURCE_DIR}/mediaAssets") + set(assets_dir "${OF_PROJECT_DIR}/mediaAssets") set(families 3) else() - set(plist "${CMAKE_CURRENT_SOURCE_DIR}/ofxiOS-Info.plist") + set(plist "${OF_PROJECT_DIR}/ofxiOS-Info.plist") set(fallback "${OF_ROOT}/scripts/templates/ios/ofxiOS-Info.plist") - set(assets_dir "${CMAKE_CURRENT_SOURCE_DIR}/mediaAssets") + set(assets_dir "${OF_PROJECT_DIR}/mediaAssets") set(families "1,2") endif() of_apple_info_plist(${target} "${plist}" "${fallback}") @@ -168,9 +168,9 @@ function(of_platform_setup_app target) set_property(TARGET ${target} APPEND PROPERTY RESOURCE ${assets}) endif() - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/data") + if(IS_DIRECTORY "${OF_PROJECT_DIR}/bin/data") add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/bin/data" "$" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${OF_PROJECT_DIR}/bin/data" "$" VERBATIM) endif() endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake b/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake index 5cd84723bc3..ee298ad6a68 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake @@ -72,10 +72,10 @@ endfunction() # Info.plist and icon the makefiles use function(of_platform_setup_app target) of_apple_info_plist(${target} - "${CMAKE_CURRENT_SOURCE_DIR}/openFrameworks-Info.plist" + "${OF_PROJECT_DIR}/openFrameworks-Info.plist" "${OF_ROOT}/scripts/templates/osx/openFrameworks-Info.plist") - set(icon "${CMAKE_CURRENT_SOURCE_DIR}/of.icns") + set(icon "${OF_PROJECT_DIR}/of.icns") if(NOT EXISTS "${icon}") set(icon "${OF_LIBS_DIR}/openFrameworksCompiled/project/osx/of.icns") endif() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/msys2.cmake b/libs/openFrameworksCompiled/project/CMake/platform/msys2.cmake index 1a9827f66e0..e396348ad29 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/msys2.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/msys2.cmake @@ -66,7 +66,7 @@ endfunction() # bin/.exe with the openFrameworks icon (Debug: _debug.exe) function(of_platform_setup_app target) - set(icon_rc "${CMAKE_CURRENT_SOURCE_DIR}/icon.rc") + set(icon_rc "${OF_PROJECT_DIR}/icon.rc") if(EXISTS "${icon_rc}") target_sources(${target} PRIVATE "${icon_rc}") endif() diff --git a/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake index a09ca871485..0ce377fdc3e 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake @@ -84,7 +84,7 @@ endfunction() # console app in bin/, like the VS templates (Debug: _debug.exe) function(of_platform_setup_app target) - set(icon_rc "${CMAKE_CURRENT_SOURCE_DIR}/icon.rc") + set(icon_rc "${OF_PROJECT_DIR}/icon.rc") if(EXISTS "${icon_rc}") target_sources(${target} PRIVATE "${icon_rc}") # icon.rc names icon.ico / icon_debug.ico, which live in project/vs (the VS diff --git a/tests/libs/jsonTest/src/main.cpp b/tests/libs/jsonTest/src/main.cpp index 240d3fccfe0..31d6a54172a 100644 --- a/tests/libs/jsonTest/src/main.cpp +++ b/tests/libs/jsonTest/src/main.cpp @@ -1,7 +1,7 @@ #include "ofMain.h" #include "ofAppNoWindow.h" #include "ofxUnitTests.h" -#include "json.hpp" +#include class ofApp: public ofxUnitTestsApp{ void run(){ From 0439bfe369b93e10d80e5576ea75add8bdc05752 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Wed, 21 Oct 2026 17:05:59 +1100 Subject: [PATCH 13/14] CMake: OF_BUILD_EXAMPLES adds every example and build_all_examples -DOF_BUILD_EXAMPLES=ON on the core build adds every example for the platform as an app (EXCLUDE_FROM_ALL, built into its own bin/) and a build_all_examples target. USE_FOLDERS groups the targets as openFrameworks, examples/ and tests/, so one Visual Studio solution, Xcode project or CLion project opens them all. On macOS all 157 examples build (svgParseExample, svgSaveExample and yunetFaceDetection need the addons.make files they're missing on master). Based on the examples setup (define_example, build_all_examples, IDE folders) in openframeworks/openFrameworks#7524. Co-authored-by: HerrNamenlos123 --- .../project/CMake/CMakeLists.txt | 15 +++++++++++++++ .../project/CMake/README.md | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt b/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt index 986d7d733ca..61484e61e3c 100644 --- a/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt +++ b/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt @@ -9,6 +9,9 @@ # # -DOF_BUILD_TESTS=ON adds the unit tests in tests/ and registers them with CTest: # cmake --build build/core && ctest --test-dir build/core --output-on-failure +# -DOF_BUILD_EXAMPLES=ON adds every example (not built by default) and a +# build_all_examples target, in IDE folders, so one solution opens them all: +# cmake --build build/core --target build_all_examples cmake_minimum_required(VERSION 3.22) project(openFrameworks LANGUAGES C CXX) @@ -16,8 +19,15 @@ project(openFrameworks LANGUAGES C CXX) include("${CMAKE_CURRENT_LIST_DIR}/openFrameworks.cmake") option(OF_BUILD_TESTS "Build the unit tests in tests/ and register them with CTest" OFF) +option(OF_BUILD_EXAMPLES "Add examples/ and a build_all_examples target" OFF) + +# IDE folders: openFrameworks, tests/, examples/ +set_property(GLOBAL PROPERTY USE_FOLDERS ON) of_add_core_library() +if(TARGET openFrameworks) + set_target_properties(openFrameworks PROPERTIES FOLDER openFrameworks) +endif() if(OF_BUILD_TESTS) if(OF_PLATFORM MATCHES "^(emscripten|ios|tvos)$") @@ -28,3 +38,8 @@ if(OF_BUILD_TESTS) of_add_projects(tests "${OF_ROOT}/tests" TESTS) endif() endif() + +if(OF_BUILD_EXAMPLES) + include("${CMAKE_CURRENT_LIST_DIR}/OFProjects.cmake") + of_add_projects(examples "${OF_ROOT}/examples" EXCLUDE_FROM_ALL) +endif() diff --git a/libs/openFrameworksCompiled/project/CMake/README.md b/libs/openFrameworksCompiled/project/CMake/README.md index 0a5783b4e73..64f20a14409 100644 --- a/libs/openFrameworksCompiled/project/CMake/README.md +++ b/libs/openFrameworksCompiled/project/CMake/README.md @@ -78,6 +78,22 @@ and `-C Debug` on `ctest`. The tests need no `CMakeLists.txt`: they're added wit `of_add_app(... PROJECT_DIR )` (`OFProjects.cmake`). This follows the CTest setup in [#7524](https://github.com/openframeworks/openFrameworks/pull/7524). +## All examples + +`-DOF_BUILD_EXAMPLES=ON` on the core build adds every example for the platform +(not built by default) and a `build_all_examples` target. Targets are grouped in +IDE folders (`openFrameworks`, `examples/`, `tests/`), so one Visual +Studio solution, Xcode project or CLion project opens them all: + +```sh +cmake -S libs/openFrameworksCompiled/project/CMake -B build/examples -DOF_BUILD_EXAMPLES=ON +cmake --build build/examples --target build_all_examples # or one example: --target graphicsExample +``` + +Each example builds into its own `bin/`, as with its own `CMakeLists.txt`. An example +name used in two groups gets the group as a prefix. This follows the examples setup in +[#7524](https://github.com/openframeworks/openFrameworks/pull/7524). + ## Options The Emscripten `PROJECT_EMSCRIPTEN_*` options have the same names and values as the @@ -87,6 +103,7 @@ makefiles' `config.make` options. |---|---|---| | `OF_BUILD_CORE` | `OFF` | build the core in this build even if a prebuilt one exists | | `OF_BUILD_TESTS` | `OFF` | core build: add `tests/` and register them with CTest | +| `OF_BUILD_EXAMPLES` | `OFF` | core build: add `examples/` and a `build_all_examples` target | | `OF_PLATFORM` | detected | `macos ios tvos emscripten linux64 linuxaarch64 linuxarmv7l linuxarmv6l vs msys2` | | `OF_USE_DAWN`, `OF_USE_ANGLE` | `OFF` | link `libs/dawn`, `libs/metalangle` | | `PROJECT_EMSCRIPTEN_WASM` | `32` | `32`: libs from `lib/emscripten/WASM`, core in `lib/emscripten`. `64`: libs and core in `lib/emscripten/WASM64` | From aeaf2b5908f64048909ce0e98d28ef6a55d9ae33 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Wed, 21 Oct 2026 17:09:46 +1100 Subject: [PATCH 14/14] CMake: OF_PRECOMPILED_HEADER precompiles ofMain.h for apps -DOF_PRECOMPILED_HEADER=ON precompiles ofMain.h for the app's own src/ C++ files. Addon and library sources skip it (SKIP_PRECOMPILE_HEADERS), so third-party code compiles as it does in the other build systems. On macOS, where C++ is compiled as Objective-C++, the header is built as Objective-C++ too, otherwise clang rejects it. CI: build-cmake (linuxaarch64, vs-x64) builds with the precompiled header, so GCC and MSVC are covered. build.sh reports the first output it finds. Based on the precompiled header setup documented in openframeworks/openFrameworks#7524. Co-authored-by: HerrNamenlos123 --- .github/workflows/of.yml | 4 ++-- .../project/CMake/OFApp.cmake | 16 ++++++++++++++++ .../project/CMake/README.md | 1 + .../project/CMake/openFrameworks.cmake | 1 + .../project/CMake/platform/macos.cmake | 4 ++++ scripts/ci/cmake/build.sh | 2 +- 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/of.yml b/.github/workflows/of.yml index 456cb79f110..25fe90350ec 100644 --- a/.github/workflows/of.yml +++ b/.github/workflows/of.yml @@ -180,7 +180,7 @@ jobs: echo "PG=$(find ~/pg -type f -name 'projectGenerator*' -perm -u+x | head -1)" >> $GITHUB_ENV - name: Build (linuxaarch64) if: matrix.name == 'linuxaarch64' - run: scripts/ci/cmake/build.sh --pg "$PG" --platform linuxaarch64 -- -G Ninja + run: scripts/ci/cmake/build.sh --pg "$PG" --platform linuxaarch64 -- -G Ninja -DOF_PRECOMPILED_HEADER=ON # same emsdk image as build-emscripten; emcmake sets the Emscripten toolchain - name: Libs and emsdk (emscripten) @@ -231,7 +231,7 @@ jobs: - name: Build (vs-x64) if: matrix.name == 'vs-x64' shell: bash - run: scripts/ci/cmake/build.sh --pg "$PWD/projectGenerator/projectGenerator.exe" --platform vs -- -G "Visual Studio 17 2022" -A x64 + run: scripts/ci/cmake/build.sh --pg "$PWD/projectGenerator/projectGenerator.exe" --platform vs -- -G "Visual Studio 17 2022" -A x64 -DOF_PRECOMPILED_HEADER=ON build-vs: runs-on: ${{ matrix.runner }} diff --git a/libs/openFrameworksCompiled/project/CMake/OFApp.cmake b/libs/openFrameworksCompiled/project/CMake/OFApp.cmake index c81f94249a7..1f33517fab0 100644 --- a/libs/openFrameworksCompiled/project/CMake/OFApp.cmake +++ b/libs/openFrameworksCompiled/project/CMake/OFApp.cmake @@ -9,6 +9,7 @@ # makefiles and IDE projects: for Release, _debug for Debug. # PROJECT_DIR is the project folder (default: the current CMakeLists.txt folder), # so a project without its own CMakeLists.txt (tests, examples) can be added. +# OF_PRECOMPILED_HEADER=ON precompiles ofMain.h for the app's src/ C++ files. include_guard(GLOBAL) @@ -59,5 +60,20 @@ function(of_add_app name) endif() of_target_addons(${name} ${OF_PLATFORM_REQUIRED_ADDONS} ${addons}) + # ofMain.h precompiled for the app's own C++ files; addon and library + # sources compile without it, as they do in the other build systems + if(OF_PRECOMPILED_HEADER) + target_precompile_headers(${name} PRIVATE + "$<$:${OF_CORE_SRC_DIR}/ofMain.h>") + get_target_property(app_sources ${name} SOURCES) + foreach(source IN LISTS app_sources) + get_filename_component(source "${source}" ABSOLUTE) + string(FIND "${source}" "${OF_PROJECT_DIR}/src/" at) + if(NOT at EQUAL 0) + set_source_files_properties("${source}" PROPERTIES SKIP_PRECOMPILE_HEADERS ON) + endif() + endforeach() + endif() + of_platform_setup_app(${name}) endfunction() diff --git a/libs/openFrameworksCompiled/project/CMake/README.md b/libs/openFrameworksCompiled/project/CMake/README.md index 64f20a14409..7f956f4eac5 100644 --- a/libs/openFrameworksCompiled/project/CMake/README.md +++ b/libs/openFrameworksCompiled/project/CMake/README.md @@ -104,6 +104,7 @@ makefiles' `config.make` options. | `OF_BUILD_CORE` | `OFF` | build the core in this build even if a prebuilt one exists | | `OF_BUILD_TESTS` | `OFF` | core build: add `tests/` and register them with CTest | | `OF_BUILD_EXAMPLES` | `OFF` | core build: add `examples/` and a `build_all_examples` target | +| `OF_PRECOMPILED_HEADER` | `OFF` | precompile `ofMain.h` for each app's own `src/` C++ files (faster rebuilds); addon sources compile without it | | `OF_PLATFORM` | detected | `macos ios tvos emscripten linux64 linuxaarch64 linuxarmv7l linuxarmv6l vs msys2` | | `OF_USE_DAWN`, `OF_USE_ANGLE` | `OFF` | link `libs/dawn`, `libs/metalangle` | | `PROJECT_EMSCRIPTEN_WASM` | `32` | `32`: libs from `lib/emscripten/WASM`, core in `lib/emscripten`. `64`: libs and core in `lib/emscripten/WASM64` | diff --git a/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake b/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake index 11574394544..42f5fd1f212 100644 --- a/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake +++ b/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake @@ -26,6 +26,7 @@ set(OF_CORE_SRC_DIR "${OF_LIBS_DIR}/openFrameworks") set(OF_ADDONS_DIR "${OF_ROOT}/addons") option(OF_BUILD_CORE "Build the openFrameworks core in this build instead of linking a prebuilt one" OFF) +option(OF_PRECOMPILED_HEADER "Precompile ofMain.h for each app's own C++ sources" OFF) set(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 23) diff --git a/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake b/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake index ee298ad6a68..b53e31fe486 100644 --- a/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake +++ b/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake @@ -29,6 +29,10 @@ set(OF_PLATFORM_COMPILE_OPTIONS # like the makefiles, C++ is compiled as Objective-C++ so it can use Cocoa "$<$:-xobjective-c++>") +# so a precompiled header (OF_PRECOMPILED_HEADER) must be Objective-C++ too, or +# clang rejects it ("Objective-C was disabled in precompiled file") +set(CMAKE_CXX_COMPILE_OPTIONS_CREATE_PCH ${CMAKE_OBJCXX_COMPILE_OPTIONS_CREATE_PCH}) + set(OF_PLATFORM_LINK_OPTIONS) set(OF_PLATFORM_LINK_LIBRARIES objc) diff --git a/scripts/ci/cmake/build.sh b/scripts/ci/cmake/build.sh index 90f36206d5d..731f9f731e8 100755 --- a/scripts/ci/cmake/build.sh +++ b/scripts/ci/cmake/build.sh @@ -85,7 +85,7 @@ for entry in "${EXAMPLES[@]}"; do name="$app"; [[ "$config" == "Debug" ]] && name="${app}_debug" found="" for out in "$dir/bin/$name" "$dir/bin/$name.exe" "$dir/bin/$name.app" "$dir/bin/em/$app/index.html"; do - [[ -e "$out" ]] && found="$out" + [[ -e "$out" ]] && { found="$out"; break; } done [[ -n "$found" ]] || fail "$example $config: no bin/$name" echo "built ${found#$ROOT/}"