#!/usr/bin/env python3 # Copyright (c) 2012 GeometryFactory (France). All rights reserved. # All rights reserved. # # This file is part of CGAL (www.cgal.org). # # $URL$ # $Id$ # SPDX-License-Identifier: GPL-3.0-or-later # # # Author(s) : Philipp Moeller #NOTE : if args.diff2 is not given, then it is considered that something went # wrong during the build of doxygen_master or the generation of the doc. import argparse import shutil import sys import subprocess import os import glob import re import operator import datetime import getpass import socket import time from xml.dom.minidom import parseString from pyquery import PyQuery as pq def write_out_html(d, fn): with open(fn, 'w') as f: # this is the normal doxygen doctype, which is thrown away by pyquery f.write('\n') f.write(d.html()) def count_errors_and_warnings(fn): with open(fn) as f: warn_count=0 error_count=0 for line in f: if re.match('^citelist.*[wW]arning.*$', line): continue if re.match('^.*[wW]arning.*$', line): warn_count=warn_count + 1 if re.match('^.*[eE]rror.*$', line): error_count=error_count + 1 return (warn_count, error_count) def get_version(): proc=subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) rev=proc.communicate()[0].strip() proc=subprocess.Popen(['git', 'log', '-n', '1', '--format=\"%ai\"', '--date=short'], stdout=subprocess.PIPE,stderr=subprocess.PIPE, universal_newlines=True) date=proc.communicate()[0] #rev=subprocess.check_output(['git', 'rev-parse', 'HEAD'], universal_newlines=True) #date=subprocess.check_output(['git', 'log', '-n', '1', '--format=\"%ai\"', '--date=short'], universal_newlines=True) date=date[1:11] return (rev, date) def write_report(args): page_header=''' CGAL Doxygen Manual Results

Doxygen Manual Results

''' page_footer='''
Doxygen 1.8.13(patched)Doxygen 1.9.6(patched)Doxygen master
Package Name Logs Warnings Errors Logs Warnings Errors Logs Warnings Errors
''' if args.publish and args.do_copy_results: suffix='' if args.doxygen_version1: suffix = ""+args.doxygen_version1 link1="Documentation built with our fork of Doxygen {_suffix}\n".format(_suffix=suffix) suffix = '' if args.doxygen_version2: suffix = args.doxygen_version2 link2="\n
Documentation built with our fork of Doxygen {_suffix} (used for the official CGAL documentation)\n".format(_suffix=suffix) suffix = '' if args.master_describe: suffix=args.master_describe link_master="\n
Documentation built with the master version of Doxygen {_suffix} (buggy), so that we see progress/regression of doxygen development as far as CGAL is concerned.\n".format(_suffix=suffix) else: link_master="\n


/!\\ Documentation with the master version of Doxygen FAILED /!\\

\n" d = pq(page_header+link1+" "+link2+" "+link_master+page_footer) else: d = pq(page_header+page_footer) results1=[] results2=[] results_master=[] err_war_sum1=(0,0) err_war_sum2=(0,0) err_war_sum_master=(0,0) os.chdir(args.doc_log_dir1) logs=sorted(glob.glob('./*.log')) for log in logs: res=count_errors_and_warnings(log) err_war_sum1=tuple(map(operator.add, err_war_sum1, res)) basename=os.path.basename(log) pretty_name=basename[0:-4] result = [(basename, pretty_name, res)] results1.extend(result) os.chdir(args.doc_log_dir2) logs=sorted(glob.glob('./*.log')) for log in logs: res=count_errors_and_warnings(log) err_war_sum2=tuple(map(operator.add, err_war_sum2, res)) basename=os.path.basename(log) result = [(basename, pretty_name, res)] results2.extend(result) if args.doc_log_dir_master: os.chdir(args.doc_log_dir_master) if(args.diff2): logs=sorted(glob.glob('./*.log')) for log in logs: res=count_errors_and_warnings(log) err_war_sum_master=tuple(map(operator.add, err_war_sum_master, res)) basename=os.path.basename(log) pretty_name=basename[0:-4] result = [(basename, pretty_name, res)] results_master.extend(result) else: for index in range(0, len(results1)): result = [('./build_logs', './build_logs', (0,1))] results_master.extend(result) for index in range(0, len(results1)): status='class="package-good"' no_errors = True no_warn = True for res_list in [results1, results2, results_master]: if res_list[index][2][0] != 0 and res_list[index][2][1] == 0: no_warn = False elif res_list[index][2][1] != 0: no_errors = False if not no_warn and no_errors : status='class="package-warnings"' elif not no_errors: status='class="package-error"' new_row=''' {pretty_name} Logs {warn_count1} {err_count1} Logs {warn_count2} {err_count2} Logs {warn_count_master} {err_count_master}'''.format( status=status, pretty_name=results1[index][1], basename1=results1[index][0],basename2=results2[index][0],basename_master=results_master[index][0], warn_count1=str(results1[index][2][0]),warn_count2=str(results2[index][2][0]),warn_count_master=str(results_master[index][2][0]), err_count1=str(results1[index][2][1]),err_count2=str(results2[index][2][1]),err_count_master=str(results_master[index][2][1])) d('.test-results').append(new_row) return (d, err_war_sum1, err_war_sum2, err_war_sum_master) def main(): parser = argparse.ArgumentParser( description='This script updates a checkout of cgal, purges the documentation, rebuilds it, creates an HTML summary of the resulting log files, and publishes the created files and logs.') parser.add_argument('--publish', metavar='/path/to/publish', help='Specify this argument if the results should be published.') parser.add_argument('--doc-log-dir1', default='.', metavar='/path/to/cgal/build/dir/doc_log 1', help='The first path of the documentation logs.') parser.add_argument('--doc-log-dir2', default='.', metavar='/path/to/cgal/build/dir/doc_log 2', help='The second path of the documentation logs.') parser.add_argument('--doc-log-dir-master', default='.', metavar='/path/to/cgal/build/dir/doc_log master', help='The path of the documentation logs from master.') parser.add_argument('--master-dir', default='.', metavar='/path/to/cgal/build/master_dir/doc_output', help='The path to the master build documentation.') parser.add_argument('--output-dir1', default='.', metavar='/path/to/cgal/build/dir1/doc_output', help='The path to the first built documentation') parser.add_argument('--output-dir2', default='.', metavar='/path/to/cgal/build/dir2/doc_output', help='The path to the second built documentation') parser.add_argument('--diff1', metavar='/path/to/diff', help='The path to the first diff file.') parser.add_argument('--diff2', metavar='/path/to/diff', help='The path to the first diff file.') parser.add_argument('--cgal-version', help='Path to a version.h file from the current release. If not specified use git hash instead.') parser.add_argument('--version-to-keep', help='indicates the number of release testsuites that should be kept at the publishing location.') parser.add_argument('--do-copy-results', action="store_true", help='Specify this argument if you want to copy the generated documentation into the publishing location.') parser.add_argument('--doxygen-version1', default ='', help='Specify this argument if you want to add a version number to the name of the link to the first documentation.') parser.add_argument('--doxygen-version2', default ='', help='Specify this argument if you want to add a version number to the name of the link to the second documentation.') parser.add_argument('--master-describe', default ='', help='Specify this argument if you want to add a suffix to the name of the link to the doxygen master documentation.') args = parser.parse_args() if args.cgal_version: version_string="CGAL-"+args.cgal_version version_date=datetime.datetime.now().strftime("%Y-%m-%d") else: version_string,version_date=get_version() version_string="Revision-"+version_string d,sum1,sum2,mastersum=write_report(args) os.chdir(args.doc_log_dir1) title=d('#maintitle') title.text(title.text() + ' for ' + version_string) write_out_html(d, './index.html') # does the diff exist ? diff1='n/a' if args.diff1: diff_file1=args.diff1 if not os.path.isfile(diff_file1): sys.stderr.write('Diff file ' + diff_file1 + ' is not a file. Cannot diff.\n') sys.exit(1) diff2='n/a' if args.diff2: diff_file2=args.diff2 if not os.path.isfile(diff_file2): sys.stderr.write('Diff file ' + diff_file2 + ' is not a file. Cannot diff.\n') sys.exit(1) if args.publish: if args.publish.endswith('/'): publish_dir=args.publish else: publish_dir=args.publish + '/' if not os.path.isdir(publish_dir): sys.stderr.write('Publish dir ' + publish_dir + ' is not a directory. Cannot publish.\n') sys.exit(1) if os.path.isdir(publish_dir + 'log' + version_string): sys.stderr.write('Logs for this revision have already been publish under: ' + publish_dir + 'log' + version_string + ' Cannot publish.\n') sys.exit(1) log_target=publish_dir + version_string # does the index file exist? if not write out a skeleton try: with open(publish_dir + 'index.html') as f: pass except IOError as e: print('No index.html in the publish directory found. Writing a skeleton.') with open(publish_dir + 'index.html', 'w') as f: f.write(''' Manual Testsuite Overview

Overviewpage of the Doxygen Manual Testsuite

Doxygen 1.8.13Doxygen 1.9.6Doxygen master
RevisionDateWarnings ErrorsWarnings ErrorsWarnings Errors Diff with doxygen masterDiff with doxygen 1.9.6
''') args_list='' for arg in sys.argv[0:]: args_list+=arg+' ' signature='

Generated with the command line
python {script_args}
by {user_name}@{host_name} at {date_time}

'.format( user_name=getpass.getuser(), host_name=socket.gethostname(), date_time=time.ctime(), script_args=args_list) f.write(signature) with open(diff_file1, 'r') as myfile: diff1=myfile.read() if not diff1: diff1='none' else: diff1='Diff between {test_version} and {master_version}.'.format( log_path=version_string, test_version=args.doxygen_version1, master_version=args.doxygen_version2) if args.diff2: with open(diff_file2, 'r') as myfile: diff2=myfile.read() if not diff2: diff2='none' else: diff2='Diff between {test_version} and {master_version}.'.format( log_path=version_string, test_version=args.doxygen_version1, master_version=args.master_describe) else: diff2='

Documentation with the master version of Doxygen FAILED

' d=pq(filename=publish_dir + 'index.html',parser="html") revs=d('#revisions tr') new_row='''{revision}{date} {warnings1}{errors1}{warnings2} {errors2}{warnings3}{errors3} {diffs1}{diffs2}'''.format( revision=version_string, date=version_date, warnings1=sum1[0], warnings2=sum2[0],warnings3=mastersum[0], errors1=sum1[1],errors2=sum2[1],errors3=mastersum[1], diffs1=diff2, diffs2=diff1) revs.eq(1).after(new_row) if args.version_to_keep: nb_items=len(revs) for k in range(int(args.version_to_keep),nb_items): dir_to_remove=revs.eq(k).text().split()[0] if os.access(publish_dir + dir_to_remove, os.W_OK): shutil.rmtree(publish_dir + dir_to_remove) else: sys.stderr.write("Warning: the directory " + publish_dir + dir_to_remove + " does not exist or is not writable!\n") revs.eq(k).remove() script_info=d('#suffix') if script_info.text()!='': print("Found") script_info.remove() args_list='' for arg in sys.argv[0:]: args_list+=arg+' ' signature='

Generated with the command line
python {script_args}
by {user_name}@{host_name} at {date_time}

'.format( user_name=getpass.getuser(), host_name=socket.gethostname(), date_time=time.ctime(), script_args=args_list) d('table').after(signature) write_out_html(d, publish_dir + 'index.html') try: #copy log files shutil.copytree(args.doc_log_dir1, log_target+'/logs1/') shutil.copyfile(args.doc_log_dir1+'/index.html', log_target+'/index.html') shutil.copytree(args.doc_log_dir2, log_target+'/logs2/') if args.doc_log_dir_master: shutil.copytree(args.doc_log_dir_master, log_target+'/logs_master/') #copy diff shutil.copyfile(diff_file1, log_target+'/diff1.txt') if args.diff2: shutil.copyfile(diff_file2, log_target+'/diff2.txt') try: #copy documentation if args.do_copy_results: tgt=os.path.join(log_target, 'output1') shutil.copytree(args.output_dir1, tgt, symlinks= True) tgt=os.path.join(log_target, 'output2') shutil.copytree(args.output_dir2, tgt, symlinks= True) os.symlink("../MathJax", os.path.join(log_target, 'MathJax')) except: sys.stderr.write("Error while copying documentation\n") raise if args.master_dir: try: #copy documentation from master if args.do_copy_results: tgt=os.path.join(log_target, 'master') shutil.copytree(args.master_dir, tgt, symlinks= True) except: sys.stderr.write("Error while copying master documentation\n") raise except: sys.stderr.write("Error while writing to "+log_target+". Does it already exists?\n") raise if __name__ == "__main__": main()