diff --git a/.github/workflows/of.yml b/.github/workflows/of.yml index 58b7ba5ef3f..25fe90350ec 100644 --- a/.github/workflows/of.yml +++ b/.github/workflows/of.yml @@ -130,6 +130,109 @@ 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: 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' && matrix.name != 'emscripten' + 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 + + # 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 + # 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 + 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 -DOF_PRECOMPILED_HEADER=ON + + # 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: | + ./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" + + # 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: + 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 -DOF_PRECOMPILED_HEADER=ON + build-vs: runs-on: ${{ matrix.runner }} strategy: 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..61484e61e3c --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/CMakeLists.txt @@ -0,0 +1,45 @@ +# 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 +# +# -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) + +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)$") + 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() + +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/OFAddons.cmake b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake new file mode 100644 index 00000000000..8dd137bff5e --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/OFAddons.cmake @@ -0,0 +1,237 @@ +# 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) + # 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() + 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) + 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 "${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 "${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() + 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}) + # 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} + 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..1f33517fab0 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/OFApp.cmake @@ -0,0 +1,79 @@ +# openFrameworks apps +# +# 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. +# OF_PRECOMPILED_HEADER=ON precompiles ofMain.h for the app's src/ C++ files. + +include_guard(GLOBAL) + +function(of_add_app name) + 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 "${OF_PROJECT_DIR}/src") + file(GLOB_RECURSE headers CONFIGURE_DEPENDS + "${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 ${OF_PROJECT_DIR}/src)") + endif() + + add_executable(${name} ${sources}) + 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 "${OF_PROJECT_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 "${OF_PROJECT_DIR}/addons.make") + 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/OFCore.cmake b/libs/openFrameworksCompiled/project/CMake/OFCore.cmake new file mode 100644 index 00000000000..e381bf4ace9 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/OFCore.cmake @@ -0,0 +1,288 @@ +# 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_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(). + +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() + +# 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) + # 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) + endif() +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() + of_expose_core() + 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) + of_expose_core() + 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/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 new file mode 100644 index 00000000000..7f956f4eac5 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/README.md @@ -0,0 +1,159 @@ +# 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) | + +## 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). + +## 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 +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_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` | +| `PROJECT_EMSCRIPTEN_TOTAL_MEMORY` | `134217728` | initial memory in bytes | +| `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` | +| `APP_BUILD` | `1` | build number (`CFBundleVersion`) | +| `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..42f5fd1f212 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake @@ -0,0 +1,49 @@ +# 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) +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) +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..d17fa18c76d --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/apple.cmake @@ -0,0 +1,78 @@ +# 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() + # 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}") + foreach(pair + "DEVELOPMENT_LANGUAGE=English" + "ICON_NAME=of.icns" + "PRODUCT_BUNDLE_IDENTIFIER=${identifier}" + "TARGET_NAME=${name}" + "PRODUCT_NAME=${name}" + "MARKETING_VERSION=${version}" + "CURRENT_PROJECT_VERSION=${build}" + "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}" + XCODE_ATTRIBUTE_MARKETING_VERSION "${version}" + XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${build}") +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..e2894278bb6 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/emscripten.cmake @@ -0,0 +1,183 @@ +# 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, same names as the makefiles' config.make options): +# 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 OFF (default) / ON +# PROJECT_EMSCRIPTEN_TEMPLATE HTML shell file (default the one in project/emscripten) +# OF_WEBGL_VERSION 2 (default) or 1 + +include_guard(GLOBAL) + +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") +# 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)") + +# 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) + +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" + "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 + -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}" + "$<$:-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(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(PROJECT_EMSCRIPTEN_WASM STREQUAL "64") + 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) +# 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 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) + +# 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}/WASM64" PARENT_SCOPE) + else() + set(${out_var} "${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 "${OF_PROJECT_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() + + # 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 "${OF_PROJECT_DIR}/bin/data/*") + if(data_files) + target_link_options(${target} PRIVATE "SHELL:--preload-file ${OF_PROJECT_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..ed0bc403b00 --- /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 "${OF_PROJECT_DIR}/ofxtvOS-Info.plist") + set(fallback "${OF_ROOT}/scripts/templates/tvOS/ofxtvOS-Info.plist") + set(assets_dir "${OF_PROJECT_DIR}/mediaAssets") + set(families 3) + else() + set(plist "${OF_PROJECT_DIR}/ofxiOS-Info.plist") + set(fallback "${OF_ROOT}/scripts/templates/ios/ofxiOS-Info.plist") + set(assets_dir "${OF_PROJECT_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 "${OF_PROJECT_DIR}/bin/data") + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${OF_PROJECT_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..b53e31fe486 --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/macos.cmake @@ -0,0 +1,88 @@ +# 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++>") + +# 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) +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} + "${OF_PROJECT_DIR}/openFrameworks-Info.plist" + "${OF_ROOT}/scripts/templates/osx/openFrameworks-Info.plist") + + set(icon "${OF_PROJECT_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..e396348ad29 --- /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 "${OF_PROJECT_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..0ce377fdc3e --- /dev/null +++ b/libs/openFrameworksCompiled/project/CMake/platform/vs.cmake @@ -0,0 +1,95 @@ +# 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 + # 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) + +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 "${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 + # 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() diff --git a/scripts/ci/cmake/build.sh b/scripts/ci/cmake/build.sh new file mode 100755 index 00000000000..731f9f731e8 --- /dev/null +++ b/scripts/ci/cmake/build.sh @@ -0,0 +1,94 @@ +#!/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 +# 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="" +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 [[ -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) +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_WRAPPER cmake -S "$src" -B "$dir" "${CONFIGURE_ARGS[@]}" -DCMAKE_BUILD_TYPE="$config" || return 1 + 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" \ + || fail "core $config" +done +echo "##[endgroup]" + +for entry in "${EXAMPLES[@]}"; do + example="${entry%%:*}" + configs="${entry#*:}" + configs="${configs//,/ }" + 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" "$dir/bin/em/$app/index.html"; do + [[ -e "$out" ]] && { found="$out"; break; } + done + [[ -n "$found" ]] || fail "$example $config: no bin/$name" + echo "built ${found#$ROOT/}" + done + echo "##[endgroup]" +done 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") diff --git a/scripts/templates/cmake/CMakeLists.txt b/scripts/templates/cmake/CMakeLists.txt new file mode 100644 index 00000000000..268fa756588 --- /dev/null +++ b/scripts/templates/cmake/CMakeLists.txt @@ -0,0 +1,28 @@ +# 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") + +# 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}) 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(){