From 9fb486cf195ef8cf8f62bd40a2ccc3296b8b41a9 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 12 Jun 2025 07:35:46 -0500 Subject: [PATCH 01/55] xo-cmake: + xo-build --build-docs --- bin/xo-build.in | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/bin/xo-build.in b/bin/xo-build.in index 194ce3df..6a450de0 100644 --- a/bin/xo-build.in +++ b/bin/xo-build.in @@ -27,6 +27,7 @@ Options: --configure run cmake for xo library NAME in immediate subdir Will use NAME/.build as build directory --build run cmake build for xo library NAME in immediate subdir + --build-docs build documentation for xo library NAME in immediate subdir --install run cmake install for xo library NAME in immediate subdir -S=SOURCEDIR override path/to/source -B=BUILDDIR override path/to/build @@ -86,6 +87,9 @@ while [[ $# > 0 ]]; do --build) build_flag=1 ;; + --build-docs) + build_docs_flag=1 + ;; --install) install_flag=1 ;; @@ -207,6 +211,18 @@ if [[ $build_flag -eq 1 ]]; then fi fi +if [[ $build_docs_flag -eq 1 ]]; then + if [[ -n "$xoname" ]]; then + cmd="cmake --build $pathtobuild -- docs" + + if [[ $noop_flag -eq 1 ]]; then + echo $cmd + else + $cmd + fi + fi +fi + if [[ $install_flag -eq 1 ]]; then if [[ -n "$xoname" ]]; then cmd="cmake --install $pathtobuild" From ccd6930cb91cc6b03a21b914446193af72047beb Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 12 Jun 2025 07:36:25 -0500 Subject: [PATCH 02/55] xo-cmake: set CMAKE_MODULE_PATH -> works properly from nix --- bin/xo-build.in | 3 ++- cmake/xo_macros/xo_cxx.cmake | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/xo-build.in b/bin/xo-build.in index 6a450de0..4399932a 100644 --- a/bin/xo-build.in +++ b/bin/xo-build.in @@ -127,6 +127,7 @@ repo() { xoname=$1 case "$xoname" in + # carve-outs for 4 snowflake xo repo names xo-indentlog) echo "${XO_REPO_STEM}/indentlog.git" ;; @@ -189,7 +190,7 @@ fi if [[ $configure_flag -eq 1 ]]; then if [[ -n "$xoname" ]]; then - cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -S $pathtosource -B $pathtobuild" + cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild" if [[ $noop_flag -eq 1 ]]; then echo $cmd diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index a5c0e3c3..6679c6c2 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -330,6 +330,8 @@ macro(xo_docdir_doxygen_config) find_program(DOXYGEN_EXECUTABLE NAMES doxygen REQUIRED) message(STATUS "DOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}") + message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --doxygen-template OUTPUT_VARIABLE DOXYGEN_CONFIG_TEMPLATE) message(STATUS "DOXYGEN_CONFIG_TEMPLATE=${DOXYGEN_CONFIG_TEMPLATE}") From 13ba95a4d3e31cdea5897fabf9462bb64fc1249b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 22 Jun 2025 16:13:37 -0500 Subject: [PATCH 03/55] xo-cmake: doxygen+sphinx working for umbrella2 submodule build --- cmake/xo_macros/xo_cxx.cmake | 183 ++++++++++++++++++++++++++++++----- share/xo-macros/Doxyfile.in | 15 +-- 2 files changed, 168 insertions(+), 30 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 6679c6c2..6fa8d9a5 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -14,7 +14,7 @@ endmacro() # deprecated -- prefer xo_cxx_toplevel_options2() macro(xo_cxx_toplevel_options) - message(WARNING "deprecated: prefer xo_cxx_toplevel_options2") + message(WARNING "deprecated: prefer xo_cxx_toplevel_options3") message("xo_cxx_toplevel_options: PROJECT=${PROJECT_NAME}") @@ -56,6 +56,11 @@ macro(xo_cxx_toplevel_options2) set_property( TARGET all_utest_executables_${PROJECT_NAME} PROPERTY targets "") + + add_custom_target(docs_${PROJECT_NAME}) + set_property( + TARGET docs_${PROJECT_NAME} + PROPERTY targets "") endif() endmacro() @@ -307,19 +312,38 @@ macro(xo_doxygen_collect_deps) message(DEBUG "_all_libs=${_all_libs}") message(DEBUG "_all_utests=${_all_utests}") - ## .hpp files reachable from xo-flatstring/include - ## - ## REMINDER: for reliability will need to re-run cmake when the set of .hpp files changes - ## - #file(GLOB_RECURSE DOX_HPP_FILES_GLOB ${PROJECT_SOURCE_DIR}/include "*.hpp") - #message(STATUS "DOX_HPP_FILES_GLOB=${DOX_HPP_FILES_GLOB}") - #set(DOX_DEPS ${_all_libs} ${_all_utests} ${DOX_HPP_FILES_GLOB}) - set(DOX_DEPS ${_all_exes} ${_all_libs} ${_all_utests}) message(STATUS "DOX_DEPS=${DOX_DEPS}") endmacro() +# for example +# xo_umbrella_doxygen_deps(xo_flatstring xo-pyjit ...) +# use +# cmake --build path/to/build --target help +# to review available targets +# +macro(xo_umbrella_doxygen_deps) + # using a target here for symmetry with satellite builds + add_custom_target(umbrella_libs) + + foreach(arg IN ITEMS ${ARGN}) + message(arg=${arg}) + + set_property( + TARGET umbrella_libs + APPEND + PROPERTY targets all_libraries_${arg}) + endforeach() + + get_target_property(_all_libs umbrella_libs targets) + + set(DOX_DEPS ${_all_libs}) + + message(STATUS "DOX_DEPS=${DOX_DEPS}") +endmacro() + + # caller must set DOX_DEPS before invoking xo_toplevel_doxygen_config() # macro(xo_docdir_doxygen_config) @@ -334,6 +358,8 @@ macro(xo_docdir_doxygen_config) execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --doxygen-template OUTPUT_VARIABLE DOXYGEN_CONFIG_TEMPLATE) message(STATUS "DOXYGEN_CONFIG_TEMPLATE=${DOXYGEN_CONFIG_TEMPLATE}") + message(STATUS "DOX_EXCLUDE=${DOX_EXCLUDE}") + message(STATUS "DOX_EXCLUDE_PATTERNS=${DOX_EXCLUDE_PATTERNS}") set(DOX_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) @@ -342,6 +368,9 @@ macro(xo_docdir_doxygen_config) set(DOX_INDEX_FILE ${DOX_OUTPUT_DIR}/html/index.html) + # note: expansion variables in Doxyfile.in: + # @PROJECT_NAME@ @DOX_INPUT_DIR@ @DOX_OUTPUT_DIR@ @DOX_EXCLUDE@ @DOX_EXCLUDE_PATTERNS@ + # configure_file( ${DOXYGEN_CONFIG_TEMPLATE} ${DOX_CONFIG_FILE} FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE @@ -350,7 +379,7 @@ macro(xo_docdir_doxygen_config) file(MAKE_DIRECTORY ${DOX_OUTPUT_DIR}) add_custom_command( OUTPUT ${DOX_INDEX_FILE} - DEPENDS ${DOX_DEPS} + DEPENDS "${DOX_DEPS}" ${DOX_CONFIG_FILE} COMMAND "${DOXYGEN_EXECUTABLE}" ${DOX_CONFIG_FILE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} MAIN_DEPENDENCY ${DOX_CONFIG_FILE} @@ -363,13 +392,63 @@ macro(xo_docdir_doxygen_config) # $ make doxygen # add_custom_target( - doxygen + doxygen_${PROJECT_NAME} DEPENDS ${DOX_INDEX_FILE} ${DOX_DEPS} ) endif() endmacro() +# config for an umbrella project that composes standalone subprojects +macro(xo_umbrella_doxygen_config) + # look for doxygen executable + find_program(DOXYGEN_EXECUTABLE NAMES doxygen REQUIRED) + message(STATUS "DOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}") + + message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --doxygen-template OUTPUT_VARIABLE DOXYGEN_CONFIG_TEMPLATE) + message(STATUS "DOXYGEN_CONFIG_TEMPLATE=${DOXYGEN_CONFIG_TEMPLATE}") + message(STATUS "DOX_EXCLUDE=${DOX_EXCLUDE}") + message(STATUS "DOX_EXCLUDE_PATTERNS=${DOX_EXCLUDE_PATTERNS}") + + set(DOX_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + set(DOX_INPUT_DIR ${PROJECT_SOURCE_DIR}) + set(DOX_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/dox) + + set(DOX_INDEX_FILE ${DOX_OUTPUT_DIR}/html/index.html) + + # note: expansion variables in Doxyfile.in: + # @PROJECT_NAME@ @DOX_INPUT_DIR@ @DOX_OUTPUT_DIR@ @DOX_EXCLUDE@ @DOX_EXCLUDE_PATTERNS@ + # + configure_file( + ${DOXYGEN_CONFIG_TEMPLATE} ${DOX_CONFIG_FILE} + FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + @ONLY) + + file(MAKE_DIRECTORY ${DOX_OUTPUT_DIR}) + add_custom_command( + OUTPUT ${DOX_INDEX_FILE} + DEPENDS "${DOX_DEPS}" + COMMAND "${DOXYGEN_EXECUTABLE}" ${DOX_CONFIG_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + MAIN_DEPENDENCY ${DOX_CONFIG_FILE} + COMMENT "Generating docs (doxygen)") + + # To build this target + # $ cmake --build .build -j -- doxygen + # or + # $ cd .build + # $ make doxygen + # + add_custom_target( + doxygen_${PROJECT_NAME} + DEPENDS ${DOX_INDEX_FILE} ${DOX_DEPS} + ) +endmacro() + + macro(xo_docdir_sphinx_config rst_files) list(APPEND SPHINX_RST_FILES ${rst_files}) foreach(arg IN ITEMS ${ARGN}) @@ -382,10 +461,7 @@ macro(xo_docdir_sphinx_config rst_files) # in submodule build, rely on toplevel docs/CMakeLists.txt file instead else() # build docs starting from here only in standalone build. - # otherwise use top-level doxygen setup instead. - - #set(ALL_LIBRARY_TARGETS xo_flatstring) # todo: automate this from xo-cmake macros - #set(ALL_UTEST_TARGETS xo_flatstring_ex1 utest.flatstring) # todo: automate this from xo-cmake macros + # otherwise use top-level doxygen setup. # look for sphinx-build executable find_program(SPHINX_EXECUTABLE NAMES sphinx-build REQUIRED) @@ -396,7 +472,8 @@ macro(xo_docdir_sphinx_config rst_files) # root of sphinx doc tree set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) - set(SPHINX_DEPS doxygen conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) + set(SPHINX_DEPS doxygen_${PROJECT_NAME} conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) + #set(SPHINX_DEPS conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) add_custom_command( OUTPUT ${SPHINX_INDEX_FILE} @@ -410,7 +487,7 @@ macro(xo_docdir_sphinx_config rst_files) # make sphinx --> generate sphinx documentation # add_custom_target( - sphinx + sphinx_${PROJECT_NAME} DEPENDS ${SPHINX_INDEX_FILE}) # - html docs generated in build/docs/sphinx @@ -431,8 +508,63 @@ macro(xo_docdir_sphinx_config rst_files) # make docs --> generate sphinx documentation add_custom_target( docs - DEPENDS sphinx) + DEPENDS sphinx_${PROJECT_NAME}) endif() +endmacro() + +# config for an umbrella project that composes standalone subprojects +# +macro(xo_umbrella_sphinx_config rst_files) + list(APPEND SPHINX_RST_FILES ${rst_files}) + foreach(arg IN ITEMS ${ARGN}) + list(APPEND SPHINX_RST_FILES ${arg}) + endforeach() + + # look for sphinx-build executable + find_program(SPHINX_EXECUTABLE NAMES sphinx-build REQUIRED) + message(STATUS "SPHINX_EXECUTABLE=${SPHINX_EXECUTABLE}") + + set(SPHINX_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/sphinx/html) + set(SPHINX_INDEX_FILE ${SPHINX_OUTPUT_DIR}/index.html) + + # root of sphinx doc tree + set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) + set(SPHINX_DEPS doxygen_${PROJECT_NAME} conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) + + add_custom_command( + OUTPUT ${SPHINX_INDEX_FILE} + DEPENDS ${SPHINX_DEPS} + COMMAND ${SPHINX_EXECUTABLE} + -b html -Dbreathe_projects.xodoxxml=${CMAKE_CURRENT_BINARY_DIR}/dox/xml + ${SPHINX_SOURCE} ${SPHINX_OUTPUT_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating docs (sphinx) -> [${SPHINX_OUTPUT_DIR}]") + + # make sphinx --> generate sphinx documentation + # + add_custom_target( + sphinx_${PROJECT_NAME} + DEPENDS ${SPHINX_INDEX_FILE}) + + # - html docs generated in build/docs/sphinx + # - copy the doc tree to share/doc/xo_unit/html + # + # DESTINATION: CMAKE_INSTALL_DOCDIR + # => DATAROOTDIR/doc/PROJECT_NAME + # => CMAKE_INSTALL_PREFIX/share/doc/xo_flatstring + # OPTIONAL: install directory tree if it exists, + # but don't complain if it's missing + install( + DIRECTORY ${SPHINX_OUTPUT_DIR} + FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + DESTINATION ${CMAKE_INSTALL_DOCDIR} + COMPONENT Documentation + OPTIONAL) + + # make docs --> generate sphinx documentation + add_custom_target( + docs + DEPENDS sphinx_${PROJECT_NAME}) endmacro() @@ -1197,16 +1329,19 @@ macro(xo_headeronly_dependency target dep) # INTERFACE library can only be used with the INTERFACE keyword of # target_link_libraries # Unfortunately target_link_libraries() does not copy dependent's INTERFACE_INCLUDE_DIRECTORIES property - # (at least asof cmake 3.25.3). Dependent's INCLUDE_DIRECTORIES property will be empty, since it's header-only. + # (at least asof cmake 3.25.3, cmake 3.29.2). + # Dependent's INCLUDE_DIRECTORIES property will be empty, since it's header-only. # - # Workaround by copying property explicity, which we do below + # Workaround by copying property explicity, which we do below. + # + # See xo-unit/examples/ex1 for an executable that needs this workaround to build # target_link_libraries(${target} INTERFACE ${dep}) - # get_target_property(xo_dependency_headeronly__tmp ${dep} INTERFACE_INCLUDE_DIRECTORIES) - # set_property( - # TARGET ${target} - # APPEND PROPERTY INCLUDE_DIRECTORIES ${xo_dependency_headeronly__tmp}) + get_target_property(xo_dependency_headeronly__tmp ${dep} INTERFACE_INCLUDE_DIRECTORIES) + set_property( + TARGET ${target} + APPEND PROPERTY INCLUDE_DIRECTORIES ${xo_dependency_headeronly__tmp}) endmacro() # dependency on external (non-xo) namespaced target diff --git a/share/xo-macros/Doxyfile.in b/share/xo-macros/Doxyfile.in index 13dfb0fa..d79a7dd5 100644 --- a/share/xo-macros/Doxyfile.in +++ b/share/xo-macros/Doxyfile.in @@ -2,9 +2,12 @@ # template (to be expanded by cmake) for real doxyfile # @SOMEVAR@ expands to value of cmake variable SOMEVAR # -# expressions to be expanded include: -# @DOX_INPUT_DIR@ -# @DOX_OUTPUT_DIR@ +# expressions to be expanded include (surrounded by @ signs): +# PROJECT_NAME +# DOX_INPUT_DIR +# DOX_OUTPUT_DIR +# DOX_EXCLUDE +# DOX_EXCLUDE_PATTERNS # # if filename is Doxyfile: # expanded template in build directory, to configure doxygen @@ -54,7 +57,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Cmake Examples" +PROJECT_NAME = @PROJECT_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -1054,7 +1057,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = @DOX_EXCLUDE@ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -1070,7 +1073,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = */utest/* +EXCLUDE_PATTERNS = @DOX_EXCLUDE_PATTERNS@ # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the From ac255de1d241c729ae8f301ea4e0ff53c73f4b43 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 5 Jul 2025 16:16:47 -0500 Subject: [PATCH 04/55] xo-cmake: comments on modules vs configs --- cmake/xo_macros/xo_cxx.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 6fa8d9a5..4ab4a86d 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1348,11 +1348,14 @@ endmacro() # e.g. # add_library(foo ..) or add_executable(foo ...) # then -# xo_external_namespaced_dependency(foo Catch2 Catch2::Catch2) +# xo_external_target_dependency(foo Catch2 Catch2::Catch2) # equivalent to # find_package(Catch2 CONFIG REQUIRED) # target_link_libraries(foo PUBLIC Catch2::Catch2) # +# NOTE: this won't work for builtin module targets. For Threads we need just +# find_package(${pkg} Threads) +# macro(xo_external_target_dependency target pkg pkgtarget) message("-- [${target}] find_package(${pkg}) (xo_external_target_dependency)") find_package(${pkg} CONFIG REQUIRED) From ff6c72fe493db8534bc4443d82d712283d556cb6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 6 Jul 2025 14:13:44 -0500 Subject: [PATCH 05/55] xo-reader xo-expression: nested lambdas working properly + docs --- cmake/xo_macros/xo_cxx.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 4ab4a86d..ab32c3f2 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1363,7 +1363,7 @@ macro(xo_external_target_dependency target pkg pkgtarget) #target_link_libraries(${target} ${pkgtarget}) endmacro() -# dependency on external (non-xo) target +# dependency on external (non-XO) target # macro(xo_external_dependency target pkg) xo_external_target_dependency(${target} ${pkg} ${target}) From 205dfd068c954d60d808d788d7e4d1d68eac535b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 13 Jul 2025 21:15:35 -0500 Subject: [PATCH 06/55] xo-cmake: xo-build: + --with-examples --with-utests --debug-build --- bin/xo-build.in | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/bin/xo-build.in b/bin/xo-build.in index 4399932a..378839ac 100644 --- a/bin/xo-build.in +++ b/bin/xo-build.in @@ -3,8 +3,9 @@ usage() { cat < 0 ]]; do case "$1" in @@ -52,7 +62,7 @@ while [[ $# > 0 ]]; do -h | --help) cmd='help' ;; - -n) + -n | --dry-run) noop_flag=1 ;; -S) @@ -93,6 +103,15 @@ while [[ $# > 0 ]]; do --install) install_flag=1 ;; + --with-examples) + with_examples=1 + ;; + --with-utests) + with_utests=1 + ;; + --debug-build) + debug_build=1 + ;; xo-*) xoname="$1" ;; @@ -105,7 +124,7 @@ while [[ $# > 0 ]]; do shift done -echo xoname=$xoname pathtosource=$pathtosource pathtobuild=$pathtobuild +#echo xoname=$xoname pathtosource=$pathtosource pathtobuild=$pathtobuild if [[ -z "$pathtosource" ]]; then pathtosource=$xoname @@ -190,7 +209,22 @@ fi if [[ $configure_flag -eq 1 ]]; then if [[ -n "$xoname" ]]; then - cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild" + testingarg= + if [[ $with_utests -eq 1 ]]; then + testingarg="-DENABLE_TESTING=1" + fi + + examplearg= + if [[ $with_examples -eq 1 ]]; then + examplearg="-DXO_ENABLE_EXAMPLES=1" + fi + + cmakebuildtype= + if [[ $debug_build -eq 1 ]]; then + cmakebuildtype="-DCMAKE_BUILD_TYPE=Debug" + fi + + cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild ${testingarg} ${examplearg} ${cmakebuildtype}" if [[ $noop_flag -eq 1 ]]; then echo $cmd From a19551081d76a3f54f6e8992da8095d85d53afee Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 3 Aug 2025 15:59:38 -0500 Subject: [PATCH 07/55] + xo-alloc + xo-object + xo-alloc docs + GC utests --- etc/xo/subsystem-list | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etc/xo/subsystem-list b/etc/xo/subsystem-list index d1dcf6c7..555bd2e9 100644 --- a/etc/xo/subsystem-list +++ b/etc/xo/subsystem-list @@ -1,5 +1,7 @@ xo-cmake xo-indentlog +xo-alloc +xo-object xo-refcnt xo-subsys xo-randomgen From aff076fa7f2c6d5ea85a1790c8b58ac4a3c8f909 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 12 Aug 2025 00:16:00 -0500 Subject: [PATCH 08/55] xo-alloc: GC mutation log works for full GC --- cmake/xo_macros/xo_cxx.cmake | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index ab32c3f2..771b18e6 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -69,6 +69,7 @@ macro(xo_cxx_toplevel_options3) xo_toplevel_config2() endmacro() +# deprecated, I think? macro(xo_toplevel_testing_options) enable_testing() add_code_coverage() @@ -1358,6 +1359,7 @@ endmacro() # macro(xo_external_target_dependency target pkg pkgtarget) message("-- [${target}] find_package(${pkg}) (xo_external_target_dependency)") + # CONFIG: insist on a ${target}Config.cmake or ${pkgtarget}-config.cmake file find_package(${pkg} CONFIG REQUIRED) target_link_libraries(${target} PUBLIC ${pkgtarget}) #target_link_libraries(${target} ${pkgtarget}) @@ -1369,6 +1371,21 @@ macro(xo_external_dependency target pkg) xo_external_target_dependency(${target} ${pkg} ${target}) endmacro() +# Dependency on external (non-XO) target that provides pkgconfig support. +# Can use this when external package doesn't provide cmake integration. +# +# For example: +# xo_external_pkgconfig_dependency(${MYAPP} IMGUI imgui) +# +macro(xo_external_pkgconfig_dependency target prefix pkg) + message("-- [${target}] invoke pkgconfig for [${pkg}] config (xo_external_pkgconfig_dependency)") + find_package(PkgConfig REQUIRED) + pkg_check_modules(${prefix} REQUIRED ${pkg}) + target_link_libraries(${target} PUBLIC ${${prefix}_LIBRARIES}) + target_include_directories(${target} PUBLIC ${${prefix}_INCLUDE_DIRS}) + target_compile_options(${target} PUBLIC ${${prefix}_CFLAGS_OTHER}) +endmacro() + # dependency on target provided from this codebase. # # 1. don't need find_package() in this case, since details of dep targets From 932a51616af8d6440c5645b3760746e0e34d524a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 22 Sep 2025 12:11:23 -0400 Subject: [PATCH 09/55] xo-indentlog: build: XO_ENABLE_DOCS for docs build --- cmake/xo_macros/xo_cxx.cmake | 244 ++++++++++++++++++----------------- 1 file changed, 128 insertions(+), 116 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 771b18e6..0598f9dc 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1,7 +1,8 @@ +option(XO_ENABLE_DOCS "enable building documentation" OFF) option(XO_ENABLE_EXAMPLES "enable building example programs" OFF) macro(xo_cxx_config_message) - message(STATUS "GUESSED_CMAKE_CMD=cmake -DXO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE} -DENABLE_TESTING=${ENABLE_TESTING} -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_DOCDIR=${CMAKE_INSTALL_DOCDIR} -B ${CMAKE_BINARY_DIR}") + message(STATUS "GUESSED_CMAKE_CMD=cmake -DXO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE} -DENABLE_TESTING=${ENABLE_TESTING} -DXO_ENABLE_DOCS=${XO_ENABLE_DCS} -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_DOCDIR=${CMAKE_INSTALL_DOCDIR} -B ${CMAKE_BINARY_DIR}") message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") message(STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") endmacro() @@ -348,9 +349,65 @@ endmacro() # caller must set DOX_DEPS before invoking xo_toplevel_doxygen_config() # macro(xo_docdir_doxygen_config) - if (XO_SUBMODULE_BUILD) - # in submodule build, rely on toplevel docs/CMakeLists.txt file instead + if (XO_ENABLE_DOCS) + if (XO_SUBMODULE_BUILD) + # in submodule build, rely on toplevel docs/CMakeLists.txt file instead + else() + # look for doxygen executable + find_program(DOXYGEN_EXECUTABLE NAMES doxygen REQUIRED) + message(STATUS "DOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}") + + message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --doxygen-template OUTPUT_VARIABLE DOXYGEN_CONFIG_TEMPLATE) + message(STATUS "DOXYGEN_CONFIG_TEMPLATE=${DOXYGEN_CONFIG_TEMPLATE}") + message(STATUS "DOX_EXCLUDE=${DOX_EXCLUDE}") + message(STATUS "DOX_EXCLUDE_PATTERNS=${DOX_EXCLUDE_PATTERNS}") + + set(DOX_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + set(DOX_INPUT_DIR ${PROJECT_SOURCE_DIR}) + set(DOX_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/dox) + + set(DOX_INDEX_FILE ${DOX_OUTPUT_DIR}/html/index.html) + + # note: expansion variables in Doxyfile.in: + # @PROJECT_NAME@ @DOX_INPUT_DIR@ @DOX_OUTPUT_DIR@ @DOX_EXCLUDE@ @DOX_EXCLUDE_PATTERNS@ + # + configure_file( + ${DOXYGEN_CONFIG_TEMPLATE} ${DOX_CONFIG_FILE} + FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + @ONLY) + + file(MAKE_DIRECTORY ${DOX_OUTPUT_DIR}) + add_custom_command( + OUTPUT ${DOX_INDEX_FILE} + DEPENDS "${DOX_DEPS}" ${DOX_CONFIG_FILE} + COMMAND "${DOXYGEN_EXECUTABLE}" ${DOX_CONFIG_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + MAIN_DEPENDENCY ${DOX_CONFIG_FILE} + COMMENT "Generating docs (doxygen)") + + # To build this target + # $ cmake --build .build -j -- doxygen + # or + # $ cd .build + # $ make doxygen + # + add_custom_target( + doxygen_${PROJECT_NAME} + DEPENDS ${DOX_INDEX_FILE} ${DOX_DEPS} + ) + + endif() else() + message(STATUS, "Docs disabled (cmake -DXO_ENABLE_DOCS=on to enable)") + endif() +endmacro() + +# config for an umbrella project that composes standalone subprojects +macro(xo_umbrella_doxygen_config) + if (XO_ENABLE_DOCS) # look for doxygen executable find_program(DOXYGEN_EXECUTABLE NAMES doxygen REQUIRED) message(STATUS "DOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}") @@ -380,7 +437,7 @@ macro(xo_docdir_doxygen_config) file(MAKE_DIRECTORY ${DOX_OUTPUT_DIR}) add_custom_command( OUTPUT ${DOX_INDEX_FILE} - DEPENDS "${DOX_DEPS}" ${DOX_CONFIG_FILE} + DEPENDS "${DOX_DEPS}" COMMAND "${DOXYGEN_EXECUTABLE}" ${DOX_CONFIG_FILE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} MAIN_DEPENDENCY ${DOX_CONFIG_FILE} @@ -396,59 +453,11 @@ macro(xo_docdir_doxygen_config) doxygen_${PROJECT_NAME} DEPENDS ${DOX_INDEX_FILE} ${DOX_DEPS} ) - + else() + message(STATUS, "Docs disabled (cmake -DXO_ENABLE_DOCS=on to enable)") endif() endmacro() -# config for an umbrella project that composes standalone subprojects -macro(xo_umbrella_doxygen_config) - # look for doxygen executable - find_program(DOXYGEN_EXECUTABLE NAMES doxygen REQUIRED) - message(STATUS "DOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}") - - message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") - - execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --doxygen-template OUTPUT_VARIABLE DOXYGEN_CONFIG_TEMPLATE) - message(STATUS "DOXYGEN_CONFIG_TEMPLATE=${DOXYGEN_CONFIG_TEMPLATE}") - message(STATUS "DOX_EXCLUDE=${DOX_EXCLUDE}") - message(STATUS "DOX_EXCLUDE_PATTERNS=${DOX_EXCLUDE_PATTERNS}") - - set(DOX_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) - - set(DOX_INPUT_DIR ${PROJECT_SOURCE_DIR}) - set(DOX_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/dox) - - set(DOX_INDEX_FILE ${DOX_OUTPUT_DIR}/html/index.html) - - # note: expansion variables in Doxyfile.in: - # @PROJECT_NAME@ @DOX_INPUT_DIR@ @DOX_OUTPUT_DIR@ @DOX_EXCLUDE@ @DOX_EXCLUDE_PATTERNS@ - # - configure_file( - ${DOXYGEN_CONFIG_TEMPLATE} ${DOX_CONFIG_FILE} - FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE - @ONLY) - - file(MAKE_DIRECTORY ${DOX_OUTPUT_DIR}) - add_custom_command( - OUTPUT ${DOX_INDEX_FILE} - DEPENDS "${DOX_DEPS}" - COMMAND "${DOXYGEN_EXECUTABLE}" ${DOX_CONFIG_FILE} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - MAIN_DEPENDENCY ${DOX_CONFIG_FILE} - COMMENT "Generating docs (doxygen)") - - # To build this target - # $ cmake --build .build -j -- doxygen - # or - # $ cd .build - # $ make doxygen - # - add_custom_target( - doxygen_${PROJECT_NAME} - DEPENDS ${DOX_INDEX_FILE} ${DOX_DEPS} - ) -endmacro() - macro(xo_docdir_sphinx_config rst_files) list(APPEND SPHINX_RST_FILES ${rst_files}) @@ -458,12 +467,72 @@ macro(xo_docdir_sphinx_config rst_files) message(STATUS "SPHINX_RST_FILES=${SPHINX_RST_FILES}") - if (XO_SUBMODULE_BUILD) - # in submodule build, rely on toplevel docs/CMakeLists.txt file instead - else() - # build docs starting from here only in standalone build. - # otherwise use top-level doxygen setup. + if (XO_ENABLE_DOCS) + if (XO_SUBMODULE_BUILD) + # in submodule build, rely on toplevel docs/CMakeLists.txt file instead + else() + # build docs starting from here only in standalone build. + # otherwise use top-level doxygen setup. + # look for sphinx-build executable + find_program(SPHINX_EXECUTABLE NAMES sphinx-build REQUIRED) + message(STATUS "SPHINX_EXECUTABLE=${SPHINX_EXECUTABLE}") + + set(SPHINX_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/sphinx/html) + set(SPHINX_INDEX_FILE ${SPHINX_OUTPUT_DIR}/index.html) + + # root of sphinx doc tree + set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) + set(SPHINX_DEPS doxygen_${PROJECT_NAME} conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) + #set(SPHINX_DEPS conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) + + add_custom_command( + OUTPUT ${SPHINX_INDEX_FILE} + DEPENDS ${SPHINX_DEPS} + COMMAND ${SPHINX_EXECUTABLE} + -b html -Dbreathe_projects.xodoxxml=${CMAKE_CURRENT_BINARY_DIR}/dox/xml + ${SPHINX_SOURCE} ${SPHINX_OUTPUT_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating docs (sphinx) -> [${SPHINX_OUTPUT_DIR}]") + + # make sphinx --> generate sphinx documentation + # + add_custom_target( + sphinx_${PROJECT_NAME} + DEPENDS ${SPHINX_INDEX_FILE}) + + # - html docs generated in build/docs/sphinx + # - copy the doc tree to share/doc/xo_unit/html + # + # DESTINATION: CMAKE_INSTALL_DOCDIR + # => DATAROOTDIR/doc/PROJECT_NAME + # => CMAKE_INSTALL_PREFIX/share/doc/xo_flatstring + # OPTIONAL: install directory tree if it exists, + # but don't complain if it's missing + install( + DIRECTORY ${SPHINX_OUTPUT_DIR} + FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + DESTINATION ${CMAKE_INSTALL_DOCDIR} + COMPONENT Documentation + OPTIONAL) + + # make docs --> generate sphinx documentation + add_custom_target( + docs + DEPENDS sphinx_${PROJECT_NAME}) + endif() + endif() +endmacro() + +# config for an umbrella project that composes standalone subprojects +# +macro(xo_umbrella_sphinx_config rst_files) + list(APPEND SPHINX_RST_FILES ${rst_files}) + foreach(arg IN ITEMS ${ARGN}) + list(APPEND SPHINX_RST_FILES ${arg}) + endforeach() + + if (XO_ENABLE_DOCS) # look for sphinx-build executable find_program(SPHINX_EXECUTABLE NAMES sphinx-build REQUIRED) message(STATUS "SPHINX_EXECUTABLE=${SPHINX_EXECUTABLE}") @@ -474,7 +543,6 @@ macro(xo_docdir_sphinx_config rst_files) # root of sphinx doc tree set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) set(SPHINX_DEPS doxygen_${PROJECT_NAME} conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) - #set(SPHINX_DEPS conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) add_custom_command( OUTPUT ${SPHINX_INDEX_FILE} @@ -513,62 +581,6 @@ macro(xo_docdir_sphinx_config rst_files) endif() endmacro() -# config for an umbrella project that composes standalone subprojects -# -macro(xo_umbrella_sphinx_config rst_files) - list(APPEND SPHINX_RST_FILES ${rst_files}) - foreach(arg IN ITEMS ${ARGN}) - list(APPEND SPHINX_RST_FILES ${arg}) - endforeach() - - # look for sphinx-build executable - find_program(SPHINX_EXECUTABLE NAMES sphinx-build REQUIRED) - message(STATUS "SPHINX_EXECUTABLE=${SPHINX_EXECUTABLE}") - - set(SPHINX_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/sphinx/html) - set(SPHINX_INDEX_FILE ${SPHINX_OUTPUT_DIR}/index.html) - - # root of sphinx doc tree - set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) - set(SPHINX_DEPS doxygen_${PROJECT_NAME} conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) - - add_custom_command( - OUTPUT ${SPHINX_INDEX_FILE} - DEPENDS ${SPHINX_DEPS} - COMMAND ${SPHINX_EXECUTABLE} - -b html -Dbreathe_projects.xodoxxml=${CMAKE_CURRENT_BINARY_DIR}/dox/xml - ${SPHINX_SOURCE} ${SPHINX_OUTPUT_DIR} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating docs (sphinx) -> [${SPHINX_OUTPUT_DIR}]") - - # make sphinx --> generate sphinx documentation - # - add_custom_target( - sphinx_${PROJECT_NAME} - DEPENDS ${SPHINX_INDEX_FILE}) - - # - html docs generated in build/docs/sphinx - # - copy the doc tree to share/doc/xo_unit/html - # - # DESTINATION: CMAKE_INSTALL_DOCDIR - # => DATAROOTDIR/doc/PROJECT_NAME - # => CMAKE_INSTALL_PREFIX/share/doc/xo_flatstring - # OPTIONAL: install directory tree if it exists, - # but don't complain if it's missing - install( - DIRECTORY ${SPHINX_OUTPUT_DIR} - FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ - DESTINATION ${CMAKE_INSTALL_DOCDIR} - COMPONENT Documentation - OPTIONAL) - - # make docs --> generate sphinx documentation - add_custom_target( - docs - DEPENDS sphinx_${PROJECT_NAME}) - -endmacro() - macro(xo_toplevel_compile_options) define_property( TARGET From 788da1af34b696e1abf17aacc8ec0b6c1ec4b5cd Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 22 Sep 2025 21:13:20 -0400 Subject: [PATCH 10/55] xo-cmake: xo-build args realclean, with-opengl --- bin/xo-build.in | 51 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/bin/xo-build.in b/bin/xo-build.in index 378839ac..67b24cc9 100644 --- a/bin/xo-build.in +++ b/bin/xo-build.in @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -euo pipefail + usage() { cat < 0 ]]; do case "$1" in @@ -103,6 +111,9 @@ while [[ $# > 0 ]]; do --install) install_flag=1 ;; + --realclean) + realclean_flag=1 + ;; --with-examples) with_examples=1 ;; @@ -112,6 +123,9 @@ while [[ $# > 0 ]]; do --debug-build) debug_build=1 ;; + --with-opengl=*) + with_opengl="${1#*=}" + ;; xo-*) xoname="$1" ;; @@ -131,7 +145,11 @@ if [[ -z "$pathtosource" ]]; then fi if [[ -z "$pathtobuild" ]]; then - pathtobuild=$xoname/.build + if [[ -n $xoname ]]; then + pathtobuild=$xoname/.build + else + pathtobuild=.build + fi fi SUBSYSTEMLIST_FILE=@CMAKE_INSTALL_FULL_DATADIR@/etc/xo/subsystem-list @@ -142,7 +160,7 @@ subsystem_list() { XO_REPO_STEM="https://github.com/Rconybea" -repo() { +printrepo() { xoname=$1 case "$xoname" in @@ -189,13 +207,13 @@ fi if [[ $repo_flag -eq 1 ]]; then if [[ -n "$xoname" ]]; then - repo $xoname + printrepo $xoname fi fi if [[ $clone_flag -eq 1 ]]; then if [[ -n "$xoname" ]]; then - url=$(repo $xoname) + url=$(printrepo $xoname) cmd="git clone $url" @@ -224,7 +242,12 @@ if [[ $configure_flag -eq 1 ]]; then cmakebuildtype="-DCMAKE_BUILD_TYPE=Debug" fi - cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild ${testingarg} ${examplearg} ${cmakebuildtype}" + openglarg= + if [[ -n $with_opengl ]]; then + openglarg="-DXO_ENABLE_OPENGL=${with_opengl}" + fi + + cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild ${testingarg} ${examplearg} ${openglarg} ${cmakebuildtype}" if [[ $noop_flag -eq 1 ]]; then echo $cmd @@ -269,3 +292,11 @@ if [[ $install_flag -eq 1 ]]; then fi fi fi + +if [[ $realclean_flag -eq 1 ]]; then + if [[ $noop_flag -eq 1 ]]; then + echo "rm -rf $pathtobuild" + else + rm -rf "${pathtobuild}" + fi +fi From 6847aa46705587e6758187d7925e98dc2ac5871a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 22 Sep 2025 21:20:09 -0400 Subject: [PATCH 11/55] xo-imgui: opengl imgui ex1 working again on wsl2 (requires etc/hostwsl symlinks) --- cmake/xo_macros/xo_cxx.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 0598f9dc..261a899c 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1,8 +1,10 @@ option(XO_ENABLE_DOCS "enable building documentation" OFF) option(XO_ENABLE_EXAMPLES "enable building example programs" OFF) +option(XO_ENABLE_VULKAN "enable vulkan dependency for imgui apps" OFF) +option(XO_ENABLE_OPENGL "enable opengl dependency for imgui apps" ON) macro(xo_cxx_config_message) - message(STATUS "GUESSED_CMAKE_CMD=cmake -DXO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE} -DENABLE_TESTING=${ENABLE_TESTING} -DXO_ENABLE_DOCS=${XO_ENABLE_DCS} -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_DOCDIR=${CMAKE_INSTALL_DOCDIR} -B ${CMAKE_BINARY_DIR}") + message(STATUS "GUESSED_CMAKE_CMD=cmake -DXO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE} -DENABLE_TESTING=${ENABLE_TESTING} -DXO_ENABLE_DOCS=${XO_ENABLE_DCS} -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} -DXO_ENABLE_VULKAN=${XO_ENABLE_VULKAN} -DXO_ENABLE_OPENGL=${XO_ENABLE_OPENGL} -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_DOCDIR=${CMAKE_INSTALL_DOCDIR} -B ${CMAKE_BINARY_DIR}") message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") message(STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") endmacro() From 000f40bee2297496d15ecb02ac489869961f25fc Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 23 Sep 2025 00:53:05 -0400 Subject: [PATCH 12/55] xo-build: add with-vulkan arg --- bin/xo-build.in | 57 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/bin/xo-build.in b/bin/xo-build.in index 67b24cc9..0cb88b8b 100644 --- a/bin/xo-build.in +++ b/bin/xo-build.in @@ -8,6 +8,7 @@ $0 [-u|--usage|-h|--help] [--list] [-n|--dry-run] [--xoname=NAME] [--repo|--clone|--configure|--build|--install] [--with-examples] [--with-utests] [--debug-build] + [--with-opengl=GLFLAG] [--with-vulkan=VKFLAG] [-S=SOURCEDIR] [-B=BUILDDIR] EOF } @@ -20,19 +21,19 @@ help() { fetch and/or build xo component libraries Options: - -u | --usage brief help message - -h | --help this help message - --list list known xo libraries - -n | --dry-run dry-run: don't actually invoke state-changing commands - --xoname=NAME operate on xo subsystem NAME - --repo report github repo for NAME - --clone git clone xo library NAME in current directory - --configure run cmake for xo library NAME in immediate subdir - Will use NAME/.build as build directory - --build run cmake build for xo library NAME in immediate subdir - --build-docs build documentation for xo library NAME in immediate subdir - --install run cmake install for xo library NAME in immediate subdir - --realclean nuke build directory + -u | --usage brief help message + -h | --help this help message + --list list known xo libraries + -n | --dry-run dry-run: don't actually invoke state-changing commands + --xoname=NAME operate on xo subsystem NAME + --repo report github repo for NAME + --clone git clone xo library NAME in current directory + --configure run cmake for xo library NAME in immediate subdir + Will use NAME/.build as build directory + --build run cmake build for xo library NAME in immediate subdir + --build-docs build documentation for xo library NAME in immediate subdir + --install run cmake install for xo library NAME in immediate subdir + --realclean nuke build directory -S=SOURCEDIR override path/to/source -B=BUILDDIR override path/to/build @@ -40,7 +41,8 @@ Options: --with-examples in configure step, set -DXO_ENABLE_EXAMPLES=1 --with-utests in configure step, set -DENABLE_TESTING=1 --debug-build in configure step, set -DCMAKE_BUILD_TYPE=Debug - --with-opengl=FLAG in configure step, set -DXO_ENABLE_OPENGL=FLAG [ON] + --with-opengl=GLFLAG in configure step, set -DXO_ENABLE_OPENGL=GLFLAG [ON] + --with-vulkan=VKFLAG in configure step, set -DXO_ENABLE_VULKAN=VKFLAG [OFF] EOF } @@ -61,6 +63,7 @@ with_examples=0 with_utests=0 debug_build=0 with_opengl= +with_vulkan= while [[ $# > 0 ]]; do case "$1" in @@ -126,6 +129,9 @@ while [[ $# > 0 ]]; do --with-opengl=*) with_opengl="${1#*=}" ;; + --with-vulkan=*) + with_vulkan="${1#*=}" + ;; xo-*) xoname="$1" ;; @@ -243,15 +249,34 @@ if [[ $configure_flag -eq 1 ]]; then fi openglarg= - if [[ -n $with_opengl ]]; then + if [[ -n ${with_opengl} ]]; then openglarg="-DXO_ENABLE_OPENGL=${with_opengl}" fi - cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild ${testingarg} ${examplearg} ${openglarg} ${cmakebuildtype}" + vulkanarg= + if [[ -n ${with_vulkan} ]]; then + vulkanarg="-DXO_ENABLE_VULKAN=${with_vulkan}" + fi + + cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild ${testingarg} ${examplearg} ${openglarg} ${vulkanarg} ${cmakebuildtype}" if [[ $noop_flag -eq 1 ]]; then echo $cmd else + cwd=$(pwd) + # write script to re-run cmake later + rebootcmake=$pathtobuild/rebootcmake + + mkdir -p $(dirname ${rebootcmake}) + + cat > ${rebootcmake} < Date: Wed, 10 Dec 2025 20:17:21 -0500 Subject: [PATCH 13/55] xo-facet: header impl + docs --- cmake/xo_macros/xo_cxx.cmake | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 261a899c..2e4ed18e 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -471,7 +471,25 @@ macro(xo_docdir_sphinx_config rst_files) if (XO_ENABLE_DOCS) if (XO_SUBMODULE_BUILD) - # in submodule build, rely on toplevel docs/CMakeLists.txt file instead + # in submodule build, rely on toplevel docs/CMakeLists.txt file instead. + # + # translate ${rst_files} to absolute paths + # + set(SPHINX_ABS_RST_FILES) + foreach(rst_file ${SPHINX_RST_FILES}) + get_filename_component( + abs_path "${rst_file}" + ABSOLUTE + BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + list(APPEND SPHINX_ABS_RST_FILES "${abs_path}") + endforeach() + + # append to global property + set_property(GLOBAL APPEND + PROPERTY XO_UMBRELLA_SPHINX_RST_FILES + ${SPHINX_ABS_RST_FILES}) + + message(STATUS "SPHINX_ABS_RST_FILES=${SPHINX_ABS_RST_FILES}") else() # build docs starting from here only in standalone build. # otherwise use top-level doxygen setup. @@ -529,6 +547,7 @@ endmacro() # config for an umbrella project that composes standalone subprojects # macro(xo_umbrella_sphinx_config rst_files) + # here SPHINX_RST_FILES refers to toplevel-only .rst files in umbrella project list(APPEND SPHINX_RST_FILES ${rst_files}) foreach(arg IN ITEMS ${ARGN}) list(APPEND SPHINX_RST_FILES ${arg}) @@ -539,12 +558,17 @@ macro(xo_umbrella_sphinx_config rst_files) find_program(SPHINX_EXECUTABLE NAMES sphinx-build REQUIRED) message(STATUS "SPHINX_EXECUTABLE=${SPHINX_EXECUTABLE}") + get_property(SPHINX_ABS_RST_FILES GLOBAL PROPERTY XO_UMBRELLA_SPHINX_RST_FILES) + message(STATUS "SPHINX_ABS_RST_FILES=${SPHINX_ABS_RST_FILES}") + set(SPHINX_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/sphinx/html) set(SPHINX_INDEX_FILE ${SPHINX_OUTPUT_DIR}/index.html) # root of sphinx doc tree set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) - set(SPHINX_DEPS doxygen_${PROJECT_NAME} conf.py ${SPHINX_RST_FILES} ${SPHINX_RST_FILES_GLOB} ${DOX_DEPS}) + # SPHINX_RST_FILES: top-level .rst files in umbrella project + # SPHINX_ABS_RST_FILES: satellite .rst files, collected via XO_SUBMODULE_BUILD, rewritten to absoluate paths + set(SPHINX_DEPS doxygen_${PROJECT_NAME} conf.py ${SPHINX_RST_FILES} ${SPHINX_ABS_RST_FILES} ${DOX_DEPS}) add_custom_command( OUTPUT ${SPHINX_INDEX_FILE} From 0956033dfbbba24b4ab140599d0ddef29402f5c3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 26 Dec 2025 02:09:25 -0500 Subject: [PATCH 14/55] xo-facet xo-object2 xo-cmake: facet tidy + build integration --- cmake/xo_macros/xo_cxx.cmake | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 2e4ed18e..d491d686 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1619,3 +1619,49 @@ endmacro() macro(xo_pybind11_header_dependency target dep) xo_dependency_helper(${target} PUBLIC ${dep}) endmacro() + +# ---------------------------------------------------------------- +# use this to streamline generating .hpp / .cpp scaffolding +# for faceted object model +# + +macro(xo_add_genfacet) + # Parse arguments + set(options "") + set(oneValueArgs + TARGET # Name for this generation target + FACET # facet name + INPUT # Input .json5 file + OUTPUT_HPP_DIR # Directory for .hpp files + OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers + OUTPUT_CPP_DIR # Directory for .cpp files + ) + set(multiValueArgs "") + + cmake_parse_arguments(GF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Build the genfacet command + add_custom_command( + OUTPUT ${GF_OUTPUT_HPP_DIR}/${FACET}.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/A${FACET}.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Any.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Xfer.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${FACET}.hpp + ${GF_OUTPUT_CPP_DIR}/I${FACET}_Any.cpp + COMMAND ${CMAKE_SOURCE_DIR}/xo-facet/codegen/genfacet + --input ${GF_INPUT} + --output-hpp ${GF_OUTPUT_HPP_DIR} + --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} + --output-cpp ${GF_OUTPUT_CPP_DIR} + --templates ${CMAKE_SOURCE_DIR}/xo-facet/codegen + DEPENDS ${GF_INPUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating facet source files from ${GF_INPUT}" + VERBATIM + ) + + # Create a target for this generation + add_custom_target(${GF_TARGET} + DEPENDS ${GF_GENERATED_FILES} + ) +endmacro() From 2c0970645ff03972aad987ff98ebfa399d2967c5 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 26 Dec 2025 02:18:16 -0500 Subject: [PATCH 15/55] xo-facet xo-object2 facet gen bugfixes --- cmake/xo_macros/xo_cxx.cmake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index d491d686..ba43b34e 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1640,6 +1640,12 @@ macro(xo_add_genfacet) cmake_parse_arguments(GF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + find_program(GENFACET_EXECUTABLE NAMES genfacet + HINTS ${CMAKE_SOURCE_DIR}/xo-facet/codegen + DOC "path to xo genfacet code generator" + REQUIRED) + message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") + # Build the genfacet command add_custom_command( OUTPUT ${GF_OUTPUT_HPP_DIR}/${FACET}.hpp @@ -1648,12 +1654,11 @@ macro(xo_add_genfacet) ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Xfer.hpp ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${FACET}.hpp ${GF_OUTPUT_CPP_DIR}/I${FACET}_Any.cpp - COMMAND ${CMAKE_SOURCE_DIR}/xo-facet/codegen/genfacet + COMMAND ${GENFACET_EXECUTABLE} --input ${GF_INPUT} --output-hpp ${GF_OUTPUT_HPP_DIR} --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} --output-cpp ${GF_OUTPUT_CPP_DIR} - --templates ${CMAKE_SOURCE_DIR}/xo-facet/codegen DEPENDS ${GF_INPUT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating facet source files from ${GF_INPUT}" From 569ce43355555e112fa28a4f876c719f9ca59460 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 29 Dec 2025 14:32:52 -0500 Subject: [PATCH 16/55] xo-gc xo-object2 xo-facet: builds w/ ISequence,Dlist --- cmake/xo_macros/xo_cxx.cmake | 64 +++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index ba43b34e..182f1599 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1646,14 +1646,17 @@ macro(xo_add_genfacet) REQUIRED) message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") + set(generatedFiles + ${GF_OUTPUT_HPP_DIR}/${FACET}.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/A${FACET}.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Any.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Xfer.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${FACET}.hpp + ${GF_OUTPUT_CPP_DIR}/I${FACET}_Any.cpp) + # Build the genfacet command add_custom_command( - OUTPUT ${GF_OUTPUT_HPP_DIR}/${FACET}.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/A${FACET}.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Any.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Xfer.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${FACET}.hpp - ${GF_OUTPUT_CPP_DIR}/I${FACET}_Any.cpp + OUTPUT $generatedFiles COMMAND ${GENFACET_EXECUTABLE} --input ${GF_INPUT} --output-hpp ${GF_OUTPUT_HPP_DIR} @@ -1666,7 +1669,50 @@ macro(xo_add_genfacet) ) # Create a target for this generation - add_custom_target(${GF_TARGET} - DEPENDS ${GF_GENERATED_FILES} - ) + add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) +endmacro() + +macro(xo_add_genfacetimpl) + # Parse arguments + set(options "") + set(oneValueArgs + TARGET # Name for this generation target + FACET # facet name + REPR # representation name + INPUT # Input .json5 file + OUTPUT_HPP_DIR # Directory for .hpp files + OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers + OUTPUT_CPP_DIR # Directory for .cpp files + ) + set(multiValueArgs "") + + cmake_parse_arguments(GF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + find_program(GENFACET_EXECUTABLE NAMES genfacet + HINTS ${CMAKE_SOURCE_DIR}/xo-facet/codegen + DOC "path to xo genfacet code generator" + REQUIRED) + message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") + + set(generatedFiles + ${GF_OUTPUT_HPP_DIR}/${FACET}.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_D${REPR}.hpp + ${GF_OUTPUT_CPP_DIR}/I${FACET}_D${REPR}.cpp) + + # Build the genfacet command + add_custom_command( + OUTPUT ${generatedFiles} + COMMAND ${GENFACET_EXECUTABLE} + --input ${GF_INPUT} + --output-hpp ${GF_OUTPUT_HPP_DIR} + --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} + --output-cpp ${GF_OUTPUT_CPP_DIR} + DEPENDS ${GF_INPUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating facet source files from ${GF_INPUT}" + VERBATIM + ) + + # Create a target for this generation + add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) endmacro() From 4c086fe1e612b7747537c261cdf084550ece194d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 4 Jan 2026 23:03:18 -0500 Subject: [PATCH 17/55] + xo-printable2 + build fixes for cmake config --- cmake/xo_macros/xo_cxx.cmake | 152 +++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 51 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 182f1599..c872c927 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -64,6 +64,11 @@ macro(xo_cxx_toplevel_options2) set_property( TARGET docs_${PROJECT_NAME} PROPERTY targets "") + + add_custom_target(idl_${PROJECT_NAME}) + set_property( + TARGET idl_${PROJECT_NAME} + PROPERTY path ${PROJECT_SOURCE_DIR}/idl) endif() endmacro() @@ -1625,7 +1630,7 @@ endmacro() # for faceted object model # -macro(xo_add_genfacet) +function(xo_add_genfacet) # Parse arguments set(options "") set(oneValueArgs @@ -1638,8 +1643,26 @@ macro(xo_add_genfacet) ) set(multiValueArgs "") + message(STATUS "oneValueArgs=${oneValueArgs}") + cmake_parse_arguments(GF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(NOT DEFINED GF_TARGET) + message(FATAL_ERROR "xo_add_genfacet: TARGET is required") + endif() + if(NOT DEFINED GF_FACET) + message(FATAL_ERROR "xo_add_genfacet: FACET is required") + endif() + if(NOT DEFINED GF_OUTPUT_HPP_DIR) + message(FATAL_ERROR "xo_add_genfacet: OUTPUT_HPP_DIR is required") + endif() + if(NOT DEFINED GF_OUTPUT_IMPL_SUBDIR) + message(FATAL_ERROR "xo_add_genfacet: OUTPUT_IMPL_SUBDIR is required") + endif() + if(NOT DEFINED GF_OUTPUT_CPP_DIR) + message(FATAL_ERROR "xo_add_genfacet: OUTPUT_CPP_DIR is required") + endif() + find_program(GENFACET_EXECUTABLE NAMES genfacet HINTS ${CMAKE_SOURCE_DIR}/xo-facet/codegen DOC "path to xo genfacet code generator" @@ -1647,57 +1670,16 @@ macro(xo_add_genfacet) message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") set(generatedFiles - ${GF_OUTPUT_HPP_DIR}/${FACET}.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/A${FACET}.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Any.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_Xfer.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${FACET}.hpp - ${GF_OUTPUT_CPP_DIR}/I${FACET}_Any.cpp) + ${GF_OUTPUT_HPP_DIR}/${GF_FACET}.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/A${GF_FACET}.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_Any.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_Xfer.hpp + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${GF_FACET}.hpp + ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_Any.cpp) - # Build the genfacet command - add_custom_command( - OUTPUT $generatedFiles - COMMAND ${GENFACET_EXECUTABLE} - --input ${GF_INPUT} - --output-hpp ${GF_OUTPUT_HPP_DIR} - --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} - --output-cpp ${GF_OUTPUT_CPP_DIR} - DEPENDS ${GF_INPUT} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Generating facet source files from ${GF_INPUT}" - VERBATIM - ) + message(STATUS "generatedFiles=${generatedFiles}") - # Create a target for this generation - add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) -endmacro() - -macro(xo_add_genfacetimpl) - # Parse arguments - set(options "") - set(oneValueArgs - TARGET # Name for this generation target - FACET # facet name - REPR # representation name - INPUT # Input .json5 file - OUTPUT_HPP_DIR # Directory for .hpp files - OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers - OUTPUT_CPP_DIR # Directory for .cpp files - ) - set(multiValueArgs "") - - cmake_parse_arguments(GF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - find_program(GENFACET_EXECUTABLE NAMES genfacet - HINTS ${CMAKE_SOURCE_DIR}/xo-facet/codegen - DOC "path to xo genfacet code generator" - REQUIRED) - message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") - - set(generatedFiles - ${GF_OUTPUT_HPP_DIR}/${FACET}.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${FACET}_D${REPR}.hpp - ${GF_OUTPUT_CPP_DIR}/I${FACET}_D${REPR}.cpp) + message(ERROR "epic fail") # Build the genfacet command add_custom_command( @@ -1715,4 +1697,72 @@ macro(xo_add_genfacetimpl) # Create a target for this generation add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) -endmacro() +endfunction() + +function(xo_add_genfacetimpl) + # Parse arguments + set(options "") + set(oneValueArgs + TARGET # Name for this generation target + FACET # facet name + REPR # representation name + INPUT # Input .json5 file + OUTPUT_HPP_DIR # Directory for .hpp files + OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers + OUTPUT_CPP_DIR # Directory for .cpp files + ) + set(multiValueArgs "") + + cmake_parse_arguments(GF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT DEFINED GF_TARGET) + message(FATAL_ERROR "xo_add_genfacetimpl: TARGET is required") + endif() + if(NOT DEFINED GF_FACET) + message(FATAL_ERROR "xo_add_genfacetimpl: FACET is required") + endif() + if(NOT DEFINED GF_REPR) + message(FATAL_ERROR "xo_add_genfacetimpl: REPR is required") + endif() + if(NOT DEFINED GF_INPUT) + message(FATAL_ERROR "xo_add_genfacetimpl: INPUT is required") + endif() + if(NOT DEFINED GF_OUTPUT_HPP_DIR) + message(FATAL_ERROR "xo_add_genfacetimpl: OUTPUT_HPP_DIR is required") + endif() + if(NOT DEFINED GF_OUTPUT_IMPL_SUBDIR) + message(FATAL_ERROR "xo_add_genfacetimpl: OUTPUT_IMPL_SUBDIR is required") + endif() + if(NOT DEFINED GF_OUTPUT_CPP_DIR) + message(FATAL_ERROR "xo_add_genfacetimpl: OUTPUT_CPP_DIR is required") + endif() + + find_program(GENFACET_EXECUTABLE NAMES genfacet + HINTS ${CMAKE_SOURCE_DIR}/xo-facet/codegen + DOC "path to xo genfacet code generator" + REQUIRED) + message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") + + set(generatedFiles + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_D${GF_REPR}.hpp + ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_D${GF_REPR}.cpp) + + # Build the genfacet command. + # But careful: can't have the same generated files in two different rules, + # so need to remove overlaps here + add_custom_command( + OUTPUT ${generatedFiles} + COMMAND ${GENFACET_EXECUTABLE} + --input ${GF_INPUT} + --output-hpp ${GF_OUTPUT_HPP_DIR} + --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} + --output-cpp ${GF_OUTPUT_CPP_DIR} + DEPENDS ${GF_INPUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating facet source files from ${GF_INPUT}" + VERBATIM + ) + + # Create a target for this generation + add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) +endfunction() From 361d8cb4f9cfad8fc80869407a4ac58fb771dec4 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Jan 2026 11:53:44 -0500 Subject: [PATCH 18/55] xo-object2: APrintable+DFloat --- cmake/xo_macros/xo_cxx.cmake | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index c872c927..dd382e7f 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -65,10 +65,10 @@ macro(xo_cxx_toplevel_options2) TARGET docs_${PROJECT_NAME} PROPERTY targets "") - add_custom_target(idl_${PROJECT_NAME}) + add_custom_target(share_${PROJECT_NAME}) set_property( - TARGET idl_${PROJECT_NAME} - PROPERTY path ${PROJECT_SOURCE_DIR}/idl) + TARGET share_${PROJECT_NAME} + PROPERTY path ${PROJECT_SOURCE_DIR}) endif() endmacro() @@ -1677,9 +1677,7 @@ function(xo_add_genfacet) ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${GF_FACET}.hpp ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_Any.cpp) - message(STATUS "generatedFiles=${generatedFiles}") - - message(ERROR "epic fail") + #message(STATUS "generatedFiles=${generatedFiles}") # Build the genfacet command add_custom_command( @@ -1704,6 +1702,7 @@ function(xo_add_genfacetimpl) set(options "") set(oneValueArgs TARGET # Name for this generation target + FACET_DIR # facet directory FACET # facet name REPR # representation name INPUT # Input .json5 file @@ -1754,6 +1753,7 @@ function(xo_add_genfacetimpl) OUTPUT ${generatedFiles} COMMAND ${GENFACET_EXECUTABLE} --input ${GF_INPUT} + --facet-dir ${GF_FACET_DIR} --output-hpp ${GF_OUTPUT_HPP_DIR} --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} --output-cpp ${GF_OUTPUT_CPP_DIR} From eb960a5fc8c3ff8219c9e765712e757e5990bb88 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Jan 2026 12:09:42 -0500 Subject: [PATCH 19/55] xo-cmake: streamline facet gen --- cmake/xo_macros/xo_cxx.cmake | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index dd382e7f..2b916698 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1702,7 +1702,8 @@ function(xo_add_genfacetimpl) set(options "") set(oneValueArgs TARGET # Name for this generation target - FACET_DIR # facet directory + FACET_PKG # package providing abstract interface + FACET_DIR # facet directory (instead of FACET_PKG) FACET # facet name REPR # representation name INPUT # Input .json5 file @@ -1736,6 +1737,15 @@ function(xo_add_genfacetimpl) message(FATAL_ERROR "xo_add_genfacetimpl: OUTPUT_CPP_DIR is required") endif() + if(NOT DEFINED GF_FACET_DIR) + if (NOT DEFINED GF_FACET_PKG) + message(FATAL_ERROR "xo_add_genfacetimpl: FACET_PKG or FACET_DIR required") + else() + get_target_property(_facet_dir share_${GF_FACET_PKG} path) + set(GF_FACET_DIR ${_facet_dir}) + endif() + endif() + find_program(GENFACET_EXECUTABLE NAMES genfacet HINTS ${CMAKE_SOURCE_DIR}/xo-facet/codegen DOC "path to xo genfacet code generator" From f6040fb765d461356f6a37be0535f6bb42a10b51 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Jan 2026 22:15:33 -0500 Subject: [PATCH 20/55] xo-arena: empty scaffold (builds, but empty!) [WIP] --- bin/scaffold-headeronly | 167 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100755 bin/scaffold-headeronly diff --git a/bin/scaffold-headeronly b/bin/scaffold-headeronly new file mode 100755 index 00000000..636dad47 --- /dev/null +++ b/bin/scaffold-headeronly @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +# +# scaffold-headeronly.sh - Create a new xo header-only subdirectory +# +# Usage: scaffold-headeronly.sh +# where is the short name (e.g., "arena" creates "xo-arena") +# + +set -euo pipefail + +usage() { + echo "Usage: $0 " + echo " Creates xo-/ with header-only library scaffolding" + echo "" + echo "Example: $0 arena" + echo " Creates xo-arena/CMakeLists.txt" + echo " xo-arena/cmake/xo-bootstrap-macros.cmake" + echo " xo-arena/cmake/xo_arenaConfig.cmake.in" + echo " xo-arena/include/xo/arena/" + exit 1 +} + +if [[ $# -ne 1 ]]; then + usage +fi + +NAME="$1" + +# Validate name: alphanumeric and underscores only +if [[ ! "$NAME" =~ ^[a-zA-Z][a-zA-Z0-9_]*$ ]]; then + echo "Error: name must start with letter and contain only alphanumeric/underscores" + exit 1 +fi + +# Derive names +DIR_NAME="xo-${NAME}" +LIB_NAME="xo_${NAME}" +PROJECT_NAME="xo_${NAME}" + +# Get script directory to find umbrella root +XO_UMBRELLA_ROOT=$(git rev-parse --show-toplevel) + +TARGET_DIR="${XO_UMBRELLA_ROOT}/${DIR_NAME}" + +if [[ -d "$TARGET_DIR" ]]; then + echo "Error: ${TARGET_DIR} already exists" + exit 1 +fi + +echo "Creating ${DIR_NAME} in ${XO_UMBRELLA_ROOT}..." + +# Create directories +mkdir -p "${TARGET_DIR}/cmake" +mkdir -p "${TARGET_DIR}/include/xo/${NAME}" + +# Create CMakeLists.txt +cat > "${TARGET_DIR}/CMakeLists.txt" << EOF +# ${DIR_NAME}/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(${PROJECT_NAME} VERSION 1.0) +enable_language(CXX) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings + +# one-time project-specific c++ flags. usually empty +set(PROJECT_CXX_FLAGS "") +add_definitions(\${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- +# output targets + +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# header-only library + +set(SELF_LIB ${LIB_NAME}) +xo_add_headeronly_library(\${SELF_LIB}) +xo_install_library4(\${SELF_LIB} \${PROJECT_NAME}Targets) +xo_export_cmake_config(\${PROJECT_NAME} \${PROJECT_VERSION} \${PROJECT_NAME}Targets) + +# ---------------------------------------------------------------- +# input dependencies +# +# NOTE: dependency set here must be kept consistent with +# ${DIR_NAME}/cmake/${LIB_NAME}Config.cmake.in + +#xo_headeronly_dependency(\${SELF_LIB} xo_flatstring) + +# end CMakeLists.txt +EOF + +# Create cmake/xo-bootstrap-macros.cmake +cat > "${TARGET_DIR}/cmake/xo-bootstrap-macros.cmake" << EOF +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=\$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if (("\${CMAKE_MODULE_PATH}" STREQUAL "") OR ("\${CMAKE_MODULE_PATH}" STREQUAL "prefix")) + message(FATAL "could not find xo-cmake-config executable") +endif() + +if (NOT XO_SUBMODULE_BUILD) + if (("\${CMAKE_MODULE_PATH}" STREQUAL "") OR ("\${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND \${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=\${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() +EOF + +# Create cmake/xo_${NAME}Config.cmake.in +cat > "${TARGET_DIR}/cmake/${LIB_NAME}Config.cmake.in" << EOF +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +# note: changes to find_dependency() calls here +# must coordinate with xo_dependency() calls +# in CMakeLists.txt +# +#find_dependency(xo_flatstring) + +include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") +EOF + +# Create placeholder .gitkeep in include dir +touch "${TARGET_DIR}/include/xo/${NAME}/.gitkeep" + +echo "" +echo "Created ${DIR_NAME}/ with:" +echo " ${DIR_NAME}/CMakeLists.txt" +echo " ${DIR_NAME}/cmake/xo-bootstrap-macros.cmake" +echo " ${DIR_NAME}/cmake/${LIB_NAME}Config.cmake.in" +echo " ${DIR_NAME}/include/xo/${NAME}/" +echo "" +echo "Next steps:" +echo " 1. Add header files to ${DIR_NAME}/include/xo/${NAME}/" +echo " 2. Add ${DIR_NAME} to umbrella CMakeLists.txt" +echo " 3. Uncomment dependencies as needed in CMakeLists.txt and Config.cmake.in" From f1d5541354017c022b60182dfdea82bd75bd42e2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Jan 2026 22:25:23 -0500 Subject: [PATCH 21/55] xo-cmake: scaffold-headeronly works for shared libs also --- bin/scaffold-headeronly | 137 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 11 deletions(-) diff --git a/bin/scaffold-headeronly b/bin/scaffold-headeronly index 636dad47..b4455677 100755 --- a/bin/scaffold-headeronly +++ b/bin/scaffold-headeronly @@ -1,25 +1,63 @@ #!/usr/bin/env bash # -# scaffold-headeronly.sh - Create a new xo header-only subdirectory +# scaffold-headeronly - Create a new xo library subdirectory # -# Usage: scaffold-headeronly.sh +# Usage: scaffold-headeronly [--mode=headeronly|shared] # where is the short name (e.g., "arena" creates "xo-arena") # set -euo pipefail usage() { - echo "Usage: $0 " - echo " Creates xo-/ with header-only library scaffolding" + echo "Usage: $0 [--mode=headeronly|shared] " + echo " Creates xo-/ with library scaffolding" + echo "" + echo "Options:" + echo " --mode=headeronly Create header-only library (default)" + echo " --mode=shared Create shared library with src/ directory" echo "" echo "Example: $0 arena" echo " Creates xo-arena/CMakeLists.txt" echo " xo-arena/cmake/xo-bootstrap-macros.cmake" echo " xo-arena/cmake/xo_arenaConfig.cmake.in" echo " xo-arena/include/xo/arena/" + echo "" + echo "Example: $0 --mode=shared mylib" + echo " Creates xo-mylib/CMakeLists.txt" + echo " xo-mylib/cmake/xo-bootstrap-macros.cmake" + echo " xo-mylib/cmake/xo_mylibConfig.cmake.in" + echo " xo-mylib/include/xo/mylib/" + echo " xo-mylib/src/CMakeLists.txt" exit 1 } +# Default mode +MODE="headeronly" + +# Parse arguments +while [[ $# -gt 0 ]]; do + case "$1" in + --mode=*) + MODE="${1#--mode=}" + if [[ "$MODE" != "headeronly" && "$MODE" != "shared" ]]; then + echo "Error: --mode must be 'headeronly' or 'shared'" + exit 1 + fi + shift + ;; + --help|-h) + usage + ;; + -*) + echo "Error: unknown option $1" + usage + ;; + *) + break + ;; + esac +done + if [[ $# -ne 1 ]]; then usage fi @@ -47,14 +85,19 @@ if [[ -d "$TARGET_DIR" ]]; then exit 1 fi -echo "Creating ${DIR_NAME} in ${XO_UMBRELLA_ROOT}..." +echo "Creating ${DIR_NAME} (${MODE}) in ${XO_UMBRELLA_ROOT}..." # Create directories mkdir -p "${TARGET_DIR}/cmake" mkdir -p "${TARGET_DIR}/include/xo/${NAME}" -# Create CMakeLists.txt -cat > "${TARGET_DIR}/CMakeLists.txt" << EOF +if [[ "$MODE" == "shared" ]]; then + mkdir -p "${TARGET_DIR}/src" +fi + +# Create CMakeLists.txt (different content based on mode) +if [[ "$MODE" == "headeronly" ]]; then + cat > "${TARGET_DIR}/CMakeLists.txt" << EOF # ${DIR_NAME}/CMakeLists.txt cmake_minimum_required(VERSION 3.10) @@ -97,6 +140,42 @@ xo_export_cmake_config(\${PROJECT_NAME} \${PROJECT_VERSION} \${PROJECT_NAME}Targ # end CMakeLists.txt EOF +else + # Shared library mode + cat > "${TARGET_DIR}/CMakeLists.txt" << EOF +# ${DIR_NAME}/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(${PROJECT_NAME} VERSION 1.0) +enable_language(CXX) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings + +# one-time project-specific c++ flags. usually empty +set(PROJECT_CXX_FLAGS "") +add_definitions(\${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- +# output targets + +add_subdirectory(src) +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# cmake export + +xo_export_cmake_config(\${PROJECT_NAME} \${PROJECT_VERSION} \${PROJECT_NAME}Targets) + +# end CMakeLists.txt +EOF +fi # Create cmake/xo-bootstrap-macros.cmake cat > "${TARGET_DIR}/cmake/xo-bootstrap-macros.cmake" << EOF @@ -151,17 +230,53 @@ include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") EOF +# Create src/CMakeLists.txt for shared library mode +if [[ "$MODE" == "shared" ]]; then + cat > "${TARGET_DIR}/src/CMakeLists.txt" << EOF +# ${DIR_NAME}/src/CMakeLists.txt + +set(SELF_LIB ${LIB_NAME}) +set(SELF_SRCS + # Add source files here, e.g.: + # ${NAME}.cpp +) + +xo_add_shared_library4(\${SELF_LIB} \${PROJECT_NAME}Targets \${PROJECT_VERSION} 1 \${SELF_SRCS}) +xo_install_include_tree3(include/xo/${NAME}) + +# ---------------------------------------------------------------- +# input dependencies +# +# NOTE: dependency set here must be kept consistent with +# ${DIR_NAME}/cmake/${LIB_NAME}Config.cmake.in + +#xo_dependency(\${SELF_LIB} xo_indentlog) + +# end src/CMakeLists.txt +EOF +fi + # Create placeholder .gitkeep in include dir touch "${TARGET_DIR}/include/xo/${NAME}/.gitkeep" echo "" -echo "Created ${DIR_NAME}/ with:" +echo "Created ${DIR_NAME}/ (${MODE}) with:" echo " ${DIR_NAME}/CMakeLists.txt" echo " ${DIR_NAME}/cmake/xo-bootstrap-macros.cmake" echo " ${DIR_NAME}/cmake/${LIB_NAME}Config.cmake.in" echo " ${DIR_NAME}/include/xo/${NAME}/" +if [[ "$MODE" == "shared" ]]; then + echo " ${DIR_NAME}/src/CMakeLists.txt" +fi echo "" echo "Next steps:" -echo " 1. Add header files to ${DIR_NAME}/include/xo/${NAME}/" -echo " 2. Add ${DIR_NAME} to umbrella CMakeLists.txt" -echo " 3. Uncomment dependencies as needed in CMakeLists.txt and Config.cmake.in" +if [[ "$MODE" == "headeronly" ]]; then + echo " 1. Add header files to ${DIR_NAME}/include/xo/${NAME}/" + echo " 2. Add ${DIR_NAME} to umbrella CMakeLists.txt" + echo " 3. Uncomment dependencies as needed in CMakeLists.txt and Config.cmake.in" +else + echo " 1. Add header files to ${DIR_NAME}/include/xo/${NAME}/" + echo " 2. Add source files to ${DIR_NAME}/src/ and update src/CMakeLists.txt SELF_SRCS" + echo " 3. Add ${DIR_NAME} to umbrella CMakeLists.txt" + echo " 4. Uncomment dependencies as needed in src/CMakeLists.txt and Config.cmake.in" +fi From 5b5381b887ec320974e86a5ab5db9b3759c98200 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 9 Jan 2026 20:12:02 -0500 Subject: [PATCH 22/55] tooling: rename scaffold-headeronly -> scaffold-subdir --- bin/{scaffold-headeronly => scaffold-subdir} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bin/{scaffold-headeronly => scaffold-subdir} (100%) diff --git a/bin/scaffold-headeronly b/bin/scaffold-subdir similarity index 100% rename from bin/scaffold-headeronly rename to bin/scaffold-subdir From f628351d04e19d0a60b45753637e76e896acc09b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 11 Jan 2026 12:32:41 -0500 Subject: [PATCH 23/55] cosmetic --- bin/scaffold-subdir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/scaffold-subdir b/bin/scaffold-subdir index b4455677..4cfe54a6 100755 --- a/bin/scaffold-subdir +++ b/bin/scaffold-subdir @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# scaffold-headeronly - Create a new xo library subdirectory +# scaffold-subdir - Create a new xo library subdirectory # # Usage: scaffold-headeronly [--mode=headeronly|shared] # where is the short name (e.g., "arena" creates "xo-arena") From 4ba9cab69e42a0f84c5b79820c4bef36ccdef70b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 11 Jan 2026 12:57:24 -0500 Subject: [PATCH 24/55] xo-cmake: + XO_ENABLE_ASM option to keep assembler output --- cmake/xo_macros/xo_cxx.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 2b916698..31e8b879 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -2,6 +2,7 @@ option(XO_ENABLE_DOCS "enable building documentation" OFF) option(XO_ENABLE_EXAMPLES "enable building example programs" OFF) option(XO_ENABLE_VULKAN "enable vulkan dependency for imgui apps" OFF) option(XO_ENABLE_OPENGL "enable opengl dependency for imgui apps" ON) +option(XO_ENABLE_ASM "generate assembler output (.s files)" OFF) macro(xo_cxx_config_message) message(STATUS "GUESSED_CMAKE_CMD=cmake -DXO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE} -DENABLE_TESTING=${ENABLE_TESTING} -DXO_ENABLE_DOCS=${XO_ENABLE_DCS} -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} -DXO_ENABLE_VULKAN=${XO_ENABLE_VULKAN} -DXO_ENABLE_OPENGL=${XO_ENABLE_OPENGL} -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_DOCDIR=${CMAKE_INSTALL_DOCDIR} -B ${CMAKE_BINARY_DIR}") @@ -219,6 +220,7 @@ macro(xo_toplevel_config2) xo_toplevel_debug_config2() xo_toplevel_asan_config2() xo_toplevel_coverage_config2() + xo_toplevel_asm_config2() endmacro() # coverage build: @@ -261,6 +263,13 @@ macro(xo_toplevel_coverage_config2) endif() endmacro() +macro(xo_toplevel_asm_config2) + if(XO_ENABLE_ASM) + message(STATUS "enabling assembler output (-save-temps=obj -fverbose-asm)") + add_compile_options(-save-temps=obj -fverbose-asm) + endif() +endmacro() + # target to build+install coverage report. # macro(xo_utest_coverage_config2) From 11f5b1119b4806a4cc407f9384cbf09cbacd723c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 11 Jan 2026 13:01:52 -0500 Subject: [PATCH 25/55] xo-build: + --with-asm argument --- bin/xo-build.in | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/bin/xo-build.in b/bin/xo-build.in index 0cb88b8b..9c36b434 100644 --- a/bin/xo-build.in +++ b/bin/xo-build.in @@ -7,7 +7,7 @@ usage() { $0 [-u|--usage|-h|--help] [--list] [-n|--dry-run] [--xoname=NAME] [--repo|--clone|--configure|--build|--install] - [--with-examples] [--with-utests] [--debug-build] + [--with-examples] [--with-utests] [--with-asm] [--debug-build] [--with-opengl=GLFLAG] [--with-vulkan=VKFLAG] [-S=SOURCEDIR] [-B=BUILDDIR] EOF @@ -40,6 +40,7 @@ Options: --with-examples in configure step, set -DXO_ENABLE_EXAMPLES=1 --with-utests in configure step, set -DENABLE_TESTING=1 + --with-asm in configure step, set -DXO_ENABLE_ASM=1 --debug-build in configure step, set -DCMAKE_BUILD_TYPE=Debug --with-opengl=GLFLAG in configure step, set -DXO_ENABLE_OPENGL=GLFLAG [ON] --with-vulkan=VKFLAG in configure step, set -DXO_ENABLE_VULKAN=VKFLAG [OFF] @@ -61,6 +62,7 @@ pathtosource= pathtobuild= with_examples=0 with_utests=0 +with_asm=0 debug_build=0 with_opengl= with_vulkan= @@ -123,6 +125,9 @@ while [[ $# > 0 ]]; do --with-utests) with_utests=1 ;; + --with-asm) + with_asm=1 + ;; --debug-build) debug_build=1 ;; @@ -238,6 +243,10 @@ if [[ $configure_flag -eq 1 ]]; then testingarg="-DENABLE_TESTING=1" fi + if [[ $with_asm -eq 1 ]]; then + asmarg="-DENABLE_ASM=1" + fi + examplearg= if [[ $with_examples -eq 1 ]]; then examplearg="-DXO_ENABLE_EXAMPLES=1" @@ -258,7 +267,7 @@ if [[ $configure_flag -eq 1 ]]; then vulkanarg="-DXO_ENABLE_VULKAN=${with_vulkan}" fi - cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild ${testingarg} ${examplearg} ${openglarg} ${vulkanarg} ${cmakebuildtype}" + cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild ${testingarg} ${examplearg} ${asmarg} ${openglarg} ${vulkanarg} ${cmakebuildtype}" if [[ $noop_flag -eq 1 ]]; then echo $cmd From 455ec899d360c27c95b566b466f8321d8e04a2ba Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 11 Jan 2026 14:05:05 -0500 Subject: [PATCH 26/55] xo-cmake: generate .build/reconfigure.sh script to rerun cmake --- cmake/xo_macros/xo_cxx.cmake | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 31e8b879..d07fb3ad 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -5,7 +5,7 @@ option(XO_ENABLE_OPENGL "enable opengl dependency for imgui apps" ON) option(XO_ENABLE_ASM "generate assembler output (.s files)" OFF) macro(xo_cxx_config_message) - message(STATUS "GUESSED_CMAKE_CMD=cmake -DXO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE} -DENABLE_TESTING=${ENABLE_TESTING} -DXO_ENABLE_DOCS=${XO_ENABLE_DCS} -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} -DXO_ENABLE_VULKAN=${XO_ENABLE_VULKAN} -DXO_ENABLE_OPENGL=${XO_ENABLE_OPENGL} -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_DOCDIR=${CMAKE_INSTALL_DOCDIR} -B ${CMAKE_BINARY_DIR}") + message(STATUS "GUESSED_CMAKE_CMD=cmake -DXO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE} -DENABLE_TESTING=${ENABLE_TESTING} -DXO_ENABLE_DOCS=${XO_ENABLE_DOCS} -DXO_ENABLE_ASM=${XO_ENABLE_ASM} -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} -DXO_ENABLE_VULKAN=${XO_ENABLE_VULKAN} -DXO_ENABLE_OPENGL=${XO_ENABLE_OPENGL} -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_DOCDIR=${CMAKE_INSTALL_DOCDIR} -B ${CMAKE_BINARY_DIR}") message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") message(STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") endmacro() @@ -40,7 +40,7 @@ endmacro() # deprecated -- prefer xo_cxx_toplevel_options3() macro(xo_cxx_toplevel_options2) if (NOT DEFINED _xo_cxx_toplevel_done) - message("xo_cxx_toplevel_options2: PROJECT=${PROJECT_NAME}") + message(STATUS "xo_cxx_toplevel_options2: PROJECT=${PROJECT_NAME}") enable_language(CXX) xo_toplevel_compile_options() @@ -76,6 +76,7 @@ endmacro() macro(xo_cxx_toplevel_options3) xo_cxx_toplevel_options2() xo_toplevel_config2() + xo_generate_reconfigure_script() endmacro() # deprecated, I think? @@ -270,6 +271,33 @@ macro(xo_toplevel_asm_config2) endif() endmacro() +function(xo_generate_reconfigure_script) + set(_reconfigure_script "${CMAKE_BINARY_DIR}/reconfigure.sh") + + # In submodule build: only generate at umbrella toplevel + # In standalone build: always generate + if(XO_SUBMODULE_BUILD AND NOT (CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)) + return() + endif() + + file(WRITE ${_reconfigure_script} "#!/bin/bash\n") + file(APPEND ${_reconfigure_script} "# Generated by cmake - rerun to reconfigure\n") + file(APPEND ${_reconfigure_script} "cmake \\\n") + file(APPEND ${_reconfigure_script} " -B ${CMAKE_BINARY_DIR} \\\n") + file(APPEND ${_reconfigure_script} " -S ${CMAKE_SOURCE_DIR} \\\n") + file(APPEND ${_reconfigure_script} " -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \\\n") + file(APPEND ${_reconfigure_script} " -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} \\\n") + file(APPEND ${_reconfigure_script} " -DXO_ENABLE_DOCS=${XO_ENABLE_DOCS} \\\n") + file(APPEND ${_reconfigure_script} " -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} \\\n") + file(APPEND ${_reconfigure_script} " -DXO_ENABLE_ASM=${XO_ENABLE_ASM} \\\n") + file(APPEND ${_reconfigure_script} " \"$@\"\n") + file(CHMOD ${_reconfigure_script} PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + message(STATUS "Generated ${_reconfigure_script}") +endfunction() + # target to build+install coverage report. # macro(xo_utest_coverage_config2) From 0b6dceead499e55f494e9f54189b1d28ab9ef84d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 11 Jan 2026 14:12:21 -0500 Subject: [PATCH 27/55] xo-cmake: update ref boostrap macros --- share/xo-macros/xo-bootstrap-macros.cmake | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/share/xo-macros/xo-bootstrap-macros.cmake b/share/xo-macros/xo-bootstrap-macros.cmake index aba31169..592272c0 100644 --- a/share/xo-macros/xo-bootstrap-macros.cmake +++ b/share/xo-macros/xo-bootstrap-macros.cmake @@ -19,7 +19,13 @@ endif() message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") -if (NOT XO_SUBMODULE_BUILD) +if (XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # local version of xo-cmake macros + set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/xo-cmake/cmake") + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +else() if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) # default to typical install location for xo-project-macros execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) From 15ecd04042768cffd13f86d29505588ea9f01e8d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 11 Jan 2026 14:35:44 -0500 Subject: [PATCH 28/55] xo-cmake: scaffold-subdir uses reference version of bootstrap macros --- bin/scaffold-subdir | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/bin/scaffold-subdir b/bin/scaffold-subdir index 4cfe54a6..ab3a6b95 100755 --- a/bin/scaffold-subdir +++ b/bin/scaffold-subdir @@ -177,42 +177,8 @@ xo_export_cmake_config(\${PROJECT_NAME} \${PROJECT_VERSION} \${PROJECT_NAME}Targ EOF fi -# Create cmake/xo-bootstrap-macros.cmake -cat > "${TARGET_DIR}/cmake/xo-bootstrap-macros.cmake" << EOF -# ---------------------------------------------------------------- -# for example: -# $ PREFIX=/usr/local # for example -# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=\$PREFIX -B .build -# -# will get -# CMAKE_MODULE_PATH -# from xo-cmake-config --cmake-module-path -# -# and expect .cmake macros in -# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake -# ---------------------------------------------------------------- - -find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) - -if (("\${CMAKE_MODULE_PATH}" STREQUAL "") OR ("\${CMAKE_MODULE_PATH}" STREQUAL "prefix")) - message(FATAL "could not find xo-cmake-config executable") -endif() - -if (NOT XO_SUBMODULE_BUILD) - if (("\${CMAKE_MODULE_PATH}" STREQUAL "") OR ("\${CMAKE_MODULE_PATH}" STREQUAL prefix)) - # default to typical install location for xo-project-macros - execute_process(COMMAND \${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) - message(STATUS "CMAKE_MODULE_PATH=\${CMAKE_MODULE_PATH}") - endif() -endif() - -# needs to have been installed somewhere on CMAKE_MODULE_PATH, -# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) -# -include(xo_macros/xo_cxx) - -xo_cxx_bootstrap_message() -EOF +# Copy cmake/xo-bootstrap-macros.cmake from xo-cmake/share +cp "${XO_UMBRELLA_ROOT}/xo-cmake/share/xo-macros/xo-bootstrap-macros.cmake" "${TARGET_DIR}/cmake/" # Create cmake/xo_${NAME}Config.cmake.in cat > "${TARGET_DIR}/cmake/${LIB_NAME}Config.cmake.in" << EOF From 816ba6e6f74225c9282350b367d06d27f9bfe697 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 11 Jan 2026 14:53:47 -0500 Subject: [PATCH 29/55] xo-cmake: refactor xo-reconfigure to use cmake template --- bin/xo-cmake-config.in | 12 +++++++++--- cmake/xo_macros/xo_cxx.cmake | 29 ++++++++++++++++------------- share/xo-macros/xo-reconfigure.in | 11 +++++++++++ 3 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 share/xo-macros/xo-reconfigure.in diff --git a/bin/xo-cmake-config.in b/bin/xo-cmake-config.in index bbea1361..8cb3f39d 100755 --- a/bin/xo-cmake-config.in +++ b/bin/xo-cmake-config.in @@ -1,7 +1,7 @@ #!/usr/bin/env bash usage() { - echo "$0 [-u|--usage|-h|--help|--lcov-exe|--genhtml-exe|--lcov-harness-exe|--gen-ccov-template|--cmake-module-path|--subsystem-list]" 1>&2 + echo "$0 [-u|--usage|-h|--help|--lcov-exe|--genhtml-exe|--lcov-harness-exe|--gen-ccov-template|--reconfigure-template|--cmake-module-path|--subsystem-list]" 1>&2 } help() { @@ -18,8 +18,9 @@ Options: --lcov-exe report path to 'lcov' executable --genhtml-exe report path to 'genhtml' executable --lcov-harness-exe report path to 'xo-cmake-lcov-harness' executable - --gen-ccov-template report path to 'gen-ccov.in' template - --doxygen-template report path to 'Doxyfile.in' template + --gen-ccov-template report path to 'gen-ccov.in' template + --reconfigure-template report path to 'xo-reconfigure.in' template + --doxygen-template report path to 'Doxyfile.in' template --subsystem-list report path to 'subsystem-ilst' EOF @@ -48,6 +49,9 @@ while [[ $# > 0 ]]; do --gen-ccov-template) cmd='gen_ccov_template' ;; + --reconfigure-template) + cmd='reconfigure_template' + ;; --doxygen-template) cmd='doxygen_template' ;; @@ -79,6 +83,8 @@ elif [[ $cmd == 'lcov_harness_exe' ]]; then echo -n @CMAKE_INSTALL_FULL_BINDIR@/xo-cmake-lcov-harness elif [[ $cmd == 'gen_ccov_template' ]]; then echo -n @CMAKE_INSTALL_FULL_DATADIR@/xo-macros/gen-ccov.in +elif [[ $cmd == 'reconfigure_template' ]]; then + echo -n @CMAKE_INSTALL_FULL_DATADIR@/xo-macros/xo-reconfigure.in elif [[ $cmd == 'doxygen_template' ]]; then echo -n @CMAKE_INSTALL_FULL_DATADIR@/xo-macros/Doxyfile.in elif [[ $cmd == 'subsystem_list' ]]; then diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index d07fb3ad..5be3ce0b 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -272,25 +272,28 @@ macro(xo_toplevel_asm_config2) endmacro() function(xo_generate_reconfigure_script) - set(_reconfigure_script "${CMAKE_BINARY_DIR}/reconfigure.sh") - # In submodule build: only generate at umbrella toplevel # In standalone build: always generate if(XO_SUBMODULE_BUILD AND NOT (CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)) return() endif() - file(WRITE ${_reconfigure_script} "#!/bin/bash\n") - file(APPEND ${_reconfigure_script} "# Generated by cmake - rerun to reconfigure\n") - file(APPEND ${_reconfigure_script} "cmake \\\n") - file(APPEND ${_reconfigure_script} " -B ${CMAKE_BINARY_DIR} \\\n") - file(APPEND ${_reconfigure_script} " -S ${CMAKE_SOURCE_DIR} \\\n") - file(APPEND ${_reconfigure_script} " -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \\\n") - file(APPEND ${_reconfigure_script} " -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} \\\n") - file(APPEND ${_reconfigure_script} " -DXO_ENABLE_DOCS=${XO_ENABLE_DOCS} \\\n") - file(APPEND ${_reconfigure_script} " -DXO_ENABLE_EXAMPLES=${XO_ENABLE_EXAMPLES} \\\n") - file(APPEND ${_reconfigure_script} " -DXO_ENABLE_ASM=${XO_ENABLE_ASM} \\\n") - file(APPEND ${_reconfigure_script} " \"$@\"\n") + # Locate template + if(XO_SUBMODULE_BUILD) + set(_reconfigure_template "${XO_UMBRELLA_SOURCE_DIR}/xo-cmake/share/xo-macros/xo-reconfigure.in") + else() + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --reconfigure-template + OUTPUT_VARIABLE _reconfigure_template + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() + + if(NOT EXISTS "${_reconfigure_template}") + message(WARNING "xo_generate_reconfigure_script: template not found: ${_reconfigure_template}") + return() + endif() + + set(_reconfigure_script "${CMAKE_BINARY_DIR}/xo-reconfigure") + configure_file(${_reconfigure_template} ${_reconfigure_script} @ONLY) file(CHMOD ${_reconfigure_script} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE diff --git a/share/xo-macros/xo-reconfigure.in b/share/xo-macros/xo-reconfigure.in new file mode 100644 index 00000000..cf5c8f72 --- /dev/null +++ b/share/xo-macros/xo-reconfigure.in @@ -0,0 +1,11 @@ +#!/bin/bash +# Generated by cmake - rerun to reconfigure +cmake \ + -B @CMAKE_BINARY_DIR@ \ + -S @CMAKE_SOURCE_DIR@ \ + -DCMAKE_BUILD_TYPE=@CMAKE_BUILD_TYPE@ \ + -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ \ + -DXO_ENABLE_DOCS=@XO_ENABLE_DOCS@ \ + -DXO_ENABLE_EXAMPLES=@XO_ENABLE_EXAMPLES@ \ + -DXO_ENABLE_ASM=@XO_ENABLE_ASM@ \ + "$@" From d028a2154765f7d443b9da6d890fa7a90de2d352 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 11 Jan 2026 15:12:22 -0500 Subject: [PATCH 30/55] xo-cmake: add --dry-run option to generated reconfigure script --- cmake/xo_macros/xo_cxx.cmake | 2 +- share/xo-macros/xo-reconfigure.in | 62 ++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 5be3ce0b..c071f061 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -292,7 +292,7 @@ function(xo_generate_reconfigure_script) return() endif() - set(_reconfigure_script "${CMAKE_BINARY_DIR}/xo-reconfigure") + set(_reconfigure_script "${CMAKE_BINARY_DIR}/reconfigure") configure_file(${_reconfigure_template} ${_reconfigure_script} @ONLY) file(CHMOD ${_reconfigure_script} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE diff --git a/share/xo-macros/xo-reconfigure.in b/share/xo-macros/xo-reconfigure.in index cf5c8f72..3d8118bb 100644 --- a/share/xo-macros/xo-reconfigure.in +++ b/share/xo-macros/xo-reconfigure.in @@ -1,11 +1,55 @@ #!/bin/bash + # Generated by cmake - rerun to reconfigure -cmake \ - -B @CMAKE_BINARY_DIR@ \ - -S @CMAKE_SOURCE_DIR@ \ - -DCMAKE_BUILD_TYPE=@CMAKE_BUILD_TYPE@ \ - -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ \ - -DXO_ENABLE_DOCS=@XO_ENABLE_DOCS@ \ - -DXO_ENABLE_EXAMPLES=@XO_ENABLE_EXAMPLES@ \ - -DXO_ENABLE_ASM=@XO_ENABLE_ASM@ \ - "$@" + +dry_run_flag=false + +# Parse arguments +while [[ $# -gt 0 ]]; do + case "$1" in + -n|--dry-run) + dry_run_flag=true + ;; + --) + break; + ;; + *) + echo "error: xo-reconfigure: unexpected argument [$1]" + echo "usage: xo-reconfigure" + echo " xo-reconfigure [-n] -- CMAKE_ARGS" + exit 1 + esac + + shift +done + +# Build the cmake command +CMAKE_CMD=( + cmake + -B @CMAKE_BINARY_DIR@ + -S @CMAKE_SOURCE_DIR@ + -DCMAKE_BUILD_TYPE=@CMAKE_BUILD_TYPE@ + -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ + -DCMAKE_INSTALL_DOCDIR=@CMAKE_INSTALL_DOCDIR@ + -DCMAKE_MODULE_PATH=@CMAKE_MODULE_PATH@ + -DCMAKE_PREFIX_PATH=@CMAKE_PREFIX_PATH@ + -DCMAKE_CXX_STANDARD=@CMAKE_CXX_STANDARD@ + -DXO_CMAKE_CONFIG_EXECUTABLE=@XO_CMAKE_CONFIG_EXECUTABLE@ + -DENABLE_TESTING=@ENABLE_TESTING@ + -DXO_ENABLE_DOCS=@XO_ENABLE_DOCS@ + -DXO_ENABLE_ASM=@XO_ENABLE_ASM@ + -DXO_ENABLE_EXAMPLES=@XO_ENABLE_EXAMPLES@ + -DXO_ENABLE_VULKAN=@XO_ENABLE_VULKAN@ + -DXO_ENABLE_OPENGL=@XO_ENABLE_OPENGL@ + + "${@}" +) + +if [[ ${dry_run_flag} = true ]]; then + # Print the command with proper quoting for copy-paste + printf '%q ' "${CMAKE_CMD[@]}" | sed -e 's: -D: \\\n -D:g' + printf '\n' +else + # Execute the command + "${CMAKE_CMD[@]}" +fi From 69fafa1f9b7eba5a740c03620deedaa411ee14b6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 20 Jan 2026 22:10:59 -0500 Subject: [PATCH 31/55] xo-cmake: + xo_add_genfacet_all() cmake function --- cmake/xo_macros/xo_cxx.cmake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index c071f061..dde3c081 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1735,6 +1735,8 @@ function(xo_add_genfacet) # Create a target for this generation add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) + + set_property(DIRECTORY APPEND PROPERTY XO_GENFACET_TARGETS ${GF_TARGET}) endfunction() function(xo_add_genfacetimpl) @@ -1815,4 +1817,16 @@ function(xo_add_genfacetimpl) # Create a target for this generation add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) + + set_property(DIRECTORY APPEND PROPERTY XO_GENFACET_TARGETS ${GF_TARGET}) +endfunction() + +# create umbrella target for all genfacet targets in current directory +function(xo_add_genfacet_all target_name) + get_property(genfacet_targets DIRECTORY PROPERTY XO_GENFACET_TARGETS) + if(genfacet_targets) + add_custom_target(${target_name} DEPENDS ${genfacet_targets}) + else() + message(WARNING "xo_add_genfacet_all: no genfacet targets found") + endif() endfunction() From 0522f546b9d7388e3144768dc0e610342570c9cd Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 26 Jan 2026 20:24:48 -0500 Subject: [PATCH 32/55] xo-cmake: + --enable/disable testing arg to reconfigure.in --- share/xo-macros/xo-reconfigure.in | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/share/xo-macros/xo-reconfigure.in b/share/xo-macros/xo-reconfigure.in index 3d8118bb..41d641c6 100644 --- a/share/xo-macros/xo-reconfigure.in +++ b/share/xo-macros/xo-reconfigure.in @@ -3,6 +3,7 @@ # Generated by cmake - rerun to reconfigure dry_run_flag=false +testing=@ENABLE_TESTING@ # Parse arguments while [[ $# -gt 0 ]]; do @@ -10,6 +11,12 @@ while [[ $# -gt 0 ]]; do -n|--dry-run) dry_run_flag=true ;; + --enable-testing) + testing=1 + ;; + --disable-testing) + testing=0 + ;; --) break; ;; @@ -35,7 +42,7 @@ CMAKE_CMD=( -DCMAKE_PREFIX_PATH=@CMAKE_PREFIX_PATH@ -DCMAKE_CXX_STANDARD=@CMAKE_CXX_STANDARD@ -DXO_CMAKE_CONFIG_EXECUTABLE=@XO_CMAKE_CONFIG_EXECUTABLE@ - -DENABLE_TESTING=@ENABLE_TESTING@ + -DENABLE_TESTING=$testing -DXO_ENABLE_DOCS=@XO_ENABLE_DOCS@ -DXO_ENABLE_ASM=@XO_ENABLE_ASM@ -DXO_ENABLE_EXAMPLES=@XO_ENABLE_EXAMPLES@ From a3bf878198263932da9380d80cdea4026ad2da5c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 30 Jan 2026 13:23:28 -0500 Subject: [PATCH 33/55] switch build to c++23 --- cmake/xo_macros/xo_cxx.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index dde3c081..fbce80bd 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -670,7 +670,7 @@ macro(xo_toplevel_compile_options) ) if(NOT DEFINED CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD 23) endif() if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED) set(CMAKE_CXX_STANDARD_REQUIRED True) From 02655ef7955638ca693f2e04cb68f6b7f2c9f59d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 16 Feb 2026 17:23:12 -0500 Subject: [PATCH 34/55] xo-procedure2 xo-cmake: drop unnecessary output-cpp-dir cmdline arg --- cmake/xo_macros/xo_cxx.cmake | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index fbce80bd..11d3560e 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1699,10 +1699,6 @@ function(xo_add_genfacet) if(NOT DEFINED GF_OUTPUT_IMPL_SUBDIR) message(FATAL_ERROR "xo_add_genfacet: OUTPUT_IMPL_SUBDIR is required") endif() - if(NOT DEFINED GF_OUTPUT_CPP_DIR) - message(FATAL_ERROR "xo_add_genfacet: OUTPUT_CPP_DIR is required") - endif() - find_program(GENFACET_EXECUTABLE NAMES genfacet HINTS ${CMAKE_SOURCE_DIR}/xo-facet/codegen DOC "path to xo genfacet code generator" @@ -1714,8 +1710,13 @@ function(xo_add_genfacet) ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/A${GF_FACET}.hpp ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_Any.hpp ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_Xfer.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${GF_FACET}.hpp - ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_Any.cpp) + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${GF_FACET}.hpp) + + set(_output_cpp_args "") + if(DEFINED GF_OUTPUT_CPP_DIR) + list(APPEND generatedFiles ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_Any.cpp) + set(_output_cpp_args --output-cpp ${GF_OUTPUT_CPP_DIR}) + endif() #message(STATUS "generatedFiles=${generatedFiles}") @@ -1726,7 +1727,7 @@ function(xo_add_genfacet) --input ${GF_INPUT} --output-hpp ${GF_OUTPUT_HPP_DIR} --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} - --output-cpp ${GF_OUTPUT_CPP_DIR} + ${_output_cpp_args} DEPENDS ${GF_INPUT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating facet source files from ${GF_INPUT}" @@ -1775,10 +1776,6 @@ function(xo_add_genfacetimpl) if(NOT DEFINED GF_OUTPUT_IMPL_SUBDIR) message(FATAL_ERROR "xo_add_genfacetimpl: OUTPUT_IMPL_SUBDIR is required") endif() - if(NOT DEFINED GF_OUTPUT_CPP_DIR) - message(FATAL_ERROR "xo_add_genfacetimpl: OUTPUT_CPP_DIR is required") - endif() - if(NOT DEFINED GF_FACET_DIR) if (NOT DEFINED GF_FACET_PKG) message(FATAL_ERROR "xo_add_genfacetimpl: FACET_PKG or FACET_DIR required") @@ -1795,8 +1792,13 @@ function(xo_add_genfacetimpl) message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") set(generatedFiles - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_D${GF_REPR}.hpp - ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_D${GF_REPR}.cpp) + ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_D${GF_REPR}.hpp) + + set(_output_cpp_args "") + if(DEFINED GF_OUTPUT_CPP_DIR) + list(APPEND generatedFiles ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_D${GF_REPR}.cpp) + set(_output_cpp_args --output-cpp ${GF_OUTPUT_CPP_DIR}) + endif() # Build the genfacet command. # But careful: can't have the same generated files in two different rules, @@ -1808,7 +1810,7 @@ function(xo_add_genfacetimpl) --facet-dir ${GF_FACET_DIR} --output-hpp ${GF_OUTPUT_HPP_DIR} --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} - --output-cpp ${GF_OUTPUT_CPP_DIR} + ${_output_cpp_args} DEPENDS ${GF_INPUT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating facet source files from ${GF_INPUT}" From f9a83106693ad3a9a1dacb25ab32e6cec8c24da8 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 16 Feb 2026 22:43:24 -0500 Subject: [PATCH 35/55] xo-gc xo-cmake: simplify idl -> *.*pp codegen --- cmake/xo_macros/xo_cxx.cmake | 58 ++++-------------------------------- 1 file changed, 6 insertions(+), 52 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 11d3560e..1550e282 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1677,9 +1677,6 @@ function(xo_add_genfacet) TARGET # Name for this generation target FACET # facet name INPUT # Input .json5 file - OUTPUT_HPP_DIR # Directory for .hpp files - OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers - OUTPUT_CPP_DIR # Directory for .cpp files ) set(multiValueArgs "") @@ -1693,41 +1690,18 @@ function(xo_add_genfacet) if(NOT DEFINED GF_FACET) message(FATAL_ERROR "xo_add_genfacet: FACET is required") endif() - if(NOT DEFINED GF_OUTPUT_HPP_DIR) - message(FATAL_ERROR "xo_add_genfacet: OUTPUT_HPP_DIR is required") - endif() - if(NOT DEFINED GF_OUTPUT_IMPL_SUBDIR) - message(FATAL_ERROR "xo_add_genfacet: OUTPUT_IMPL_SUBDIR is required") - endif() find_program(GENFACET_EXECUTABLE NAMES genfacet HINTS ${CMAKE_SOURCE_DIR}/xo-facet/codegen DOC "path to xo genfacet code generator" REQUIRED) message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") - set(generatedFiles - ${GF_OUTPUT_HPP_DIR}/${GF_FACET}.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/A${GF_FACET}.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_Any.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_Xfer.hpp - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/R${GF_FACET}.hpp) - - set(_output_cpp_args "") - if(DEFINED GF_OUTPUT_CPP_DIR) - list(APPEND generatedFiles ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_Any.cpp) - set(_output_cpp_args --output-cpp ${GF_OUTPUT_CPP_DIR}) - endif() - #message(STATUS "generatedFiles=${generatedFiles}") # Build the genfacet command add_custom_command( - OUTPUT ${generatedFiles} - COMMAND ${GENFACET_EXECUTABLE} - --input ${GF_INPUT} - --output-hpp ${GF_OUTPUT_HPP_DIR} - --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} - ${_output_cpp_args} + OUTPUT ${GF_INPUT}.out + COMMAND ${GENFACET_EXECUTABLE} --input ${GF_INPUT} DEPENDS ${GF_INPUT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating facet source files from ${GF_INPUT}" @@ -1735,7 +1709,7 @@ function(xo_add_genfacet) ) # Create a target for this generation - add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) + add_custom_target(${GF_TARGET} DEPENDS ${GF_INPUT}) set_property(DIRECTORY APPEND PROPERTY XO_GENFACET_TARGETS ${GF_TARGET}) endfunction() @@ -1770,12 +1744,6 @@ function(xo_add_genfacetimpl) if(NOT DEFINED GF_INPUT) message(FATAL_ERROR "xo_add_genfacetimpl: INPUT is required") endif() - if(NOT DEFINED GF_OUTPUT_HPP_DIR) - message(FATAL_ERROR "xo_add_genfacetimpl: OUTPUT_HPP_DIR is required") - endif() - if(NOT DEFINED GF_OUTPUT_IMPL_SUBDIR) - message(FATAL_ERROR "xo_add_genfacetimpl: OUTPUT_IMPL_SUBDIR is required") - endif() if(NOT DEFINED GF_FACET_DIR) if (NOT DEFINED GF_FACET_PKG) message(FATAL_ERROR "xo_add_genfacetimpl: FACET_PKG or FACET_DIR required") @@ -1791,26 +1759,12 @@ function(xo_add_genfacetimpl) REQUIRED) message(STATUS "GENFACET_EXECUTABLE=${GENFACET_EXECUTABLE}") - set(generatedFiles - ${GF_OUTPUT_HPP_DIR}/${GF_OUTPUT_IMPL_SUBDIR}/I${GF_FACET}_D${GF_REPR}.hpp) - - set(_output_cpp_args "") - if(DEFINED GF_OUTPUT_CPP_DIR) - list(APPEND generatedFiles ${GF_OUTPUT_CPP_DIR}/I${GF_FACET}_D${GF_REPR}.cpp) - set(_output_cpp_args --output-cpp ${GF_OUTPUT_CPP_DIR}) - endif() - # Build the genfacet command. # But careful: can't have the same generated files in two different rules, # so need to remove overlaps here add_custom_command( - OUTPUT ${generatedFiles} - COMMAND ${GENFACET_EXECUTABLE} - --input ${GF_INPUT} - --facet-dir ${GF_FACET_DIR} - --output-hpp ${GF_OUTPUT_HPP_DIR} - --output-impl-hpp ${GF_OUTPUT_IMPL_SUBDIR} - ${_output_cpp_args} + OUTPUT ${GF_INPUT}.out + COMMAND ${GENFACET_EXECUTABLE} --input ${GF_INPUT} --facet-dir ${GF_FACET_DIR} DEPENDS ${GF_INPUT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating facet source files from ${GF_INPUT}" @@ -1818,7 +1772,7 @@ function(xo_add_genfacetimpl) ) # Create a target for this generation - add_custom_target(${GF_TARGET} DEPENDS ${generatedFiles}) + add_custom_target(${GF_TARGET} DEPENDS ${GF_INPUT}) set_property(DIRECTORY APPEND PROPERTY XO_GENFACET_TARGETS ${GF_TARGET}) endfunction() From 11a02cc5b66a69100d0b98a1161cb47884d13747 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Feb 2026 14:42:17 -0500 Subject: [PATCH 36/55] xo-interpreter2 stack: define-expr's work at top-level --- cmake/xo_macros/xo_cxx.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 1550e282..9d94e9b7 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1772,7 +1772,7 @@ function(xo_add_genfacetimpl) ) # Create a target for this generation - add_custom_target(${GF_TARGET} DEPENDS ${GF_INPUT}) + add_custom_target(${GF_TARGET} DEPENDS ${GF_INPUT}.out) set_property(DIRECTORY APPEND PROPERTY XO_GENFACET_TARGETS ${GF_TARGET}) endfunction() From 469d55588fa10fb423029316a00e39455b4f6ad3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Feb 2026 15:55:11 -0500 Subject: [PATCH 37/55] xo-cmake: bugfix: dependency for genfacet target --- cmake/xo_macros/xo_cxx.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 9d94e9b7..38d01df3 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1709,7 +1709,7 @@ function(xo_add_genfacet) ) # Create a target for this generation - add_custom_target(${GF_TARGET} DEPENDS ${GF_INPUT}) + add_custom_target(${GF_TARGET} DEPENDS ${GF_INPUT}.out) set_property(DIRECTORY APPEND PROPERTY XO_GENFACET_TARGETS ${GF_TARGET}) endfunction() From 301025e216d39288453e03b790ec6ad60daab49f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 26 Feb 2026 00:10:47 +1100 Subject: [PATCH 38/55] xo-cmake: + xo-reconfigure.in install --- CMakeLists.txt | 1 + share/xo-macros/xo-reconfigure.in | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14226afa..51f2a45a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ install( "share/xo-macros/gen-ccov.in" "share/xo-macros/Doxyfile.in" "share/xo-macros/xo-bootstrap-macros.cmake" + "share/xo-macros/xo-reconfigure.in" PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION ${CMAKE_INSTALL_DATADIR}/xo-macros ) diff --git a/share/xo-macros/xo-reconfigure.in b/share/xo-macros/xo-reconfigure.in index 41d641c6..d3e55e55 100644 --- a/share/xo-macros/xo-reconfigure.in +++ b/share/xo-macros/xo-reconfigure.in @@ -23,7 +23,9 @@ while [[ $# -gt 0 ]]; do *) echo "error: xo-reconfigure: unexpected argument [$1]" echo "usage: xo-reconfigure" - echo " xo-reconfigure [-n] -- CMAKE_ARGS" + echo " xo-reconfigure [-n|--dry-run]" + echo " [--enable-testing|--disable-testing]" + echo " -- CMAKE_ARGS" exit 1 esac From 2ff5ab3fc97ff8899a178a96af93b464c7bf6a41 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 27 Feb 2026 13:04:46 +1100 Subject: [PATCH 39/55] nix-build: + xo-object2 builds --- cmake/xo_macros/xo_cxx.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 38d01df3..9c70505a 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1748,6 +1748,18 @@ function(xo_add_genfacetimpl) if (NOT DEFINED GF_FACET_PKG) message(FATAL_ERROR "xo_add_genfacetimpl: FACET_PKG or FACET_DIR required") else() + # share_${GF_FACET_PKG} is a cmake target created by xo_add_shared_library4() + # or similar when a facet-provider (e.g. xo-gc) builds from source in the same + # cmake context (XO_SUBMODULE_BUILD=True). + # + # It is NOT exported in the installed cmake + # config, so it won't exist when the facet package is consumed as an installed + # dependency (e.g. in a standalone nix build). + # + if(NOT TARGET share_${GF_FACET_PKG}) + message(STATUS "xo_add_genfacetimpl: share_${GF_FACET_PKG} not available; skipping ${GF_TARGET}") + return() + endif() get_target_property(_facet_dir share_${GF_FACET_PKG} path) set(GF_FACET_DIR ${_facet_dir}) endif() From 9f977c002594c53468794e709fb0f1d1d0f237bd Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 27 Feb 2026 19:38:53 +1100 Subject: [PATCH 40/55] xo-cmake: setup to make share target available via cmake install --- cmake/xo_macros/xo_cxx.cmake | 11 ++++ docs/history/2026/cmake-facet-export.rst | 81 ++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 docs/history/2026/cmake-facet-export.rst diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 9c70505a..7966559a 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1227,6 +1227,17 @@ macro(xo_export_cmake_config projectname projectversion projecttargets) PERMISSIONS OWNER_READ GROUP_READ WORLD_READ DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/cmake/${projectname} ) + # generate Share file that recreates share_${projectname} in consuming projects + file(WRITE "${PROJECT_BINARY_DIR}/${projectname}Share.cmake" + "if(NOT TARGET share_${projectname})\n" + " add_custom_target(share_${projectname})\n" + " set_property(TARGET share_${projectname} PROPERTY path\n" + " \"\${CMAKE_CURRENT_LIST_DIR}/../../../share/${projectname}\")\n" + "endif()\n") + install( + FILES "${PROJECT_BINARY_DIR}/${projectname}Share.cmake" + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/cmake/${projectname}) endmacro() # ---------------------------------------------------------------- diff --git a/docs/history/2026/cmake-facet-export.rst b/docs/history/2026/cmake-facet-export.rst new file mode 100644 index 00000000..f2de60d6 --- /dev/null +++ b/docs/history/2026/cmake-facet-export.rst @@ -0,0 +1,81 @@ +cmake-facet-export +================== + +:date: 2026-02 + +Background +---------- + +``xo_add_genfacetimpl()`` in ``xo_cxx.cmake`` uses ``genfacet`` to regenerate facet +implementation boilerplate from ``.json5`` IDL files. When ``FACET_PKG`` is specified +(e.g. ``FACET_PKG xo_gc``), the macro resolves the IDL directory via a cmake custom target:: + + get_target_property(_facet_dir share_${GF_FACET_PKG} path) + +``share_${PROJECT_NAME}`` is a custom target created by ``xo_cxx_toplevel_options2()`` for +every xo satellite, with its ``path`` property set to ``${PROJECT_SOURCE_DIR}`` (the package +source root). Subdirectory ``idl/`` within that root holds the facet IDL files. + +Problem +------- + +Custom cmake targets are not exportable through the standard ``install(TARGETS ... EXPORT ...)`` +mechanism. As a result, ``share_xo_gc`` and similar targets do not exist when a satellite is +consumed as an installed dependency (e.g. in a standalone nix build or any build that uses +``find_package(xo_gc)`` rather than building xo-gc in the same cmake invocation). + +This caused ``nix-build -A xo.object2`` to fail at cmake configure time with:: + + CMake Error: get_target_property() called with non-existent target "share_xo_gc". + +xo-gc was unaffected because it uses only ``xo_add_genfacet()`` (which does not reference +``share_*`` targets), while xo-object2 uses ``xo_add_genfacetimpl()`` with ``FACET_PKG``. + +Immediate fix (2026-02) +----------------------- + +A guard was added to ``xo_add_genfacetimpl()`` to skip target creation gracefully when the +``share_*`` target is absent, since all callers annotate these as +``# note: manual target; generated code committed to git``:: + + if(NOT TARGET share_${GF_FACET_PKG}) + message(STATUS "xo_add_genfacetimpl: share_${GF_FACET_PKG} not available; skipping") + return() + endif() + +This unblocked the nix build while leaving a forward path to full support. + +Long-term design +---------------- + +To make ``xo_add_genfacetimpl()`` fully functional in standalone builds: + +1. **Export ``share_`` targets universally** — ``xo_export_cmake_config()`` generates and + installs a ``${PROJECT_NAME}Share.cmake`` file for every satellite. This file recreates + ``share_${PROJECT_NAME}`` pointing to the installed data root:: + + if(NOT TARGET share_xo_gc) + add_custom_target(share_xo_gc) + set_property(TARGET share_xo_gc PROPERTY path + "${CMAKE_CURRENT_LIST_DIR}/../../../share/xo_gc") + endif() + + Doing this universally (not only for facet-providers) keeps ``*Config.cmake.in`` templates + consistent and provides future-proofing for other attributes that may be added to + ``share_`` targets. + +2. **Include the Share file** — every ``cmake/${pkg}Config.cmake.in`` adds:: + + include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Share.cmake") + +3. **Install IDL files** — packages whose IDL is consumed by ``xo_add_genfacetimpl()`` + install their ``idl/`` tree:: + + install(DIRECTORY idl/ + DESTINATION share/${PROJECT_NAME}/idl + FILES_MATCHING PATTERN "*.json5") + + This maps to the same relative path that ``genfacet`` would find under the source root. + +The skip-if-absent guard is retained as graceful degradation for older installed packages +that predate this change. From ec3d65feb1dab1cef6566d6eac4e351936b52d58 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 4 Mar 2026 14:51:26 +1100 Subject: [PATCH 41/55] xo-cmake: bugfix src/foo/CMakeLists.txt location --- bin/scaffold-subdir | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/scaffold-subdir b/bin/scaffold-subdir index ab3a6b95..f0052502 100755 --- a/bin/scaffold-subdir +++ b/bin/scaffold-subdir @@ -27,7 +27,7 @@ usage() { echo " xo-mylib/cmake/xo-bootstrap-macros.cmake" echo " xo-mylib/cmake/xo_mylibConfig.cmake.in" echo " xo-mylib/include/xo/mylib/" - echo " xo-mylib/src/CMakeLists.txt" + echo " xo-mylib/src/mylib/CMakeLists.txt" exit 1 } @@ -92,7 +92,7 @@ mkdir -p "${TARGET_DIR}/cmake" mkdir -p "${TARGET_DIR}/include/xo/${NAME}" if [[ "$MODE" == "shared" ]]; then - mkdir -p "${TARGET_DIR}/src" + mkdir -p "${TARGET_DIR}/src/${NAME}" fi # Create CMakeLists.txt (different content based on mode) @@ -196,10 +196,10 @@ include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") EOF -# Create src/CMakeLists.txt for shared library mode +# Create src/${NAME}/CMakeLists.txt for shared library mode if [[ "$MODE" == "shared" ]]; then - cat > "${TARGET_DIR}/src/CMakeLists.txt" << EOF -# ${DIR_NAME}/src/CMakeLists.txt + cat > "${TARGET_DIR}/src/${NAME}/CMakeLists.txt" << EOF +# ${DIR_NAME}/src/${NAME}/CMakeLists.txt set(SELF_LIB ${LIB_NAME}) set(SELF_SRCS @@ -232,7 +232,7 @@ echo " ${DIR_NAME}/cmake/xo-bootstrap-macros.cmake" echo " ${DIR_NAME}/cmake/${LIB_NAME}Config.cmake.in" echo " ${DIR_NAME}/include/xo/${NAME}/" if [[ "$MODE" == "shared" ]]; then - echo " ${DIR_NAME}/src/CMakeLists.txt" + echo " ${DIR_NAME}/src/${NAME}/CMakeLists.txt" fi echo "" echo "Next steps:" @@ -242,7 +242,7 @@ if [[ "$MODE" == "headeronly" ]]; then echo " 3. Uncomment dependencies as needed in CMakeLists.txt and Config.cmake.in" else echo " 1. Add header files to ${DIR_NAME}/include/xo/${NAME}/" - echo " 2. Add source files to ${DIR_NAME}/src/ and update src/CMakeLists.txt SELF_SRCS" + echo " 2. Add source files to ${DIR_NAME}/src/${NAME}/ and update src/${NAME}/CMakeLists.txt SELF_SRCS" echo " 3. Add ${DIR_NAME} to umbrella CMakeLists.txt" - echo " 4. Uncomment dependencies as needed in src/CMakeLists.txt and Config.cmake.in" + echo " 4. Uncomment dependencies as needed in src/${NAME}/CMakeLists.txt and Config.cmake.in" fi From 7af082de59ef9d13e5b55503a1e1e15e8731fdbd Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 4 Mar 2026 14:56:15 +1100 Subject: [PATCH 42/55] xo-cmake: bugfix scaffold-subdir: export FooShare.cmake for idl --- bin/scaffold-subdir | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/scaffold-subdir b/bin/scaffold-subdir index f0052502..4a88f3d3 100755 --- a/bin/scaffold-subdir +++ b/bin/scaffold-subdir @@ -193,6 +193,7 @@ include(CMakeFindDependencyMacro) #find_dependency(xo_flatstring) include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Share.cmake") check_required_components("@PROJECT_NAME@") EOF From 1687767096e946646e2fbb1047878eb37ad6722c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 4 Mar 2026 15:17:43 +1100 Subject: [PATCH 43/55] xo-cmake: scaffold-subdir: --with-facetidl feature --- bin/scaffold-subdir | 192 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 184 insertions(+), 8 deletions(-) diff --git a/bin/scaffold-subdir b/bin/scaffold-subdir index 4a88f3d3..5cc8aba3 100755 --- a/bin/scaffold-subdir +++ b/bin/scaffold-subdir @@ -9,12 +9,13 @@ set -euo pipefail usage() { - echo "Usage: $0 [--mode=headeronly|shared] " + echo "Usage: $0 [--mode=headeronly|shared] [--with-facet=FACET] " echo " Creates xo-/ with library scaffolding" echo "" echo "Options:" - echo " --mode=headeronly Create header-only library (default)" - echo " --mode=shared Create shared library with src/ directory" + echo " --mode=headeronly Create header-only library (default)" + echo " --mode=shared Create shared library with src/ directory" + echo " --with-facet=FACET Add facet IDL scaffolding (requires --mode=shared)" echo "" echo "Example: $0 arena" echo " Creates xo-arena/CMakeLists.txt" @@ -28,11 +29,21 @@ usage() { echo " xo-mylib/cmake/xo_mylibConfig.cmake.in" echo " xo-mylib/include/xo/mylib/" echo " xo-mylib/src/mylib/CMakeLists.txt" + echo "" + echo "Example: $0 --mode=shared --with-facet=Equable equable2" + echo " Creates xo-equable2/CMakeLists.txt (with xo_add_genfacet)" + echo " xo-equable2/cmake/xo-bootstrap-macros.cmake" + echo " xo-equable2/cmake/xo_equable2Config.cmake.in" + echo " xo-equable2/idl/Equable.json5" + echo " xo-equable2/include/xo/equable2/" + echo " xo-equable2/include/xo/equable2/detail/" + echo " xo-equable2/src/equable2/CMakeLists.txt" exit 1 } # Default mode MODE="headeronly" +FACET="" # Parse arguments while [[ $# -gt 0 ]]; do @@ -45,6 +56,10 @@ while [[ $# -gt 0 ]]; do fi shift ;; + --with-facet=*) + FACET="${1#--with-facet=}" + shift + ;; --help|-h) usage ;; @@ -62,6 +77,11 @@ if [[ $# -ne 1 ]]; then usage fi +if [[ -n "$FACET" && "$MODE" == "headeronly" ]]; then + echo "Error: --with-facet requires --mode=shared" + exit 1 +fi + NAME="$1" # Validate name: alphanumeric and underscores only @@ -74,6 +94,7 @@ fi DIR_NAME="xo-${NAME}" LIB_NAME="xo_${NAME}" PROJECT_NAME="xo_${NAME}" +FACET_LC="$(echo "$FACET" | tr '[:upper:]' '[:lower:]')" # Get script directory to find umbrella root XO_UMBRELLA_ROOT=$(git rev-parse --show-toplevel) @@ -95,6 +116,11 @@ if [[ "$MODE" == "shared" ]]; then mkdir -p "${TARGET_DIR}/src/${NAME}" fi +if [[ -n "$FACET" ]]; then + mkdir -p "${TARGET_DIR}/idl" + mkdir -p "${TARGET_DIR}/include/xo/${NAME}/detail" +fi + # Create CMakeLists.txt (different content based on mode) if [[ "$MODE" == "headeronly" ]]; then cat > "${TARGET_DIR}/CMakeLists.txt" << EOF @@ -142,7 +168,8 @@ xo_export_cmake_config(\${PROJECT_NAME} \${PROJECT_VERSION} \${PROJECT_NAME}Targ EOF else # Shared library mode - cat > "${TARGET_DIR}/CMakeLists.txt" << EOF + if [[ -z "$FACET" ]]; then + cat > "${TARGET_DIR}/CMakeLists.txt" << EOF # ${DIR_NAME}/CMakeLists.txt cmake_minimum_required(VERSION 3.10) @@ -165,7 +192,7 @@ add_definitions(\${PROJECT_CXX_FLAGS}) # ---------------------------------------------------------------- # output targets -add_subdirectory(src) +add_subdirectory(src/${NAME}) #add_subdirectory(utest) # ---------------------------------------------------------------- @@ -175,13 +202,81 @@ xo_export_cmake_config(\${PROJECT_NAME} \${PROJECT_VERSION} \${PROJECT_NAME}Targ # end CMakeLists.txt EOF + else + cat > "${TARGET_DIR}/CMakeLists.txt" << EOF +# ${DIR_NAME}/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(${PROJECT_NAME} VERSION 1.0) +enable_language(CXX) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings + +# one-time project-specific c++ flags. usually empty +set(PROJECT_CXX_FLAGS "") +add_definitions(\${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- +# facet code generation + +xo_add_genfacet( + TARGET ${DIR_NAME}-facet-${FACET_LC} + FACET ${FACET} + INPUT idl/${FACET}.json5 + OUTPUT_HPP_DIR include/xo/${NAME} + OUTPUT_IMPL_SUBDIR detail +) + +install(DIRECTORY idl/ + DESTINATION share/\${PROJECT_NAME}/idl + FILES_MATCHING PATTERN "*.json5") + +# ---------------------------------------------------------------- +# output targets + +add_subdirectory(src/${NAME}) +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# cmake export + +xo_export_cmake_config(\${PROJECT_NAME} \${PROJECT_VERSION} \${PROJECT_NAME}Targets) + +# end CMakeLists.txt +EOF + fi fi # Copy cmake/xo-bootstrap-macros.cmake from xo-cmake/share cp "${XO_UMBRELLA_ROOT}/xo-cmake/share/xo-macros/xo-bootstrap-macros.cmake" "${TARGET_DIR}/cmake/" # Create cmake/xo_${NAME}Config.cmake.in -cat > "${TARGET_DIR}/cmake/${LIB_NAME}Config.cmake.in" << EOF +if [[ -n "$FACET" ]]; then + cat > "${TARGET_DIR}/cmake/${LIB_NAME}Config.cmake.in" << EOF +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(xo_facet) + +# note: changes to find_dependency() calls here +# must coordinate with xo_dependency() calls +# in src/${NAME}/CMakeLists.txt +# +#find_dependency(xo_indentlog) + +include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Share.cmake") +check_required_components("@PROJECT_NAME@") +EOF +else + cat > "${TARGET_DIR}/cmake/${LIB_NAME}Config.cmake.in" << EOF @PACKAGE_INIT@ include(CMakeFindDependencyMacro) @@ -196,10 +291,34 @@ include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") include("\${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Share.cmake") check_required_components("@PROJECT_NAME@") EOF +fi # Create src/${NAME}/CMakeLists.txt for shared library mode if [[ "$MODE" == "shared" ]]; then - cat > "${TARGET_DIR}/src/${NAME}/CMakeLists.txt" << EOF + if [[ -n "$FACET" ]]; then + cat > "${TARGET_DIR}/src/${NAME}/CMakeLists.txt" << EOF +# ${DIR_NAME}/src/${NAME}/CMakeLists.txt + +set(SELF_LIB ${LIB_NAME}) +set(SELF_SRCS + I${FACET}_Any.cpp +) + +xo_add_shared_library4(\${SELF_LIB} \${PROJECT_NAME}Targets \${PROJECT_VERSION} 1 \${SELF_SRCS}) + +# ---------------------------------------------------------------- +# input dependencies +# +# NOTE: dependency set here must be kept consistent with +# ${DIR_NAME}/cmake/${LIB_NAME}Config.cmake.in + +xo_dependency(\${SELF_LIB} xo_facet) +#xo_dependency(\${SELF_LIB} xo_indentlog) + +# end src/${NAME}/CMakeLists.txt +EOF + else + cat > "${TARGET_DIR}/src/${NAME}/CMakeLists.txt" << EOF # ${DIR_NAME}/src/${NAME}/CMakeLists.txt set(SELF_LIB ${LIB_NAME}) @@ -219,7 +338,54 @@ xo_install_include_tree3(include/xo/${NAME}) #xo_dependency(\${SELF_LIB} xo_indentlog) -# end src/CMakeLists.txt +# end src/${NAME}/CMakeLists.txt +EOF + fi +fi + +# Create idl/${FACET}.json5 stub for facet mode +if [[ -n "$FACET" ]]; then + cat > "${TARGET_DIR}/idl/${FACET}.json5" << EOF +{ + mode: "facet", + output_cpp_dir: "src/${NAME}", + output_hpp_dir: "include/xo/${NAME}", + output_impl_subdir: "detail", + includes: [ + ], + user_hpp_includes: [ + ], + namespace1: "xo", + namespace2: "${FACET_LC}", // TODO: change to project namespace if different + pretext: [], + facet: "${FACET}", + detail_subdir: "detail", + brief: "TODO brief description", + using_doxygen: true, + doc: [ + "TODO: document facet ${FACET}" + ], + types: [ + { + name: "obj_A${FACET}", + doc: [], + definition: "xo::facet::obj", + } + ], + const_methods: [ + // TODO: define methods, e.g.: + // { + // name: "my_method", + // doc: ["description"], + // return_type: "bool", + // args: [], + // const: true, + // noexcept: true, + // }, + ], + nonconst_methods: [], + router_facet_explicit_content: [], +} EOF fi @@ -235,12 +401,22 @@ echo " ${DIR_NAME}/include/xo/${NAME}/" if [[ "$MODE" == "shared" ]]; then echo " ${DIR_NAME}/src/${NAME}/CMakeLists.txt" fi +if [[ -n "$FACET" ]]; then + echo " ${DIR_NAME}/idl/${FACET}.json5" + echo " ${DIR_NAME}/include/xo/${NAME}/detail/ (populated by genfacet at build time)" +fi echo "" echo "Next steps:" if [[ "$MODE" == "headeronly" ]]; then echo " 1. Add header files to ${DIR_NAME}/include/xo/${NAME}/" echo " 2. Add ${DIR_NAME} to umbrella CMakeLists.txt" echo " 3. Uncomment dependencies as needed in CMakeLists.txt and Config.cmake.in" +elif [[ -n "$FACET" ]]; then + echo " 1. Edit ${DIR_NAME}/idl/${FACET}.json5: define methods, set namespace2" + echo " 2. Build (or run genfacet manually) to generate detail/*.hpp and src/${NAME}/I${FACET}_Any.cpp" + echo " 3. Add header files to ${DIR_NAME}/include/xo/${NAME}/" + echo " 4. Add ${DIR_NAME} to umbrella CMakeLists.txt" + echo " 5. Uncomment dependencies as needed in src/${NAME}/CMakeLists.txt and Config.cmake.in" else echo " 1. Add header files to ${DIR_NAME}/include/xo/${NAME}/" echo " 2. Add source files to ${DIR_NAME}/src/${NAME}/ and update src/${NAME}/CMakeLists.txt SELF_SRCS" From f6563502eb1a59513141454d5943d2ea5ab39a4a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 4 Mar 2026 15:23:21 +1100 Subject: [PATCH 44/55] xo-cmake: scaffold-subdir: allow preexisting directories --- bin/scaffold-subdir | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/bin/scaffold-subdir b/bin/scaffold-subdir index 5cc8aba3..7da5a1be 100755 --- a/bin/scaffold-subdir +++ b/bin/scaffold-subdir @@ -101,8 +101,31 @@ XO_UMBRELLA_ROOT=$(git rev-parse --show-toplevel) TARGET_DIR="${XO_UMBRELLA_ROOT}/${DIR_NAME}" -if [[ -d "$TARGET_DIR" ]]; then - echo "Error: ${TARGET_DIR} already exists" +# Collect files that would be created +WOULD_CREATE=( + "${TARGET_DIR}/CMakeLists.txt" + "${TARGET_DIR}/cmake/xo-bootstrap-macros.cmake" + "${TARGET_DIR}/cmake/${LIB_NAME}Config.cmake.in" +) +if [[ "$MODE" == "shared" ]]; then + WOULD_CREATE+=("${TARGET_DIR}/src/${NAME}/CMakeLists.txt") +fi +if [[ -n "$FACET" ]]; then + WOULD_CREATE+=("${TARGET_DIR}/idl/${FACET}.json5") +fi + +# Error if any would-be-created file already exists +CONFLICTS=() +for f in "${WOULD_CREATE[@]}"; do + if [[ -e "$f" ]]; then + CONFLICTS+=("$f") + fi +done +if [[ ${#CONFLICTS[@]} -gt 0 ]]; then + echo "Error: the following files already exist:" + for f in "${CONFLICTS[@]}"; do + echo " $f" + done exit 1 fi From bd173f3a1d8e751e3642d1a7ed62a3c6326e2c93 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 5 Mar 2026 13:02:42 +1100 Subject: [PATCH 45/55] xo-cmake: fix per-satellite targets to trigger local builds --- cmake/xo_macros/xo_cxx.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 7966559a..2003515f 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -841,6 +841,7 @@ macro(xo_add_shared_library3 target projectTargets targetversion soversion sourc TARGET all_libraries_${PROJECT_NAME} APPEND PROPERTY targets ${target}) + add_dependencies(all_libraries_${PROJECT_NAME} ${target}) set_target_properties( ${target} @@ -876,6 +877,7 @@ macro(xo_add_shared_library target targetversion soversion sources) TARGET all_libraries_${PROJECT_NAME} APPEND PROPERTY targets ${target}) + add_dependencies(all_libraries_${PROJECT_NAME} ${target}) set_target_properties( ${target} @@ -957,6 +959,7 @@ macro(xo_add_executable target sources) TARGET all_executables_${PROJECT_NAME} APPEND PROPERTY targets ${target}) + add_dependencies(all_executables_${PROJECT_NAME} ${target}) endmacro() # ---------------------------------------------------------------- @@ -978,6 +981,7 @@ macro(xo_add_utest_executable target sources) TARGET all_utest_executables_${PROJECT_NAME} APPEND PROPERTY targets ${target}) + add_dependencies(all_utest_executables_${PROJECT_NAME} ${target}) endmacro() @@ -1597,6 +1601,7 @@ macro(xo_pybind11_library target projectTargets source_files) TARGET all_libraries_${PROJECT_NAME} APPEND PROPERTY targets ${target}) + add_dependencies(all_libraries_${PROJECT_NAME} ${target}) set_property( TARGET ${target} From 1c03229d0b957be4f0cc1b0a904e6c5476e049fa Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 11 Mar 2026 08:41:57 -0500 Subject: [PATCH 46/55] build: retiring REPR argument to xo_add_genfacetimpl() --- cmake/xo_macros/xo_cxx.cmake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 2003515f..13ca3f9a 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1738,7 +1738,6 @@ function(xo_add_genfacetimpl) FACET_PKG # package providing abstract interface FACET_DIR # facet directory (instead of FACET_PKG) FACET # facet name - REPR # representation name INPUT # Input .json5 file OUTPUT_HPP_DIR # Directory for .hpp files OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers @@ -1754,9 +1753,6 @@ function(xo_add_genfacetimpl) if(NOT DEFINED GF_FACET) message(FATAL_ERROR "xo_add_genfacetimpl: FACET is required") endif() - if(NOT DEFINED GF_REPR) - message(FATAL_ERROR "xo_add_genfacetimpl: REPR is required") - endif() if(NOT DEFINED GF_INPUT) message(FATAL_ERROR "xo_add_genfacetimpl: INPUT is required") endif() From a1b707c4d8934091b91f9286ca5799af9633e790 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 11 Mar 2026 09:14:30 -0500 Subject: [PATCH 47/55] cmake build: + xo-genfacet-all target for umbrella build --- cmake/xo_macros/xo_cxx.cmake | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 13ca3f9a..4506ceaa 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1806,7 +1806,22 @@ function(xo_add_genfacet_all target_name) get_property(genfacet_targets DIRECTORY PROPERTY XO_GENFACET_TARGETS) if(genfacet_targets) add_custom_target(${target_name} DEPENDS ${genfacet_targets}) + + if(XO_SUBMODULE_BUILD) + set_property(GLOBAL APPEND PROPERTY XO_UMBRELLA_GENFACET_TARGETS ${target_name}) + endif() else() message(WARNING "xo_add_genfacet_all: no genfacet targets found") endif() endfunction() + +# create umbrella target that depends on all per-satellite genfacet-all targets. +# only meaningful in umbrella build (XO_SUBMODULE_BUILD=True). +function(xo_umbrella_genfacet_all target_name) + get_property(all_targets GLOBAL PROPERTY XO_UMBRELLA_GENFACET_TARGETS) + if(all_targets) + add_custom_target(${target_name} DEPENDS ${all_targets}) + else() + message(WARNING "xo_umbrella_genfacet_all: no umbrella genfacet targets found") + endif() +endfunction() From e5f68f93dff9fcf7d98efd63783d9960783dd586 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 11 Mar 2026 09:16:41 -0500 Subject: [PATCH 48/55] xo-cmake: retire OUTPUT_CPP_DIR in genfacetimpl macro --- cmake/xo_macros/xo_cxx.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 4506ceaa..ceedc3e2 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1741,7 +1741,6 @@ function(xo_add_genfacetimpl) INPUT # Input .json5 file OUTPUT_HPP_DIR # Directory for .hpp files OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers - OUTPUT_CPP_DIR # Directory for .cpp files ) set(multiValueArgs "") From 7fa0d491b7f5cd15f8047d9c82fb1a8158ad385c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 11 Mar 2026 09:49:19 -0500 Subject: [PATCH 49/55] xo-expression2: drop redundant OUTPUT_IMPL_SUBDIR args --- cmake/xo_macros/xo_cxx.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index ceedc3e2..4872043c 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1739,7 +1739,6 @@ function(xo_add_genfacetimpl) FACET_DIR # facet directory (instead of FACET_PKG) FACET # facet name INPUT # Input .json5 file - OUTPUT_HPP_DIR # Directory for .hpp files OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers ) set(multiValueArgs "") From c271c70146319ff76b344918979bf1278be7e8f7 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 11 Mar 2026 09:55:48 -0500 Subject: [PATCH 50/55] build: retire unused args from xo_add_genfacetimpl --- cmake/xo_macros/xo_cxx.cmake | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 4872043c..d6fb9369 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1736,10 +1736,8 @@ function(xo_add_genfacetimpl) set(oneValueArgs TARGET # Name for this generation target FACET_PKG # package providing abstract interface - FACET_DIR # facet directory (instead of FACET_PKG) FACET # facet name INPUT # Input .json5 file - OUTPUT_IMPL_SUBDIR # Subdirectory name for impl headers ) set(multiValueArgs "") @@ -1754,25 +1752,23 @@ function(xo_add_genfacetimpl) if(NOT DEFINED GF_INPUT) message(FATAL_ERROR "xo_add_genfacetimpl: INPUT is required") endif() - if(NOT DEFINED GF_FACET_DIR) - if (NOT DEFINED GF_FACET_PKG) - message(FATAL_ERROR "xo_add_genfacetimpl: FACET_PKG or FACET_DIR required") - else() - # share_${GF_FACET_PKG} is a cmake target created by xo_add_shared_library4() - # or similar when a facet-provider (e.g. xo-gc) builds from source in the same - # cmake context (XO_SUBMODULE_BUILD=True). - # - # It is NOT exported in the installed cmake - # config, so it won't exist when the facet package is consumed as an installed - # dependency (e.g. in a standalone nix build). - # - if(NOT TARGET share_${GF_FACET_PKG}) - message(STATUS "xo_add_genfacetimpl: share_${GF_FACET_PKG} not available; skipping ${GF_TARGET}") - return() - endif() - get_target_property(_facet_dir share_${GF_FACET_PKG} path) - set(GF_FACET_DIR ${_facet_dir}) + if (NOT DEFINED GF_FACET_PKG) + message(FATAL_ERROR "xo_add_genfacetimpl: FACET_PKG required") + else() + # share_${GF_FACET_PKG} is a cmake target created by xo_add_shared_library4() + # or similar when a facet-provider (e.g. xo-gc) builds from source in the same + # cmake context (XO_SUBMODULE_BUILD=True). + # + # It is NOT exported in the installed cmake + # config, so it won't exist when the facet package is consumed as an installed + # dependency (e.g. in a standalone nix build). + # + if(NOT TARGET share_${GF_FACET_PKG}) + message(FATAL_ERROR "xo_add_genfacetimpl: share_${GF_FACET_PKG} not available; skipping ${GF_TARGET}") + return() endif() + get_target_property(_facet_dir share_${GF_FACET_PKG} path) + set(GF_FACET_DIR ${_facet_dir}) endif() find_program(GENFACET_EXECUTABLE NAMES genfacet From ce708ed8ee6ca869f9965246b9625198650a7447 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 11 Mar 2026 10:03:46 -0500 Subject: [PATCH 51/55] build: retire FACET argument to genfacetimpl --- cmake/xo_macros/xo_cxx.cmake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index d6fb9369..52169a3d 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -1736,7 +1736,6 @@ function(xo_add_genfacetimpl) set(oneValueArgs TARGET # Name for this generation target FACET_PKG # package providing abstract interface - FACET # facet name INPUT # Input .json5 file ) set(multiValueArgs "") @@ -1746,9 +1745,6 @@ function(xo_add_genfacetimpl) if(NOT DEFINED GF_TARGET) message(FATAL_ERROR "xo_add_genfacetimpl: TARGET is required") endif() - if(NOT DEFINED GF_FACET) - message(FATAL_ERROR "xo_add_genfacetimpl: FACET is required") - endif() if(NOT DEFINED GF_INPUT) message(FATAL_ERROR "xo_add_genfacetimpl: INPUT is required") endif() From 3557ab833545e5f4657d3391f3b833ba4aaaeacc Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 4 Apr 2026 14:34:57 -0400 Subject: [PATCH 52/55] build: + --clobber feature for .build/reconfigure script copies reconfigure script to /tmp before killing build directory. --- share/xo-macros/xo-reconfigure.in | 76 ++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/share/xo-macros/xo-reconfigure.in b/share/xo-macros/xo-reconfigure.in index d3e55e55..9078b4d3 100644 --- a/share/xo-macros/xo-reconfigure.in +++ b/share/xo-macros/xo-reconfigure.in @@ -3,20 +3,35 @@ # Generated by cmake - rerun to reconfigure dry_run_flag=false +clobber=false testing=@ENABLE_TESTING@ +orig_path=@CMAKE_BINARY_DIR@/reconfigure +self_path=$(cd $(dirname $0) && pwd)/$(basename $0) + # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in -n|--dry-run) dry_run_flag=true ;; + --print-source-dir) + echo @CMAKE_SOURCE_DIR@ + exit 0 + ;; + --print-build-dir) + echo @CMAKE_BINARY_DIR@ + exit 0 + ;; --enable-testing) testing=1 ;; --disable-testing) testing=0 ;; + --clobber) + clobber=true + ;; --) break; ;; @@ -24,8 +39,14 @@ while [[ $# -gt 0 ]]; do echo "error: xo-reconfigure: unexpected argument [$1]" echo "usage: xo-reconfigure" echo " xo-reconfigure [-n|--dry-run]" + echo " [--print-source-dir|--print-build-dir]" + echo " [--clobber]" echo " [--enable-testing|--disable-testing]" echo " -- CMAKE_ARGS" + echo "" + echo "1. clobber ignores --enable-testing/--disable-testing flags" + echo " use to cleanse build directory when stale build artifacts" + echo " are confusing cmake" exit 1 esac @@ -54,11 +75,54 @@ CMAKE_CMD=( "${@}" ) -if [[ ${dry_run_flag} = true ]]; then - # Print the command with proper quoting for copy-paste - printf '%q ' "${CMAKE_CMD[@]}" | sed -e 's: -D: \\\n -D:g' - printf '\n' +if [[ ${clobber} == true ]]; then + if [[ ${dry_run_flag} == true ]]; then + 2>&1 echo "would clobber directory [@CMAKE_BINARY_DIR@], then run reconfigure:" + 2>&1 echo + else + if [[ ${orig_path} == ${self_path} ]]; then + # must copy self out of doomed build directory before we clobber + + # 1. rescue reconfigure script from build directory + tmpfile=$(mktemp /tmp/reconfigure-XXX) + cp ${self_path} ${tmpfile} + + # 2. copy needs to be executable + chmod +x ${tmpfile} + + # 2. run reconfigure from non-build directory + exec ${tmpfile} --clobber + else + # running outside build directory + + 2>&1 echo "removing build directory [@CMAKE_BINARY_DIR@]" + rm -rf @CMAKE_BINARY_DIR@ + + 2>&1 echo "run cmake:" + 2>&1 echo "${CMAKE_CMD[@]}" + 2>&1 echo + + # run reconfigure + "${CMAKE_CMD[@]}" + + if [[ $(realpath ${self_path}) == /tmp/* ]]; then + # since we're in /tmp, delete ourselves as final step + rm -f ${self_path} + fi + + # rehearse cmake command for visibility + 2>&1 echo "" + 2>&1 echo "cmake command was:" + 2>&1 echo "${CMAKE_CMD[@]}" + fi + fi else - # Execute the command - "${CMAKE_CMD[@]}" + if [[ ${dry_run_flag} = true ]]; then + # Print the command with proper quoting for copy-paste + printf '%q ' "${CMAKE_CMD[@]}" | sed -e 's: -D: \\\n -D:g' + printf '\n' + else + # Execute the command + "${CMAKE_CMD[@]}" + fi fi From 346fe128980db1970ddafbd6bd46c3adcfd0e999 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 9 Apr 2026 20:50:32 -0400 Subject: [PATCH 53/55] build: coverage build working in umbrella builds (!) 1. more recent lcov is persnickety, need to ignore some errors. 2. need more careful assembly of subdirs with .gcda/.gcno info. lcov doesn't like duplication here. --- bin/xo-cmake-lcov-harness.in | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/bin/xo-cmake-lcov-harness.in b/bin/xo-cmake-lcov-harness.in index 414b950a..2a54b8e5 100755 --- a/bin/xo-cmake-lcov-harness.in +++ b/bin/xo-cmake-lcov-harness.in @@ -83,14 +83,32 @@ mkdir -p $builddir/ccov # --base-directory ${srcdir}/foo --directory ${builddir}/foo/CMakeFiles/foo_target.dir \ # --base-directory ${srcdir}/bar/quux --directory ${builddir}/bar/quux/CMakeFiles/target4quux.dir # -primarydirs=$(cd ${builddir} && find -name '*.gcno' \ - | xargs --replace=xx dirname xx \ - | uniq \ - | sed -e 's:^\./::') +# collect unique directories containing .gcno files, +# then remove any that are subdirectories of another in the list. +# lcov scans recursively, so passing both parent/ and parent/facet/ +# causes "duplicate file" errors. +_alldirs=$(cd ${builddir} && find -name '*.gcno' \ + | xargs --replace=xx dirname xx \ + | sort -u \ + | sed -e 's:^\./::') + +primarydirs="" +for d in ${_alldirs}; do + # check if any existing entry is a prefix of d + is_nested=false + for p in ${primarydirs}; do + case "${d}" in + "${p}"/*) is_nested=true; break ;; + esac + done + if ! ${is_nested}; then + primarydirs="${primarydirs} ${d}" + fi +done #echo "primarydirs=${primarydirs}" -cmd="${lcov} --output ${outputstem}.info --capture --ignore-errors source" +cmd="${lcov} --output ${outputstem}.info --capture --ignore-errors source --ignore-errors empty --ignore-errors inconsistent,inconsistent" for bdir in ${primarydirs}; do sdir=$(dirname $(dirname ${bdir})) @@ -107,11 +125,11 @@ ${cmd} # keep only files with paths under source tree # (don't want coverage for external libraries such as libstdc++ etc) -${lcov} --extract ${outputstem}.info "${srcdir}/*" --output ${outputstem}2.info +${lcov} --extract ${outputstem}.info "${srcdir}/*" --output ${outputstem}2.info --ignore-errors inconsistent,inconsistent # remove unit test dirs # (we're interested in coverage of our installed code, not of the unit tests that exercise it) -${lcov} --remove ${outputstem}2.info '*/utest/*' --output ${outputstem}3.info +${lcov} --remove ${outputstem}2.info '*/utest/*' --output ${outputstem}3.info --ignore-errors inconsistent,inconsistent # generate .html tree mkdir -p ${builddir}/ccov/html From 0cb64a72f2e335b517c9f4985e227543e2ae510e Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 20 Apr 2026 08:57:22 -0400 Subject: [PATCH 54/55] build: coverage working at umbrella level on osx Uses llvm-cov tooling --- CMakeLists.txt | 19 ++++- bin/xo-cmake-config.in | 10 ++- bin/xo-cmake-llvmcov-harness.in | 118 +++++++++++++++++++++++++++++ cmake/xo_macros/xo_cxx.cmake | 127 ++++++++++++++++++++++++++++---- share/xo-macros/gen-ccov.in | 31 +++----- 5 files changed, 265 insertions(+), 40 deletions(-) create mode 100644 bin/xo-cmake-llvmcov-harness.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 51f2a45a..b54c712c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,31 @@ include (GNUInstallDirs) set(XO_PROJECT_NAME xo_macros) -# LCOV_EXECUTABLE,GENHTML_EXECUTABLE: needed by xo-cmake-lcov-harness.in +# LCOV_EXECUTABLE,GENHTML_EXECUTABLE: needed by xo-cmake-lcov-harness.in (gcov toolchain) find_program(LCOV_EXECUTABLE NAMES lcov) find_program(GENHTML_EXECUTABLE NAMES genhtml) +# LLVM_PROFDATA_EXECUTABLE,LLVM_COV_EXECUTABLE: needed by xo-cmake-llvmcov-harness.in (llvm toolchain) +find_program(LLVM_PROFDATA_EXECUTABLE NAMES llvm-profdata) +find_program(LLVM_COV_EXECUTABLE NAMES llvm-cov) + configure_file( ${PROJECT_SOURCE_DIR}/bin/xo-cmake-lcov-harness.in ${PROJECT_BINARY_DIR}/xo-cmake-lcov-harness @ONLY ) +file(CHMOD ${PROJECT_BINARY_DIR}/xo-cmake-lcov-harness + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +configure_file( + ${PROJECT_SOURCE_DIR}/bin/xo-cmake-llvmcov-harness.in + ${PROJECT_BINARY_DIR}/xo-cmake-llvmcov-harness + @ONLY + ) +file(CHMOD ${PROJECT_BINARY_DIR}/xo-cmake-llvmcov-harness + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) configure_file( ${PROJECT_SOURCE_DIR}/bin/xo-cmake-config.in @@ -41,6 +57,7 @@ install( install( FILES "${PROJECT_BINARY_DIR}/xo-cmake-lcov-harness" + "${PROJECT_BINARY_DIR}/xo-cmake-llvmcov-harness" "${PROJECT_BINARY_DIR}/xo-cmake-config" "${PROJECT_BINARY_DIR}/xo-build" PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE diff --git a/bin/xo-cmake-config.in b/bin/xo-cmake-config.in index 8cb3f39d..5a501a98 100755 --- a/bin/xo-cmake-config.in +++ b/bin/xo-cmake-config.in @@ -1,7 +1,7 @@ #!/usr/bin/env bash usage() { - echo "$0 [-u|--usage|-h|--help|--lcov-exe|--genhtml-exe|--lcov-harness-exe|--gen-ccov-template|--reconfigure-template|--cmake-module-path|--subsystem-list]" 1>&2 + echo "$0 [-u|--usage|-h|--help|--lcov-exe|--genhtml-exe|--lcov-harness-exe|--llvmcov-harness-exe|--gen-ccov-template|--reconfigure-template|--cmake-module-path|--subsystem-list]" 1>&2 } help() { @@ -17,7 +17,8 @@ Options: --cmake-module-path report directory providing xo-cmake macros (will use/appear-in CMAKE_MODULE_PATH) --lcov-exe report path to 'lcov' executable --genhtml-exe report path to 'genhtml' executable - --lcov-harness-exe report path to 'xo-cmake-lcov-harness' executable + --lcov-harness-exe report path to 'xo-cmake-lcov-harness' executable (gcov toolchain) + --llvmcov-harness-exe report path to 'xo-cmake-llvmcov-harness' executable (llvm toolchain) --gen-ccov-template report path to 'gen-ccov.in' template --reconfigure-template report path to 'xo-reconfigure.in' template --doxygen-template report path to 'Doxyfile.in' template @@ -46,6 +47,9 @@ while [[ $# > 0 ]]; do --lcov-harness-exe) cmd='lcov_harness_exe' ;; + --llvmcov-harness-exe) + cmd='llvmcov_harness_exe' + ;; --gen-ccov-template) cmd='gen_ccov_template' ;; @@ -81,6 +85,8 @@ elif [[ $cmd == 'genhtml_exe' ]]; then echo -n @GENHTML_EXECUTABLE@ elif [[ $cmd == 'lcov_harness_exe' ]]; then echo -n @CMAKE_INSTALL_FULL_BINDIR@/xo-cmake-lcov-harness +elif [[ $cmd == 'llvmcov_harness_exe' ]]; then + echo -n @CMAKE_INSTALL_FULL_BINDIR@/xo-cmake-llvmcov-harness elif [[ $cmd == 'gen_ccov_template' ]]; then echo -n @CMAKE_INSTALL_FULL_DATADIR@/xo-macros/gen-ccov.in elif [[ $cmd == 'reconfigure_template' ]]; then diff --git a/bin/xo-cmake-llvmcov-harness.in b/bin/xo-cmake-llvmcov-harness.in new file mode 100644 index 00000000..13a47601 --- /dev/null +++ b/bin/xo-cmake-llvmcov-harness.in @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# +# xo-cmake-llvmcov-harness: +# clang/llvm source-based coverage driver. +# Parallel to xo-cmake-lcov-harness (gcov path). +# +# Pipeline: +# 1. run ctest with LLVM_PROFILE_FILE pointing into ${builddir}/ccov/raw +# 2. merge the produced .profraw files to ${outputstem}.profdata +# 3. read ${builddir}/ccov/utest-binaries.list (written by xo_umbrella_coverage_config) +# 4. emit browsable HTML under ${builddir}/ccov/html +# 5. print summary table to stdout +# + +set -euo pipefail + +srcdir=${1:-} +builddir=${2:-} +outputstem=${3:-} + +if [[ -z "${srcdir}" || -z "${builddir}" ]]; then + echo "usage: $0 srcdir builddir [outputstem]" >&2 + exit 1 +fi + +if [[ -z "${outputstem}" ]]; then + outputstem=${builddir}/ccov/out +fi + +llvm_profdata=@LLVM_PROFDATA_EXECUTABLE@ +llvm_cov=@LLVM_COV_EXECUTABLE@ + +for kv in "llvm-profdata:${llvm_profdata}" "llvm-cov:${llvm_cov}"; do + name=${kv%%:*} + path=${kv#*:} + if [[ -z "${path}" || "${path}" == *NOTFOUND ]]; then + echo "xo-cmake-llvmcov-harness: ${name} not found during xo-cmake build/install" >&2 + exit 1 + fi +done + +manifest=${builddir}/ccov/utest-binaries.list +if [[ ! -f "${manifest}" ]]; then + echo "xo-cmake-llvmcov-harness: missing ${manifest}" >&2 + echo "xo-cmake-llvmcov-harness: expected xo_umbrella_coverage_config() to produce it" >&2 + exit 1 +fi + +rawdir=${builddir}/ccov/raw +htmldir=${builddir}/ccov/html +mkdir -p "${rawdir}" "${htmldir}" + +# clear prior raws so we don't mix stale data from a past run +rm -f "${rawdir}"/*.profraw + +# 1. run tests. LLVM_PROFILE_FILE pattern: %h=hostname %m=binary-signature %p=pid +# keeps output unique across parallel tests and across binaries. +# +# tolerate ctest failures: a crashing/asserting utest still emits .profraw +# before dying, and we want a coverage report even when some tests are broken. +# Surface the exit code at the end. +ctest_rc=0 +(cd "${builddir}" && \ + LLVM_PROFILE_FILE="${rawdir}/%h-%m-%p.profraw" \ + ctest --output-on-failure) || ctest_rc=$? + +# 2. merge .profraw -> .profdata +shopt -s nullglob +raws=("${rawdir}"/*.profraw) +if [[ ${#raws[@]} -eq 0 ]]; then + echo "xo-cmake-llvmcov-harness: no .profraw produced - was the build instrumented?" >&2 + exit 1 +fi +"${llvm_profdata}" merge -sparse "${raws[@]}" -o "${outputstem}.profdata" + +# 3. read binary list. drop blank lines and anything not executable +# (target may be in manifest but unbuilt if 'ninja ccov' was invoked +# before a full build; defensive). +readarray -t all_bins < "${manifest}" +bins=() +for b in "${all_bins[@]}"; do + [[ -n "${b}" && -x "${b}" ]] && bins+=("${b}") +done +if [[ ${#bins[@]} -eq 0 ]]; then + echo "xo-cmake-llvmcov-harness: no executable utests found in manifest" >&2 + exit 1 +fi + +# llvm-cov takes the first binary positionally; the rest via -object. +first=${bins[0]} +objargs=() +if [[ ${#bins[@]} -gt 1 ]]; then + for b in "${bins[@]:1}"; do objargs+=(-object "${b}"); done +fi + +# exclude test code and toolchain headers from the report +ignore_re='/utest/|/nix/store/' + +# 4. HTML +"${llvm_cov}" show \ + -instr-profile="${outputstem}.profdata" \ + -format=html \ + -output-dir="${htmldir}" \ + -show-line-counts-or-regions \ + -ignore-filename-regex="${ignore_re}" \ + "${first}" "${objargs[@]}" + +# 5. summary to stdout +"${llvm_cov}" report \ + -instr-profile="${outputstem}.profdata" \ + -ignore-filename-regex="${ignore_re}" \ + "${first}" "${objargs[@]}" + +echo +echo "xo-cmake-llvmcov-harness: HTML report -> ${htmldir}/index.html" +if [[ ${ctest_rc} -ne 0 ]]; then + echo "xo-cmake-llvmcov-harness: WARNING ctest exited ${ctest_rc} - coverage is from successful tests only" >&2 +fi diff --git a/cmake/xo_macros/xo_cxx.cmake b/cmake/xo_macros/xo_cxx.cmake index 52169a3d..6d10beb7 100644 --- a/cmake/xo_macros/xo_cxx.cmake +++ b/cmake/xo_macros/xo_cxx.cmake @@ -236,30 +236,54 @@ endmacro() # macro(xo_toplevel_coverage_config2) if ("${CMAKE_BUILD_TYPE}" STREQUAL "coverage") - #find_program(LCOV_EXECUTABLE NAMES lcov) - #find_program(GENHTML_EXECUTABLE NAMES genhtml) - # see bin/xo-cmake-lcov-harness in this repo - execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --lcov-harness-exe OUTPUT_VARIABLE XO_CMAKE_LCOV_HARNESS_EXECUTABLE) - execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --gen-ccov-template OUTPUT_VARIABLE XO_CMAKE_GEN_CCOV_TEMPLATE) + # select coverage toolchain by compiler family: + # GNU -> gcov (post-process with lcov/genhtml) + # Clang/AppleClang -> llvm source-based coverage (post-process with llvm-profdata/llvm-cov) + # Apple clang ships no libgcov, so the gcov path does not link on darwin. + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + set(XO_COVERAGE_TOOLCHAIN "llvm" CACHE STRING "coverage toolchain: gcov or llvm") + set(_xo_cov_compile -ggdb -Og -fprofile-instr-generate -fcoverage-mapping) + set(_xo_cov_link -fprofile-instr-generate) + else() + set(XO_COVERAGE_TOOLCHAIN "gcov" CACHE STRING "coverage toolchain: gcov or llvm") + set(_xo_cov_compile -ggdb -Og -fprofile-arcs -ftest-coverage) + set(_xo_cov_link --coverage) + endif() + message(STATUS "XO_COVERAGE_TOOLCHAIN=${XO_COVERAGE_TOOLCHAIN}") if (NOT DEFINED PROJECT_CXX_FLAGS_COVERAGE) - # note: for clang would use -fprofile-instr-generate -fcoverage-mapping here instead and also at link time - set(PROJECT_CXX_FLAGS_COVERAGE -ggdb -Og -fprofile-arcs -ftest-coverage + set(PROJECT_CXX_FLAGS_COVERAGE ${_xo_cov_compile} CACHE STRING "coverage c++ compiler flags") endif() message(STATUS "PROJECT_CXX_FLAGS_COVERAGE: coverage c++ flags are [${PROJECT_CXX_FLAGS_COVERAGE}]") add_compile_options("$<$:${PROJECT_CXX_FLAGS_COVERAGE}>") - # when -DCMAKE_BUILD_TYPE=coverage, must also link executables with gcov - link_libraries("$<$:gcov>") + add_link_options("$<$:${_xo_cov_link}>") - if("${XO_CMAKE_GEN_CCOV_TEMPLATE}" STREQUAL "") - message(ERROR "xo_toplevel_testing_config2: XO_CMAKE_GEN_CCOV_TEMPLATE not set") - message(ERROR "see xo_toplevel_testing_options2()") - else() - message(DEBUG "XO_CMAKE_GEN_CCOV_TEMPLATE=${XO_CMAKE_GEN_CCOV_TEMPLATE}") - configure_file(${XO_CMAKE_GEN_CCOV_TEMPLATE} ${PROJECT_BINARY_DIR}/gen-ccov @ONLY) - file(CHMOD ${PROJECT_BINARY_DIR}/gen-ccov PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + # per-project gen-ccov: only in standalone satellite builds. + # umbrella builds delegate to xo_umbrella_coverage_config(), which reads + # the in-tree template directly (bypassing the installed xo-cmake-config). + if (NOT XO_SUBMODULE_BUILD) + # xo-cmake-config reports installed paths of the harnesses + template. + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --lcov-harness-exe OUTPUT_VARIABLE _xo_cov_lcov_harness) + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --llvmcov-harness-exe OUTPUT_VARIABLE _xo_cov_llvmcov_harness) + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --gen-ccov-template OUTPUT_VARIABLE XO_CMAKE_GEN_CCOV_TEMPLATE) + + # pick the harness matching XO_COVERAGE_TOOLCHAIN + if (XO_COVERAGE_TOOLCHAIN STREQUAL "llvm") + set(XO_CMAKE_COV_HARNESS_EXECUTABLE ${_xo_cov_llvmcov_harness}) + else() + set(XO_CMAKE_COV_HARNESS_EXECUTABLE ${_xo_cov_lcov_harness}) + endif() + + if("${XO_CMAKE_GEN_CCOV_TEMPLATE}" STREQUAL "") + message(WARNING "xo_toplevel_coverage_config2: XO_CMAKE_GEN_CCOV_TEMPLATE not set " + "(xo-cmake not installed? skipping per-project gen-ccov)") + else() + message(DEBUG "XO_CMAKE_GEN_CCOV_TEMPLATE=${XO_CMAKE_GEN_CCOV_TEMPLATE}") + configure_file(${XO_CMAKE_GEN_CCOV_TEMPLATE} ${PROJECT_BINARY_DIR}/gen-ccov @ONLY) + file(CHMOD ${PROJECT_BINARY_DIR}/gen-ccov PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + endif() endif() endif() endmacro() @@ -301,6 +325,68 @@ function(xo_generate_reconfigure_script) message(STATUS "Generated ${_reconfigure_script}") endfunction() +# umbrella-level coverage wiring: emits utest-binaries.list manifest +# and the `ccov` target, driven by the singleton global utest list +# populated by xo_add_utest_executable. +# +# Call once from the umbrella CMakeLists.txt, after all add_subdirectory() +# calls for satellites. Inside a satellite-standalone build, +# xo_utest_coverage_config2 fills the equivalent role. +# +macro(xo_umbrella_coverage_config) + if ("${CMAKE_BUILD_TYPE}" STREQUAL "coverage") + get_property(_all_utests GLOBAL PROPERTY xo_all_utest_executables) + get_property(_all_libs GLOBAL PROPERTY xo_all_shared_libraries) + list(LENGTH _all_utests _n_utests) + list(LENGTH _all_libs _n_libs) + message(STATUS "xo_umbrella_coverage_config: ${_n_utests} utests + ${_n_libs} libraries") + + # manifest: one resolved binary path per line. Both utest executables + # and satellite shared libraries must appear so that llvm-cov can + # resolve coverage mappings from every instrumented object (.cpp in + # libs, templates/header code in exes). $ is + # deferred until build-graph time, so file(GENERATE) is required + # (configure_file wouldn't resolve the generator expressions). + set(_bin_lines) + foreach(_t ${_all_utests} ${_all_libs}) + list(APPEND _bin_lines "$") + endforeach() + string(JOIN "\n" _bin_contents ${_bin_lines}) + file(GENERATE + OUTPUT ${CMAKE_BINARY_DIR}/ccov/utest-binaries.list + CONTENT "${_bin_contents}\n") + + # gen-ccov: use in-tree template directly, bypassing the installed + # xo-cmake-config (which doesn't exist in a from-scratch umbrella build). + # pick the harness matching XO_COVERAGE_TOOLCHAIN (set by xo_toplevel_coverage_config2). + if (XO_COVERAGE_TOOLCHAIN STREQUAL "llvm") + set(XO_CMAKE_COV_HARNESS_EXECUTABLE ${CMAKE_BINARY_DIR}/xo-cmake/xo-cmake-llvmcov-harness) + else() + set(XO_CMAKE_COV_HARNESS_EXECUTABLE ${CMAKE_BINARY_DIR}/xo-cmake/xo-cmake-lcov-harness) + endif() + message(STATUS "xo_umbrella_coverage_config: harness=${XO_CMAKE_COV_HARNESS_EXECUTABLE}") + + set(_gen_ccov_template ${XO_UMBRELLA_SOURCE_DIR}/xo-cmake/share/xo-macros/gen-ccov.in) + if (EXISTS ${_gen_ccov_template}) + configure_file(${_gen_ccov_template} ${CMAKE_BINARY_DIR}/gen-ccov @ONLY) + file(CHMOD ${CMAKE_BINARY_DIR}/gen-ccov + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + else() + message(WARNING "xo_umbrella_coverage_config: template not found: ${_gen_ccov_template}") + endif() + + # ccov target. DEPENDS on utest targets ensures they build before + # gen-ccov runs (which itself drives `ctest`). + add_custom_target( + ccov + DEPENDS ${_all_utests} + COMMAND ${CMAKE_BINARY_DIR}/gen-ccov + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Generating coverage report -> ${CMAKE_BINARY_DIR}/ccov/html") + endif() +endmacro() + # target to build+install coverage report. # macro(xo_utest_coverage_config2) @@ -806,6 +892,11 @@ macro(xo_add_shared_library4 target projectTargets targetversion soversion sourc APPEND PROPERTY targets ${target}) + # singleton global list of every shared library across satellites. + # consumed by xo_umbrella_coverage_config to extend the ccov binary + # manifest so llvm-cov resolves .cpp coverage in satellite .dylibs/.so. + set_property(GLOBAL APPEND PROPERTY xo_all_shared_libraries ${target}) + set_target_properties( ${target} PROPERTIES @@ -983,6 +1074,10 @@ macro(xo_add_utest_executable target sources) PROPERTY targets ${target}) add_dependencies(all_utest_executables_${PROJECT_NAME} ${target}) + # singleton global list of every utest target: survives across project() scopes, + # so umbrella and standalone-satellite builds can query one source of truth + set_property(GLOBAL APPEND PROPERTY xo_all_utest_executables ${target}) + endmacro() # ---------------------------------------------------------------- diff --git a/share/xo-macros/gen-ccov.in b/share/xo-macros/gen-ccov.in index c81c3f53..1d51e688 100644 --- a/share/xo-macros/gen-ccov.in +++ b/share/xo-macros/gen-ccov.in @@ -3,28 +3,17 @@ srcdir=@PROJECT_SOURCE_DIR@ builddir=@PROJECT_BINARY_DIR@ -#lcov=@LCOV_EXECUTABLE@ -#genhtml=@GENHTML_EXECUTABLE@ -# -#if [[ $lcov == "LCOV_EXECUTABLE-NOTFOUND" ]]; then -# echo "gen-ccov: lcov executable not found" -# exit 1 -#fi -# -#if [[ $genhtml == "GENHTML_EXECUTABLE-NOTFOUND" ]]; then -# echo "gen-ccov: genhtml executable not found" -# exit 1 -#fi +# path to the toolchain-specific harness. Configured by either +# xo_toplevel_coverage_config2 (standalone satellite build) or +# xo_umbrella_coverage_config (umbrella build). Resolves to one of: +# xo-cmake-lcov-harness (gcov toolchain) +# xo-cmake-llvmcov-harness (llvm toolchain) +covharness=@XO_CMAKE_COV_HARNESS_EXECUTABLE@ -lcovharness=@XO_CMAKE_LCOV_HARNESS_EXECUTABLE@ - -if [[ -z $lcovharness ]]; then - echo "gen-ccov: lcov-harness executable (XO_CMAKE_LCOV_HARNESS_EXECUTABLE) not configured" - echo "gen-ccov: expect value of path/to/xo-cmake-config --lcov-harness-exe" - echo "gen-ccov: stored in XO_CMAKE_LCOV_HARNESS_EXECUTABLE by xo_toplevel_testing_options2()" +if [[ -z $covharness ]]; then + echo "gen-ccov: coverage harness (XO_CMAKE_COV_HARNESS_EXECUTABLE) not configured" + echo "gen-ccov: should be set by xo_umbrella_coverage_config or xo_toplevel_coverage_config2" exit 1 fi -# TODO: allow providing LCOV_EXECUTABLE GENHTML_EXECUTABLE here - -$lcovharness $srcdir $builddir +$covharness $srcdir $builddir From 09f7ad16806f320f8dcdee6c0e3d5e2c781dc134 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 22 May 2026 09:20:35 -0400 Subject: [PATCH 55/55] xo-cmake: xo-build + git subtree for satellites xo-build --add-umbrella-remote XONAME --pull-umbrella-remote XONAME --push-umbrella-remote XONAME --- bin/xo-build.in | 84 +++++++++++++++++++++++++++++++++++++++---- etc/xo/subsystem-list | 1 + 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/bin/xo-build.in b/bin/xo-build.in index 9c36b434..4f1caf92 100644 --- a/bin/xo-build.in +++ b/bin/xo-build.in @@ -6,10 +6,12 @@ usage() { cat < 0 ]]; do case "$1" in @@ -98,9 +107,6 @@ while [[ $# > 0 ]]; do --xoname=*) xoname="${1#*=}" ;; - --repo) - repo_flag=1 - ;; --clone) clone_flag=1 ;; @@ -137,6 +143,18 @@ while [[ $# > 0 ]]; do --with-vulkan=*) with_vulkan="${1#*=}" ;; + --repo) + repo_flag=1 + ;; + --add-umbrella-remote) + add_umbrella_remote_flag=1 + ;; + --pull-umbrella-remote) + pull_umbrella_remote_flag=1 + ;; + --push-umbrella-remote) + push_umbrella_remote_flag=1 + ;; xo-*) xoname="$1" ;; @@ -169,7 +187,12 @@ subsystem_list() { cat ${SUBSYSTEMLIST_FILE} } -XO_REPO_STEM="https://github.com/Rconybea" +XO_REPO_STEM= +if [[ $(whoami) == "roland" ]]; then + XO_REPO_STEM="git@github.com:Rconybea" +else + XO_REPO_STEM="https://github.com/Rconybea" +fi printrepo() { xoname=$1 @@ -222,6 +245,52 @@ if [[ $repo_flag -eq 1 ]]; then fi fi +if [[ $add_umbrella_remote_flag -eq 1 ]]; then + if [[ $clone_flag -eq 1 || $configure_flag -eq 1 || $build_flag -eq 1 || $build_docs_flag -eq 1 || $install_flag -eq 1 || $realclean_flag -eq 1 ]]; then + 2>&1 echo "error: xo-build: repo operation --add-umbrella-remote conflicts with" + 2>&1 echo " build operation --clone|--configure|--build|--build-docs|--install|--realclean" + exit 1 + fi + + if [[ -n "$xoname" ]]; then + url=$(printrepo $xoname) + + cmd="git remote add $xoname $url" + + if [[ $noop_flag -eq 1 ]]; then + echo $cmd + else + $cmd + fi + fi +fi + +if [[ $pull_umbrella_remote_flag -eq 1 ]]; then + if [[ -n "$xoname" ]]; then + branch=$(git branch --show-current) + cmd="git subtree pull --prefix=$xoname $xoname ${branch}" + + if [[ $noop_flag -eq 1 ]]; then + echo $cmd + else + $cmd + fi + fi +fi + +if [[ $push_umbrella_remote_flag -eq 1 ]]; then + if [[ -n "$xoname" ]]; then + branch=$(git branch --show-current) + cmd="git subtree push --prefix=$xoname $xoname ${branch}" + + if [[ $noop_flag -eq 1 ]]; then + echo $cmd + else + $cmd + fi + fi +fi + if [[ $clone_flag -eq 1 ]]; then if [[ -n "$xoname" ]]; then url=$(printrepo $xoname) @@ -243,6 +312,7 @@ if [[ $configure_flag -eq 1 ]]; then testingarg="-DENABLE_TESTING=1" fi + asmarg= if [[ $with_asm -eq 1 ]]; then asmarg="-DENABLE_ASM=1" fi diff --git a/etc/xo/subsystem-list b/etc/xo/subsystem-list index 555bd2e9..900cbb19 100644 --- a/etc/xo/subsystem-list +++ b/etc/xo/subsystem-list @@ -4,6 +4,7 @@ xo-alloc xo-object xo-refcnt xo-subsys +xo-testutil xo-randomgen xo-ordinaltree xo-pyutil