gcc.py 7.9 KB

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