set -e SCRIPT_NAME="vk" PROJECT_ROOT=$(pwd) BUILD_DIR="$PROJECT_ROOT/build" function help() { echo "Vulkan Cookbook Project Management Tool" echo "" echo "Usage: $SCRIPT_NAME [command]" echo "" echo "Commands:" echo " new CHAPTER SAMPLE NAME Create a new sample (e.g. vk new 2 1 'GLFW Window')" echo " build [sample] Build project using Ninja (all or specific sample)" echo " run CHAPTER SAMPLE Run a specific sample" echo " cmake Regenerate CMake files" echo " list List all samples" echo " compile-commands Copy compile_commands.json to project root" echo " deps Run deps bootstrapping" echo " help Show this help message" } function new_sample() { if [ $# -lt 3 ]; then echo "Error: Missing arguments for 'new'" echo "Usage: $SCRIPT_NAME new CHAPTER SAMPLE NAME" return 1 fi CHAPTER=$1 SAMPLE=$2 NAME=$3 PADDED_CHAPTER=$(printf "%02d" "$CHAPTER") PADDED_SAMPLE=$(printf "%02d" "$SAMPLE") SAMPLE_DIR="$PROJECT_ROOT/Chapter$PADDED_CHAPTER/$PADDED_SAMPLE"_"$NAME" if [ -d "$SAMPLE_DIR" ]; then echo "Error: Sample directory already exists: $SAMPLE_DIR" return 1 fi echo "Creating new sample Ch${PADDED_CHAPTER}_${PADDED_SAMPLE}_${NAME}..." mkdir -p "$SAMPLE_DIR/src" # Create CMakeLists.txt cat >"$SAMPLE_DIR/CMakeLists.txt" <"$SAMPLE_DIR/src/main.cpp" < #include #include int main(int argc, char* argv[]) { std::cout << "Chapter ${CHAPTER}, Sample ${SAMPLE}: ${NAME}" << std::endl; uint32_t width = 1280; uint32_t height = 720; GLFWwindow *window = initWindow("Ch${PADDED_CHAPTER}_${PADDED_SAMPLE}: ${NAME}", width, height); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } glfwDestroyWindow(window); glfwTerminate(); return 0; } EOL echo "Successfully created new sample: $SAMPLE_DIR" echo "Run '$SCRIPT_NAME cmake' to update CMake configuration" } function build_project() { if [ ! -d "$BUILD_DIR" ]; then echo "Creating build directory..." mkdir -p "$BUILD_DIR" fi cd "$BUILD_DIR" if [ $# -eq 0 ]; then echo "Building all targets..." ninja else TARGET="Ch$1" echo "Building target: $TARGET" ninja "$TARGET" fi cd "$PROJECT_ROOT" } function run_sample() { if [ $# -lt 2 ]; then echo "Error: Missing arguments for 'run'" echo "Usage: $SCRIPT_NAME run CHAPTER SAMPLE" return 1 fi CHAPTER=$(printf "%02d" "$1") SAMPLE=$(printf "%02d" "$2") # Find the exact sample name SAMPLE_PATTERN="Ch${CHAPTER}_${SAMPLE}_*" SAMPLE_BINARY=$(find "$PROJECT_ROOT/bin" -name "$SAMPLE_PATTERN" -type f -executable 2>/dev/null | head -1) if [ -z "$SAMPLE_BINARY" ]; then echo "Error: Sample not found or not built: Chapter $CHAPTER, Sample $SAMPLE" echo "Try building it first: $SCRIPT_NAME build" return 1 fi echo "Running $SAMPLE_BINARY..." "$SAMPLE_BINARY" } function regenerate_cmake() { if [ ! -d "$BUILD_DIR" ]; then mkdir -p "$BUILD_DIR" fi # Update root CMakeLists.txt to include all chapters/samples echo "Updating root CMakeLists.txt with new samples..." # Find all chapter/sample directories, excluding the build directory SAMPLES=$(find "$PROJECT_ROOT" -type d -path "*/Chapter*/[0-9][0-9]_*" -not -path "*/**/build/*" -not -path "*/**/src" | sort) # Create a temporary file with updated subdirectories TEMP_CMAKE=$(mktemp) # Extract the content up to line 100 (before add_subdirectory calls) head -n "$(grep -n "BINARIES" CMakeLists.txt | head -n 1 | cut -d: -f1)" "$PROJECT_ROOT/CMakeLists.txt" >"$TEMP_CMAKE" # Add subdirectories for all samples for SAMPLE_DIR in $SAMPLES; do # Skip src directories if [[ "$SAMPLE_DIR" == */src ]]; then continue fi # Get relative path from project root REL_PATH=$(realpath --relative-to="$PROJECT_ROOT" "$SAMPLE_DIR") echo "add_subdirectory($REL_PATH)" >>"$TEMP_CMAKE" done # Update the CMakeLists.txt file mv "$TEMP_CMAKE" "$PROJECT_ROOT/CMakeLists.txt" cd "$BUILD_DIR" echo "Regenerating CMake files..." cmake -G Ninja .. cd "$PROJECT_ROOT" # Copy compile_commands.json to project root if [ -f "$BUILD_DIR/compile_commands.json" ]; then cp "$BUILD_DIR/compile_commands.json" "$PROJECT_ROOT/" echo "Copied compile_commands.json to project root" fi } function update_deps() { echo "Updating Dependencies" pushd "$PROJECT_ROOT/deps" ./bootstrap.py popd echo "Dependencies updated" } function list_samples() { echo "Available samples:" find "$PROJECT_ROOT" -type d -path "*/Chapter*/[0-9][0-9]_*" -not -path "build/*" -not -path "*/**/src" | sort | while read -r dir; do # Skip src directories if [[ "$dir" == */src ]]; then continue fi echo " $(basename "$(dirname "$dir")")/$(basename "$dir")" done } # Update main CMakeLists.txt if sample is added or removed function update_root_cmake() { # Check if we need to update the root CMakeLists.txt if [ "$1" == "new" ] || [ "$1" == "delete" ]; then regenerate_cmake fi } # Main command dispatcher if [ $# -eq 0 ]; then help exit 0 fi COMMAND=$1 shift case "$COMMAND" in "new") new_sample "$@" update_root_cmake "new" ;; "build") build_project "$@" ;; "run") run_sample "$@" ;; "cmake") regenerate_cmake ;; "list") list_samples ;; "deps") update_deps ;; "compile-commands") cp "$BUILD_DIR/compile_commands.json" "$PROJECT_ROOT/" 2>/dev/null || echo "No compile_commands.json found in build directory" ;; "help") help ;; *) echo "Unknown command: $COMMAND" help exit 1 ;; esac