mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Convert build scripts to CMake (#13427)
This commit is contained in:
704
cmake/Macros.cmake
Normal file
704
cmake/Macros.cmake
Normal file
@@ -0,0 +1,704 @@
|
||||
include(CMakeParseArguments)
|
||||
|
||||
function(parse_semver value variable)
|
||||
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" match "${value}")
|
||||
|
||||
if(NOT match)
|
||||
message(FATAL_ERROR "Invalid semver: \"${value}\"")
|
||||
endif()
|
||||
|
||||
set(${variable}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE)
|
||||
set(${variable}_VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
|
||||
set(${variable}_VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE)
|
||||
set(${variable}_VERSION_PATCH "${CMAKE_MATCH_3}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# setx()
|
||||
# Description:
|
||||
# Sets a variable, similar to `set()`, but also prints the value.
|
||||
# Arguments:
|
||||
# variable string - The variable to set
|
||||
# value string - The value to set the variable to
|
||||
macro(setx)
|
||||
set(${ARGV})
|
||||
message(STATUS "Set ${ARGV0}: ${${ARGV0}}")
|
||||
endmacro()
|
||||
|
||||
# setenv()
|
||||
# Description:
|
||||
# Sets an environment variable during the build step, and writes it to a .env file.
|
||||
# Arguments:
|
||||
# variable string - The variable to set
|
||||
# value string - The value to set the variable to
|
||||
function(setenv variable value)
|
||||
set(ENV_PATH ${BUILD_PATH}/.env)
|
||||
if(value MATCHES "/|\\\\")
|
||||
file(TO_NATIVE_PATH ${value} value)
|
||||
endif()
|
||||
set(ENV_LINE "${variable}=${value}")
|
||||
|
||||
if(EXISTS ${ENV_PATH})
|
||||
file(STRINGS ${ENV_PATH} ENV_FILE ENCODING UTF-8)
|
||||
|
||||
foreach(line ${ENV_FILE})
|
||||
if(line MATCHES "^${variable}=")
|
||||
list(REMOVE_ITEM ENV_FILE ${line})
|
||||
set(ENV_MODIFIED ON)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(ENV_MODIFIED)
|
||||
list(APPEND ENV_FILE "${variable}=${value}")
|
||||
list(JOIN ENV_FILE "\n" ENV_FILE)
|
||||
file(WRITE ${ENV_PATH} ${ENV_FILE})
|
||||
else()
|
||||
file(APPEND ${ENV_PATH} "\n${variable}=${value}")
|
||||
endif()
|
||||
else()
|
||||
file(WRITE ${ENV_PATH} ${ENV_LINE})
|
||||
endif()
|
||||
|
||||
message(STATUS "Set ENV ${variable}: ${value}")
|
||||
endfunction()
|
||||
|
||||
# optionx()
|
||||
# Description:
|
||||
# Defines an option, similar to `option()`, but allows for bool, string, and regex types.
|
||||
# Arguments:
|
||||
# variable string - The variable to set
|
||||
# type string - The type of the variable
|
||||
# description string - The description of the variable
|
||||
# DEFAULT string - The default value of the variable
|
||||
# PREVIEW string - The preview value of the variable
|
||||
# REGEX string - The regex to match the value
|
||||
# REQUIRED bool - Whether the variable is required
|
||||
macro(optionx variable type description)
|
||||
set(options REQUIRED)
|
||||
set(oneValueArgs DEFAULT PREVIEW REGEX)
|
||||
set(multiValueArgs)
|
||||
cmake_parse_arguments(${variable} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT ${type} MATCHES "^(BOOL|STRING|FILEPATH|PATH|INTERNAL)$")
|
||||
set(${variable}_REGEX ${type})
|
||||
set(${variable}_TYPE STRING)
|
||||
else()
|
||||
set(${variable}_TYPE ${type})
|
||||
endif()
|
||||
|
||||
set(${variable} ${${variable}_DEFAULT} CACHE ${${variable}_TYPE} ${description})
|
||||
set(${variable}_SOURCE "argument")
|
||||
set(${variable}_PREVIEW -D${variable})
|
||||
|
||||
if(DEFINED ENV{${variable}})
|
||||
# if(DEFINED ${variable} AND NOT ${variable} STREQUAL $ENV{${variable}})
|
||||
# message(FATAL_ERROR "Invalid ${${variable}_SOURCE}: ${${variable}_PREVIEW}=\"${${variable}}\" conflicts with environment variable ${variable}=\"$ENV{${variable}}\"")
|
||||
# endif()
|
||||
|
||||
set(${variable} $ENV{${variable}} CACHE ${${variable}_TYPE} ${description} FORCE)
|
||||
set(${variable}_SOURCE "environment variable")
|
||||
set(${variable}_PREVIEW ${variable})
|
||||
endif()
|
||||
|
||||
if(NOT ${variable} AND ${${variable}_REQUIRED})
|
||||
message(FATAL_ERROR "Required ${${variable}_SOURCE} is missing: please set, ${${variable}_PREVIEW}=<${${variable}_REGEX}>")
|
||||
endif()
|
||||
|
||||
if(${type} STREQUAL "BOOL")
|
||||
if("${${variable}}" MATCHES "^(TRUE|true|ON|on|YES|yes|1)$")
|
||||
set(${variable} ON)
|
||||
elseif("${${variable}}" MATCHES "^(FALSE|false|OFF|off|NO|no|0)$")
|
||||
set(${variable} OFF)
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid ${${variable}_SOURCE}: ${${variable}_PREVIEW}=\"${${variable}}\", please use ${${variable}_PREVIEW}=<ON|OFF>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DEFINED ${variable}_REGEX AND NOT "^(${${variable}_REGEX})$" MATCHES "${${variable}}")
|
||||
message(FATAL_ERROR "Invalid ${${variable}_SOURCE}: ${${variable}_PREVIEW}=\"${${variable}}\", please use ${${variable}_PREVIEW}=<${${variable}_REGEX}>")
|
||||
endif()
|
||||
|
||||
message(STATUS "Set ${variable}: ${${variable}}")
|
||||
endmacro()
|
||||
|
||||
# check_command()
|
||||
# Description:
|
||||
# Checks if a command is available, used by `find_command()` as a validator.
|
||||
# Arguments:
|
||||
# FOUND bool - The variable to set to true if the version is found
|
||||
# CMD string - The executable to check the version of
|
||||
function(check_command FOUND CMD)
|
||||
set(${FOUND} OFF PARENT_SCOPE)
|
||||
|
||||
if(${CMD} MATCHES "zig")
|
||||
set(CHECK_COMMAND ${CMD} version)
|
||||
else()
|
||||
set(CHECK_COMMAND ${CMD} --version)
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CHECK_COMMAND}
|
||||
RESULT_VARIABLE RESULT
|
||||
OUTPUT_VARIABLE OUTPUT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(NOT RESULT EQUAL 0)
|
||||
message(DEBUG "${CHECK_COMMAND}, exited with code ${RESULT}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
parse_semver(${OUTPUT} CMD)
|
||||
parse_semver(${CHECK_COMMAND_VERSION} CHECK)
|
||||
|
||||
if(CHECK_COMMAND_VERSION MATCHES ">=")
|
||||
if(NOT CMD_VERSION VERSION_GREATER_EQUAL ${CHECK_VERSION})
|
||||
message(DEBUG "${CHECK_COMMAND}, actual: ${CMD_VERSION}, expected: ${CHECK_COMMAND_VERSION}")
|
||||
return()
|
||||
endif()
|
||||
elseif(CHECK_COMMAND_VERSION MATCHES ">")
|
||||
if(NOT CMD_VERSION VERSION_GREATER ${CHECK_VERSION})
|
||||
message(DEBUG "${CHECK_COMMAND}, actual: ${CMD_VERSION}, expected: ${CHECK_COMMAND_VERSION}")
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
if(NOT CMD_VERSION VERSION_EQUAL ${CHECK_VERSION})
|
||||
message(DEBUG "${CHECK_COMMAND}, actual: ${CMD_VERSION}, expected: =${CHECK_COMMAND_VERSION}")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${FOUND} TRUE PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# find_command()
|
||||
# Description:
|
||||
# Finds a command, similar to `find_program()`, but allows for version checking.
|
||||
# Arguments:
|
||||
# VARIABLE string - The variable to set
|
||||
# COMMAND string[] - The names of the command to find
|
||||
# PATHS string[] - The paths to search for the command
|
||||
# REQUIRED bool - If false, the command is optional
|
||||
# VERSION string - The version of the command to find (e.g. "1.2.3" or ">1.2.3")
|
||||
function(find_command)
|
||||
set(options)
|
||||
set(args VARIABLE VERSION MIN_VERSION REQUIRED)
|
||||
set(multiArgs COMMAND PATHS)
|
||||
cmake_parse_arguments(CMD "${options}" "${args}" "${multiArgs}" ${ARGN})
|
||||
|
||||
if(NOT CMD_VARIABLE)
|
||||
message(FATAL_ERROR "find_command: VARIABLE is required")
|
||||
endif()
|
||||
|
||||
if(NOT CMD_COMMAND)
|
||||
message(FATAL_ERROR "find_command: COMMAND is required")
|
||||
endif()
|
||||
|
||||
if(CMD_VERSION)
|
||||
set(CHECK_COMMAND_VERSION ${CMD_VERSION}) # special global variable
|
||||
set(CMD_VALIDATOR VALIDATOR check_command)
|
||||
endif()
|
||||
|
||||
find_program(
|
||||
${CMD_VARIABLE}
|
||||
NAMES ${CMD_COMMAND}
|
||||
PATHS ${CMD_PATHS}
|
||||
${CMD_VALIDATOR}
|
||||
)
|
||||
|
||||
if(NOT CMD_REQUIRED STREQUAL "OFF" AND ${CMD_VARIABLE} MATCHES "NOTFOUND")
|
||||
if(CMD_VERSION)
|
||||
message(FATAL_ERROR "Command not found: \"${CMD_COMMAND}\" that matches version \"${CHECK_COMMAND_VERSION}\"")
|
||||
endif()
|
||||
message(FATAL_ERROR "Command not found: \"${CMD_COMMAND}\"")
|
||||
endif()
|
||||
|
||||
setx(${CMD_VARIABLE} ${${CMD_VARIABLE}})
|
||||
endfunction()
|
||||
|
||||
# register_command()
|
||||
# Description:
|
||||
# Registers a command, similar to `add_custom_command()`, but has more validation and features.
|
||||
# Arguments:
|
||||
# COMMAND string[] - The command to run
|
||||
# COMMENT string - The comment to display in the log
|
||||
# CWD string - The working directory to run the command in
|
||||
# ENVIRONMENT string[] - The environment variables to set (e.g. "DEBUG=1")
|
||||
# TARGETS string[] - The targets that this command depends on
|
||||
# SOURCES string[] - The files that this command depends on
|
||||
# OUTPUTS string[] - The files that this command produces
|
||||
# ARTIFACTS string[] - The files that this command produces, and uploads as an artifact in CI
|
||||
# ALWAYS_RUN bool - If true, the command will always run
|
||||
# TARGET string - The target to register the command with
|
||||
# TARGET_PHASE string - The target phase to register the command with (e.g. PRE_BUILD, PRE_LINK, POST_BUILD)
|
||||
# GROUP string - The group to register the command with (e.g. similar to JOB_POOL)
|
||||
function(register_command)
|
||||
set(options ALWAYS_RUN)
|
||||
set(args COMMENT CWD TARGET TARGET_PHASE GROUP)
|
||||
set(multiArgs COMMAND ENVIRONMENT TARGETS SOURCES OUTPUTS ARTIFACTS)
|
||||
cmake_parse_arguments(CMD "${options}" "${args}" "${multiArgs}" ${ARGN})
|
||||
|
||||
if(NOT CMD_COMMAND)
|
||||
message(FATAL_ERROR "register_command: COMMAND is required")
|
||||
endif()
|
||||
|
||||
if(NOT CMD_CWD)
|
||||
set(CMD_CWD ${CWD})
|
||||
endif()
|
||||
|
||||
if(CMD_ENVIRONMENT)
|
||||
set(CMD_COMMAND ${CMAKE_COMMAND} -E env ${CMD_ENVIRONMENT} ${CMD_COMMAND})
|
||||
endif()
|
||||
|
||||
if(NOT CMD_COMMENT)
|
||||
string(JOIN " " CMD_COMMENT ${CMD_COMMAND})
|
||||
endif()
|
||||
|
||||
set(CMD_COMMANDS COMMAND ${CMD_COMMAND})
|
||||
set(CMD_EFFECTIVE_DEPENDS)
|
||||
|
||||
list(GET CMD_COMMAND 0 CMD_EXECUTABLE)
|
||||
if(CMD_EXECUTABLE MATCHES "/|\\\\")
|
||||
list(APPEND CMD_EFFECTIVE_DEPENDS ${CMD_EXECUTABLE})
|
||||
endif()
|
||||
|
||||
foreach(target ${CMD_TARGETS})
|
||||
if(target MATCHES "/|\\\\")
|
||||
message(FATAL_ERROR "register_command: TARGETS contains \"${target}\", if it's a path add it to SOURCES instead")
|
||||
endif()
|
||||
if(NOT TARGET ${target})
|
||||
message(FATAL_ERROR "register_command: TARGETS contains \"${target}\", but it's not a target")
|
||||
endif()
|
||||
list(APPEND CMD_EFFECTIVE_DEPENDS ${target})
|
||||
endforeach()
|
||||
|
||||
foreach(source ${CMD_SOURCES})
|
||||
if(NOT source MATCHES "^(${CWD}|${BUILD_PATH})")
|
||||
message(FATAL_ERROR "register_command: SOURCES contains \"${source}\", if it's a path, make it absolute, otherwise add it to TARGETS instead")
|
||||
endif()
|
||||
list(APPEND CMD_EFFECTIVE_DEPENDS ${source})
|
||||
endforeach()
|
||||
|
||||
if(NOT CMD_EFFECTIVE_DEPENDS AND NOT CMD_ALWAYS_RUN)
|
||||
message(FATAL_ERROR "register_command: TARGETS or SOURCES is required")
|
||||
endif()
|
||||
|
||||
set(CMD_EFFECTIVE_OUTPUTS)
|
||||
|
||||
foreach(output ${CMD_OUTPUTS})
|
||||
if(NOT output MATCHES "^(${CWD}|${BUILD_PATH})")
|
||||
message(FATAL_ERROR "register_command: OUTPUTS contains \"${output}\", if it's a path, make it absolute")
|
||||
endif()
|
||||
list(APPEND CMD_EFFECTIVE_OUTPUTS ${output})
|
||||
endforeach()
|
||||
|
||||
foreach(artifact ${CMD_ARTIFACTS})
|
||||
if(NOT artifact MATCHES "^(${CWD}|${BUILD_PATH})")
|
||||
message(FATAL_ERROR "register_command: ARTIFACTS contains \"${artifact}\", if it's a path, make it absolute")
|
||||
endif()
|
||||
list(APPEND CMD_EFFECTIVE_OUTPUTS ${artifact})
|
||||
if(BUILDKITE)
|
||||
file(RELATIVE_PATH filename ${BUILD_PATH} ${artifact})
|
||||
list(APPEND CMD_COMMANDS COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} buildkite-agent artifact upload ${filename})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(output ${CMD_EFFECTIVE_OUTPUTS})
|
||||
get_source_file_property(generated ${output} GENERATED)
|
||||
if(generated)
|
||||
list(REMOVE_ITEM CMD_EFFECTIVE_OUTPUTS ${output})
|
||||
list(APPEND CMD_EFFECTIVE_OUTPUTS ${output}.always_run)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(CMD_ALWAYS_RUN)
|
||||
list(APPEND CMD_EFFECTIVE_OUTPUTS ${CMD_CWD}/.always_run)
|
||||
endif()
|
||||
|
||||
if(CMD_TARGET_PHASE)
|
||||
if(NOT CMD_TARGET)
|
||||
message(FATAL_ERROR "register_command: TARGET is required when TARGET_PHASE is set")
|
||||
endif()
|
||||
if(NOT TARGET ${CMD_TARGET})
|
||||
message(FATAL_ERROR "register_command: TARGET is not a valid target: ${CMD_TARGET}")
|
||||
endif()
|
||||
add_custom_command(
|
||||
TARGET ${CMD_TARGET} ${CMD_TARGET_PHASE}
|
||||
COMMENT ${CMD_COMMENT}
|
||||
WORKING_DIRECTORY ${CMD_CWD}
|
||||
VERBATIM ${CMD_COMMANDS}
|
||||
)
|
||||
set_property(TARGET ${CMD_TARGET} PROPERTY OUTPUT ${CMD_EFFECTIVE_OUTPUTS} APPEND)
|
||||
set_property(TARGET ${CMD_TARGET} PROPERTY DEPENDS ${CMD_EFFECTIVE_DEPENDS} APPEND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT CMD_EFFECTIVE_OUTPUTS)
|
||||
message(FATAL_ERROR "register_command: OUTPUTS or ARTIFACTS is required, or set ALWAYS_RUN")
|
||||
endif()
|
||||
|
||||
if(CMD_TARGET)
|
||||
if(TARGET ${CMD_TARGET})
|
||||
message(FATAL_ERROR "register_command: TARGET is already registered: ${CMD_TARGET}")
|
||||
endif()
|
||||
add_custom_target(${CMD_TARGET}
|
||||
COMMENT ${CMD_COMMENT}
|
||||
DEPENDS ${CMD_EFFECTIVE_OUTPUTS}
|
||||
JOB_POOL ${CMD_GROUP}
|
||||
)
|
||||
if(TARGET clone-${CMD_TARGET})
|
||||
add_dependencies(${CMD_TARGET} clone-${CMD_TARGET})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
VERBATIM ${CMD_COMMANDS}
|
||||
WORKING_DIRECTORY ${CMD_CWD}
|
||||
COMMENT ${CMD_COMMENT}
|
||||
DEPENDS ${CMD_EFFECTIVE_DEPENDS}
|
||||
OUTPUT ${CMD_EFFECTIVE_OUTPUTS}
|
||||
JOB_POOL ${CMD_GROUP}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# parse_package_json()
|
||||
# Description:
|
||||
# Parses a package.json file.
|
||||
# Arguments:
|
||||
# CWD string - The directory to look for the package.json file
|
||||
# VERSION_VARIABLE string - The variable to set to the package version
|
||||
# NODE_MODULES_VARIABLE string - The variable to set to list of node_modules sources
|
||||
function(parse_package_json)
|
||||
set(args CWD VERSION_VARIABLE NODE_MODULES_VARIABLE)
|
||||
cmake_parse_arguments(NPM "" "${args}" "" ${ARGN})
|
||||
|
||||
if(NOT NPM_CWD)
|
||||
set(NPM_CWD ${CWD})
|
||||
endif()
|
||||
|
||||
set(NPM_PACKAGE_JSON_PATH ${NPM_CWD}/package.json)
|
||||
|
||||
if(NOT EXISTS ${NPM_PACKAGE_JSON_PATH})
|
||||
message(FATAL_ERROR "parse_package_json: package.json not found: ${NPM_PACKAGE_JSON_PATH}")
|
||||
endif()
|
||||
|
||||
file(READ ${NPM_PACKAGE_JSON_PATH} NPM_PACKAGE_JSON)
|
||||
if(NOT NPM_PACKAGE_JSON)
|
||||
message(FATAL_ERROR "parse_package_json: failed to read package.json: ${NPM_PACKAGE_JSON_PATH}")
|
||||
endif()
|
||||
|
||||
if(NPM_VERSION_VARIABLE)
|
||||
string(JSON NPM_VERSION ERROR_VARIABLE error GET "${NPM_PACKAGE_JSON}" version)
|
||||
if(error)
|
||||
message(FATAL_ERROR "parse_package_json: failed to read 'version': ${error}")
|
||||
endif()
|
||||
set(${NPM_VERSION_VARIABLE} ${NPM_VERSION} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if(NPM_NODE_MODULES_VARIABLE)
|
||||
set(NPM_NODE_MODULES)
|
||||
set(NPM_NODE_MODULES_PATH ${NPM_CWD}/node_modules)
|
||||
set(NPM_NODE_MODULES_PROPERTIES "devDependencies" "dependencies")
|
||||
|
||||
foreach(property ${NPM_NODE_MODULES_PROPERTIES})
|
||||
string(JSON NPM_${property} ERROR_VARIABLE error GET "${NPM_PACKAGE_JSON}" "${property}")
|
||||
if(error MATCHES "not found")
|
||||
continue()
|
||||
endif()
|
||||
if(error)
|
||||
message(FATAL_ERROR "parse_package_json: failed to read '${property}': ${error}")
|
||||
endif()
|
||||
|
||||
string(JSON NPM_${property}_LENGTH ERROR_VARIABLE error LENGTH "${NPM_${property}}")
|
||||
if(error)
|
||||
message(FATAL_ERROR "parse_package_json: failed to read '${property}' length: ${error}")
|
||||
endif()
|
||||
|
||||
math(EXPR NPM_${property}_MAX_INDEX "${NPM_${property}_LENGTH} - 1")
|
||||
foreach(i RANGE 0 ${NPM_${property}_MAX_INDEX})
|
||||
string(JSON NPM_${property}_${i} ERROR_VARIABLE error MEMBER "${NPM_${property}}" ${i})
|
||||
if(error)
|
||||
message(FATAL_ERROR "parse_package_json: failed to index '${property}' at ${i}: ${error}")
|
||||
endif()
|
||||
list(APPEND NPM_NODE_MODULES ${NPM_NODE_MODULES_PATH}/${NPM_${property}_${i}}/package.json)
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
set(${NPM_NODE_MODULES_VARIABLE} ${NPM_NODE_MODULES} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# register_bun_install()
|
||||
# Description:
|
||||
# Registers a command to run `bun install` in a directory.
|
||||
# Arguments:
|
||||
# CWD string - The directory to run `bun install`
|
||||
# NODE_MODULES_VARIABLE string - The variable to set to list of node_modules sources
|
||||
function(register_bun_install)
|
||||
set(args CWD NODE_MODULES_VARIABLE)
|
||||
cmake_parse_arguments(NPM "" "${args}" "" ${ARGN})
|
||||
|
||||
if(NOT NPM_CWD)
|
||||
set(NPM_CWD ${CWD})
|
||||
endif()
|
||||
|
||||
if(NPM_CWD STREQUAL ${CWD})
|
||||
set(NPM_COMMENT "bun install")
|
||||
else()
|
||||
set(NPM_COMMENT "bun install --cwd ${NPM_CWD}")
|
||||
endif()
|
||||
|
||||
parse_package_json(
|
||||
CWD
|
||||
${NPM_CWD}
|
||||
NODE_MODULES_VARIABLE
|
||||
NPM_NODE_MODULES
|
||||
)
|
||||
|
||||
if(NOT NPM_NODE_MODULES)
|
||||
message(FATAL_ERROR "register_bun_install: ${NPM_CWD}/package.json does not have dependencies?")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
COMMENT
|
||||
${NPM_COMMENT}
|
||||
CWD
|
||||
${NPM_CWD}
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
install
|
||||
--frozen-lockfile
|
||||
SOURCES
|
||||
${NPM_CWD}/package.json
|
||||
OUTPUTS
|
||||
${NPM_NODE_MODULES}
|
||||
)
|
||||
|
||||
set(${NPM_NODE_MODULES_VARIABLE} ${NPM_NODE_MODULES} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# register_repository()
|
||||
# Description:
|
||||
# Registers a git repository.
|
||||
# Arguments:
|
||||
# NAME string - The name of the repository
|
||||
# REPOSITORY string - The repository to clone
|
||||
# BRANCH string - The branch to clone
|
||||
# TAG string - The tag to clone
|
||||
# COMMIT string - The commit to clone
|
||||
# PATH string - The path to clone the repository to
|
||||
# OUTPUTS string - The outputs of the repository
|
||||
function(register_repository)
|
||||
set(args NAME REPOSITORY BRANCH TAG COMMIT PATH)
|
||||
set(multiArgs OUTPUTS)
|
||||
cmake_parse_arguments(GIT "" "${args}" "${multiArgs}" ${ARGN})
|
||||
|
||||
if(NOT GIT_REPOSITORY)
|
||||
message(FATAL_ERROR "git_clone: REPOSITORY is required")
|
||||
endif()
|
||||
|
||||
if(NOT GIT_BRANCH AND NOT GIT_TAG AND NOT GIT_COMMIT)
|
||||
message(FATAL_ERROR "git_clone: COMMIT, TAG, or BRANCH is required")
|
||||
endif()
|
||||
|
||||
if(NOT GIT_PATH)
|
||||
set(GIT_PATH ${CWD}/src/deps/${GIT_NAME})
|
||||
endif()
|
||||
|
||||
if(GIT_COMMIT)
|
||||
set(GIT_REF ${GIT_COMMIT})
|
||||
elseif(GIT_TAG)
|
||||
set(GIT_REF refs/tags/${GIT_TAG})
|
||||
else()
|
||||
set(GIT_REF refs/heads/${GIT_BRANCH})
|
||||
endif()
|
||||
|
||||
set(GIT_EFFECTIVE_OUTPUTS)
|
||||
foreach(output ${GIT_OUTPUTS})
|
||||
list(APPEND GIT_EFFECTIVE_OUTPUTS ${GIT_PATH}/${output})
|
||||
endforeach()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
clone-${GIT_NAME}
|
||||
COMMENT
|
||||
"Cloning ${GIT_NAME}"
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
-DGIT_PATH=${GIT_PATH}
|
||||
-DGIT_REPOSITORY=${GIT_REPOSITORY}
|
||||
-DGIT_REF=${GIT_REF}
|
||||
-DGIT_NAME=${GIT_NAME}
|
||||
-P ${CWD}/cmake/scripts/GitClone.cmake
|
||||
OUTPUTS
|
||||
${GIT_PATH}
|
||||
${GIT_EFFECTIVE_OUTPUTS}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# register_cmake_command()
|
||||
# Description:
|
||||
# Registers a command that builds an external CMake project.
|
||||
# Arguments:
|
||||
# TARGET string - The target to register the command with
|
||||
# ARGS string[] - The arguments to pass to CMake (e.g. -DKEY=VALUE)
|
||||
# CWD string - The directory where the CMake files are located
|
||||
# BUILD_PATH string - The path to build the project to
|
||||
# LIB_PATH string - The path to the libraries
|
||||
# TARGETS string[] - The targets to build from CMake
|
||||
# LIBRARIES string[] - The libraries that are built
|
||||
# INCLUDES string[] - The include paths
|
||||
function(register_cmake_command)
|
||||
set(args TARGET CWD BUILD_PATH LIB_PATH)
|
||||
set(multiArgs ARGS TARGETS LIBRARIES INCLUDES)
|
||||
# Use "MAKE" instead of "CMAKE" to prevent conflicts with CMake's own CMAKE_* variables
|
||||
cmake_parse_arguments(MAKE "" "${args}" "${multiArgs}" ${ARGN})
|
||||
|
||||
if(NOT MAKE_TARGET)
|
||||
message(FATAL_ERROR "register_cmake_command: TARGET is required")
|
||||
endif()
|
||||
|
||||
if(TARGET ${MAKE_TARGET})
|
||||
message(FATAL_ERROR "register_cmake_command: TARGET is already a target: ${MAKE_TARGET}")
|
||||
endif()
|
||||
|
||||
if(NOT MAKE_CWD)
|
||||
set(MAKE_CWD ${CWD}/src/deps/${MAKE_TARGET})
|
||||
endif()
|
||||
|
||||
if(NOT MAKE_BUILD_PATH)
|
||||
set(MAKE_BUILD_PATH ${BUILD_PATH}/${MAKE_TARGET})
|
||||
endif()
|
||||
|
||||
if(MAKE_LIB_PATH)
|
||||
set(MAKE_LIB_PATH ${MAKE_BUILD_PATH}/${MAKE_LIB_PATH})
|
||||
else()
|
||||
set(MAKE_LIB_PATH ${MAKE_BUILD_PATH})
|
||||
endif()
|
||||
|
||||
set(MAKE_EFFECTIVE_ARGS -B${MAKE_BUILD_PATH} ${CMAKE_ARGS})
|
||||
|
||||
set(setFlags GENERATOR BUILD_TYPE)
|
||||
set(appendFlags C_FLAGS CXX_FLAGS LINKER_FLAGS)
|
||||
set(specialFlags POSITION_INDEPENDENT_CODE)
|
||||
set(flags ${setFlags} ${appendFlags} ${specialFlags})
|
||||
|
||||
foreach(arg ${MAKE_ARGS})
|
||||
foreach(flag ${flags})
|
||||
if(arg MATCHES "-DCMAKE_${flag}=(.*)")
|
||||
if(DEFINED MAKE_${flag})
|
||||
message(FATAL_ERROR "register_cmake_command: CMAKE_${flag} was already set: \"${MAKE_${flag}}\"")
|
||||
endif()
|
||||
set(MAKE_${flag} ${CMAKE_MATCH_1})
|
||||
set(${arg}_USED ON)
|
||||
endif()
|
||||
endforeach()
|
||||
if(NOT ${arg}_USED)
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS ${arg})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(flag ${setFlags})
|
||||
if(NOT DEFINED MAKE_${flag} AND DEFINED CMAKE_${flag})
|
||||
set(MAKE_${flag} ${CMAKE_${flag}})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(flag ${appendFlags})
|
||||
if(MAKE_${flag})
|
||||
set(MAKE_${flag} "${CMAKE_${flag}} ${MAKE_${flag}}")
|
||||
else()
|
||||
set(MAKE_${flag} ${CMAKE_${flag}})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(MAKE_POSITION_INDEPENDENT_CODE AND NOT WIN32)
|
||||
set(MAKE_C_FLAGS "${MAKE_C_FLAGS} -fPIC")
|
||||
set(MAKE_CXX_FLAGS "${MAKE_CXX_FLAGS} -fPIC")
|
||||
elseif(APPLE)
|
||||
set(MAKE_C_FLAGS "${MAKE_C_FLAGS} -fno-pic -fno-pie")
|
||||
set(MAKE_CXX_FLAGS "${MAKE_CXX_FLAGS} -fno-pic -fno-pie")
|
||||
endif()
|
||||
|
||||
set(effectiveFlags ${setFlags} ${appendFlags})
|
||||
foreach(flag ${effectiveFlags})
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS -DCMAKE_${flag}=${MAKE_${flag}})
|
||||
endforeach()
|
||||
|
||||
register_command(
|
||||
COMMENT "Configuring ${MAKE_TARGET}"
|
||||
TARGET configure-${MAKE_TARGET}
|
||||
COMMAND ${CMAKE_COMMAND} ${MAKE_EFFECTIVE_ARGS}
|
||||
CWD ${MAKE_CWD}
|
||||
OUTPUTS ${MAKE_BUILD_PATH}/CMakeCache.txt
|
||||
)
|
||||
|
||||
if(TARGET clone-${MAKE_TARGET})
|
||||
add_dependencies(configure-${MAKE_TARGET} clone-${MAKE_TARGET})
|
||||
endif()
|
||||
|
||||
set(MAKE_BUILD_ARGS --build ${MAKE_BUILD_PATH} --config ${MAKE_BUILD_TYPE})
|
||||
|
||||
set(MAKE_EFFECTIVE_LIBRARIES)
|
||||
set(MAKE_ARTIFACTS)
|
||||
foreach(lib ${MAKE_LIBRARIES})
|
||||
if(lib MATCHES "^(WIN32|UNIX|APPLE)$")
|
||||
if(${lib})
|
||||
continue()
|
||||
else()
|
||||
list(POP_BACK MAKE_ARTIFACTS)
|
||||
endif()
|
||||
else()
|
||||
list(APPEND MAKE_EFFECTIVE_LIBRARIES ${lib})
|
||||
if(lib MATCHES "\\.")
|
||||
list(APPEND MAKE_ARTIFACTS ${MAKE_LIB_PATH}/${lib})
|
||||
else()
|
||||
list(APPEND MAKE_ARTIFACTS ${MAKE_LIB_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT MAKE_TARGETS)
|
||||
set(MAKE_TARGETS ${MAKE_EFFECTIVE_LIBRARIES})
|
||||
endif()
|
||||
|
||||
foreach(target ${MAKE_TARGETS})
|
||||
list(APPEND MAKE_BUILD_ARGS --target ${target})
|
||||
endforeach()
|
||||
|
||||
set(MAKE_EFFECTIVE_INCLUDES)
|
||||
foreach(include ${MAKE_INCLUDES})
|
||||
if(include STREQUAL ".")
|
||||
list(APPEND MAKE_EFFECTIVE_INCLUDES ${MAKE_CWD})
|
||||
else()
|
||||
list(APPEND MAKE_EFFECTIVE_INCLUDES ${MAKE_CWD}/${include})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
register_command(
|
||||
COMMENT "Building ${MAKE_TARGET}"
|
||||
TARGET ${MAKE_TARGET}
|
||||
TARGETS configure-${MAKE_TARGET}
|
||||
COMMAND ${CMAKE_COMMAND} ${MAKE_BUILD_ARGS}
|
||||
CWD ${MAKE_CWD}
|
||||
ARTIFACTS ${MAKE_ARTIFACTS}
|
||||
)
|
||||
|
||||
if(MAKE_EFFECTIVE_INCLUDES)
|
||||
target_include_directories(${bun} PRIVATE ${MAKE_EFFECTIVE_INCLUDES})
|
||||
if(TARGET clone-${MAKE_TARGET} AND NOT BUN_LINK_ONLY)
|
||||
add_dependencies(${bun} clone-${MAKE_TARGET})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# HACK: Workaround for duplicate symbols when linking mimalloc.o
|
||||
# >| duplicate symbol '_mi_page_queue_append(mi_heap_s*, mi_page_queue_s*, mi_page_queue_s*)' in:
|
||||
# >| mimalloc/CMakeFiles/mimalloc-obj.dir/src/static.c.o
|
||||
# >| ld: 287 duplicate symbols for architecture arm64
|
||||
if(NOT BUN_LINK_ONLY OR NOT MAKE_ARTIFACTS MATCHES "static.c.o")
|
||||
target_link_libraries(${bun} PRIVATE ${MAKE_ARTIFACTS})
|
||||
endif()
|
||||
|
||||
if(BUN_LINK_ONLY)
|
||||
target_sources(${bun} PRIVATE ${MAKE_ARTIFACTS})
|
||||
endif()
|
||||
endfunction()
|
||||
Reference in New Issue
Block a user