소스 검색

[libc][picolibc] improve gcc picolibc support

Xiang.Lin 1 년 전
부모
커밋
da55491608

+ 7 - 1
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'):

+ 19 - 0
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 <rtthread.h>
+#include <sys/types.h>
+
+/* for exit() and abort() */
+rt_noreturn void _exit (int status)
+{
+    extern void __rt_libc_exit(int status);
+    __rt_libc_exit(status);
+    while(1);
+}

+ 36 - 0
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 <sys/_default_fcntl.h>
+
+#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

+ 73 - 0
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 <rtthread.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#ifdef RT_USING_POSIX_STDIO
+#include <posix/stdio.h>
+#endif /* RT_USING_POSIX_STDIO */
+#define DBG_TAG    "picolibc.iob"
+#define DBG_LVL    DBG_INFO
+#include <rtdbg.h>
+
+#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

+ 100 - 20
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,37 +120,61 @@ 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 GetNewLibVersion(rtconfig):
+def GetPicoLibcVersion(rtconfig):
     version = None
-
     try:
         rtconfig.PREFIX
     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')
+    # 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('_NEWLIB_VERSION') != -1 and line.find('"') != -1:
+                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
+
+    try:
+        rtconfig.PREFIX
+    except:
+        return version
+
+    # 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):