brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.8 KiB · 3ba12e1 Raw
352 lines · plain
1#! /usr/bin/env python32import sys, os, re, urllib.request3 4latest_release = 215 6clang_www_dir = os.path.dirname(__file__)7default_issue_list_path = os.path.join(clang_www_dir, 'cwg_index.html')8issue_list_url = "https://raw.githubusercontent.com/cplusplus/CWG/gh-pages/issues/cwg_index.html"9output = os.path.join(clang_www_dir, 'cxx_dr_status.html')10dr_test_dir = os.path.join(clang_www_dir, '../test/CXX/drs')11 12class DR:13  def __init__(self, *, section_number, section_name, section_link, number, url, status, liaison, title):14    self.section_number, self.section_name, self.section_link, self.number, self.url, self.status, self.liaison, self.title = \15        section_number, section_name, section_link, number, url, status, liaison, title16  def __repr__(self):17    return '%s (%s): %s' % (self.number, self.status, self.title)18 19  pattern = re.compile('''20<TD.*>(?P<section_number>.*) <A href="(?P<section_link>.*)">(?P<section_name>.*)</A>21</TD>22<TD.*><A HREF="(?P<url>.*)">(?P<number>.*)</A></TD>23<TD.*>(?P<status>.*)</TD>24<TD.*>(?P<liaison>.*)</TD>25<TD.*><issue_title>(?P<title>[\\w\\W]*)</issue_title></TD>26</TR>''')27 28  @classmethod29  def parse_from_html(cls, html_string):30    match = cls.pattern.match(html_string)31    if match is None:32        print(f"Parse error: {html_string}", file=sys.stderr)33        exit(1)34    return cls(35      section_number=match.group('section_number'),36      section_name=match.group('section_name'),37      section_link=match.group('section_link'),38      number=int(match.group('number')),39      url=match.group('url'),40      status=match.group('status'),41      liaison=match.group('liaison'),42      title=match.group('title').replace('\n', ' ').strip())43 44def collect_tests():45  status_re = re.compile(r'\bcwg([0-9]+): (.*)')46  status_map = {}47  for test_cpp in os.listdir(dr_test_dir):48    if not test_cpp.endswith('.cpp'):49      continue50    test_cpp = os.path.join(dr_test_dir, test_cpp)51    found_any = False;52    for match in re.finditer(status_re, open(test_cpp, 'r', encoding="utf-8").read()):53      dr_number = int(match.group(1))54      if dr_number in status_map:55        print("error: Comment for cwg{} encountered more than once. Duplicate found in {}".format(dr_number, test_cpp))56        sys.exit(1)57      status_map[dr_number] = match.group(2)58      found_any = True59    if not found_any:60      print("warning:%s: no '// cwg123: foo' comments in this file" % test_cpp, file=sys.stderr)61  return status_map62 63def get_issues(path):64  buffer = None65  if not path and os.path.exists(default_issue_list_path):66    path = default_issue_list_path67  try:68    if path is None:69      print('Fetching issue list from {}'.format(issue_list_url))70      with urllib.request.urlopen(issue_list_url) as f:71        buffer = f.read().decode('utf-8')72    else:73      print('Opening issue list from file {}'.format(path))74      with open(path, 'r') as f:75        buffer = f.read()76  except Exception as ex:77     print('Unable to read the core issue list', file=sys.stderr)78     print(ex, file=sys.stderr)79     sys.exit(1)80 81  return sorted((DR.parse_from_html(dr) for dr in buffer.split('<TR>')[2:]),82                key = lambda dr: dr.number)83 84 85issue_list_path  = None86if len(sys.argv) == 1:87  pass88elif len(sys.argv) == 2:89  issue_list_path = sys.argv[1]90else:91  print('Usage: {} [<path to cwg_index.html>]'.format(sys.argv[0]), file=sys.stderr)92  sys.exit(1)93 94status_map = collect_tests()95drs = get_issues(issue_list_path)96out_html = []97out_html.append('''\98<!DOCTYPE html>99<!-- This file is auto-generated by make_cxx_dr_status. Do not modify. -->100<html>101<head>102  <META http-equiv="Content-Type" content="text/html; charset=utf-8">103  <title>Clang - C++ Defect Report Status</title>104  <link type="text/css" rel="stylesheet" href="menu.css">105  <link type="text/css" rel="stylesheet" href="content.css">106  <style type="text/css">107    .none { background-color: #FFCCCC }108    .none-superseded { background-color: rgba(255, 204, 204, 0.65) }109    .unknown { background-color: #EBCAFE }110    .unknown-superseded { background-color: rgba(234, 200, 254, 0.65) }111    .partial { background-color: #FFE0B0 }112    .partial-superseded { background-color: rgba(255, 224, 179, 0.65) }113    .unreleased { background-color: #FFFF99 }114    .unreleased-superseded { background-color: rgba(255, 255, 153, 0.65) }115    .full { background-color: #CCFF99 }116    .full-superseded { background-color: rgba(214, 255, 173, 0.65) }117    .na { background-color: #DDDDDD }118    .na-superseded { background-color: rgba(222, 222, 222, 0.65) }119    .open * { color: #AAAAAA }120    .open-superseded * { color: rgba(171, 171, 171, 0.65) }121    //.open { filter: opacity(0.2) }122    tr:target { background-color: #FFFFBB }123    th { background-color: #FFDDAA }124  </style>125</head>126<body>127 128<!--#include virtual="menu.html.incl"-->129 130<div id="content">131 132<!--*************************************************************************-->133<h1>C++ Defect Report Support in Clang</h1>134<!--*************************************************************************-->135 136<h2 id="cxxdr">C++ defect report implementation status</h2>137 138<p>This page tracks which C++ defect reports are implemented within Clang.</p>139 140<table width="892" border="1" cellspacing="0">141  <tr>142    <th>Number</th>143    <th>Section</th>144    <th>Status</th>145    <th>Issue title</th>146    <th>Available in Clang?</th>147  </tr>''')148 149class AvailabilityError(RuntimeError):150  pass151 152availability_error_occurred = False153 154def availability(issue):155  status = status_map.get(issue, 'unknown')156  unresolved_status = ''157  proposed_resolution = ''158  unresolved_status_match = re.search(r' (open|drafting|review|tentatively ready|ready)', status)159  if unresolved_status_match:160    unresolved_status = unresolved_status_match.group(1)161    proposed_resolution_match = re.search(r' (open|drafting|review|tentatively ready|ready) (\d{4}-\d{2}(?:-\d{2})?|P\d{4}R\d+)$', status)162    if proposed_resolution_match is None:163      raise AvailabilityError('error: issue {}: \'{}\' status should be followed by a paper number (P1234R5) or proposed resolution in YYYY-MM-DD format'.format(dr.number, unresolved_status))164    proposed_resolution = proposed_resolution_match.group(2)165    status = status[:-1-len(proposed_resolution)]166    status = status[:-1-len(unresolved_status)]167 168  avail_suffix = ''169  avail_style = ''170  details = ''171  if status.endswith(' c++11'):172    status = status[:-6]173    avail_suffix = ' (C++11 onwards)'174  elif status.endswith(' c++14'):175    status = status[:-6]176    avail_suffix = ' (C++14 onwards)'177  elif status.endswith(' c++17'):178    status = status[:-6]179    avail_suffix = ' (C++17 onwards)'180  elif status.endswith(' c++20'):181    status = status[:-6]182    avail_suffix = ' (C++20 onwards)'183  elif status.endswith(' c++23'):184    status = status[:-6]185    avail_suffix = ' (C++23 onwards)'186  elif status.endswith(' c++26'):187    status = status[:-6]188    avail_suffix = ' (C++26 onwards)'189  if status == 'unknown':190    avail = 'Unknown'191    avail_style = 'unknown'192  elif re.match(r'^[0-9]+\.?[0-9]*', status):193    if not proposed_resolution:194      avail = 'Clang %s' % status195      if float(status) > latest_release:196        avail_style = 'unreleased'197      else:198        avail_style = 'full'199    else: 200      avail = 'Not resolved'201      details = f'Clang {status} implements {proposed_resolution} resolution'202  elif status == 'yes':203    if not proposed_resolution:204      avail = 'Yes'205      avail_style = 'full'206    else:207      avail = 'Not resolved'208      details = f'Clang implements {proposed_resolution} resolution'209  elif status == 'partial':210    if not proposed_resolution:211      avail = 'Partial'212      avail_style = 'partial'213    else:214      avail = 'Not resolved'215      details = f'Clang partially implements {proposed_resolution} resolution'216  elif status == 'no':217    if not proposed_resolution:218      avail = 'No'219      avail_style = 'none'220    else:221      avail = 'Not resolved'222      details = f'Clang does not implement {proposed_resolution} resolution'223  elif status == 'na':224    avail = 'N/A'225    avail_style = 'na'226  elif status == 'na lib':227    avail = 'N/A (Library DR)'228    avail_style = 'na'229  elif status == 'na abi':230    avail = 'N/A (ABI constraint)'231    avail_style = 'na'232  elif status.startswith('sup '):233    dup = status.split(' ', 1)[1]234    if dup.startswith('P'):235      avail = 'Superseded by <a href="https://wg21.link/%s">%s</a>' % (dup, dup)236      avail_style = 'na'237    else:238      avail = 'Superseded by <a href="#%s">%s</a>' % (dup, dup)239      try:240        _, avail_style, _, _ = availability(int(dup))241        avail_style += '-superseded'242      except:243        print("issue %s marked as sup %s" % (issue, dup), file=sys.stderr)244        avail_style = 'none'245  elif status.startswith('dup '):246    dup = int(status.split(' ', 1)[1])247    avail = 'Duplicate of <a href="#%s">%s</a>' % (dup, dup)248    _, avail_style, _, _ = availability(dup)249  else:250    raise AvailabilityError('error: unknown status %s for issue %s' % (status, dr.number))251  return (avail + avail_suffix, avail_style, unresolved_status, details)252 253count = {}254for dr in drs:255  if dr.status in ('concepts',):256    # This refers to the old ("C++0x") concepts feature, which was not part257    # of any C++ International Standard or Technical Specification.258    continue259 260  elif dr.status == 'extension':261    row_style = ' class="open"'262    avail = 'Extension'263    avail_style = ''264 265  elif dr.status in ('open', 'drafting', 'review', 'tentatively ready', 'ready'):266    row_style = ' class="open"'267    try:268      avail, avail_style, unresolved_status, details = availability(dr.number)269    except AvailabilityError as e:270      availability_error_occurred = True271      print(e.args[0])272      continue273      274    if avail == 'Unknown':275      avail = 'Not resolved'276      avail_style = ''277    else:278      if unresolved_status != dr.status:279        availability_error_occurred = True280        print("error: issue %s is marked '%s', which differs from CWG index status '%s'" \281             % (dr.number, unresolved_status, dr.status))282        continue283  else:284    row_style = ''285    try:286      avail, avail_style, unresolved_status, details = availability(dr.number)287    except AvailabilityError as e:288      availability_error_occurred = True289      print(e.args[0])290      continue291 292    if unresolved_status:293      availability_error_occurred = True294      print("error: issue %s is marked '%s', even though it is resolved in CWG index" \295           % (dr.number, unresolved_status))296      continue297 298  if not avail.startswith('Sup') and not avail.startswith('Dup'):299    count[avail] = count.get(avail, 0) + 1300 301  if avail_style != '':302    avail_style = ' class="{}"'.format(avail_style)303 304  if details != '':305    avail = f'''306      <details>307        <summary>{avail}</summary>308        {details}309      </details>'''310  out_html.append(f'''311  <tr{row_style} id="{dr.number}">312    <td><a href="https://cplusplus.github.io/CWG/issues/{dr.number}.html">{dr.number}</a></td>313    <td>[<a href="{dr.section_link}">{dr.section_name}</a>]</td>314    <td>{dr.status}</td>315    <td>{dr.title}</td>316    <td{avail_style} align="center">{avail}</td>317  </tr>''')318 319if availability_error_occurred:320  exit(1)321 322for status, num in sorted(count.items()):323  print("%s: %s" % (status, num), file=sys.stderr)324 325out_html.append('''\326</table>327 328</div>329</body>330</html>331''')332 333# Make an effort to remain consistent with the existing file.334# We can't pick one newline style and use it on Windows,335# because we can't be compatible with all 'autocrlf' modes at once.336def detect_newline_style(file_path):337  if not os.path.exists(file_path):338    return '\n'339  f = open(file_path)340  f.readline()341  if f.newlines is None:342    return '\n'343  if isinstance(f.newlines, str):344    return f.newlines345  newline = f.newlines[0]346  print(f"Existing '{file_path}' has inconsistent newlines; picking '{newline.encode('unicode_escape').decode('utf-8')}'")347  return newline348    349out_file = open(output, 'w', newline=detect_newline_style(output))350out_file.write(''.join(out_html))351out_file.close()352