# # File : gcc.py # This file is part of RT-Thread RTOS # COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # 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 prefix = rtconfig.PREFIX if prefix.endswith('-'): prefix = prefix[:-1] if exec_path == '/usr/bin': root_path = os.path.join('/usr/lib', prefix) else: root_path = os.path.join(exec_path, '..', prefix) return root_path # 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() command = [gcc_cmd] + device_flags + ['-xc', '-E', '-v', os.devnull] # if gcc_cmd can not access , return empty list if not os.access(gcc_cmd, os.X_OK): return [] if(platform.system() == 'Windows'): child = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) else: child = subprocess.Popen(' '.join(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) stdout = child.communicate() stdout_string = (b''.join(stdout)).decode() lines = stdout_string.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 fn # Usually the cross compiling gcc toolchain has directory as: # # bin # lib # share # arm-none-eabi # bin # include # lib # share prefix = rtconfig.PREFIX if prefix.endswith('-'): prefix = prefix[:-1] fn = os.path.join(root, prefix, 'include', filename) if os.path.isfile(fn): return fn 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 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 def GetMuslVersion(rtconfig): version = None try: rtconfig.PREFIX except: return version if 'musl' in rtconfig.PREFIX: version = 'unknown' return version def GCCResult(rtconfig, str): result = '' def checkAndGetResult(pattern, string): if re.search(pattern, string): return re.search(pattern, string).group(0) return None gcc_cmd = os.path.join(rtconfig.EXEC_PATH, rtconfig.CC) # use temp file to get more information f = open('__tmp.c', 'w') if f: f.write(str) f.close() # '-fdirectives-only', if(platform.system() == 'Windows'): child = subprocess.Popen([gcc_cmd, '-E', '-P', '__tmp.c'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) else: child = subprocess.Popen(gcc_cmd + ' -E -P __tmp.c', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) stdout, stderr = child.communicate() # print(stdout) if stderr != '' and stderr != b'': print(stderr) have_fdset = 0 have_sigaction = 0 have_sigevent = 0 have_siginfo = 0 have_sigval = 0 version = None stdc = '1989' posix_thread = 0 for line in stdout.split(b'\n'): line = line.decode() if re.search('fd_set', line): have_fdset = 1 # check for sigal if re.search('struct[ \t]+sigaction', line): have_sigaction = 1 if re.search('struct[ \t]+sigevent', line): have_sigevent = 1 if re.search('siginfo_t', line): have_siginfo = 1 if re.search('union[ \t]+sigval', line): have_sigval = 1 if re.search(r'char\* version', line): version = re.search(r'"([^"]+)"', line).groups()[0] if re.findall(r'iso_c_visible = \d+', line): stdc = re.findall(r'\d+', line)[0] if re.findall('pthread_create', line): posix_thread = 1 if have_fdset: result += '#define HAVE_FDSET 1\n' if have_sigaction: result += '#define HAVE_SIGACTION 1\n' if have_sigevent: result += '#define HAVE_SIGEVENT 1\n' if have_siginfo: result += '#define HAVE_SIGINFO 1\n' if have_sigval: result += '#define HAVE_SIGVAL 1\n' if version: result += '#define GCC_VERSION_STR "%s"\n' % version result += '#define STDC "%s"\n' % stdc if posix_thread: result += '#define LIBC_POSIX_THREADS 1\n' os.remove('__tmp.c') return result