#------------------------------------------------------------------------------- # Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # #------------------------------------------------------------------------------- # Add newlib specific porting files to the project. if (NOT DEFINED TGT) message(FATAL_ERROR "mandatory parameter TGT is not defined.") endif() # Adding libc interface add_components(TARGET ${TGT} BASE_DIR ${TS_ROOT} COMPONENTS components/common/libc ) # Compile TS specific newlib porting files. target_sources(${TGT} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/newlib_init.c" "${CMAKE_CURRENT_LIST_DIR}/newlib_sp_assert.c" "${CMAKE_CURRENT_LIST_DIR}/newlib_sp_heap.c" ) # Fetch newlib from external repository set(NEWLIB_URL "https://sourceware.org/git/newlib-cygwin.git" CACHE STRING "newlib repository URL") set(NEWLIB_REFSPEC "newlib-4.1.0" CACHE STRING "newlib git refspec") set(NEWLIB_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_deps/newlib-src" CACHE PATH "newlib source-code location") set(NEWLIB_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/newlib_install" CACHE PATH "newlib installation directory") # Supported build types: "Release" "Debug" "RelWithDebInfo" "Off" # If set to "Off" -DNEWLIB_CFLAGS_TARGET can be used to set compiler # switches from the command line. set(NEWLIB_BUILD_TYPE "Release" CACHE STRING "newlib build type") # Extracting compiler prefix without the trailing hyphen from the C compiler name get_filename_component(COMPILER_PATH ${CMAKE_C_COMPILER} DIRECTORY) get_filename_component(COMPILER_NAME ${CMAKE_C_COMPILER} NAME) string(REGEX REPLACE "(.*)-[^-]+$" "\\1" COMPILER_PREFIX ${COMPILER_NAME}) find_library(NEWLIB_LIBC_PATH NAMES libc.a c.a libc.lib c.lib PATHS ${NEWLIB_INSTALL_DIR} PATH_SUFFIXES "${COMPILER_PREFIX}/lib" DOC "Location of newlib::libc library." NO_DEFAULT_PATH ) set(NEWLIB_LIBC_PATH ${NEWLIB_LIBC_PATH}) unset(NEWLIB_LIBC_PATH CACHE) find_library(NEWLIB_LIBNOSYS_PATH NAMES libnosys.a nosys.a nosys.lib nosys.lib PATHS ${NEWLIB_INSTALL_DIR} PATH_SUFFIXES "${COMPILER_PREFIX}/lib" DOC "Location of newlib::libnosys library." NO_DEFAULT_PATH ) set(NEWLIB_LIBNOSYS_PATH ${NEWLIB_LIBNOSYS_PATH}) unset(NEWLIB_LIBNOSYS_PATH CACHE) # libc - get compiler specific configuration from GCC add_library(c STATIC IMPORTED) # We need "freestandig" mode. Ask the compile to do the needed configurations. include(${TS_ROOT}/tools/cmake/compiler/GCC.cmake) compiler_set_freestanding(TARGET c) if (NOT NEWLIB_LIBC_PATH) # Determine the number of processes to run while running parallel builds. # Pass -DPROCESSOR_COUNT= to cmake to override. if(NOT DEFINED PROCESSOR_COUNT) include(ProcessorCount) ProcessorCount(PROCESSOR_COUNT) set(PROCESSOR_COUNT ${PROCESSOR_COUNT} CACHE STRING "Number of cores to use for parallel builds.") endif() # See if the source is available locally find_file(NEWLIB_HEADER_FILE NAMES newlib.h PATHS ${NEWLIB_SOURCE_DIR} PATH_SUFFIXES "newlib/libc/include" NO_DEFAULT_PATH ) set(NEWLIB_HEADER_FILE ${NEWLIB_HEADER_FILE}) unset(NEWLIB_HEADER_FILE CACHE) # Source not found, fetch it. if (NOT NEWLIB_HEADER_FILE) include(FetchContent) # Checking git find_program(GIT_COMMAND "git") if (NOT GIT_COMMAND) message(FATAL_ERROR "Please install git") endif() # List patch files. file(GLOB _patch_files LIST_DIRECTORIES false "${CMAKE_CURRENT_LIST_DIR}/[0-9]*-[!0-9]*.patch") # Sort items in natural order to ensure patches are amended in the right order. list(SORT _patch_files COMPARE NATURAL) # Convert the list to a string of concatenated quoted list items. string(REPLACE ";" "\" \"" _patch_files "${_patch_files}") set(_patch_files "\"${_patch_files}\"") # Create a shell script patching newlib with the files listed above string(APPEND _patch_script "#!/bin/sh\n" " ${GIT_COMMAND} stash\n" " ${GIT_COMMAND} branch ts-bf-am\n" " ${GIT_COMMAND} am ${_patch_files}\n" " ${GIT_COMMAND} reset ts-bf-am\n" " ${GIT_COMMAND} branch -D ts-bf-am\n" ) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/patch-newlib "${_patch_script}") # Fetching newlib FetchContent_Declare( newlib SOURCE_DIR ${NEWLIB_SOURCE_DIR} GIT_REPOSITORY ${NEWLIB_URL} GIT_TAG ${NEWLIB_REFSPEC} GIT_SHALLOW FALSE PATCH_COMMAND sh ${CMAKE_CURRENT_BINARY_DIR}/patch-newlib ) # FetchContent_GetProperties exports newlib_SOURCE_DIR and newlib_BINARY_DIR variables FetchContent_GetProperties(newlib) # FetchContent_Populate will fail if the source directory is removed since it will try to # do an "update" and not a "populate" action. As a workaround, remove the subbuild directory. # Note: this fix assumes, the default subbuild location is used. file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/_deps/newlib-subbuild") if(NOT newlib_POPULATED) message(STATUS "Fetching newlib") FetchContent_Populate(newlib) endif() set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${NEWLIB_SOURCE_DIR}) endif() # Get NEWLIB_EXTRA_PARAMS value from environment set(NEWLIB_EXTRA_PARAMS $ENV{NEWLIB_EXTRA_PARAMS} CACHE STRING "") # Split a newlib extra build parameter into a list of parameters set(_extra_params ${NEWLIB_EXTRA_PARAMS}) separate_arguments(_extra_params) # Transfer libgcc specific settings to newlib, and set position independent compilation string(REPLACE ";" " -I" _more_cflags_target "${LIBGCC_INCLUDE_DIRS}" ) set(_more_cflags_target "-fpic -I${_more_cflags_target}") string(TOUPPER ${NEWLIB_BUILD_TYPE} UC_NEWLIB_BUILD_TYPE) if ("${UC_NEWLIB_BUILD_TYPE}" STREQUAL "DEBUG") set(_more_cflags_target "${_more_cflags_target} -g -O0") elseif ("${UC_NEWLIB_BUILD_TYPE}" STREQUAL "RELEASE") set(_more_cflags_target "${_more_cflags_target} -O2") elseif ("${UC_NEWLIB_BUILD_TYPE}" STREQUAL "RELWITHDEBINFO") set(_more_cflags_target "${_more_cflags_target} -g -O2") elseif (NOT "${UC_NEWLIB_BUILD_TYPE}" STREQUAL "OFF") message(FATAL_ERROR "unsupported build type to newlib.") endif() # Get external extra flags for target from environment. set(NEWLIB_CFLAGS_TARGET $ENV{NEWLIB_CFLAGS_TARGET} CACHE STRING "") # Merge our CFLAGS with external CFLAGS if (NOT "${NEWLIB_CFLAGS_TARGET}" STREQUAL "") # Add a space separator if external value is not empty string(APPEND NEWLIB_CFLAGS_TARGET " ") endif() # Concatenate, and override CACHE value set(NEWLIB_CFLAGS_TARGET "${NEWLIB_CFLAGS_TARGET}${_more_cflags_target}" CACHE STRING "" FORCE) # Get extra external CFLAGS for host from environment set(NEWLIB_CFLAGS $ENV{NEWLIB_CFLAGS} CACHE STRING "") # Newlib is keeping build artifacts in the source directory. If the source is pre-fetched, # intermediate files of previoud build migth be still present. # Run distclean to avoid build errors due to reconfiguration. execute_process(COMMAND ${CMAKE_COMMAND} -E env --unset=CC PATH=${COMPILER_PATH}:$ENV{PATH} make -j${PROCESSOR_COUNT} distclean WORKING_DIRECTORY ${NEWLIB_SOURCE_DIR} RESULT_VARIABLE _newlib_error ) #ignore error as distclean-host is failing. #if (_newlib_error) # message(FATAL_ERROR "\"distclean\" step of newlib failed with ${_newlib_error}.") #endif() # Newlib configure step # CC env var must be unset otherwise configure will assume the cross compiler is the host # compiler. # The configure options are set to minimize code size and memory usage. execute_process(COMMAND ${CMAKE_COMMAND} -E env --unset=CC PATH=${COMPILER_PATH}:$ENV{PATH} ./configure --target=${COMPILER_PREFIX} --host=${COMPILER_PREFIX} --prefix=${NEWLIB_INSTALL_DIR} --enable-newlib-nano-formatted-io --enable-newlib-nano-malloc --enable-lite-exit --enable-newlib-reent-small --enable-newlib-global-atexit --disable-multilib ${_extra_params} CFLAGS_FOR_TARGET=${NEWLIB_CFLAGS_TARGET} CFLAGS=${NEWLIB_CFLAGS} LDFLAGS_FOR_TARGET=-fpie WORKING_DIRECTORY ${NEWLIB_SOURCE_DIR} RESULT_VARIABLE _newlib_error ) if (_newlib_error) message(FATAL_ERROR "Configuration step of newlib failed with ${_newlib_error}.") endif() # Newlib build step execute_process(COMMAND ${CMAKE_COMMAND} -E env --unset=CC PATH=${COMPILER_PATH}:$ENV{PATH} make -j${PROCESSOR_COUNT} WORKING_DIRECTORY ${NEWLIB_SOURCE_DIR} RESULT_VARIABLE _newlib_error ) if (_newlib_error) message(FATAL_ERROR "Build step of newlib failed with ${_newlib_error}.") endif() # Newlib install step execute_process(COMMAND ${CMAKE_COMMAND} -E env --unset=CC PATH=${COMPILER_PATH}:$ENV{PATH} make install WORKING_DIRECTORY ${NEWLIB_SOURCE_DIR} RESULT_VARIABLE _newlib_error ) if (_newlib_error) message(FATAL_ERROR "Install step of newlib failed with ${_newlib_error}.") endif() set(NEWLIB_LIBC_PATH "${NEWLIB_INSTALL_DIR}/${COMPILER_PREFIX}/lib/libc.a") set(NEWLIB_LIBNOSYS_PATH "${NEWLIB_INSTALL_DIR}/${COMPILER_PREFIX}/lib/libnosys.a") endif() set_property(DIRECTORY ${CMAKE_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${NEWLIB_LIBC_PATH}) # libc - continue configuration set_property(TARGET c PROPERTY IMPORTED_LOCATION "${NEWLIB_LIBC_PATH}") target_compile_definitions(c INTERFACE ENABLE_CDEFSH_FIX) set_property(TARGET c PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${NEWLIB_INSTALL_DIR}/${COMPILER_PREFIX}/include") # Make standard library available in the build system. add_library(stdlib::c ALIAS c) # libnosys add_library(nosys STATIC IMPORTED) set_property(TARGET nosys PROPERTY IMPORTED_LOCATION "${NEWLIB_LIBNOSYS_PATH}") set_property(TARGET nosys PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${NEWLIB_INSTALL_DIR}/${COMPILER_PREFIX}/include") compiler_set_freestanding(TARGET nosys) target_link_libraries(c INTERFACE nosys)