diff --git a/CMakeLists.txt b/CMakeLists.txt index 34e043dd41..f580e8de85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,9 @@ if(NOT BOOTLOADER_BUILD) if(CONFIG_COMPILER_OPTIMIZATION_SIZE) list(APPEND compile_options "-Os") - list(APPEND compile_options "-freorder-blocks") + if(CMAKE_C_COMPILER_ID MATCHES "GNU") + list(APPEND compile_options "-freorder-blocks") + endif() elseif(CONFIG_COMPILER_OPTIMIZATION_DEFAULT) list(APPEND compile_options "-Og") elseif(CONFIG_COMPILER_OPTIMIZATION_NONE) @@ -33,7 +35,9 @@ else() # BOOTLOADER_BUILD if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE) list(APPEND compile_options "-Os") - list(APPEND compile_options "-freorder-blocks") + if(CMAKE_C_COMPILER_ID MATCHES "GNU") + list(APPEND compile_options "-freorder-blocks") + endif() elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG) list(APPEND compile_options "-Og") elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE) @@ -74,6 +78,48 @@ if(CONFIG_COMPILER_DISABLE_GCC8_WARNINGS) "-Wno-int-in-bool-context") endif() +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + list(APPEND c_compile_options "-Wno-old-style-declaration") +endif() + +# Clang finds some warnings in IDF code which GCC doesn't. +# All these warnings should be fixed before Clang is presented +# as a toolchain choice for users. +if(CMAKE_C_COMPILER_ID MATCHES "Clang") + # Clang checks Doxygen comments for being in sync with function prototype. + # There are some inconsistencies, especially in ROM headers. + list(APPEND compile_options "-Wno-documentation") + # GCC allows repeated typedefs when the source and target types are the same. + # Clang doesn't allow this. This occurs in many components due to forward + # declarations. + list(APPEND compile_options "-Wno-typedef-redefinition") + # This issue is seemingly related to newlib's char type functions. + # Fix is not clear yet. + list(APPEND compile_options "-Wno-char-subscripts") + # Clang seems to notice format string issues which GCC doesn't. + list(APPEND compile_options "-Wno-format-security") + # Logic bug in essl component + list(APPEND compile_options "-Wno-tautological-overlap-compare") + # Some pointer checks in mDNS component check addresses which can't be NULL + list(APPEND compile_options "-Wno-tautological-pointer-compare") + # Similar to the above, in tcp_transport + list(APPEND compile_options "-Wno-pointer-bool-conversion") + # mbedTLS md5.c triggers this warning in md5_test_buf (false positive) + list(APPEND compile_options "-Wno-string-concatenation") + # multiple cases of implict convertions between unrelated enum types + list(APPEND compile_options "-Wno-enum-conversion") + # When IRAM_ATTR is specified both in function declaration and definition, + # it produces different section names, since section names include __COUNTER__. + # Occurs in multiple places. + list(APPEND compile_options "-Wno-section") + # Multiple cases of attributes unknown to clang, for example + # __attribute__((optimize("-O3"))) + list(APPEND compile_options "-Wno-unknown-attributes") + # Clang also produces many -Wunused-function warnings which GCC doesn't. + # However these aren't treated as errors. +endif() +# More warnings may exist in unit tests and example projects. + if(CONFIG_COMPILER_WARN_WRITE_STRINGS) list(APPEND compile_options "-Wwrite-strings") endif() @@ -118,7 +164,15 @@ list(APPEND link_options "-fno-lto") # Placing jump tables in flash would cause issues with code that required # to be placed in IRAM list(APPEND compile_options "-fno-jump-tables") -list(APPEND compile_options "-fno-tree-switch-conversion") +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + # This flag is GCC-specific. + # Not clear yet if some other flag should be used for Clang. + list(APPEND compile_options "-fno-tree-switch-conversion") +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "LLVM") + list(APPEND compile_options "-fno-use-cxa-atexit") +endif() idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND) idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND) diff --git a/components/cbor/CMakeLists.txt b/components/cbor/CMakeLists.txt index 7f12fbe9c9..f213c22733 100644 --- a/components/cbor/CMakeLists.txt +++ b/components/cbor/CMakeLists.txt @@ -13,3 +13,8 @@ idf_component_register(SRCS "tinycbor/src/cborencoder_close_container_checked.c" # for open_memstream.c set_source_files_properties(tinycbor/src/open_memstream.c PROPERTIES COMPILE_DEFINITIONS "__linux__") + +# workaround for the fact that we are passing -ffreestanding to Clang +if(CMAKE_C_COMPILER_ID MATCHES "Clang") + target_compile_options(${COMPONENT_LIB} PRIVATE "-U __STDC_HOSTED__") +endif() diff --git a/components/console/CMakeLists.txt b/components/console/CMakeLists.txt index 6fddca7f61..cde2b05cd2 100644 --- a/components/console/CMakeLists.txt +++ b/components/console/CMakeLists.txt @@ -6,3 +6,7 @@ idf_component_register(SRCS "commands.c" INCLUDE_DIRS "." REQUIRES vfs PRIV_REQUIRES driver) + +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + set_source_files_properties(argtable3/argtable3.c PROPERTIES COMPILE_FLAGS -Wno-clobbered) +endif() diff --git a/components/console/argtable3/argtable3.c b/components/console/argtable3/argtable3.c index ca9149277c..0c3cb729fd 100644 --- a/components/console/argtable3/argtable3.c +++ b/components/console/argtable3/argtable3.c @@ -30,8 +30,6 @@ #include "argtable3.h" -#pragma GCC diagnostic ignored "-Wclobbered" - /******************************************************************************* * This file is part of the argtable3 library. * diff --git a/components/console/component.mk b/components/console/component.mk index 0a6d4945e3..703e86d80d 100644 --- a/components/console/component.mk +++ b/components/console/component.mk @@ -1,2 +1,4 @@ COMPONENT_ADD_INCLUDEDIRS := . COMPONENT_SRCDIRS := linenoise argtable3 . + +argtable3/argtable3.o: CFLAGS += -Wno-clobbered diff --git a/components/esp_common/project_include.cmake b/components/esp_common/project_include.cmake index 2199570434..e7b1d65448 100644 --- a/components/esp_common/project_include.cmake +++ b/components/esp_common/project_include.cmake @@ -1,8 +1,12 @@ # # Warn if the toolchain version doesn't match # -if(NOT ${target} STREQUAL "linux") +if(NOT (${target} STREQUAL "linux" OR CMAKE_C_COMPILER_ID MATCHES "Clang")) get_expected_ctng_version(expected_toolchain expected_gcc) gcc_version_check("${expected_gcc}") crosstool_version_check("${expected_toolchain}") endif() + +if(NOT ${target} STREQUAL "linux" AND CMAKE_C_COMPILER_ID MATCHES "Clang") + message(WARNING "Building ESP-IDF with clang is an experimental feature and is not yet officially supported.") +endif() diff --git a/components/esp_system/panic.c b/components/esp_system/panic.c index f673307dca..722975b7c7 100644 --- a/components/esp_system/panic.c +++ b/components/esp_system/panic.c @@ -399,7 +399,7 @@ void IRAM_ATTR __attribute__((noreturn, no_sanitize_undefined)) panic_abort(cons #endif #endif - *((int *) 0) = 0; // NOLINT(clang-analyzer-core.NullDereference) should be an invalid operation on targets + *((volatile int *) 0) = 0; // NOLINT(clang-analyzer-core.NullDereference) should be an invalid operation on targets while (1); } diff --git a/components/newlib/syscalls.c b/components/newlib/syscalls.c index 9cf5827166..e72fd14442 100644 --- a/components/newlib/syscalls.c +++ b/components/newlib/syscalls.c @@ -94,8 +94,10 @@ int _fstat_r (struct _reent *r, int fd, struct stat *st) * doesn't have the same signature as the original function. * Disable type mismatch warnings for this reason. */ +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattribute-alias" +#endif int _open_r(struct _reent *r, const char * path, int flags, int mode) __attribute__((weak,alias("syscall_not_implemented"))); @@ -133,7 +135,9 @@ int _kill_r(struct _reent *r, int pid, int sig) void _exit(int __status) __attribute__((alias("syscall_not_implemented_aborts"))); +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop +#endif /* Similar to syscall_not_implemented, but not taking struct _reent argument */ int system(const char* str) diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index 9f0263f964..9cc02602f2 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -55,5 +55,9 @@ idf_component_register(SRCS "${srcs}" LDFRAGMENTS linker.lf) # Avoid cache miss by unexpected inlineing when built by -Os -set_source_files_properties(${cache_srcs} PROPERTIES COMPILE_FLAGS - "-fno-inline-functions -fno-inline-small-functions -fno-inline-functions-called-once") +set_source_files_properties(${cache_srcs} PROPERTIES COMPILE_FLAGS "-fno-inline-functions") +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + # These flags are GCC specific + set_property(SOURCE ${cache_srcs} APPEND_STRING PROPERTY COMPILE_FLAGS + " -fno-inline-small-functions -fno-inline-functions-called-once") +endif() diff --git a/components/spi_flash/include/spi_flash_chip_generic.h b/components/spi_flash/include/spi_flash_chip_generic.h index 000d2c943c..019a6966a4 100644 --- a/components/spi_flash/include/spi_flash_chip_generic.h +++ b/components/spi_flash/include/spi_flash_chip_generic.h @@ -408,4 +408,4 @@ esp_err_t spi_flash_chip_generic_suspend_cmd_conf(esp_flash_t *chip); esp_err_t spi_flash_chip_generic_read_unique_id_none(esp_flash_t *chip, uint64_t* flash_unique_id); /// Default timeout configuration used by most chips -const flash_chip_op_timeout_t spi_flash_chip_generic_timeout; +extern const flash_chip_op_timeout_t spi_flash_chip_generic_timeout; diff --git a/components/spiffs/CMakeLists.txt b/components/spiffs/CMakeLists.txt index 9a9d35f5b0..5954094c43 100644 --- a/components/spiffs/CMakeLists.txt +++ b/components/spiffs/CMakeLists.txt @@ -10,4 +10,6 @@ idf_component_register(SRCS "esp_spiffs.c" REQUIRES spi_flash PRIV_REQUIRES bootloader_support esptool_py) -set_source_files_properties(spiffs/src/spiffs_nucleus.c PROPERTIES COMPILE_FLAGS -Wno-stringop-truncation) +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + set_source_files_properties(spiffs/src/spiffs_nucleus.c PROPERTIES COMPILE_FLAGS -Wno-stringop-truncation) +endif() diff --git a/tools/ci/test_build_system_cmake.sh b/tools/ci/test_build_system_cmake.sh index b864c03c2c..14f54ca70d 100755 --- a/tools/ci/test_build_system_cmake.sh +++ b/tools/ci/test_build_system_cmake.sh @@ -446,12 +446,6 @@ function run_tests() mv CMakeLists.bak CMakeLists.txt assert_built ${APP_BINS} ${BOOTLOADER_BINS} ${PARTITION_BIN} - print_status "Can find toolchain file in component directory" - clean_build_dir - mv ${IDF_PATH}/tools/cmake/toolchain-esp32.cmake ${IDF_PATH}/components/esp32/ - (idf.py reconfigure > /dev/null && grep "${IDF_PATH}/components/esp32/toolchain-esp32.cmake" build/CMakeCache.txt) || failure "Failed to find toolchain file in component directory" - mv ${IDF_PATH}/components/esp32/toolchain-esp32.cmake ${IDF_PATH}/tools/cmake/ - print_status "Setting EXTRA_COMPONENT_DIRS works" clean_build_dir (idf.py reconfigure | grep "$PWD/main") || failure "Failed to verify original `main` directory" diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index f67aaf991b..d0abf84386 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -107,8 +107,7 @@ function(__build_set_default_build_specifications) # go into the final binary so have no impact on size "-ggdb") - list(APPEND c_compile_options "-std=gnu99" - "-Wno-old-style-declaration") + list(APPEND c_compile_options "-std=gnu99") list(APPEND cxx_compile_options "-std=gnu++11") diff --git a/tools/cmake/targets.cmake b/tools/cmake/targets.cmake index ecaaaf1818..23d3ff4848 100644 --- a/tools/cmake/targets.cmake +++ b/tools/cmake/targets.cmake @@ -47,23 +47,37 @@ endfunction() # macro(__target_set_toolchain) idf_build_get_property(idf_path IDF_PATH) + + # See if Clang toolchain should be used + set(env_idf_toolchain $ENV{IDF_TOOLCHAIN}) + if(NOT env_idf_toolchain) + # IDF_TOOLCHAIN not set in environment, see if it is set in cache + if(IDF_TOOLCHAIN) + set(env_idf_toolchain ${IDF_TOOLCHAIN}) + else() + set(env_idf_toolchain gcc) + endif() + else() + # IDF_TOOLCHAIN set both in environment and in cache, must be the same + if(NOT ${IDF_TOOLCHAIN} STREQUAL ${env_idf_toolchain}) + message(FATAL_ERROR "IDF_TOOLCHAIN in CMake cache does not match " + "IDF_TOOLCHAIN environment variable. To change the toolchain, clear " + "the build directory and sdkconfig file, and build the project again") + endif() + endif() + + # Finally, set IDF_TOOLCHAIN in cache + set(IDF_TOOLCHAIN ${env_idf_toolchain} CACHE STRING "IDF Build Toolchain Type") + + if(${env_idf_toolchain} STREQUAL "clang") + set(toolchain_type "clang-") + endif() + # First try to load the toolchain file from the tools/cmake/directory of IDF - set(toolchain_file_global ${idf_path}/tools/cmake/toolchain-${IDF_TARGET}.cmake) + set(toolchain_file_global ${idf_path}/tools/cmake/toolchain-${toolchain_type}${IDF_TARGET}.cmake) if(EXISTS ${toolchain_file_global}) set(CMAKE_TOOLCHAIN_FILE ${toolchain_file_global}) else() - __component_get_target(component_target ${IDF_TARGET}) - if(NOT component_target) - message(FATAL_ERROR "Unable to resolve '${IDF_TARGET}' for setting toolchain file.") - endif() - get_property(component_dir TARGET ${component_target} PROPERTY COMPONENT_DIR) - # Try to load the toolchain file from the directory of IDF_TARGET component - set(toolchain_file_component ${component_dir}/toolchain-${IDF_TARGET}.cmake) - if(EXISTS ${toolchain_file_component}) - set(CMAKE_TOOLCHAIN_FILE ${toolchain_file_component}) - else() - message(FATAL_ERROR "Toolchain file toolchain-${IDF_TARGET}.cmake not found," - "checked ${toolchain_file_global} and ${toolchain_file_component}") - endif() + message(FATAL_ERROR "Toolchain file ${toolchain_file_global} not found") endif() endmacro() diff --git a/tools/cmake/toolchain-clang-esp32.cmake b/tools/cmake/toolchain-clang-esp32.cmake new file mode 100644 index 0000000000..bb25f98298 --- /dev/null +++ b/tools/cmake/toolchain-clang-esp32.cmake @@ -0,0 +1,16 @@ +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER clang) +set(CMAKE_CXX_COMPILER clang++) +set(CMAKE_ASM_COMPILER clang) + +set(CMAKE_AR xtensa-esp32-elf-ar) +set(CMAKE_RANLIB xtensa-esp32-elf-gcc-ranlib) +set(CMAKE_OBJDUMP xtensa-esp32-elf-objdump) + +# -freestanding is a hack to force Clang to use its own stdatomic.h, +# without falling back to the (incompatible) GCC stdatomic.h +# https://github.com/espressif/llvm-project/blob/d9341b81/clang/lib/Headers/stdatomic.h#L13-L18 +set(CMAKE_C_FLAGS "--target=xtensa -mcpu=esp32 -ffreestanding" CACHE STRING "C Compiler Base Flags") +set(CMAKE_CXX_FLAGS "--target=xtensa -mcpu=esp32 -ffreestanding" CACHE STRING "C++ Compiler Base Flags") +set(CMAKE_ASM_FLAGS "--target=xtensa -mcpu=esp32" CACHE STRING "Assembler Base Flags") diff --git a/tools/cmake/toolchain-clang-esp32s2.cmake b/tools/cmake/toolchain-clang-esp32s2.cmake new file mode 100644 index 0000000000..bb6e47bd0c --- /dev/null +++ b/tools/cmake/toolchain-clang-esp32s2.cmake @@ -0,0 +1,16 @@ +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER clang) +set(CMAKE_CXX_COMPILER clang++) +set(CMAKE_ASM_COMPILER clang) + +set(CMAKE_AR xtensa-esp32-elf-ar) +set(CMAKE_RANLIB xtensa-esp32-elf-gcc-ranlib) +set(CMAKE_OBJDUMP xtensa-esp32-elf-objdump) + +# -freestanding is a hack to force Clang to use its own stdatomic.h, +# without falling back to the (incompatible) GCC stdatomic.h +# https://github.com/espressif/llvm-project/blob/d9341b81/clang/lib/Headers/stdatomic.h#L13-L18 +set(CMAKE_C_FLAGS "--target=xtensa -mcpu=esp32s2 -ffreestanding" CACHE STRING "C Compiler Base Flags") +set(CMAKE_CXX_FLAGS "--target=xtensa -mcpu=esp32s2 -ffreestanding" CACHE STRING "C++ Compiler Base Flags") +set(CMAKE_ASM_FLAGS "--target=xtensa -mcpu=esp32s2" CACHE STRING "Assembler Base Flags")