diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dd20c7b..294bc32d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,4 +22,10 @@ endif() set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --extended-lambda") +# Include installation configuration +include(cmake/InstallSamples.cmake) + add_subdirectory(Samples) + +# Setup installation for all samples +setup_samples_install() diff --git a/cmake/InstallSamples.cmake b/cmake/InstallSamples.cmake new file mode 100644 index 00000000..98e16605 --- /dev/null +++ b/cmake/InstallSamples.cmake @@ -0,0 +1,168 @@ +# InstallSamples.cmake +# Configuration for installing CUDA samples to organized directory structure +# +# This module sets up installation paths organized by: +# - Target Architecture (x86_64, aarch64, etc.) +# - Target OS (linux, windows, darwin) +# - Build Type (release, debug) +# +# Default installation path: build/bin/${TARGET_ARCH}/${TARGET_OS}/${BUILD_TYPE} +# +# Installation structure: +# - Executables: installed to flat root directory only (easy access) +# - Data files (.ll, .ptx, .fatbin, etc.): installed to subdirectories (preserves relative paths) +# - run_tests.py handles path resolution automatically for both nested and flat structures +# +# Users can override by setting CMAKE_INSTALL_PREFIX or CUDA_SAMPLES_INSTALL_DIR + +# Detect target architecture - use lowercase of CMAKE_SYSTEM_PROCESSOR +string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" TARGET_ARCH) + +# Detect target OS +if(WIN32) + set(TARGET_OS "windows") +elseif(APPLE) + set(TARGET_OS "darwin") +elseif(UNIX) + if(CMAKE_SYSTEM_NAME MATCHES QNX) + set(TARGET_OS "qnx") + else() + set(TARGET_OS "linux") + endif() +else() + set(TARGET_OS "unknown") +endif() + +# Get build type (default to release if not specified) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "release") +endif() +string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_LOWER) + +# Set default install prefix to build/bin if not explicitly set by user +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/bin" CACHE PATH "Installation directory" FORCE) +endif() + +# Create the installation path: bin/$(TARGET_ARCH)/$(TARGET_OS)/$(BUILD_TYPE) +set(CUDA_SAMPLES_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${TARGET_ARCH}/${TARGET_OS}/${BUILD_TYPE_LOWER}" CACHE PATH "Installation directory for CUDA samples") + +# Print installation configuration +message(STATUS "CUDA Samples installation configured:") +message(STATUS " Architecture: ${TARGET_ARCH}") +message(STATUS " OS: ${TARGET_OS}") +message(STATUS " Build Type: ${BUILD_TYPE_LOWER}") +message(STATUS " Install Directory: ${CUDA_SAMPLES_INSTALL_DIR}") + +# Function to setup installation for the project +# This should be called after all targets are defined +function(setup_samples_install) + # Create an install script that will copy executables and specific file types + # - Executables: copied to flat root directory (easy access) + # - Data files: copied to subdirectories (preserves relative paths) + # - run_tests.py automatically tries flattened paths as fallback + # This script runs at install time, after the build is complete + install(CODE " + file(GLOB_RECURSE SAMPLE_FILES + LIST_DIRECTORIES false + \"${CMAKE_BINARY_DIR}/Samples/*/*\" + \"${CMAKE_BINARY_DIR}/Samples/*/*/*\") + + # Filter to include executable files and specific file types + foreach(SAMPLE_FILE IN LISTS SAMPLE_FILES) + # Skip non-files + if(NOT IS_DIRECTORY \"\${SAMPLE_FILE}\") + get_filename_component(SAMPLE_EXT \"\${SAMPLE_FILE}\" EXT) + get_filename_component(SAMPLE_NAME \"\${SAMPLE_FILE}\" NAME) + + set(SHOULD_INSTALL FALSE) + + # Skip build artifacts and CMake files + if(NOT SAMPLE_EXT MATCHES \"\\\\.(o|a|so|cmake)$\" AND + NOT SAMPLE_NAME MATCHES \"^(Makefile|cmake_install\\\\.cmake)$\" AND + NOT \"\${SAMPLE_FILE}\" MATCHES \"/CMakeFiles/\") + + # Determine file type and whether to install + set(IS_EXECUTABLE FALSE) + set(IS_INTERMEDIATE_FILE FALSE) + + # Check if file has required extension (fatbin, ptx, bc, raw, ppm) + if(SAMPLE_EXT MATCHES \"\\\\.(fatbin|ptx|bc|raw|ppm)$\") + set(SHOULD_INSTALL TRUE) + set(IS_INTERMEDIATE_FILE TRUE) + else() + # Check if file is executable + if(IS_SYMLINK \"\${SAMPLE_FILE}\" OR + (EXISTS \"\${SAMPLE_FILE}\" AND NOT IS_DIRECTORY \"\${SAMPLE_FILE}\")) + execute_process( + COMMAND test -x \"\${SAMPLE_FILE}\" + RESULT_VARIABLE HAS_EXEC_BIT + OUTPUT_QUIET ERROR_QUIET + ) + + if(HAS_EXEC_BIT EQUAL 0) + set(SHOULD_INSTALL TRUE) + set(IS_EXECUTABLE TRUE) + endif() + endif() + endif() + endif() + + if(SHOULD_INSTALL) + get_filename_component(FILE_NAME \"\${SAMPLE_FILE}\" NAME) + + if(IS_EXECUTABLE) + # Executables: install to flat directory for easy access + # run_tests.py will handle finding data files via flattened paths + message(STATUS \"Installing: ${CUDA_SAMPLES_INSTALL_DIR}/\${FILE_NAME}\") + file(COPY \"\${SAMPLE_FILE}\" + DESTINATION \"${CUDA_SAMPLES_INSTALL_DIR}\" + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + else() + # Data/intermediate files: preserve subdirectory structure (no execute bit) + # This maintains original relative paths for compatibility + # file(RELATIVE_PATH REL_PATH \"${CMAKE_BINARY_DIR}/Samples\" \"\${SAMPLE_FILE}\") + # get_filename_component(REL_DIR \"\${REL_PATH}\" DIRECTORY) + # set(DEST_DIR \"${CUDA_SAMPLES_INSTALL_DIR}/\${REL_DIR}\") + message(STATUS \"Installing: ${CUDA_SAMPLES_INSTALL_DIR}/\${FILE_NAME}\") + file(COPY \"\${SAMPLE_FILE}\" + DESTINATION \"${CUDA_SAMPLES_INSTALL_DIR}\" + FILE_PERMISSIONS OWNER_READ OWNER_WRITE + GROUP_READ + WORLD_READ) + endif() + endif() + endif() + endforeach() + + # Copy everything from bin/bin (created by libNVVM samples install) + # The libNVVM samples install their .ll and data files to bin/bin + # Skip files that already exist (e.g., executable files already copied above) + set(BIN_BIN_DIR \"${CMAKE_BINARY_DIR}/bin/bin\") + if(EXISTS \"\${BIN_BIN_DIR}\" AND IS_DIRECTORY \"\${BIN_BIN_DIR}\") + file(GLOB_RECURSE BIN_BIN_FILES + RELATIVE \"\${BIN_BIN_DIR}\" + \"\${BIN_BIN_DIR}/*\") + foreach(REL_FILE IN LISTS BIN_BIN_FILES) + set(SRC_FILE \"\${BIN_BIN_DIR}/\${REL_FILE}\") + set(DEST_FILE \"${CUDA_SAMPLES_INSTALL_DIR}/\${REL_FILE}\") + + # Only copy if destination doesn't exist (avoid duplicate executables) + if(NOT EXISTS \"\${DEST_FILE}\") + get_filename_component(DEST_DIR \"\${DEST_FILE}\" DIRECTORY) + message(STATUS \"Installing from bin/bin: \${DEST_FILE}\") + file(COPY \"\${SRC_FILE}\" + DESTINATION \"\${DEST_DIR}\" + USE_SOURCE_PERMISSIONS) + endif() + endforeach() + + # Remove bin/bin after copying + message(STATUS \"Removing bin/bin directory\") + file(REMOVE_RECURSE \"\${BIN_BIN_DIR}\") + endif() + ") +endfunction() +