#!/usr/bin/env bash # # 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") # set -euo pipefail usage() { 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 " --with-facet=FACET Add facet IDL scaffolding (requires --mode=shared)" 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/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 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 ;; --with-facet=*) FACET="${1#--with-facet=}" shift ;; --help|-h) usage ;; -*) echo "Error: unknown option $1" usage ;; *) break ;; esac done 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 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}" FACET_LC="$(echo "$FACET" | tr '[:upper:]' '[:lower:]')" # Get script directory to find umbrella root XO_UMBRELLA_ROOT=$(git rev-parse --show-toplevel) TARGET_DIR="${XO_UMBRELLA_ROOT}/${DIR_NAME}" # 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 echo "Creating ${DIR_NAME} (${MODE}) in ${XO_UMBRELLA_ROOT}..." # Create directories mkdir -p "${TARGET_DIR}/cmake" mkdir -p "${TARGET_DIR}/include/xo/${NAME}" 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 # ${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 else # Shared library mode if [[ -z "$FACET" ]]; then 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/${NAME}) #add_subdirectory(utest) # ---------------------------------------------------------------- # cmake export xo_export_cmake_config(\${PROJECT_NAME} \${PROJECT_VERSION} \${PROJECT_NAME}Targets) # 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 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) # 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") 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 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}) 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/${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 # Create placeholder .gitkeep in include dir touch "${TARGET_DIR}/include/xo/${NAME}/.gitkeep" echo "" 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/${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" echo " 3. Add ${DIR_NAME} to umbrella CMakeLists.txt" echo " 4. Uncomment dependencies as needed in src/${NAME}/CMakeLists.txt and Config.cmake.in" fi