123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- #
- # 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()
- args = [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 []
- 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 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('char\* version', line):
- version = re.search(r'\"([^"]+)\"', line).groups()[0]
- if re.findall('iso_c_visible = [\d]+', line):
- stdc = re.findall('[\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
|