gcc.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. # if gcc_cmd can not access , return empty list
  63. if not os.access(gcc_cmd, os.X_OK):
  64. return []
  65. proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
  66. lines = proc.stdout.splitlines()
  67. start_it = match_pattern(start_pattern, lines)
  68. if start_it == None:
  69. return []
  70. end_it = match_pattern(end_pattern, lines, start_it)
  71. if end_it == None:
  72. return []
  73. # theres no paths between them
  74. if (end_it - start_it) == 1:
  75. return []
  76. return lines[start_it + 1 : end_it]
  77. def GetHeader(rtconfig, filename):
  78. include_dirs = GetGccDefaultSearchDirs(rtconfig)
  79. for directory in include_dirs:
  80. fn = os.path.join(directory, filename).strip()
  81. if os.path.isfile(fn):
  82. return fn
  83. # fallback to use fixed method if can't autodetect
  84. root = GetGCCRoot(rtconfig)
  85. fn = os.path.join(root, 'include', filename)
  86. if os.path.isfile(fn):
  87. return fn
  88. # Usually the cross compiling gcc toolchain has directory as:
  89. #
  90. # bin
  91. # lib
  92. # share
  93. # arm-none-eabi
  94. # bin
  95. # include
  96. # lib
  97. # share
  98. prefix = rtconfig.PREFIX
  99. if prefix.endswith('-'):
  100. prefix = prefix[:-1]
  101. fn = os.path.join(root, prefix, 'include', filename)
  102. if os.path.isfile(fn):
  103. return fn
  104. return None
  105. # GCC like means the toolchains which are compatible with GCC
  106. def GetGCCLikePLATFORM():
  107. return ['gcc', 'armclang', 'llvm-arm']
  108. def GetPicoLibcVersion(rtconfig):
  109. version = None
  110. try:
  111. rtconfig.PREFIX
  112. except:
  113. return version
  114. # get version from picolibc.h
  115. fn = GetHeader(rtconfig, 'picolibc.h')
  116. if fn:
  117. f = open(fn, 'r')
  118. if f:
  119. for line in f:
  120. if line.find('__PICOLIBC_VERSION__') != -1 and line.find('"') != -1:
  121. version = re.search(r'\"([^"]+)\"', line).groups()[0]
  122. f.close()
  123. return version
  124. def GetNewLibVersion(rtconfig):
  125. version = None
  126. try:
  127. rtconfig.PREFIX
  128. except:
  129. return version
  130. # if find picolibc.h, use picolibc
  131. fn = GetHeader(rtconfig, 'picolibc.h')
  132. if fn:
  133. return version
  134. # get version from _newlib_version.h file
  135. fn = GetHeader(rtconfig, '_newlib_version.h')
  136. # get version from newlib.h
  137. if not fn:
  138. fn = GetHeader(rtconfig, 'newlib.h')
  139. if fn:
  140. f = open(fn, 'r')
  141. for line in f:
  142. if line.find('_NEWLIB_VERSION') != -1 and line.find('"') != -1:
  143. version = re.search(r'\"([^"]+)\"', line).groups()[0]
  144. f.close()
  145. return version
  146. # FIXME: there is no musl version or musl macros can be found officially
  147. def GetMuslVersion(rtconfig):
  148. version = None
  149. try:
  150. rtconfig.PREFIX
  151. except:
  152. return version
  153. if 'musl' in rtconfig.PREFIX:
  154. version = 'unknown'
  155. return version
  156. def GCCResult(rtconfig, str):
  157. result = ''
  158. def checkAndGetResult(pattern, string):
  159. if re.search(pattern, string):
  160. return re.search(pattern, string).group(0)
  161. return None
  162. gcc_cmd = os.path.join(rtconfig.EXEC_PATH, rtconfig.CC)
  163. # use temp file to get more information
  164. f = open('__tmp.c', 'w')
  165. if f:
  166. f.write(str)
  167. f.close()
  168. # '-fdirectives-only',
  169. if(platform.system() == 'Windows'):
  170. child = subprocess.Popen([gcc_cmd, '-E', '-P', '__tmp.c'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
  171. else:
  172. child = subprocess.Popen(gcc_cmd + ' -E -P __tmp.c', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
  173. stdout, stderr = child.communicate()
  174. # print(stdout)
  175. if stderr != '' and stderr != b'':
  176. print(stderr)
  177. have_fdset = 0
  178. have_sigaction = 0
  179. have_sigevent = 0
  180. have_siginfo = 0
  181. have_sigval = 0
  182. version = None
  183. stdc = '1989'
  184. posix_thread = 0
  185. for line in stdout.split(b'\n'):
  186. line = line.decode()
  187. if re.search('fd_set', line):
  188. have_fdset = 1
  189. # check for sigal
  190. if re.search('struct[ \t]+sigaction', line):
  191. have_sigaction = 1
  192. if re.search('struct[ \t]+sigevent', line):
  193. have_sigevent = 1
  194. if re.search('siginfo_t', line):
  195. have_siginfo = 1
  196. if re.search('union[ \t]+sigval', line):
  197. have_sigval = 1
  198. if re.search('char\* version', line):
  199. version = re.search(r'\"([^"]+)\"', line).groups()[0]
  200. if re.findall('iso_c_visible = [\d]+', line):
  201. stdc = re.findall('[\d]+', line)[0]
  202. if re.findall('pthread_create', line):
  203. posix_thread = 1
  204. if have_fdset:
  205. result += '#define HAVE_FDSET 1\n'
  206. if have_sigaction:
  207. result += '#define HAVE_SIGACTION 1\n'
  208. if have_sigevent:
  209. result += '#define HAVE_SIGEVENT 1\n'
  210. if have_siginfo:
  211. result += '#define HAVE_SIGINFO 1\n'
  212. if have_sigval:
  213. result += '#define HAVE_SIGVAL 1\n'
  214. if version:
  215. result += '#define GCC_VERSION_STR "%s"\n' % version
  216. result += '#define STDC "%s"\n' % stdc
  217. if posix_thread:
  218. result += '#define LIBC_POSIX_THREADS 1\n'
  219. os.remove('__tmp.c')
  220. return result