blob: 3a0eb23f69d9401f8dcd32918665a5b56a85c7ac [file] [log] [blame]
#-------------------------------------------------------------------------------
# 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=<n> 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)