diff --git a/components/libc/compilers/picolibc/SConscript b/components/libc/compilers/picolibc/SConscript index 999f7cca56..c0de465a2f 100644 --- a/components/libc/compilers/picolibc/SConscript +++ b/components/libc/compilers/picolibc/SConscript @@ -1,10 +1,16 @@ import os from building import * -from llvm_arm import * Import('rtconfig') group = [] +if rtconfig.PLATFORM == 'gcc': + from gcc import * +elif rtconfig.PLATFORM == 'llvm-arm': + from llvm_arm import * +else: + Return('group') + picolibc_version = GetPicoLibcVersion(rtconfig) if picolibc_version and not GetDepend('RT_USING_EXTERNAL_LIBC'): diff --git a/components/libc/compilers/picolibc/exit.c b/components/libc/compilers/picolibc/exit.c new file mode 100644 index 0000000000..f3f3ce831a --- /dev/null +++ b/components/libc/compilers/picolibc/exit.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include + +/* for exit() and abort() */ +rt_noreturn void _exit (int status) +{ + extern void __rt_libc_exit(int status); + __rt_libc_exit(status); + while(1); +} diff --git a/components/libc/compilers/picolibc/fcntl.h b/components/libc/compilers/picolibc/fcntl.h new file mode 100644 index 0000000000..ce8b621125 --- /dev/null +++ b/components/libc/compilers/picolibc/fcntl.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-09-02 Meco Man First version + */ + +#ifndef __FCNTL_H__ +#define __FCNTL_H__ + +#include + +#ifndef O_EXEC +#define O_EXEC 0x400000 +#endif + +#ifndef O_TMPFILE +#define O_TMPFILE 0x800000 +#endif + +#ifndef O_BINARY +#define O_BINARY 0x10000 +#endif + +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0x100000 +#endif + +#ifndef O_DIRECTORY +#define O_DIRECTORY 0x200000 +#endif + +#endif diff --git a/components/libc/compilers/picolibc/iob.c b/components/libc/compilers/picolibc/iob.c new file mode 100644 index 0000000000..a13eb3a0e4 --- /dev/null +++ b/components/libc/compilers/picolibc/iob.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include +#include +#ifdef RT_USING_POSIX_STDIO +#include +#endif /* RT_USING_POSIX_STDIO */ +#define DBG_TAG "picolibc.iob" +#define DBG_LVL DBG_INFO +#include + +#ifdef TINY_STDIO + +static int __fputc(char c, FILE *file); +static int __fgetc(FILE *file); + +static FILE __stdio_in = FDEV_SETUP_STREAM(NULL, __fgetc, NULL, _FDEV_SETUP_READ); +static FILE __stdio_out = FDEV_SETUP_STREAM(__fputc, NULL, NULL, _FDEV_SETUP_WRITE); + +#ifdef __strong_reference +#define STDIO_ALIAS(x) __strong_reference(stdout, x); +#else +#define STDIO_ALIAS(x) FILE *const x = &__stdio_out; +#endif + +FILE *const stdin = &__stdio_in; +FILE *const stdout = &__stdio_out; +STDIO_ALIAS(stderr); + +static int __fputc(char c, FILE *file) +{ + if (file == &__stdio_out) + { +#if defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE) + rt_device_t console = rt_console_get_device(); + if (console) + { + rt_ssize_t rc = rt_device_write(console, -1, &c, 1); + return rc > 0 ? rc : -1; + } +#endif /* defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE) */ + } + + return -1; +} + +static int __fgetc(FILE *file) +{ + if (file == &__stdio_in) + { +#ifdef RT_USING_POSIX_STDIO + if (rt_posix_stdio_get_console() >= 0) + { + char c; + int rc = read(STDIN_FILENO, &c, 1); + return rc == 1 ? c : EOF; + } +#endif /* RT_USING_POSIX_STDIO */ + } + + return EOF; +} + +#endif diff --git a/tools/gcc.py b/tools/gcc.py index 556ede8f23..356842a8e8 100644 --- a/tools/gcc.py +++ b/tools/gcc.py @@ -20,10 +20,12 @@ # Change Logs: # Date Author Notes # 2018-05-22 Bernard The first version +# 2023-11-03 idings return file path in GetHeader import os import re import platform +import subprocess def GetGCCRoot(rtconfig): exec_path = rtconfig.EXEC_PATH @@ -39,12 +41,68 @@ def GetGCCRoot(rtconfig): return root_path -def CheckHeader(rtconfig, filename): - root = GetGCCRoot(rtconfig) +# https://stackoverflow.com/questions/4980819/what-are-the-gcc-default-include-directories +# https://stackoverflow.com/questions/53937211/how-can-i-parse-gcc-output-by-regex-to-get-default-include-paths +def match_pattern(pattern, input, start = 0, stop = -1, flags = 0): + length = len(input) + if length == 0: + return None + + end_it = max(0, length - 1) + + if start >= end_it: + return None + + if stop<0: + stop = length + + if stop <= start: + return None + + for it in range(max(0, start), min(stop, length)): + elem = input[it] + match = re.match(pattern, elem, flags) + if match: + return it + +def GetGccDefaultSearchDirs(rtconfig): + start_pattern = r' *#include <\.\.\.> search starts here: *' + end_pattern = r' *End of search list\. *' + + gcc_cmd = os.path.join(rtconfig.EXEC_PATH, rtconfig.CC) + device_flags = rtconfig.DEVICE.split() + args = [gcc_cmd] + device_flags + ['-xc', '-E', '-v', os.devnull] + + proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + lines = proc.stdout.splitlines() + + start_it = match_pattern(start_pattern, lines) + if start_it == None: + return [] + + end_it = match_pattern(end_pattern, lines, start_it) + if end_it == None: + return [] + + # theres no paths between them + if (end_it - start_it) == 1: + return [] + + return lines[start_it + 1 : end_it] + +def GetHeader(rtconfig, filename): + include_dirs = GetGccDefaultSearchDirs(rtconfig) + for directory in include_dirs: + fn = os.path.join(directory, filename).strip() + if os.path.isfile(fn): + return fn + + # fallback to use fixed method if can't autodetect + root = GetGCCRoot(rtconfig) fn = os.path.join(root, 'include', filename) if os.path.isfile(fn): - return True + return fn # Usually the cross compiling gcc toolchain has directory as: # @@ -62,14 +120,34 @@ def CheckHeader(rtconfig, filename): fn = os.path.join(root, prefix, 'include', filename) if os.path.isfile(fn): - return True + return fn - return False + return None # GCC like means the toolchains which are compatible with GCC def GetGCCLikePLATFORM(): return ['gcc', 'armclang', 'llvm-arm'] +def GetPicoLibcVersion(rtconfig): + version = None + try: + rtconfig.PREFIX + except: + return version + + # get version from picolibc.h + fn = GetHeader(rtconfig, 'picolibc.h') + + if fn: + f = open(fn, 'r') + if f: + for line in f: + if line.find('__PICOLIBC_VERSION__') != -1 and line.find('"') != -1: + version = re.search(r'\"([^"]+)\"', line).groups()[0] + f.close() + + return version + def GetNewLibVersion(rtconfig): version = None @@ -78,21 +156,25 @@ def GetNewLibVersion(rtconfig): except: return version - root = GetGCCRoot(rtconfig) - if CheckHeader(rtconfig, '_newlib_version.h'): # get version from _newlib_version.h file - f = open(os.path.join(root, 'include', '_newlib_version.h'), 'r') - if f: - for line in f: - if line.find('_NEWLIB_VERSION') != -1 and line.find('"') != -1: - version = re.search(r'\"([^"]+)\"', line).groups()[0] - f.close() - elif CheckHeader(rtconfig, 'newlib.h'): # get version from newlib.h - f = open(os.path.join(root, 'include', 'newlib.h'), 'r') - if f: - for line in f: - if line.find('_NEWLIB_VERSION') != -1 and line.find('"') != -1: - version = re.search(r'\"([^"]+)\"', line).groups()[0] - f.close() + # if find picolibc.h, use picolibc + fn = GetHeader(rtconfig, 'picolibc.h') + if fn: + return version + + # get version from _newlib_version.h file + fn = GetHeader(rtconfig, '_newlib_version.h') + + # get version from newlib.h + if not fn: + fn = GetHeader(rtconfig, 'newlib.h') + + if fn: + f = open(fn, 'r') + for line in f: + if line.find('_NEWLIB_VERSION') != -1 and line.find('"') != -1: + version = re.search(r'\"([^"]+)\"', line).groups()[0] + f.close() + return version # FIXME: there is no musl version or musl macros can be found officially @@ -109,8 +191,6 @@ def GetMuslVersion(rtconfig): return version def GCCResult(rtconfig, str): - import subprocess - result = '' def checkAndGetResult(pattern, string):