--- /dev/null
+import os.path
+import re, sys
+
+from . import common as _common
+from . import gcc as _gcc
+
+_normpath = _gcc._normpath
+
+TOOL = 'clang'
+
+META_FILES = {
+ '<built-in>',
+ '<command line>',
+}
+
+
+def preprocess(filename,
+ incldirs=None,
+ includes=None,
+ macros=None,
+ samefiles=None,
+ cwd=None,
+ ):
+ if not cwd or not os.path.isabs(cwd):
+ cwd = os.path.abspath(cwd or '.')
+ filename = _normpath(filename, cwd)
+
+ postargs = _gcc.POST_ARGS
+ basename = os.path.basename(filename)
+ dirname = os.path.basename(os.path.dirname(filename))
+ if (basename not in _gcc.FILES_WITHOUT_INTERNAL_CAPI
+ and dirname not in _gcc.DIRS_WITHOUT_INTERNAL_CAPI):
+ postargs += ('-DPy_BUILD_CORE=1',)
+
+ text = _common.preprocess(
+ TOOL,
+ filename,
+ incldirs=incldirs,
+ includes=includes,
+ macros=macros,
+ #preargs=PRE_ARGS,
+ postargs=postargs,
+ executable=['clang'],
+ compiler='unix',
+ cwd=cwd,
+ )
+ return _iter_lines(text, filename, samefiles, cwd)
+
+
+EXIT_MARKERS = {'# 2 "<built-in>" 2', '# 3 "<built-in>" 2', '# 4 "<built-in>" 2'}
+
+
+def _iter_lines(text, reqfile, samefiles, cwd, raw=False):
+ lines = iter(text.splitlines())
+
+ # The first line is special.
+ # The subsequent lines are consistent.
+ firstlines = [
+ f'# 1 "{reqfile}"',
+ '# 1 "<built-in>" 1',
+ '# 1 "<built-in>" 3',
+ '# 370 "<built-in>" 3',
+ '# 1 "<command line>" 1',
+ '# 1 "<built-in>" 2',
+ ]
+ for expected in firstlines:
+ line = next(lines)
+ if line != expected:
+ raise NotImplementedError((line, expected))
+
+ # Do all the CLI-provided includes.
+ filter_reqfile = (lambda f: _gcc._filter_reqfile(f, reqfile, samefiles))
+ make_info = (lambda lno: _common.FileInfo(reqfile, lno))
+ last = None
+ for line in lines:
+ assert last != reqfile, (last,)
+ # NOTE:condition is clang specific
+ if not line:
+ continue
+ lno, included, flags = _gcc._parse_marker_line(line, reqfile)
+ if not included:
+ raise NotImplementedError((line,))
+ if included == reqfile:
+ # This will be the last one.
+ assert 2 in flags, (line, flags)
+ else:
+ # NOTE:first condition is specific to clang
+ if _normpath(included, cwd) == reqfile:
+ assert 1 in flags or 2 in flags, (line, flags, included, reqfile)
+ else:
+ assert 1 in flags, (line, flags, included, reqfile)
+ yield from _gcc._iter_top_include_lines(
+ lines,
+ _normpath(included, cwd),
+ cwd,
+ filter_reqfile,
+ make_info,
+ raw,
+ EXIT_MARKERS
+ )
+ last = included
+ # The last one is always the requested file.
+ # NOTE:_normpath is clang specific
+ assert _normpath(included, cwd) == reqfile, (line,)
'-E',
)
+EXIT_MARKERS = {'# 0 "<command-line>" 2', '# 1 "<command-line>" 2'}
+
def preprocess(filename,
incldirs=None,
filter_reqfile,
make_info,
raw,
+ EXIT_MARKERS
)
last = included
# The last one is always the requested file.
def _iter_top_include_lines(lines, topfile, cwd,
filter_reqfile, make_info,
- raw):
+ raw, exit_markers):
partial = 0 # depth
files = [topfile]
# We start at 1 in case there are source lines (including blank ones)
# _parse_marker_line() that the preprocessor reported lno as 1.
lno = 1
for line in lines:
- if line == '# 0 "<command-line>" 2' or line == '# 1 "<command-line>" 2':
+ if line in exit_markers:
# We're done with this top-level include.
return
_lno, included, flags = _parse_marker_line(line)
if included:
+ # HACK:
+ # Mixes curses.h and ncurses.h marker lines
+ # gcc silently passes this, while clang fails
+ # See: /Include/py_curses.h #if-elif directives
+ # And compare with preprocessor output
+ if os.path.basename(included) == 'curses.h':
+ included = os.path.join(os.path.dirname(included), 'ncurses.h')
+
lno = _lno
included = _normpath(included, cwd)
# We hit a marker line.