From 879e5964a9fe57eb96d0e0d313dd05d58a3351c7 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid Date: Thu, 23 Jan 2020 10:47:40 +0200 Subject: Unify build_native via eng/native/build-commons (#1753) * Unify build_native via eng/native/build-commons In order to add new platform, architecture, compiler or its newer version, there are currently multiple places to update, which can be deduplicated. This patch unifies: * directories creation * prerequisites checking * `version.c` generation * native build invocation * common argument parsing * building help menu for various build scripts under coreclr, installer and libraries. The common entry-point script is now `eng/native/build-commons.sh` and rest of the scripts under `eng/native` are implementation detail. Also extracted CMake platform detection in `eng/native/configureplatform.cmake`, to share with coreclr and `installer/corehost`. * Use if [[ cond ]] in all places --- eng/native/functions.cmake | 389 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 eng/native/functions.cmake (limited to 'eng/native/functions.cmake') diff --git a/eng/native/functions.cmake b/eng/native/functions.cmake new file mode 100644 index 00000000000..f11c56c8283 --- /dev/null +++ b/eng/native/functions.cmake @@ -0,0 +1,389 @@ +function(clr_unknown_arch) + if (WIN32) + message(FATAL_ERROR "Only AMD64, ARM64, ARM and I386 are supported") + elseif(CLR_CROSS_COMPONENTS_BUILD) + message(FATAL_ERROR "Only AMD64, I386 host are supported for linux cross-architecture component") + else() + message(FATAL_ERROR "Only AMD64, ARM64 and ARM are supported") + endif() +endfunction() + +# Build a list of compiler definitions by putting -D in front of each define. +function(get_compile_definitions DefinitionName) + # Get the current list of definitions + get_directory_property(COMPILE_DEFINITIONS_LIST COMPILE_DEFINITIONS) + + # The entries that contain generator expressions must have the -D inside of the + # expression. So we transform e.g. $<$:_DEBUG> to $<$:-D_DEBUG> + + # CMake's support for multiple values within a single generator expression is somewhat ad-hoc. + # Since we have a number of complex generator expressions, we use them with multiple values to ensure that + # we don't forget to update all of the generator expressions if one needs to be updated. + # As a result, we need to expand out the multi-valued generator expression to wrap each individual value here. + # Otherwise, CMake will fail to expand it. + set(LastGeneratorExpression "") + foreach(DEFINITION IN LISTS COMPILE_DEFINITIONS_LIST) + # If there is a definition that uses the $ generator expression + # we need to remove it since that generator expression is only valid on binary targets. + # Assume that the value is 0. + string(REGEX REPLACE "\\$]+>" "0" DEFINITION "${DEFINITION}") + + if (${DEFINITION} MATCHES "^\\$<(.+):([^>]+)(>?)$") + if("${CMAKE_MATCH_3}" STREQUAL "") + set(DEFINITION "$<${CMAKE_MATCH_1}:-D${CMAKE_MATCH_2}>") + set(LastGeneratorExpression "${CMAKE_MATCH_1}") + else() + set(DEFINITION "$<${CMAKE_MATCH_1}:-D${CMAKE_MATCH_2}>") + endif() + elseif(${DEFINITION} MATCHES "([^>]+)>$") + # This entry is the last in a list nested within a generator expression. + set(DEFINITION "$<${LastGeneratorExpression}:-D${CMAKE_MATCH_1}>") + set(LastGeneratorExpression "") + elseif(NOT "${LastGeneratorExpression}" STREQUAL "") + set(DEFINITION "$<${LastGeneratorExpression}:-D${DEFINITION}>") + else() + set(DEFINITION -D${DEFINITION}) + endif() + list(APPEND DEFINITIONS ${DEFINITION}) + endforeach() + set(${DefinitionName} ${DEFINITIONS} PARENT_SCOPE) +endfunction(get_compile_definitions) + +# Build a list of include directories +function(get_include_directories IncludeDirectories) + get_directory_property(dirs INCLUDE_DIRECTORIES) + foreach(dir IN LISTS dirs) + + if (CLR_CMAKE_HOST_ARCH_ARM AND WIN32) + list(APPEND INC_DIRECTORIES /I${dir}) + else() + list(APPEND INC_DIRECTORIES -I${dir}) + endif(CLR_CMAKE_HOST_ARCH_ARM AND WIN32) + + endforeach() + set(${IncludeDirectories} ${INC_DIRECTORIES} PARENT_SCOPE) +endfunction(get_include_directories) + +# Build a list of include directories for consumption by the assembler +function(get_include_directories_asm IncludeDirectories) + get_directory_property(dirs INCLUDE_DIRECTORIES) + + if (CLR_CMAKE_HOST_ARCH_ARM AND WIN32) + list(APPEND INC_DIRECTORIES "-I ") + endif() + + foreach(dir IN LISTS dirs) + if (CLR_CMAKE_HOST_ARCH_ARM AND WIN32) + list(APPEND INC_DIRECTORIES ${dir};) + else() + list(APPEND INC_DIRECTORIES -I${dir}) + endif() + endforeach() + + set(${IncludeDirectories} ${INC_DIRECTORIES} PARENT_SCOPE) +endfunction(get_include_directories_asm) + +# Set the passed in RetSources variable to the list of sources with added current source directory +# to form absolute paths. +# The parameters after the RetSources are the input files. +function(convert_to_absolute_path RetSources) + set(Sources ${ARGN}) + foreach(Source IN LISTS Sources) + list(APPEND AbsolutePathSources ${CMAKE_CURRENT_SOURCE_DIR}/${Source}) + endforeach() + set(${RetSources} ${AbsolutePathSources} PARENT_SCOPE) +endfunction(convert_to_absolute_path) + +#Preprocess file +function(preprocess_file inputFilename outputFilename) + get_compile_definitions(PREPROCESS_DEFINITIONS) + get_include_directories(PREPROCESS_INCLUDE_DIRECTORIES) + if (MSVC) + add_custom_command( + OUTPUT ${outputFilename} + COMMAND ${CMAKE_CXX_COMPILER} ${PREPROCESS_INCLUDE_DIRECTORIES} /P /EP /TC ${PREPROCESS_DEFINITIONS} /Fi${outputFilename} ${inputFilename} + DEPENDS ${inputFilename} + COMMENT "Preprocessing ${inputFilename}. Outputting to ${outputFilename}" + ) + else() + add_custom_command( + OUTPUT ${outputFilename} + COMMAND ${CMAKE_CXX_COMPILER} -E -P ${PREPROCESS_DEFINITIONS} ${PREPROCESS_INCLUDE_DIRECTORIES} -o ${outputFilename} -x c ${inputFilename} + DEPENDS ${inputFilename} + COMMENT "Preprocessing ${inputFilename}. Outputting to ${outputFilename}" + ) + endif() + + set_source_files_properties(${outputFilename} + PROPERTIES GENERATED TRUE) +endfunction() + +# preprocess_compile_asm(ASM_FILES file1 [file2 ...] OUTPUT_OBJECTS [variableName]) +function(preprocess_compile_asm) + set(options "") + set(oneValueArgs OUTPUT_OBJECTS) + set(multiValueArgs ASM_FILES) + cmake_parse_arguments(PARSE_ARGV 0 COMPILE_ASM "${options}" "${oneValueArgs}" "${multiValueArgs}") + + get_include_directories_asm(ASM_INCLUDE_DIRECTORIES) + + set (ASSEMBLED_OBJECTS "") + + foreach(ASM_FILE ${COMPILE_ASM_ASM_FILES}) + # Inserts a custom command in CMake build to preprocess each asm source file + get_filename_component(name ${ASM_FILE} NAME_WE) + file(TO_CMAKE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${name}.asm" ASM_PREPROCESSED_FILE) + preprocess_file(${ASM_FILE} ${ASM_PREPROCESSED_FILE}) + + # We do not pass any defines since we have already done pre-processing above + set (ASM_CMDLINE "-o ${CMAKE_CURRENT_BINARY_DIR}/${name}.obj ${ASM_PREPROCESSED_FILE}") + + # Generate the batch file that will invoke the assembler + file(TO_CMAKE_PATH "${CMAKE_CURRENT_BINARY_DIR}/runasm_${name}.cmd" ASM_SCRIPT_FILE) + + file(GENERATE OUTPUT "${ASM_SCRIPT_FILE}" + CONTENT "\"${CMAKE_ASM_MASM_COMPILER}\" -g ${ASM_INCLUDE_DIRECTORIES} ${ASM_CMDLINE}") + + message("Generated - ${ASM_SCRIPT_FILE}") + + # Need to compile asm file using custom command as include directories are not provided to asm compiler + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${name}.obj + COMMAND ${ASM_SCRIPT_FILE} + DEPENDS ${ASM_PREPROCESSED_FILE} + COMMENT "Assembling ${ASM_PREPROCESSED_FILE} - ${ASM_SCRIPT_FILE}") + + # mark obj as source that does not require compile + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${name}.obj PROPERTIES EXTERNAL_OBJECT TRUE) + + # Add the generated OBJ in the dependency list so that it gets consumed during linkage + list(APPEND ASSEMBLED_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/${name}.obj) + endforeach() + + set(${COMPILE_ASM_OUTPUT_OBJECTS} ${ASSEMBLED_OBJECTS} PARENT_SCOPE) +endfunction() + +function(generate_exports_file) + set(INPUT_LIST ${ARGN}) + list(GET INPUT_LIST -1 outputFilename) + list(REMOVE_AT INPUT_LIST -1) + + if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(AWK_SCRIPT generateexportedsymbols.awk) + else() + set(AWK_SCRIPT generateversionscript.awk) + endif(CMAKE_SYSTEM_NAME STREQUAL Darwin) + + add_custom_command( + OUTPUT ${outputFilename} + COMMAND ${AWK} -f ${CMAKE_SOURCE_DIR}/${AWK_SCRIPT} ${INPUT_LIST} >${outputFilename} + DEPENDS ${INPUT_LIST} ${CMAKE_SOURCE_DIR}/${AWK_SCRIPT} + COMMENT "Generating exports file ${outputFilename}" + ) + set_source_files_properties(${outputFilename} + PROPERTIES GENERATED TRUE) +endfunction() + +function(generate_exports_file_prefix inputFilename outputFilename prefix) + + if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(AWK_SCRIPT generateexportedsymbols.awk) + else() + set(AWK_SCRIPT generateversionscript.awk) + if (NOT ${prefix} STREQUAL "") + set(AWK_VARS ${AWK_VARS} -v prefix=${prefix}) + endif() + endif(CMAKE_SYSTEM_NAME STREQUAL Darwin) + + add_custom_command( + OUTPUT ${outputFilename} + COMMAND ${AWK} -f ${CMAKE_SOURCE_DIR}/${AWK_SCRIPT} ${AWK_VARS} ${inputFilename} >${outputFilename} + DEPENDS ${inputFilename} ${CMAKE_SOURCE_DIR}/${AWK_SCRIPT} + COMMENT "Generating exports file ${outputFilename}" + ) + set_source_files_properties(${outputFilename} + PROPERTIES GENERATED TRUE) +endfunction() + +# target_precompile_header(TARGET targetName HEADER headerName [ADDITIONAL_INCLUDE_DIRECTORIES includeDirs]) +function(target_precompile_header) + set(options "") + set(oneValueArgs TARGET HEADER) + set(multiValueArgs ADDITIONAL_INCLUDE_DIRECTORIES) + cmake_parse_arguments(PARSE_ARGV 0 PRECOMPILE_HEADERS "${options}" "${oneValueArgs}" "${multiValueArgs}") + + if ("${PRECOMPILE_HEADERS_TARGET}" STREQUAL "") + message(SEND_ERROR "No target supplied to target_precompile_header.") + endif() + if ("${PRECOMPILE_HEADERS_HEADER}" STREQUAL "") + message(SEND_ERROR "No header supplied to target_precompile_header.") + endif() + + if(MSVC) + get_filename_component(PCH_NAME ${PRECOMPILE_HEADERS_HEADER} NAME_WE) + # We need to use the $ generator here instead of the ${targetName} variable since + # CMake evaluates source file properties once per directory. If we just use ${targetName}, we end up sharing + # the same PCH between targets, which doesn't work. + set(precompiledBinary "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${PCH_NAME}.$.pch") + set(pchSourceFile "${CMAKE_CURRENT_BINARY_DIR}/${PCH_NAME}.${PRECOMPILE_HEADERS_TARGET}.cpp") + + file(GENERATE OUTPUT ${pchSourceFile} CONTENT "#include \"${PRECOMPILE_HEADERS_HEADER}\"") + + set(PCH_SOURCE_FILE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} ${PRECOMPILE_HEADERS_ADDITIONAL_INCLUDE_DIRECTORIES}) + + set_source_files_properties(${pchSourceFile} + PROPERTIES COMPILE_FLAGS "/Yc\"${PRECOMPILE_HEADERS_HEADER}\" /Fp\"${precompiledBinary}\"" + OBJECT_OUTPUTS "${precompiledBinary}" + INCLUDE_DIRECTORIES "${PCH_SOURCE_FILE_INCLUDE_DIRECTORIES}") + get_target_property(TARGET_SOURCES ${PRECOMPILE_HEADERS_TARGET} SOURCES) + + foreach (SOURCE ${TARGET_SOURCES}) + get_source_file_property(SOURCE_LANG ${SOURCE} LANGUAGE) + if (("${SOURCE_LANG}" STREQUAL "C") OR ("${SOURCE_LANG}" STREQUAL "CXX")) + set_source_files_properties(${SOURCE} + PROPERTIES COMPILE_FLAGS "/Yu\"${PRECOMPILE_HEADERS_HEADER}\" /Fp\"${precompiledBinary}\"" + OBJECT_DEPENDS "${precompiledBinary}") + endif() + endforeach() + + # Add pchSourceFile to PRECOMPILE_HEADERS_TARGET target + target_sources(${PRECOMPILE_HEADERS_TARGET} PRIVATE ${pchSourceFile}) + endif(MSVC) +endfunction() + +function(strip_symbols targetName outputFilename skipStrip) + if (CLR_CMAKE_HOST_UNIX) + if (STRIP_SYMBOLS) + set(strip_source_file $) + + if (CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(strip_destination_file ${strip_source_file}.dwarf) + + if(NOT ${skipStrip}) + add_custom_command( + TARGET ${targetName} + POST_BUILD + VERBATIM + COMMAND ${DSYMUTIL} --flat --minimize ${strip_source_file} + COMMAND ${STRIP} -S ${strip_source_file} + COMMENT Stripping symbols from ${strip_source_file} into file ${strip_destination_file} + ) + endif() + else (CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(strip_destination_file ${strip_source_file}.dbg) + + if(NOT ${skipStrip}) + add_custom_command( + TARGET ${targetName} + POST_BUILD + VERBATIM + COMMAND ${OBJCOPY} --only-keep-debug ${strip_source_file} ${strip_destination_file} + COMMAND ${OBJCOPY} --strip-debug ${strip_source_file} + COMMAND ${OBJCOPY} --add-gnu-debuglink=${strip_destination_file} ${strip_source_file} + COMMENT Stripping symbols from ${strip_source_file} into file ${strip_destination_file} + ) + endif() + endif (CMAKE_SYSTEM_NAME STREQUAL Darwin) + + set(${outputFilename} ${strip_destination_file} PARENT_SCOPE) + endif (STRIP_SYMBOLS) + endif(CLR_CMAKE_HOST_UNIX) +endfunction() + +# install_clr(TARGETS TARGETS targetName [targetName2 ...] [DESTINATION destination] [SKIP_STRIP]) +function(install_clr) + set(options SKIP_STRIP) + set(oneValueArgs DESTINATION) + set(multiValueArgs TARGETS) + cmake_parse_arguments(PARSE_ARGV 0 INSTALL_CLR "${options}" "${oneValueArgs}" "${multiValueArgs}") + + if ("${INSTALL_CLR_TARGETS}" STREQUAL "") + message(FATAL_ERROR "At least one target must be passed to install_clr(TARGETS )") + endif() + + if ("${INSTALL_CLR_DESTINATION}" STREQUAL "") + set(INSTALL_CLR_DESTINATION ".") + endif() + + foreach(targetName ${INSTALL_CLR_TARGETS}) + list(FIND CLR_CROSS_COMPONENTS_LIST ${targetName} INDEX) + if (NOT DEFINED CLR_CROSS_COMPONENTS_LIST OR NOT ${INDEX} EQUAL -1) + if("${INSTALL_CLR_SKIP_STRIP}" STREQUAL "") + set(INSTALL_CLR_SKIP_STRIP FALSE) + endif() + strip_symbols(${targetName} strip_destination_file ${INSTALL_CLR_SKIP_STRIP}) + + # We don't need to install the export libraries for our DLLs + # since they won't be directly linked against. + install(PROGRAMS $ DESTINATION ${INSTALL_CLR_DESTINATION}) + if(WIN32) + # We can't use the $ generator expression here since + # the generator expression isn't supported on resource DLLs. + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/$/${targetName}.pdb DESTINATION ${INSTALL_CLR_DESTINATION}/PDB) + else() + install(FILES ${strip_destination_file} DESTINATION ${INSTALL_CLR_DESTINATION}) + endif() + if(CLR_CMAKE_PGO_INSTRUMENT) + if(WIN32) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/$/${targetName}.pgd DESTINATION ${INSTALL_CLR_DESTINATION}/PGD OPTIONAL) + endif() + endif() + endif() + endforeach() +endfunction() + +# Disable PAX mprotect that would prevent JIT and other codegen in coreclr from working. +# PAX mprotect prevents: +# - changing the executable status of memory pages that were +# not originally created as executable, +# - making read-only executable pages writable again, +# - creating executable pages from anonymous memory, +# - making read-only-after-relocations (RELRO) data pages writable again. +function(disable_pax_mprotect targetName) + if (NOT PAXCTL STREQUAL "PAXCTL-NOTFOUND") + add_custom_command( + TARGET ${targetName} + POST_BUILD + VERBATIM + COMMAND ${PAXCTL} -c -m $ + ) + endif() +endfunction() + +function(_add_executable) + if(NOT WIN32) + add_executable(${ARGV} ${VERSION_FILE_PATH}) + disable_pax_mprotect(${ARGV}) + else() + add_executable(${ARGV}) + endif(NOT WIN32) + list(FIND CLR_CROSS_COMPONENTS_LIST ${ARGV0} INDEX) + if (DEFINED CLR_CROSS_COMPONENTS_LIST AND ${INDEX} EQUAL -1) + set_target_properties(${ARGV0} PROPERTIES EXCLUDE_FROM_ALL 1) + endif() +endfunction() + +function(_add_library) + if(NOT WIN32) + add_library(${ARGV} ${VERSION_FILE_PATH}) + else() + add_library(${ARGV}) + endif(NOT WIN32) + list(FIND CLR_CROSS_COMPONENTS_LIST ${ARGV0} INDEX) + if (DEFINED CLR_CROSS_COMPONENTS_LIST AND ${INDEX} EQUAL -1) + set_target_properties(${ARGV0} PROPERTIES EXCLUDE_FROM_ALL 1) + endif() +endfunction() + +function(_install) + if(NOT DEFINED CLR_CROSS_COMPONENTS_BUILD) + install(${ARGV}) + endif() +endfunction() + +function(add_library_clr) + _add_library(${ARGV}) +endfunction() + +function(add_executable_clr) + _add_executable(${ARGV}) +endfunction() -- cgit v1.2.3