From 800051487f13e5506502c0c4021a97a53b89bcde Mon Sep 17 00:00:00 2001 From: Tue Ly Date: Sun, 8 May 2022 13:45:40 -0400 Subject: [PATCH] [libc] Implement FLAGS option for generating all combinations for targets. Add FLAGS option for add_header_library, add_object_library, add_entrypoint_object, and add_libc_unittest. In general, a flag is a string provided for supported functions under the multi-valued option `FLAGS`. It should be one of the following forms: FLAG_NAME FLAG_NAME__NO FLAG_NAME__ONLY A target will inherit all the flags of its upstream dependency. When we create a target `TARGET_NAME` with a flag using (add_header_library, add_object_library, ...), its behavior will depend on the flag form as follow: - FLAG_NAME: The following 2 targets will be generated: `TARGET_NAME` that has `FLAG_NAME` in its `FLAGS` property. `TARGET_NAME.__NO_FLAG_NAME` that depends on `DEP.__NO_FLAG_NAME` if `TARGET_NAME` depends on `DEP` and `DEP` has `FLAG_NAME` in its `FLAGS` property. - FLAG_NAME__ONLY: Only generate 1 target `TARGET_NAME` that has `FLAG_NAME` in its `FLAGS` property. - FLAG_NAME__NO: Only generate 1 target `TARGET_NAME.__NO_FLAG_NAME` that depends on `DEP.__NO_FLAG_NAME` if `DEP` is in its DEPENDS list and `DEP` has `FLAG_NAME` in its `FLAGS` property. To show all the targets generated, pass SHOW_INTERMEDIATE_OBJECTS=ON to cmake. To show all the targets' dependency and flags, pass `SHOW_INTERMEDIATE_OBJECTS=DEPS` to cmake. To completely disable a flag FLAG_NAME expansion, set the variable `SKIP_FLAG_EXPANSION_FLAG_NAME=TRUE`. Reviewed By: michaelrj, sivachandra Differential Revision: https://reviews.llvm.org/D125174 --- libc/CMakeLists.txt | 2 +- libc/cmake/modules/LLVMLibCFlagRules.cmake | 133 +++++++++ libc/cmake/modules/LLVMLibCLibraryRules.cmake | 142 ++++++++-- libc/cmake/modules/LLVMLibCObjectRules.cmake | 301 ++++++++++++++++++--- libc/cmake/modules/LLVMLibCRules.cmake | 1 + libc/cmake/modules/LLVMLibCTestRules.cmake | 121 ++++++++- libc/test/src/CMakeLists.txt | 2 +- libc/test/src/__support/File/CMakeLists.txt | 4 +- .../__support/OSUtil/linux/x86_64/CMakeLists.txt | 4 +- libc/test/src/math/exhaustive/CMakeLists.txt | 4 +- libc/test/src/stdio/printf_core/CMakeLists.txt | 4 +- libc/test/src/string/CMakeLists.txt | 4 +- libc/test/src/string/memory_utils/CMakeLists.txt | 6 +- 13 files changed, 644 insertions(+), 84 deletions(-) create mode 100644 libc/cmake/modules/LLVMLibCFlagRules.cmake diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index ca9a819..30d7c33 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -105,8 +105,8 @@ endif() option(LIBC_INCLUDE_DOCS "Build the libc documentation." ${LLVM_INCLUDE_DOCS}) include(CMakeParseArguments) -include(LLVMLibCRules) include(LLVMLibCCheckCpuFeatures) +include(LLVMLibCRules) if(EXISTS "${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_ARCHITECTURE}/entrypoints.txt") set(entrypoint_file "${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_ARCHITECTURE}/entrypoints.txt") diff --git a/libc/cmake/modules/LLVMLibCFlagRules.cmake b/libc/cmake/modules/LLVMLibCFlagRules.cmake new file mode 100644 index 0000000..f43b645 --- /dev/null +++ b/libc/cmake/modules/LLVMLibCFlagRules.cmake @@ -0,0 +1,133 @@ +# In general, a flag is a string provided for supported functions under the +# multi-valued option `FLAGS`. It should be one of the following forms: +# FLAG_NAME +# FLAG_NAME__NO +# FLAG_NAME__ONLY +# A target will inherit all the flags of its upstream dependency. +# +# When we create a target `TARGET_NAME` with a flag using (add_header_library, +# add_object_library, ...), its behavior will depend on the flag form as follow: +# - FLAG_NAME: The following 2 targets will be generated: +# `TARGET_NAME` that has `FLAG_NAME` in its `FLAGS` property. +# `TARGET_NAME.__NO_FLAG_NAME` that depends on `DEP.__NO_FLAG_NAME` if +# `TARGET_NAME` depends on `DEP` and `DEP` has `FLAG_NAME` in its `FLAGS` +# property. +# - FLAG_NAME__ONLY: Only generate 1 target `TARGET_NAME` that has `FLAG_NAME` +# in its `FLAGS` property. +# - FLAG_NAME__NO: Only generate 1 target `TARGET_NAME` that depends on +# `DEP.__NO_FLAG_NAME` if `DEP` is in its DEPENDS list and `DEP` has `FLAG_NAME` +# in its `FLAGS` property. +# +# To show all the targets generated, pass SHOW_INTERMEDIATE_OBJECTS=ON to cmake. +# To show all the targets' dependency and flags, pass +# SHOW_INTERMEDIATE_OBJECTS=DEPS to cmake. +# +# To completely disable a flag FLAG_NAME expansion, set the variable +# SKIP_FLAG_EXPANSION_FLAG_NAME=TRUE in this file. + + +function(extract_flag_modifier input_flag output_flag modifier) + if(${input_flag} MATCHES "__NO$") + string(REGEX REPLACE "__NO$" "" flag "${input_flag}") + set(${output_flag} ${flag} PARENT_SCOPE) + set(${modifier} "NO" PARENT_SCOPE) + elseif(${input_flag} MATCHES "__ONLY$") + string(REGEX REPLACE "__ONLY$" "" flag "${input_flag}") + set(${output_flag} ${flag} PARENT_SCOPE) + set(${modifier} "ONLY" PARENT_SCOPE) + else() + set(${output_flag} ${input_flag} PARENT_SCOPE) + set(${modifier} "" PARENT_SCOPE) + endif() +endfunction(extract_flag_modifier) + +function(remove_duplicated_flags input_flags output_flags) + set(out_flags "") + foreach(input_flag IN LISTS input_flags) + if(NOT input_flag) + continue() + endif() + + extract_flag_modifier(${input_flag} flag modifier) + + # Check if the flag is skipped. + if(${SKIP_FLAG_EXPANSION_${flag}}) + if("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS") + message(STATUS " Flag ${flag} is ignored.") + endif() + continue() + endif() + + set(found FALSE) + foreach(out_flag IN LISTS out_flags) + extract_flag_modifier(${out_flag} o_flag o_modifier) + if("${flag}" STREQUAL "${o_flag}") + set(found TRUE) + break() + endif() + endforeach() + if(NOT found) + list(APPEND out_flags ${input_flag}) + endif() + endforeach() + + set(${output_flags} "${out_flags}" PARENT_SCOPE) +endfunction(remove_duplicated_flags) + +# Collect flags from dependency list. To see which flags come with each +# dependence, pass `SHOW_INTERMEDIATE_OBJECTS=DEPS` to cmake. +function(get_flags_from_dep_list output_list) + set(flag_list "") + foreach(dep IN LISTS ARGN) + if(NOT dep) + continue() + endif() + + get_fq_dep_name(fq_dep_name ${dep}) + + if(NOT TARGET ${fq_dep_name}) + continue() + endif() + + get_target_property(flags ${fq_dep_name} "FLAGS") + + if(flags AND "${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS") + message(STATUS " FLAGS from dependency ${fq_dep_name} are ${flags}") + endif() + + foreach(flag IN LISTS flags) + if(flag) + list(APPEND flag_list ${flag}) + endif() + endforeach() + endforeach(dep) + + list(REMOVE_DUPLICATES flag_list) + + set(${output_list} ${flag_list} PARENT_SCOPE) +endfunction(get_flags_from_dep_list) + +# Given a `flag` without modifier, scan through the list of dependency, append +# `.__NO_flag` to any target that has `flag` in its FLAGS property. +function(get_fq_dep_list_without_flag output_list flag) + set(fq_dep_no_flag_list "") + foreach(dep IN LISTS ARGN) + get_fq_dep_name(fq_dep_name ${dep}) + if(TARGET ${fq_dep_name}) + get_target_property(dep_flags ${fq_dep_name} "FLAGS") + # Only target with `flag` has `.__NO_flag` target, `flag__NO` and + # `flag__ONLY` do not. + if(${flag} IN_LIST dep_flags) + list(APPEND fq_dep_no_flag_list "${fq_dep_name}.__NO_${flag}") + else() + list(APPEND fq_dep_no_flag_list ${fq_dep_name}) + endif() + else() + list(APPEND fq_dep_no_flag_list ${fq_dep_name}) + endif() + endforeach(dep) + set(${output_list} ${fq_dep_no_flag_list} PARENT_SCOPE) +endfunction(get_fq_dep_list_without_flag) + +# Special flags +set(FMA_OPT_FLAG "FMA_OPT") diff --git a/libc/cmake/modules/LLVMLibCLibraryRules.cmake b/libc/cmake/modules/LLVMLibCLibraryRules.cmake index 5d7c654..78362ce 100644 --- a/libc/cmake/modules/LLVMLibCLibraryRules.cmake +++ b/libc/cmake/modules/LLVMLibCLibraryRules.cmake @@ -124,19 +124,13 @@ endfunction(add_redirector_library) set(HDR_LIBRARY_TARGET_TYPE "HDR_LIBRARY") -# Rule to add header only libraries. -# Usage -# add_header_library( -# -# HDRS -# DEPENDS -# ) -function(add_header_library target_name) +# Internal function, used by `add_header_library`. +function(create_header_library fq_target_name) cmake_parse_arguments( "ADD_HEADER" - "" # No optional arguments - "" # No Single value arguments - "HDRS;DEPENDS" # Multi-value arguments + "" # Optional arguments + "" # Single value arguments + "HDRS;DEPENDS;FLAGS" # Multi-value arguments ${ARGN} ) @@ -144,8 +138,6 @@ function(add_header_library target_name) message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.") endif() - get_fq_target_name(${target_name} fq_target_name) - set(FULL_HDR_PATHS "") # TODO: Remove this foreach block when we can switch to the new # version of the CMake policy CMP0076. @@ -153,21 +145,137 @@ function(add_header_library target_name) list(APPEND FULL_HDR_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/${hdr}) endforeach() + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Adding header library ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS ADD_HEADER_DEPENDS) + message(STATUS " ${fq_target_name} depends on ${dep}") + endforeach() + endif() + endif() set(interface_target_name "${fq_target_name}.__header_library__") add_library(${interface_target_name} INTERFACE) target_sources(${interface_target_name} INTERFACE ${FULL_HDR_PATHS}) - get_fq_deps_list(fq_deps_list ${ADD_HEADER_DEPENDS}) if(ADD_HEADER_DEPENDS) - add_dependencies(${interface_target_name} ${fq_deps_list}) + add_dependencies(${interface_target_name} ${ADD_HEADER_DEPENDS}) endif() + set_target_properties( + ${interface_target_name} + PROPERTIES + INTERFACE_FLAGS "${ADD_HEADER_FLAGS}" + ) add_custom_target(${fq_target_name}) add_dependencies(${fq_target_name} ${interface_target_name}) set_target_properties( ${fq_target_name} PROPERTIES - "TARGET_TYPE" "${HDR_LIBRARY_TARGET_TYPE}" - "DEPS" "${fq_deps_list}" + TARGET_TYPE "${HDR_LIBRARY_TARGET_TYPE}" + DEPS "${ADD_HEADER_DEPENDS}" + FLAGS "${ADD_HEADER_FLAGS}" + ) +endfunction(create_header_library) + +# Rule to add header only libraries. +# Usage +# add_header_library( +# +# HDRS +# DEPENDS +# FLAGS +# ) + +# Internal function, used by `add_header_library`. +function(expand_flags_for_header_library target_name flags) + cmake_parse_arguments( + "EXPAND_FLAGS" + "IGNORE_MARKER" # Optional arguments + "" # Single-value arguments + "DEPENDS;FLAGS" # Multi-value arguments + ${ARGN} + ) + + list(LENGTH flags nflags) + if(NOT ${nflags}) + create_header_library( + ${target_name} + DEPENDS ${EXPAND_FLAGS_DEPENDS} + FLAGS ${EXPAND_FLAGS_FLAGS} + ${EXPAND_FLAGS_UNPARSED_ARGUMENTS} + ) + return() + endif() + + list(POP_FRONT flags flag) + extract_flag_modifier(${flag} real_flag modifier) + + if(NOT "${modifier}" STREQUAL "NO") + expand_flags_for_header_library( + ${target_name} + "${flags}" + DEPENDS ${EXPAND_FLAGS_DEPENDS} IGNORE_MARKER + FLAGS ${EXPAND_FLAGS_FLAGS} IGNORE_MARKER + ${EXPAND_FLAGS_UNPARSED_ARGUMENTS} + ) + endif() + + if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY") + return() + endif() + + set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS}) + list(REMOVE_ITEM NEW_FLAGS ${flag}) + get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS}) + + # Only target with `flag` has `.__NO_flag` target, `flag__NO` and + # `flag__ONLY` do not. + if(NOT "${modifier}") + set(TARGET_NAME "${target_name}.__NO_${flag}") + else() + set(TARGET_NAME "${target_name}") + endif() + + expand_flags_for_header_library( + ${TARGET_NAME} + "${flags}" + DEPENDS ${NEW_DEPS} IGNORE_MARKER + FLAGS ${NEW_FLAGS} IGNORE_MARKER + ${EXPAND_FLAGS_UNPARSED_ARGUMENTS} + ) +endfunction(expand_flags_for_header_library) + +function(add_header_library target_name) + cmake_parse_arguments( + "ADD_TO_EXPAND" + "" # Optional arguments + "" # Single value arguments + "DEPENDS;FLAGS" # Multi-value arguments + ${ARGN} + ) + + get_fq_target_name(${target_name} fq_target_name) + + if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")) + message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}") + endif() + + get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS}) + get_flags_from_dep_list(deps_flag_list ${fq_deps_list}) + + list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list}) + remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags) + list(SORT flags) + + if(SHOW_INTERMEDIATE_OBJECTS AND flags) + message(STATUS "Header library ${fq_target_name} has FLAGS: ${flags}") + endif() + + expand_flags_for_header_library( + ${fq_target_name} + "${flags}" + DEPENDS ${fq_deps_list} IGNORE_MARKER + FLAGS ${flags} IGNORE_MARKER + ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS} ) endfunction(add_header_library) diff --git a/libc/cmake/modules/LLVMLibCObjectRules.cmake b/libc/cmake/modules/LLVMLibCObjectRules.cmake index 8e2f489..ae49e13 100644 --- a/libc/cmake/modules/LLVMLibCObjectRules.cmake +++ b/libc/cmake/modules/LLVMLibCObjectRules.cmake @@ -1,7 +1,7 @@ set(OBJECT_LIBRARY_TARGET_TYPE "OBJECT_LIBRARY") function(_get_common_compile_options output_var) - set(compile_options ${LLVM_CXX_STD_default} ${LIBC_COMPILE_OPTIONS_DEFAULT} ${ARGN}) + set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT} ${ARGN}) if(NOT ${LIBC_TARGET_OS} STREQUAL "windows") set(compile_options ${compile_options} -fpie -ffreestanding) endif() @@ -26,12 +26,13 @@ endfunction() # SRCS # DEPENDS # COMPILE_OPTIONS -function(add_object_library target_name) +# FLAGS +function(create_object_library fq_target_name) cmake_parse_arguments( "ADD_OBJECT" "" # No optional arguments "CXX_STANDARD" # Single value arguments - "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS" # Multivalue arguments + "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS;FLAGS" # Multivalue arguments ${ARGN} ) @@ -39,7 +40,6 @@ function(add_object_library target_name) message(FATAL_ERROR "'add_object_library' rule requires SRCS to be specified.") endif() - get_fq_target_name(${target_name} fq_target_name) add_library( ${fq_target_name} EXCLUDE_FROM_ALL @@ -58,24 +58,126 @@ function(add_object_library target_name) target_compile_options(${fq_target_name} PRIVATE ${compile_options}) get_fq_deps_list(fq_deps_list ${ADD_OBJECT_DEPENDS}) + + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Adding object library ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS ADD_OBJECT_DEPENDS) + message(STATUS " ${fq_target_name} depends on ${dep}") + endforeach() + endif() + endif() + if(fq_deps_list) add_dependencies(${fq_target_name} ${fq_deps_list}) endif() - if(ADD_OBJECT_CXX_STANDARD) - set_target_properties( - ${fq_target_name} - PROPERTIES - CXX_STANDARD ${ADD_OBJECT_CXX_STANDARD} - ) + if(NOT ADD_OBJECT_CXX_STANDARD) + set(ADD_OBJECT_CXX_STANDARD ${CMAKE_CXX_STANDARD}) endif() - + set_target_properties( ${fq_target_name} PROPERTIES - "TARGET_TYPE" ${OBJECT_LIBRARY_TARGET_TYPE} - "OBJECT_FILES" "$" - "DEPS" "${fq_deps_list}" + TARGET_TYPE ${OBJECT_LIBRARY_TARGET_TYPE} + OBJECT_FILES "$" + CXX_STANDARD ${ADD_OBJECT_CXX_STANDARD} + DEPS "${fq_deps_list}" + FLAGS "${ADD_OBJECT_FLAGS}" + ) +endfunction(create_object_library) + +# Internal function, used by `add_object_library`. +function(expand_flags_for_object_library target_name flags) + cmake_parse_arguments( + "EXPAND_FLAGS" + "IGNORE_MARKER" # Optional arguments + "" # Single-value arguments + "DEPENDS;FLAGS" # Multi-value arguments + ${ARGN} + ) + + list(LENGTH flags nflags) + if(NOT ${nflags}) + create_object_library( + ${target_name} + DEPENDS ${EXPAND_FLAGS_DEPENDS} + FLAGS ${EXPAND_FLAGS_FLAGS} + ${EXPAND_FLAGS_UNPARSED_ARGUMENTS} + ) + return() + endif() + + list(POP_FRONT flags flag) + extract_flag_modifier(${flag} real_flag modifier) + + if(NOT "${modifier}" STREQUAL "NO") + expand_flags_for_object_library( + ${target_name} + "${flags}" + DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER + FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER + "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" + ) + endif() + + if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY") + return() + endif() + + set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS}) + list(REMOVE_ITEM NEW_FLAGS ${flag}) + get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS}) + + # Only target with `flag` has `.__NO_flag` target, `flag__NO` and + # `flag__ONLY` do not. + if(NOT "${modifier}") + set(TARGET_NAME "${target_name}.__NO_${flag}") + else() + set(TARGET_NAME "${target_name}") + endif() + + expand_flags_for_object_library( + ${TARGET_NAME} + "${flags}" + DEPENDS "${NEW_DEPS}" IGNORE_MARKER + FLAGS "${NEW_FLAGS}" IGNORE_MARKER + "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" + ) +endfunction(expand_flags_for_object_library) + +function(add_object_library target_name) + cmake_parse_arguments( + "ADD_TO_EXPAND" + "" # Optional arguments + "" # Single value arguments + "DEPENDS;FLAGS" # Multi-value arguments + ${ARGN} + ) + + get_fq_target_name(${target_name} fq_target_name) + + if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")) + message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}") + endif() + + get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS}) + get_flags_from_dep_list(deps_flag_list ${fq_deps_list}) + + list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list}) + remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags) + list(SORT flags) + + if(SHOW_INTERMEDIATE_OBJECTS AND flags) + message(STATUS "Object library ${fq_target_name} has FLAGS: ${flags}") + endif() + + expand_flags_for_object_library( + ${fq_target_name} + "${flags}" + DEPENDS "${fq_deps_list}" IGNORE_MARKER + FLAGS "${flags}" IGNORE_MARKER + ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS} ) endfunction(add_object_library) @@ -92,29 +194,24 @@ set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ") # DEPENDS # COMPILE_OPTIONS # SPECIAL_OBJECTS +# FLAGS # ) -function(add_entrypoint_object target_name) +function(create_entrypoint_object fq_target_name) cmake_parse_arguments( "ADD_ENTRYPOINT_OBJ" "ALIAS;REDIRECTED" # Optional argument "NAME;CXX_STANDARD" # Single value arguments - "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi value arguments + "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;FLAGS" # Multi value arguments ${ARGN} ) - get_fq_target_name(${target_name} fq_target_name) - set(entrypoint_name ${target_name}) - if(ADD_ENTRYPOINT_OBJ_NAME) - set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME}) - endif() - - list(FIND TARGET_ENTRYPOINT_NAME_LIST ${entrypoint_name} entrypoint_name_index) + list(FIND TARGET_ENTRYPOINT_NAME_LIST ${ADD_ENTRYPOINT_OBJ_NAME} entrypoint_name_index) if(${entrypoint_name_index} EQUAL -1) add_custom_target(${fq_target_name}) set_target_properties( ${fq_target_name} PROPERTIES - "ENTRYPOINT_NAME" ${entrypoint_name} + "ENTRYPOINT_NAME" ${ADD_ENTRYPOINT_OBJ_NAME} "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE} "OBJECT_FILE" "" "OBJECT_FILE_RAW" "" @@ -134,6 +231,12 @@ function(add_entrypoint_object target_name) endif() list(GET ADD_ENTRYPOINT_OBJ_DEPENDS 0 dep_target) get_fq_dep_name(fq_dep_name ${dep_target}) + + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Adding entrypoint object ${fq_target_name} as an alias of" + " ${fq_dep_name}") + endif() + if(NOT TARGET ${fq_dep_name}) message(WARNING "Aliasee ${fq_dep_name} for entrypoint alias ${target_name} missing; " "Target ${target_name} will be ignored.") @@ -152,12 +255,13 @@ function(add_entrypoint_object target_name) set_target_properties( ${fq_target_name} PROPERTIES - "ENTRYPOINT_NAME" ${entrypoint_name} - "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE} - "IS_ALIAS" "YES" - "OBJECT_FILE" "" - "OBJECT_FILE_RAW" "" - "DEPS" "${fq_dep_name}" + ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME} + TARGET_TYPE ${ENTRYPOINT_OBJ_TARGET_TYPE} + IS_ALIAS "YES" + OBJECT_FILE "" + OBJECT_FILE_RAW "" + DEPS "${fq_dep_name}" + FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}" ) return() endif() @@ -168,6 +272,9 @@ function(add_entrypoint_object target_name) if(NOT ADD_ENTRYPOINT_OBJ_HDRS) message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.") endif() + if(NOT ADD_ENTRYPOINT_OBJ_CXX_STANDARD) + set(ADD_ENTRYPOINT_OBJ_CXX_STANDARD ${CMAKE_CXX_STANDARD}) + endif() _get_common_compile_options(common_compile_options ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS}) set(internal_target_name ${fq_target_name}.__internal__) @@ -175,6 +282,15 @@ function(add_entrypoint_object target_name) get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS}) set(full_deps_list ${fq_deps_list} libc.src.__support.common) + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Adding entrypoint object ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS ADD_OBJECT_DEPENDS) + message(STATUS " ${fq_target_name} depends on ${dep}") + endforeach() + endif() + endif() + add_library( ${internal_target_name} # TODO: We don't need an object library for internal consumption. @@ -187,6 +303,12 @@ function(add_entrypoint_object target_name) target_compile_options(${internal_target_name} BEFORE PRIVATE ${common_compile_options}) target_include_directories(${internal_target_name} PRIVATE ${include_dirs}) add_dependencies(${internal_target_name} ${full_deps_list}) + set_target_properties( + ${internal_target_name} + PROPERTIES + CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD} + FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}" + ) add_library( ${fq_target_name} @@ -201,24 +323,18 @@ function(add_entrypoint_object target_name) target_include_directories(${fq_target_name} PRIVATE ${include_dirs}) add_dependencies(${fq_target_name} ${full_deps_list}) - if(ADD_ENTRYPOINT_OBJ_CXX_STANDARD) - set_target_properties( - ${fq_target_name} ${internal_target_name} - PROPERTIES - CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD} - ) - endif() - set_target_properties( ${fq_target_name} PROPERTIES - "ENTRYPOINT_NAME" ${entrypoint_name} - "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE} - "OBJECT_FILE" $ + ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME} + TARGET_TYPE ${ENTRYPOINT_OBJ_TARGET_TYPE} + OBJECT_FILE "$" # TODO: We don't need to list internal object files if the internal # target is a normal static library. - "OBJECT_FILE_RAW" $ - "DEPS" "${fq_deps_list}" + OBJECT_FILE_RAW "$" + CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD} + DEPS "${fq_deps_list}" + FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}" ) if(LLVM_LIBC_ENABLE_LINTING) @@ -280,6 +396,105 @@ function(add_entrypoint_object target_name) ) endif() +endfunction(create_entrypoint_object) + +# Internal function, used by `add_entrypoint_object`. +function(expand_flags_for_entrypoint_object target_name flags) + cmake_parse_arguments( + "EXPAND_FLAGS" + "IGNORE_MARKER" # Optional arguments + "" # Single-value arguments + "DEPENDS;FLAGS" # Multi-value arguments + ${ARGN} + ) + + list(LENGTH flags nflags) + if(NOT ${nflags}) + create_entrypoint_object( + ${target_name} + DEPENDS ${EXPAND_FLAGS_DEPENDS} + FLAGS ${EXPAND_FLAGS_FLAGS} + ${EXPAND_FLAGS_UNPARSED_ARGUMENTS} + ) + return() + endif() + + list(POP_FRONT flags flag) + extract_flag_modifier(${flag} real_flag modifier) + + if(NOT "${modifier}" STREQUAL "NO") + expand_flags_for_entrypoint_object( + ${target_name} + "${flags}" + DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER + FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER + "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" + ) + endif() + + if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY") + return() + endif() + + set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS}) + list(REMOVE_ITEM NEW_FLAGS ${flag}) + get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS}) + + # Only target with `flag` has `.__NO_flag` target, `flag__NO` and + # `flag__ONLY` do not. + if(NOT "${modifier}") + set(TARGET_NAME "${target_name}.__NO_${flag}") + else() + set(TARGET_NAME "${target_name}") + endif() + + expand_flags_for_entrypoint_object( + ${TARGET_NAME} + "${flags}" + DEPENDS "${NEW_DEPS}" IGNORE_MARKER + FLAGS "${NEW_FLAGS}" IGNORE_MARKER + "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" + ) +endfunction(expand_flags_for_entrypoint_object) + +function(add_entrypoint_object target_name) + cmake_parse_arguments( + "ADD_TO_EXPAND" + "" # Optional arguments + "NAME" # Single value arguments + "DEPENDS;FLAGS" # Multi-value arguments + ${ARGN} + ) + + get_fq_target_name(${target_name} fq_target_name) + + if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")) + message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}") + endif() + + get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS}) + get_flags_from_dep_list(deps_flag_list ${fq_deps_list}) + + list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list}) + remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags) + list(SORT flags) + + if(SHOW_INTERMEDIATE_OBJECTS AND flags) + message(STATUS "Object library ${fq_target_name} has FLAGS: ${flags}") + endif() + + if(NOT ADD_TO_EXPAND_NAME) + set(ADD_TO_EXPAND_NAME ${target_name}) + endif() + + expand_flags_for_entrypoint_object( + ${fq_target_name} + "${flags}" + NAME ${ADD_TO_EXPAND_NAME} IGNORE_MARKER + DEPENDS "${fq_deps_list}" IGNORE_MARKER + FLAGS "${flags}" IGNORE_MARKER + ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS} + ) endfunction(add_entrypoint_object) set(ENTRYPOINT_EXT_TARGET_TYPE "ENTRYPOINT_EXT") diff --git a/libc/cmake/modules/LLVMLibCRules.cmake b/libc/cmake/modules/LLVMLibCRules.cmake index f76d6e5..c45a3f6 100644 --- a/libc/cmake/modules/LLVMLibCRules.cmake +++ b/libc/cmake/modules/LLVMLibCRules.cmake @@ -1,4 +1,5 @@ include(LLVMLibCTargetNameUtils) +include(LLVMLibCFlagRules) include(LLVMLibCHeaderRules) include(LLVMLibCObjectRules) include(LLVMLibCLibraryRules) diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake index b1a34fe..cd4b7b7 100644 --- a/libc/cmake/modules/LLVMLibCTestRules.cmake +++ b/libc/cmake/modules/LLVMLibCTestRules.cmake @@ -65,7 +65,7 @@ endfunction(get_object_files_for_test) # COMPILE_OPTIONS # LINK_LIBRARIES # ) -function(add_libc_unittest target_name) +function(create_libc_unittest fq_target_name) if(NOT LLVM_INCLUDE_TESTS) return() endif() @@ -74,7 +74,7 @@ function(add_libc_unittest target_name) "LIBC_UNITTEST" "NO_RUN_POSTBUILD;NO_LIBC_UNITTEST_TEST_MAIN" # Optional arguments "SUITE;CXX_STANDARD" # Single value arguments - "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;LINK_LIBRARIES" # Multi-value arguments + "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;LINK_LIBRARIES;FLAGS" # Multi-value arguments ${ARGN} ) if(NOT LIBC_UNITTEST_SRCS) @@ -86,7 +86,6 @@ function(add_libc_unittest target_name) "'add_entrypoint_object' targets.") endif() - get_fq_target_name(${target_name} fq_target_name) get_fq_deps_list(fq_deps_list ${LIBC_UNITTEST_DEPENDS}) get_object_files_for_test( link_object_files skipped_entrypoints_list ${fq_deps_list}) @@ -115,6 +114,15 @@ function(add_libc_unittest target_name) return() endif() + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Adding unit test ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS ADD_OBJECT_DEPENDS) + message(STATUS " ${fq_target_name} depends on ${dep}") + endforeach() + endif() + endif() + add_executable( ${fq_target_name} EXCLUDE_FROM_ALL @@ -138,13 +146,14 @@ function(add_libc_unittest target_name) PRIVATE ${LIBC_UNITTEST_COMPILE_OPTIONS} ) endif() - if(LIBC_UNITTEST_CXX_STANDARD) - set_target_properties( - ${fq_target_name} - PROPERTIES - CXX_STANDARD ${LIBC_UNITTEST_CXX_STANDARD} - ) + if(NOT LIBC_UNITTEST_CXX_STANDARD) + set(LIBC_UNITTEST_CXX_STANDARD ${CMAKE_CXX_STANDARD}) endif() + set_target_properties( + ${fq_target_name} + PROPERTIES + CXX_STANDARD ${LIBC_UNITTEST_CXX_STANDARD} + ) # Test object files will depend on LINK_LIBRARIES passed down from `add_fp_unittest` set(link_libraries ${link_object_files} ${LIBC_UNITTEST_LINK_LIBRARIES}) @@ -180,6 +189,100 @@ function(add_libc_unittest target_name) ${fq_target_name} ) endif() +endfunction(create_libc_unittest) + +# Internal function, used by `add_libc_unittest`. +function(expand_flags_for_libc_unittest target_name flags) + cmake_parse_arguments( + "EXPAND_FLAGS" + "IGNORE_MARKER" # No Optional arguments + "" # No Single-value arguments + "DEPENDS;FLAGS" # Multi-value arguments + ${ARGN} + ) + + list(LENGTH flags nflags) + if(NOT ${nflags}) + create_libc_unittest( + ${target_name} + DEPENDS "${EXPAND_FLAGS_DEPENDS}" + FLAGS "${EXPAND_FLAGS_FLAGS}" + "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" + ) + return() + endif() + + list(POP_FRONT flags flag) + extract_flag_modifier(${flag} real_flag modifier) + + if(NOT "${modifier}" STREQUAL "NO") + expand_flags_for_libc_unittest( + ${target_name} + "${flags}" + DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER + FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER + "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" + ) + endif() + + if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY") + return() + endif() + + set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS}) + list(REMOVE_ITEM NEW_FLAGS ${flag}) + get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS}) + + # Only target with `flag` has `.__NO_flag` target, `flag__NO` and + # `flag__ONLY` do not. + if(NOT "${modifier}") + set(TARGET_NAME "${target_name}.__NO_${flag}") + else() + set(TARGET_NAME "${target_name}") + endif() + + expand_flags_for_libc_unittest( + ${TARGET_NAME} + "${flags}" + DEPENDS "${NEW_DEPS}" IGNORE_MARKER + FLAGS "${NEW_FLAGS}" IGNORE_MARKER + "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" + ) +endfunction(expand_flags_for_libc_unittest) + +function(add_libc_unittest target_name) + cmake_parse_arguments( + "ADD_TO_EXPAND" + "" # Optional arguments + "" # Single value arguments + "DEPENDS;FLAGS" # Multi-value arguments + ${ARGN} + ) + + get_fq_target_name(${target_name} fq_target_name) + + if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")) + message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}") + endif() + + get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS}) + get_flags_from_dep_list(deps_flag_list ${fq_deps_list}) + + list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list}) + remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags) + list(SORT flags) + + if(SHOW_INTERMEDIATE_OBJECTS AND flags) + message(STATUS "Unit test ${fq_target_name} has FLAGS: ${flags}") + endif() + + expand_flags_for_libc_unittest( + ${fq_target_name} + "${flags}" + DEPENDS ${fq_deps_list} IGNORE_MARKER + FLAGS ${flags} IGNORE_MARKER + ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS} + ) endfunction(add_libc_unittest) function(add_libc_testsuite suite_name) diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt index 50ccc9e..2895eba 100644 --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -21,8 +21,8 @@ function(add_fp_unittest name) add_libc_unittest( ${name} - "${MATH_UNITTEST_UNPARSED_ARGUMENTS}" LINK_LIBRARIES "${MATH_UNITTEST_LINK_LIBRARIES}" + "${MATH_UNITTEST_UNPARSED_ARGUMENTS}" ) endfunction(add_fp_unittest) diff --git a/libc/test/src/__support/File/CMakeLists.txt b/libc/test/src/__support/File/CMakeLists.txt index de8df1c..17ca25b 100644 --- a/libc/test/src/__support/File/CMakeLists.txt +++ b/libc/test/src/__support/File/CMakeLists.txt @@ -10,13 +10,13 @@ add_libc_unittest( libc_support_unittests SRCS file_test.cpp + LINK_LIBRARIES + LibcMemoryHelpers DEPENDS libc.include.errno libc.include.stdio libc.include.stdlib libc.src.__support.File.file - LINK_LIBRARIES - LibcMemoryHelpers ) if (TARGET libc.src.__support.File.platform_file) diff --git a/libc/test/src/__support/OSUtil/linux/x86_64/CMakeLists.txt b/libc/test/src/__support/OSUtil/linux/x86_64/CMakeLists.txt index 63e33ba..e0f2549 100644 --- a/libc/test/src/__support/OSUtil/linux/x86_64/CMakeLists.txt +++ b/libc/test/src/__support/OSUtil/linux/x86_64/CMakeLists.txt @@ -2,8 +2,8 @@ add_libc_unittest( syscall_unittest SUITE libc_osutil_tests SRCS syscall_test.cpp - DEPENDS - libc.src.__support.OSUtil.osutil COMPILE_OPTIONS -Wno-unused-variable # Only signature tests, declared variables are unused. + DEPENDS + libc.src.__support.OSUtil.osutil ) diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt index f985dab..b91f3c2 100644 --- a/libc/test/src/math/exhaustive/CMakeLists.txt +++ b/libc/test/src/math/exhaustive/CMakeLists.txt @@ -175,13 +175,13 @@ add_fp_unittest( libc_math_exhaustive_tests SRCS hypotf_test.cpp + COMPILE_OPTIONS + -O3 DEPENDS .exhaustive_test libc.include.math libc.src.math.hypotf libc.src.__support.FPUtil.fputil - COMPILE_OPTIONS - -O3 LINK_LIBRARIES -lpthread ) diff --git a/libc/test/src/stdio/printf_core/CMakeLists.txt b/libc/test/src/stdio/printf_core/CMakeLists.txt index 692a3962..3b38f2c 100644 --- a/libc/test/src/stdio/printf_core/CMakeLists.txt +++ b/libc/test/src/stdio/printf_core/CMakeLists.txt @@ -4,12 +4,12 @@ add_libc_unittest( libc_stdio_unittests SRCS parser_test.cpp + LINK_LIBRARIES + LibcPrintfHelpers DEPENDS libc.src.stdio.printf_core.parser libc.src.stdio.printf_core.core_structs libc.src.__support.arg_list - LINK_LIBRARIES - LibcPrintfHelpers ) add_libc_unittest( diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt index e60f9ab..6e4c227 100644 --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -275,13 +275,13 @@ function(add_libc_multi_impl_test name) ${fq_config_name}_test SUITE libc_string_unittests - DEPENDS - ${fq_config_name} COMPILE_OPTIONS ${LIBC_COMPILE_OPTIONS_NATIVE} LINK_LIBRARIES LibcMemoryHelpers ${ARGN} + DEPENDS + ${fq_config_name} ) get_fq_target_name(${fq_config_name}_test fq_target_name) else() diff --git a/libc/test/src/string/memory_utils/CMakeLists.txt b/libc/test/src/string/memory_utils/CMakeLists.txt index 5705190..7836076 100644 --- a/libc/test/src/string/memory_utils/CMakeLists.txt +++ b/libc/test/src/string/memory_utils/CMakeLists.txt @@ -6,11 +6,11 @@ add_libc_unittest( elements_test.cpp memory_access_test.cpp utils_test.cpp + COMPILE_OPTIONS + ${LIBC_COMPILE_OPTIONS_NATIVE} + -ffreestanding DEPENDS libc.src.string.memory_utils.memory_utils libc.src.__support.CPP.array libc.src.__support.CPP.array_ref - COMPILE_OPTIONS - ${LIBC_COMPILE_OPTIONS_NATIVE} - -ffreestanding ) -- 2.7.4