gcc.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #
  2. # File : gcc.py
  3. # This file is part of RT-Thread RTOS
  4. # COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License along
  17. # with this program; if not, write to the Free Software Foundation, Inc.,
  18. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. #
  20. # Change Logs:
  21. # Date Author Notes
  22. # 2018-05-22 Bernard The first version
  23. # 2023-11-03 idings return file path in GetHeader
  24. import os
  25. import re
  26. import platform
  27. import subprocess
  28. def GetGCCRoot(rtconfig):
  29. exec_path = rtconfig.EXEC_PATH
  30. prefix = rtconfig.PREFIX
  31. if prefix.endswith('-'):
  32. prefix = prefix[:-1]
  33. if exec_path == '/usr/bin':
  34. root_path = os.path.join('/usr/lib', prefix)
  35. else:
  36. root_path = os.path.join(exec_path, '..', prefix)
  37. return root_path
  38. # https://stackoverflow.com/questions/4980819/what-are-the-gcc-default-include-directories
  39. # https://stackoverflow.com/questions/53937211/how-can-i-parse-gcc-output-by-regex-to-get-default-include-paths
  40. def match_pattern(pattern, input, start = 0, stop = -1, flags = 0):
  41. length = len(input)
  42. if length == 0:
  43. return None
  44. end_it = max(0, length - 1)
  45. if start >= end_it:
  46. return None
  47. if stop<0:
  48. stop = length
  49. if stop <= start:
  50. return None
  51. for it in range(max(0, start), min(stop, length)):
  52. elem = input[it]
  53. match = re.match(pattern, elem, flags)
  54. if match:
  55. return it
  56. def GetGccDefaultSearchDirs(rtconfig):
  57. start_pattern = r' *#include <\.\.\.> search starts here: *'
  58. end_pattern = r' *End of search list\. *'
  59. gcc_cmd = os.path.join(rtconfig.EXEC_PATH, rtconfig.CC)
  60. device_flags = rtconfig.DEVICE.split()
  61. args = [gcc_cmd] + device_flags + ['-xc', '-E', '-v', os.devnull]
  62. proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
  63. lines = proc.stdout.splitlines()
  64. start_it = match_pattern(start_pattern, lines)
  65. if start_it == None:
  66. return []
  67. end_it = match_pattern(end_pattern, lines, start_it)
  68. if end_it == None:
  69. return []
  70. # theres no paths between them
  71. if (end_it - start_it) == 1:
  72. return []
  73. return lines[start_it + 1 : end_it]
  74. def GetHeader(rtconfig, filename):
  75. include_dirs = GetGccDefaultSearchDirs(rtconfig)
  76. for directory in include_dirs:
  77. fn = os.path.join(directory, filename).strip()
  78. if os.path.isfile(fn):
  79. return fn
  80. # fallback to use fixed method if can't autodetect
  81. root = GetGCCRoot(rtconfig)
  82. fn = os.path.join(root, 'include', filename)
  83. if os.path.isfile(fn):
  84. return fn
  85. # Usually the cross compiling gcc toolchain has directory as:
  86. #
  87. # bin
  88. # lib
  89. # share
  90. # arm-none-eabi
  91. # bin
  92. # include
  93. # lib
  94. # share
  95. prefix = rtconfig.PREFIX
  96. if prefix.endswith('-'):
  97. prefix = prefix[:-1]
  98. fn = os.path.join(root, prefix, 'include', filename)
  99. if os.path.isfile(fn):
  100. return fn
  101. return None
  102. # GCC like means the toolchains which are compatible with GCC
  103. def GetGCCLikePLATFORM():
  104. return ['gcc', 'armclang', 'llvm-arm']
  105. def GetPicoLibcVersion(rtconfig):
  106. version = None
  107. try:
  108. rtconfig.PREFIX
  109. except:
  110. return version
  111. # get version from picolibc.h
  112. fn = GetHeader(rtconfig, 'picolibc.h')
  113. if fn:
  114. f = open(fn, 'r')
  115. if f:
  116. for line in f:
  117. if line.find('__PICOLIBC_VERSION__') != -1 and line.find('"') != -1:
  118. version = re.search(r'\"([^"]+)\"', line).groups()[0]
  119. f.close()
  120. return version
  121. def GetNewLibVersion(rtconfig):
  122. version = None
  123. try:
  124. rtconfig.PREFIX
  125. except:
  126. return version
  127. # if find picolibc.h, use picolibc
  128. fn = GetHeader(rtconfig, 'picolibc.h')
  129. if fn:
  130. return version
  131. # get version from _newlib_version.h file
  132. fn = GetHeader(rtconfig, '_newlib_version.h')
  133. # get version from newlib.h
  134. if not fn:
  135. fn = GetHeader(rtconfig, 'newlib.h')
  136. if fn:
  137. f = open(fn, 'r')
  138. for line in f:
  139. if line.find('_NEWLIB_VERSION') != -1 and line.find('"') != -1:
  140. version = re.search(r'\"([^"]+)\"', line).groups()[0]
  141. f.close()
  142. return version
  143. # FIXME: there is no musl version or musl macros can be found officially
  144. def GetMuslVersion(rtconfig):
  145. version = None
  146. try:
  147. rtconfig.PREFIX
  148. except:
  149. return version
  150. if 'musl' in rtconfig.PREFIX:
  151. version = 'unknown'
  152. return version
  153. def GCCResult(rtconfig, str):
  154. result = ''
  155. def checkAndGetResult(pattern, string):
  156. if re.search(pattern, string):
  157. return re.search(pattern, string).group(0)
  158. return None
  159. gcc_cmd = os.path.join(rtconfig.EXEC_PATH, rtconfig.CC)
  160. # use temp file to get more information
  161. f = open('__tmp.c', 'w')
  162. if f:
  163. f.write(str)
  164. f.close()
  165. # '-fdirectives-only',
  166. if(platform.system() == 'Windows'):
  167. child = subprocess.Popen([gcc_cmd, '-E', '-P', '__tmp.c'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
  168. else:
  169. child = subprocess.Popen(gcc_cmd + ' -E -P __tmp.c', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
  170. stdout, stderr = child.communicate()
  171. # print(stdout)
  172. if stderr != '' and stderr != b'':
  173. print(stderr)
  174. have_fdset = 0
  175. have_sigaction = 0
  176. have_sigevent = 0
  177. have_siginfo = 0
  178. have_sigval = 0
  179. version = None
  180. stdc = '1989'
  181. posix_thread = 0
  182. for line in stdout.split(b'\n'):
  183. line = line.decode()
  184. if re.search('fd_set', line):
  185. have_fdset = 1
  186. # check for sigal
  187. if re.search('struct[ \t]+sigaction', line):
  188. have_sigaction = 1
  189. if re.search('struct[ \t]+sigevent', line):
  190. have_sigevent = 1
  191. if re.search('siginfo_t', line):
  192. have_siginfo = 1
  193. if re.search('union[ \t]+sigval', line):
  194. have_sigval = 1
  195. if re.search('char\* version', line):
  196. version = re.search(r'\"([^"]+)\"', line).groups()[0]
  197. if re.findall('iso_c_visible = [\d]+', line):
  198. stdc = re.findall('[\d]+', line)[0]
  199. if re.findall('pthread_create', line):
  200. posix_thread = 1
  201. if have_fdset:
  202. result += '#define HAVE_FDSET 1\n'
  203. if have_sigaction:
  204. result += '#define HAVE_SIGACTION 1\n'
  205. if have_sigevent:
  206. result += '#define HAVE_SIGEVENT 1\n'
  207. if have_siginfo:
  208. result += '#define HAVE_SIGINFO 1\n'
  209. if have_sigval:
  210. result += '#define HAVE_SIGVAL 1\n'
  211. if version:
  212. result += '#define GCC_VERSION_STR "%s"\n' % version
  213. result += '#define STDC "%s"\n' % stdc
  214. if posix_thread:
  215. result += '#define LIBC_POSIX_THREADS 1\n'
  216. os.remove('__tmp.c')
  217. return result