Files
clice/cmake/llvm_setup.cmake
2025-09-07 23:30:46 +08:00

214 lines
8.0 KiB
CMake

include_guard()
include(${CMAKE_CURRENT_LIST_DIR}/github.cmake)
# Check if LLVM version is supported
function(check_llvm_version llvm_ver OUTPUT_VAR)
if((NOT DEFINED llvm_ver) OR (llvm_ver STREQUAL ""))
message(WARNING "LLVM version is not set.")
set(${OUTPUT_VAR} FALSE PARENT_SCOPE)
elseif(NOT (llvm_ver VERSION_GREATER_EQUAL "20.1" AND llvm_ver VERSION_LESS "20.2"))
message(WARNING "Unsupported LLVM version: ${llvm_ver}. Only LLVM 20.1.x is supported.")
set(${OUTPUT_VAR} FALSE PARENT_SCOPE)
else()
set(${OUTPUT_VAR} TRUE PARENT_SCOPE)
endif()
endfunction()
# Look up LLVM version's corresponding commit SHA
function(lookup_commit llvm_ver OUTPUT_VAR)
set(LLVM_TAG "llvmorg-${llvm_ver}")
github_lookup_tag_commit("llvm" "llvm-project" ${LLVM_TAG} COMMIT_SHA)
set(${OUTPUT_VAR} ${COMMIT_SHA} PARENT_SCOPE)
endfunction()
# Fetch private Clang header files from LLVM source
function(fetch_private_clang_files llvm_ver)
set(PRIVATE_CLANG_FILE_LIST
"Sema/CoroutineStmtBuilder.h"
"Sema/TypeLocBuilder.h"
"Sema/TreeTransform.h"
)
# Check if all files already exist
set(PRIVATE_FILE_EXISTS TRUE)
foreach(FILE ${PRIVATE_CLANG_FILE_LIST})
if(EXISTS "${LLVM_INSTALL_PATH}/include/clang/${FILE}")
message(STATUS "Private clang file found in LLVM installation: ${FILE}")
elseif(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/include/clang/${FILE}")
message(STATUS "Private clang file already exists: ${FILE}")
else()
set(PRIVATE_FILE_EXISTS FALSE)
break()
endif()
endforeach()
if(PRIVATE_FILE_EXISTS)
message(STATUS "All required private clang files already exist.")
return()
endif()
# Skip download in offline build mode
if(CLICE_OFFLINE_BUILD)
message(WARNING "CLICE_OFFLINE_BUILD is enabled, skipping private clang files download")
message(WARNING "Build may fail if required private headers are missing")
return()
endif()
message(WARNING "Required private clang files incomplete, fetching from llvm-project source...")
# Get the commit SHA for this LLVM version
lookup_commit(${llvm_ver} LLVM_COMMIT)
if(LLVM_COMMIT STREQUAL "NOTFOUND")
message(WARNING "Failed to lookup commit for LLVM ${llvm_ver}, skipping private clang files download")
return()
endif()
message(STATUS "LLVM ${llvm_ver} corresponds to commit ${LLVM_COMMIT}")
# Download missing files
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/clang")
foreach(FILE ${PRIVATE_CLANG_FILE_LIST})
set(FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/include/clang/${FILE}")
if(NOT EXISTS "${FILE_PATH}")
message(STATUS "Downloading ${FILE}...")
file(DOWNLOAD "https://raw.githubusercontent.com/llvm/llvm-project/${LLVM_COMMIT}/clang/lib/${FILE}"
"${FILE_PATH}"
STATUS DOWNLOAD_STATUS
TLS_VERIFY ON)
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
if(NOT STATUS_CODE EQUAL 0)
message(FATAL_ERROR "Failed to download private clang file: ${FILE}")
endif()
endif()
endforeach()
endfunction()
# Detect system-installed LLVM using llvm-config
function(detect_llvm OUTPUT_VAR)
find_program(LLVM_CONFIG_EXEC llvm-config)
if(NOT LLVM_CONFIG_EXEC)
set(${OUTPUT_VAR} "" PARENT_SCOPE)
return()
endif()
# Get LLVM version and paths
execute_process(
COMMAND "${LLVM_CONFIG_EXEC}" --version
OUTPUT_VARIABLE LLVM_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND "${LLVM_CONFIG_EXEC}" --prefix
OUTPUT_VARIABLE LLVM_INSTALL_PATH_DETECTED
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND "${LLVM_CONFIG_EXEC}" --cmakedir
OUTPUT_VARIABLE LLVM_CMAKE_DIR_DETECTED
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Set cache variables
set(LLVM_INSTALL_PATH "${LLVM_INSTALL_PATH_DETECTED}" CACHE PATH "Path to LLVM installation" FORCE)
set(LLVM_CMAKE_DIR "${LLVM_CMAKE_DIR_DETECTED}" CACHE PATH "Path to LLVM CMake files" FORCE)
set(${OUTPUT_VAR} ${LLVM_VERSION} PARENT_SCOPE)
endfunction()
# Download and install prebuilt LLVM binaries with error checking
function(install_prebuilt_llvm llvm_ver)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.llvm")
# Determine platform-specific package name
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(LLVM_BUILD_TYPE "debug")
elseif(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(LLVM_BUILD_TYPE "release")
else()
set(LLVM_BUILD_TYPE "release-lto")
endif()
if(WIN32)
set(LLVM_PACKAGE "x64-windows-msvc-${LLVM_BUILD_TYPE}.7z")
elseif(APPLE)
set(LLVM_PACKAGE "arm64-macosx-apple-${LLVM_BUILD_TYPE}.tar.xz")
elseif(UNIX)
set(LLVM_PACKAGE "x86_64-linux-gnu-${LLVM_BUILD_TYPE}.tar.xz")
endif()
if(NOT LLVM_PACKAGE)
message(FATAL_ERROR "Unsupported platform or build type for prebuilt LLVM.")
endif()
set(DOWNLOAD_PATH "${CMAKE_CURRENT_BINARY_DIR}/${LLVM_PACKAGE}")
# Download if file does not exist
if(NOT EXISTS "${DOWNLOAD_PATH}")
message(STATUS "Downloading prebuilt LLVM package: ${LLVM_PACKAGE}")
set(DOWNLOAD_URL "https://github.com/clice-io/llvm-binary/releases/download/${llvm_ver}/${LLVM_PACKAGE}")
file(DOWNLOAD "${DOWNLOAD_URL}"
"${DOWNLOAD_PATH}"
STATUS DOWNLOAD_STATUS
SHOW_PROGRESS
TLS_VERIFY ON)
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
list(GET DOWNLOAD_STATUS 1 ERROR_MESSAGE)
if(NOT STATUS_CODE EQUAL 0)
# Download failed, remove the incomplete file to force a fresh download next time
if(EXISTS "${DOWNLOAD_PATH}")
file(REMOVE "${DOWNLOAD_PATH}")
message(STATUS "Removed incomplete file: ${DOWNLOAD_PATH}")
endif()
message(FATAL_ERROR "Failed to download prebuilt LLVM package from ${DOWNLOAD_URL}.\nError: ${ERROR_MESSAGE}")
endif()
else()
message(STATUS "Prebuilt LLVM package already exists, skipping download.")
endif()
message(STATUS "Extracting LLVM package: ${LLVM_PACKAGE}")
execute_process(
COMMAND "${CMAKE_COMMAND}" -E tar xvf "${DOWNLOAD_PATH}"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.llvm"
RESULT_VARIABLE TAR_RESULT
OUTPUT_QUIET
ERROR_QUIET
)
if(NOT TAR_RESULT EQUAL "0")
message(FATAL_ERROR "Failed to extract archive. The file may be corrupted or the tool is missing.")
endif()
# Set installation paths
set(LLVM_INSTALL_PATH "${CMAKE_CURRENT_BINARY_DIR}/.llvm" CACHE PATH "Path to LLVM installation" FORCE)
set(LLVM_CMAKE_DIR "${LLVM_INSTALL_PATH}/lib/cmake/llvm" CACHE PATH "Path to LLVM CMake files" FORCE)
message(STATUS "LLVM installation path set to: ${LLVM_INSTALL_PATH}")
endfunction()
# Main function to set up LLVM for the project
function(setup_llvm)
# Use existing LLVM installation if path is already set
if(DEFINED LLVM_INSTALL_PATH AND NOT LLVM_INSTALL_PATH STREQUAL "")
message(STATUS "LLVM_INSTALL_PATH is set to ${LLVM_INSTALL_PATH}, using it directly.")
return()
endif()
set(LLVM_VERSION_OK false)
if (CMAKE_BUILD_TYPE STREQUAL "Release")
# Try to detect system LLVM
detect_llvm(LLVM_VERSION)
check_llvm_version("${LLVM_VERSION}" LLVM_VERSION_OK)
endif()
# Download prebuilt LLVM if system version is not suitable
if(NOT LLVM_VERSION_OK)
set(LLVM_VERSION "20.1.5")
message(WARNING "System LLVM not found, version mismatch or incompatible build type, downloading prebuilt LLVM...")
install_prebuilt_llvm("${LLVM_VERSION}")
endif()
# Fetch required private Clang headers
fetch_private_clang_files("${LLVM_VERSION}")
endfunction()