|
| 1 | +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 2 | +# SPDX-License-Identifier: Apache-2.0 OR ISC |
| 3 | + |
| 4 | +# CompilerWrapper.cmake |
| 5 | +# |
| 6 | +# CMake module for handling compiler flag conflicts, particularly the -S/-c conflict |
| 7 | +# in newer Clang versions when generating assembly output. |
| 8 | +# |
| 9 | +# This module dynamically generates wrapper scripts with hardcoded compiler paths, |
| 10 | +# eliminating the need for complex compiler discovery logic. |
| 11 | +# |
| 12 | +# Usage: |
| 13 | +# include(CompilerWrapper) |
| 14 | +# setup_compiler_wrapper() |
| 15 | +# use_compiler_wrapper_for_target(my_target) |
| 16 | + |
| 17 | +cmake_minimum_required(VERSION 3.5) |
| 18 | + |
| 19 | +# Check if we need the compiler wrapper based on compiler version |
| 20 | +function(compiler_wrapper_needed result_var) |
| 21 | + set(${result_var} FALSE PARENT_SCOPE) |
| 22 | + |
| 23 | + # Check C compiler |
| 24 | + if(CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER MATCHES "clang") |
| 25 | + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER "19.99.99") |
| 26 | + set(${result_var} TRUE PARENT_SCOPE) |
| 27 | + return() |
| 28 | + endif() |
| 29 | + endif() |
| 30 | + |
| 31 | + # Allow manual override via CMake variable |
| 32 | + if(FORCE_COMPILER_WRAPPER) |
| 33 | + set(${result_var} TRUE PARENT_SCOPE) |
| 34 | + endif() |
| 35 | +endfunction() |
| 36 | + |
| 37 | +# Generate wrapper scripts with hardcoded compiler paths |
| 38 | +function(generate_compiler_wrapper) |
| 39 | + # Get current timestamp for generated file header |
| 40 | + string(TIMESTAMP CURRENT_TIMESTAMP UTC) |
| 41 | + |
| 42 | + # Set output directory for generated wrappers |
| 43 | + set(WRAPPER_OUTPUT_DIR "${CMAKE_BINARY_DIR}/compiler_wrapper") |
| 44 | + file(MAKE_DIRECTORY "${WRAPPER_OUTPUT_DIR}") |
| 45 | + |
| 46 | + # Generate shell script wrapper (Unix/Linux/macOS) |
| 47 | + set(SHELL_TEMPLATE "${CMAKE_SOURCE_DIR}/util/compiler_wrapper/compiler_wrapper_template.sh.in") |
| 48 | + set(SHELL_OUTPUT "${WRAPPER_OUTPUT_DIR}/compiler_wrapper.sh") |
| 49 | + |
| 50 | + if(EXISTS "${SHELL_TEMPLATE}") |
| 51 | + # Output file retains permissions of source file. |
| 52 | + configure_file( |
| 53 | + "${SHELL_TEMPLATE}" |
| 54 | + "${SHELL_OUTPUT}" |
| 55 | + @ONLY |
| 56 | + ) |
| 57 | + |
| 58 | + set(COMPILER_WRAPPER_SHELL "${SHELL_OUTPUT}" PARENT_SCOPE) |
| 59 | + message(STATUS "Generated shell wrapper: ${SHELL_OUTPUT}") |
| 60 | + else() |
| 61 | + message(WARNING "Shell wrapper template not found: ${SHELL_TEMPLATE}") |
| 62 | + endif() |
| 63 | + |
| 64 | + # Generate batch file wrapper (Windows) |
| 65 | + set(BAT_TEMPLATE "${CMAKE_SOURCE_DIR}/util/compiler_wrapper/compiler_wrapper_template.bat.in") |
| 66 | + set(BAT_OUTPUT "${WRAPPER_OUTPUT_DIR}/compiler_wrapper.bat") |
| 67 | + |
| 68 | + if(EXISTS "${BAT_TEMPLATE}") |
| 69 | + configure_file( |
| 70 | + "${BAT_TEMPLATE}" |
| 71 | + "${BAT_OUTPUT}" |
| 72 | + @ONLY |
| 73 | + ) |
| 74 | + |
| 75 | + set(COMPILER_WRAPPER_BAT "${BAT_OUTPUT}" PARENT_SCOPE) |
| 76 | + message(STATUS "Generated batch wrapper: ${BAT_OUTPUT}") |
| 77 | + else() |
| 78 | + message(WARNING "Batch wrapper template not found: ${BAT_TEMPLATE}") |
| 79 | + endif() |
| 80 | +endfunction() |
| 81 | + |
| 82 | +# Get the appropriate wrapper script for the current platform |
| 83 | +function(get_platform_wrapper result_var) |
| 84 | + if(WIN32 AND COMPILER_WRAPPER_BAT |
| 85 | + AND EXISTS "${COMPILER_WRAPPER_BAT}") |
| 86 | + set(${result_var} "${COMPILER_WRAPPER_BAT}" PARENT_SCOPE) |
| 87 | + elseif(COMPILER_WRAPPER_SHELL AND EXISTS "${COMPILER_WRAPPER_SHELL}") |
| 88 | + set(${result_var} "${COMPILER_WRAPPER_SHELL}" PARENT_SCOPE) |
| 89 | + else() |
| 90 | + set(${result_var} "" PARENT_SCOPE) |
| 91 | + endif() |
| 92 | +endfunction() |
| 93 | + |
| 94 | +# Set up the compiler wrapper system |
| 95 | +function(setup_compiler_wrapper) |
| 96 | + # Check if we need the wrapper |
| 97 | + compiler_wrapper_needed(NEED_WRAPPER) |
| 98 | + if(NOT NEED_WRAPPER) |
| 99 | + message(STATUS "Compiler wrapper not needed for current compiler version") |
| 100 | + set(COMPILER_WRAPPER_AVAILABLE FALSE CACHE INTERNAL "Whether compiler wrapper is available") |
| 101 | + return() |
| 102 | + endif() |
| 103 | + |
| 104 | + # Determine the compiler to wrap |
| 105 | + set(REAL_COMPILER "${CMAKE_C_COMPILER}") |
| 106 | + if(NOT REAL_COMPILER) |
| 107 | + message(FATAL_ERROR "CMAKE_C_COMPILER is not set, cannot generate compiler wrapper") |
| 108 | + endif() |
| 109 | + |
| 110 | + # Convert to absolute path if needed |
| 111 | + if(NOT IS_ABSOLUTE "${REAL_COMPILER}") |
| 112 | + find_program(REAL_COMPILER_ABS "${REAL_COMPILER}") |
| 113 | + if(REAL_COMPILER_ABS) |
| 114 | + set(REAL_COMPILER "${REAL_COMPILER_ABS}") |
| 115 | + else() |
| 116 | + message(WARNING "Could not find absolute path for compiler: ${REAL_COMPILER}") |
| 117 | + endif() |
| 118 | + endif() |
| 119 | + |
| 120 | + # Verify compiler exists |
| 121 | + if(NOT EXISTS "${REAL_COMPILER}") |
| 122 | + message(FATAL_ERROR "Compiler not found: ${REAL_COMPILER}") |
| 123 | + endif() |
| 124 | + |
| 125 | + message(STATUS "Generating compiler wrapper for: ${REAL_COMPILER}") |
| 126 | + |
| 127 | + # Generate the wrapper scripts |
| 128 | + generate_compiler_wrapper() |
| 129 | + # Get the platform-appropriate wrapper |
| 130 | + get_platform_wrapper(WRAPPER_SCRIPT) |
| 131 | + |
| 132 | + if(NOT WRAPPER_SCRIPT OR NOT EXISTS "${WRAPPER_SCRIPT}") |
| 133 | + message(FATAL_ERROR "Failed to generate compiler wrapper script") |
| 134 | + endif() |
| 135 | + |
| 136 | + # Store wrapper information in cache for use by other functions |
| 137 | + set(COMPILER_WRAPPER_SCRIPT "${WRAPPER_SCRIPT}" CACHE INTERNAL "Path to generated compiler wrapper script") |
| 138 | + set(COMPILER_WRAPPER_REAL_COMPILER "${REAL_COMPILER}" CACHE INTERNAL "Real compiler being wrapped") |
| 139 | + set(COMPILER_WRAPPER_AVAILABLE TRUE CACHE INTERNAL "Whether compiler wrapper is available") |
| 140 | + |
| 141 | + message(STATUS "Compiler wrapper ready: ${WRAPPER_SCRIPT}") |
| 142 | +endfunction() |
| 143 | + |
| 144 | +# Apply the compiler wrapper to a specific target |
| 145 | +function(use_compiler_wrapper_for_target target_name) |
| 146 | + # Check that wrapper is available |
| 147 | + if(NOT COMPILER_WRAPPER_AVAILABLE) |
| 148 | + message(STATUS "Compiler wrapper not available for target ${target_name}") |
| 149 | + return() |
| 150 | + endif() |
| 151 | + |
| 152 | + # Check that target exists |
| 153 | + if(NOT TARGET ${target_name}) |
| 154 | + message(WARNING "Target ${target_name} does not exist, cannot apply compiler wrapper") |
| 155 | + return() |
| 156 | + endif() |
| 157 | + |
| 158 | + # Set the compiler launcher to use our wrapper |
| 159 | + set_target_properties(${target_name} PROPERTIES |
| 160 | + C_COMPILER_LAUNCHER "${COMPILER_WRAPPER_SCRIPT}" |
| 161 | + ) |
| 162 | + |
| 163 | + message(STATUS "Applied compiler wrapper to target: ${target_name}") |
| 164 | +endfunction() |
0 commit comments