diff --git a/AABB_tree/include/CGAL/AABB_tree.h b/AABB_tree/include/CGAL/AABB_tree.h index 41f93215fd8..65a82b6e5b3 100644 --- a/AABB_tree/include/CGAL/AABB_tree.h +++ b/AABB_tree/include/CGAL/AABB_tree.h @@ -1130,7 +1130,7 @@ public: this->traversal(query, traversal_traits); return traversal_traits.is_intersection_found(); } - +#ifndef DOXYGEN_RUNNING //To avoid doxygen to consider definition and declaration as 2 different functions (size_type causes problems) template template typename AABB_tree::size_type @@ -1149,7 +1149,7 @@ public: this->traversal(query, traversal_traits); return counter; } - +#endif template template OutputIterator diff --git a/Documentation/doc/CMakeLists.txt b/Documentation/doc/CMakeLists.txt index 889a6d15a82..144461b8c65 100644 --- a/Documentation/doc/CMakeLists.txt +++ b/Documentation/doc/CMakeLists.txt @@ -78,6 +78,10 @@ function(configure_doxygen_package CGAL_PACKAGE_NAME) if(EXISTS "${CGAL_PACKAGE_DIR}/examples") file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "EXAMPLE_PATH = ${CGAL_PACKAGE_DIR}/examples\n") endif() + if(CGAL_GENERATE_XML) + file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "GENERATE_XML = YES\n") + endif() + file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "GENERATE_TAGFILE = ${CGAL_DOC_TAG_GEN_DIR}/${CGAL_PACKAGE_NAME}.tag\n") file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "STRIP_FROM_PATH = ${CGAL_PACKAGE_DOC_DIR}/\n") file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "STRIP_FROM_PATH += ${CGAL_PACKAGE_DIR}/include/\n") @@ -199,7 +203,7 @@ set(CGAL_DOC_DXY_DIR "${CMAKE_BINARY_DIR}/doc_dxy") file(MAKE_DIRECTORY "${CGAL_DOC_DXY_DIR}") #Setting the resource directory depending on the version of doxygen -set(CGAL_DOC_RESOURCE_DIR_DEFAULT "${CMAKE_CURRENT_LIST_DIR}/resources/1.8.4") +set(CGAL_DOC_RESOURCE_DIR_DEFAULT "${CMAKE_CURRENT_LIST_DIR}/resources/1.8.13") # first look if resources for the specific doxygen version is available, fallback # on the default otherwise diff --git a/Documentation/doc/resources/1.8.13/BaseDoxyfile.in b/Documentation/doc/resources/1.8.13/BaseDoxyfile.in index 6121fdf771c..448bae7be6f 100644 --- a/Documentation/doc/resources/1.8.13/BaseDoxyfile.in +++ b/Documentation/doc/resources/1.8.13/BaseDoxyfile.in @@ -931,7 +931,8 @@ EXCLUDE_SYMBOLS = Tr \ Cb \ Fb \ K \ - Traits + Traits \ + internal # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include diff --git a/Documentation/doc/resources/1.8.4/BaseDoxyfile.in b/Documentation/doc/resources/1.8.4/BaseDoxyfile.in index 972fb8ed031..03daafc1db4 100644 --- a/Documentation/doc/resources/1.8.4/BaseDoxyfile.in +++ b/Documentation/doc/resources/1.8.4/BaseDoxyfile.in @@ -816,7 +816,8 @@ EXCLUDE_SYMBOLS = Tr \ Cb \ Fb \ K \ - Traits + Traits \ + internal # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see diff --git a/Documentation/doc/scripts/compare_testsuites.sh b/Documentation/doc/scripts/compare_testsuites.sh new file mode 100644 index 00000000000..ec4e941e451 --- /dev/null +++ b/Documentation/doc/scripts/compare_testsuites.sh @@ -0,0 +1,47 @@ +#!/bin/bash +if [ "$1" == '--help' ]; then + echo "Usage: $0 [doc 2]" + exho "doc 1 and doc 2 are paths to doxygen outputs (doc_output)." + echo "Parse the xml output of doc 1 and creates a directory with organized text files." + echo "Then, if doc_2 is specified, do the same for its xml output and make the diff between them." + exit 0 +fi +#Path to the CGAL_Documentation_build_directory/doc_output +PATH_TO_DOC="$1" + +if ! [ -d "$PATH_TO_DOC" ] || [ $(basename $PATH_TO_DOC) != "doc_output" ]; then + echo "wrong path" + exit 1 +fi + +#path to the repository containing the output of this script for the reference documentation. +DOC_REF="$2" +#output in a new directory +mkdir -p doc_data +cd ./doc_data +if [ $# -gt 5 ]; then + echo "too many arguments" + exit 1 + fi + +FAILURES=() +for dir in $PATH_TO_DOC/* +do + OUTPUT=$(basename $dir) + python ../documentation_parser.py $dir/xml > ./"$OUTPUT.txt" + if [ $? -eq 0 ]; then + echo "$dir OK" + else + echo "$dir FAILED" + FAILURES+="$dir " + fi +done +cd .. +echo "Output generated" +if ! [ -d "$DOC_REF" ]; then + echo "No reference given. Script is finished." + exit 2 +fi + +#diff the output and the reference output, ignoring the differences in whitespaces +diff -u -N -w ./doc_data $DOC_REF > ./diff.txt diff --git a/Documentation/doc/scripts/documentation_parser.py b/Documentation/doc/scripts/documentation_parser.py new file mode 100644 index 00000000000..187b175bf4f --- /dev/null +++ b/Documentation/doc/scripts/documentation_parser.py @@ -0,0 +1,182 @@ +from pyquery import PyQuery as pq +from collections import defaultdict +from sys import argv +import os.path as op + +# if _in is part of args, return true. +def check_type(_in, args): + if _in in args: + return True + else: + return False + +root_path=argv[1] +d = pq(filename=op.join(op.sep, root_path,'index.xml'), parser="xml") +compounds=[p.text() for p in d('compound').items()] +types=[p.attr('kind') for p in d('compound').items()] +type_map = defaultdict(list) #map +dict_map = defaultdict(dict)#map > +#FOREACH compounds : fill maps +for i in xrange(0,len(compounds)): + if check_type(types[i], "typedef"): + types[i]="type" + name=d('compound').children("name").eq(i).text() + members=[p.text() for p in d('compound').eq(i).children("member").items()] + m_types=[p.attr('kind') for p in d('compound').eq(i).children("member").items()] + if (not check_type(types[i], ['example', 'file', 'dir', 'page', 'group']) and + not (types[i] == "namespace" and len(members) == 0) and + not (types[i] == "enum" and len(members) == 0) ): + if (types[i] == "class"):#check if the class is a concept class + compound=d('compound').children("name").eq(i).text().replace('_', '__').replace('::', '_1_1') + filepath='class'+compound+'.xml' + total_path=op.join(op.sep, root_path,filepath) + if(op.isfile(total_path)): + e = pq(filename=total_path, parser="xml") + compoundnames=[p.text() for p in e('includes').items()] + + if(len(compoundnames) > 1 and compoundnames[0].find("Concept") != -1): + types[i] = 'Concept '+types[i].lower() + type_map[types[i]].append(name) + + mtype_map = defaultdict(list)# map + + #FOREACH member : + for j in xrange(0,len(members)): + if(check_type(types[i], ['class', 'Concept class']) + and m_types[j] == "function"): + m_types[j]="method" + if(m_types[j] == "typedef"): + m_types[j]="type" + mtype_map[m_types[j]].append(members[j]) + #end FOREACH member + dict_map[name]=mtype_map +#end FOREACH compound +indent="" + + + + +#print +#FOREACH type +for btype in type_map: + out=btype + if btype.endswith('s'): + out+='e' + print out.title()+'s' + indent+=" " + #FOREACH name + for name in type_map[btype]: + filepath="" + if check_type(btype, ['class', 'Concept class']): + filepath='/class'+name.replace('_', '__').replace('::', '_1_1')+'.xml' + elif btype == 'namespace': + filepath='/namespace'+name.replace('_', '__').replace('::', '_1_1')+'.xml' + templates=[] + if op.isfile(op.join(op.sep, root_path,filepath)): + f=pq(filename=op.join(op.sep, root_path,filepath), parser="xml") + templateparams=f("compounddef").children("templateparamlist").eq(0).children("param").items() + for param in templateparams: + template_type="" + template_name="" + template_defval="" + template_type=param.children("type").text() + template_name=param.children("declname").text() + template_defval=param.children("defval").text() + complete_template="" + if not template_type is None: + complete_template+=template_type+' ' + if not template_name is None: + complete_template+=template_name + if not template_defval is None: + complete_template+=' = '+template_defval + templates.append(complete_template) + if templates==[]:#if no child was found, just take param.text() + templates=[t.text() for t in param.items()] + suffix="<" + #as template got type, defname and declname, name is twice in template. keep only one of them. + to_remove=[""] + for template in templates: + suffix+=template+', ' + if suffix == "<": + suffix="" + if suffix.endswith(', '): + suffix = suffix[:-2]+'>' + print indent+name+suffix + + indent+=" " + #FOREACH mtype + for mtype in (dict_map[name]): + out=mtype + if mtype.endswith('s'): + out+='e' + print indent+out.title()+'s' + indent+=" " + #FOREACH member + overload_map = defaultdict(int) #contains the number of times a member has appeared (to manage the overloads) + templateparams=[] + for member in dict_map[name][mtype]: + templates=[] + args="" # will contain the arguments of the methods and functions + return_type="" #will contain the return type of a function/method + + #look for arguments + if op.isfile(op.join(op.sep, root_path,filepath)): + f=pq(filename=op.join(op.sep, root_path,filepath), parser="xml") + index=0 + memberdefs=[m.text() for m in f("memberdef").items()] + for i in xrange(0,len(memberdefs)): + member_names=[member_name.text() for member_name in f('memberdef').eq(i).children("name").items()] + if f('memberdef').eq(i).children("name").text() == member: + if (index < overload_map[member]): + index+=1 + elif (index == overload_map[member]): + if check_type(mtype, ['function', 'method']): + args=[f('memberdef').eq(i).children("argsstring").text()] + templateparams=f('memberdef').eq(i).children("templateparamlist").children("param").items() + if check_type(mtype, ['function', 'method', 'type', 'variable']): + return_type=[f('memberdef').eq(i).children("type").text()] + break; + #end foreach memberdef + arguments="" + for arg in args: + arguments+=arg + template_types=[] + template_names=[] + for param in templateparams: + template_type="" + template_name="" + template_defval="" + template_type=param.children("type").text() + template_name=param.children("declname").text() + template_defval=param.children("defval").text() + complete_template="" + if not template_type is None: + complete_template+=template_type+' ' + if not template_name is None: + complete_template+=template_name + if not template_defval is None: + complete_template+=' = '+template_defval + templates.append(complete_template) + if templates==[]:#if no child was found, just take param.text() + templates=[t.text() for t in param.items()] + + prefix="template <" + for template in templates: + prefix+=template+', ' + if prefix == "template <": + prefix="" + if prefix.endswith(', '): + prefix = prefix[:-2]+'>\n'+indent+" " + for definition in return_type: + prefix+=definition + if(prefix != ""): + prefix+=" " + print indent+prefix+member+arguments + overload_map[member]+=1 + #END foreach member + indent=indent[:-2] + #END foreach mtype + indent=indent[:-2] + #END foreach name + indent=indent[:-2] +#END foreach type diff --git a/Documentation/doc/scripts/test_doxygen_versions.sh b/Documentation/doc/scripts/test_doxygen_versions.sh new file mode 100644 index 00000000000..1b435c88d77 --- /dev/null +++ b/Documentation/doc/scripts/test_doxygen_versions.sh @@ -0,0 +1,106 @@ +#!/bin/bash +if [ "$1" == '--help' ]; then + echo "Usage: $0 [doxygen_2] [publish_dir]" + echo "Compares the output of doxygen_1 and doxygen_2 of this CGAL version, " + echo "where doxygen_1 and doxygen_2 are valid paths to doxygen executables." + echo "If doxygen_2 is not specified, the master branch of doxygen will be cloned, built and used as doxygen_2." + echo "publish_dir is the path to the dir where the testsuite results are kept" + echo "$0 must be called from doc/scripts" + exit 0 +fi +#build reference +PATH_TO_1="$1" +PATH_TO_2="$2" +PUBLISH_DIR="$3" +NB_CORES="$(grep -c ^processor /proc/cpuinfo)" + +if [ -z $PATH_TO_1 ] || [ $(basename $PATH_TO_1) != "doxygen" ] || [ ! -e $PATH_TO_1 ]; then + echo "Please specify a valid path to a doxygen executable." + echo "$0 --help for more information." + exit 0 +fi + +#Find the CGAL directory. If there is a directory called Documentation, this is a branch build. +#Else it is from a release. +TEMP=$PWD +IS_RELEASE=1 +cd $PWD/../.. +if [ "$(basename $PWD)" = 'Documentation' ]; then + ROOT=$PWD/.. + IS_RELEASE=0 +else + ROOT=$PWD +fi +cd $TEMP #scripts + + +mkdir ./build_doc +cd ./build_doc +cmake -DCGAL_GENERATE_XML=ON -DDOXYGEN_EXECUTABLE="$PATH_TO_1" ../.. &> /dev/null +make -j$NB_CORES doc &> /dev/null +cd ../ #scripts +bash compare_testsuites.sh $PWD/build_doc/doc_output +mv ./doc_data ./doc_ref + +#download and build doxygen_master +if [ -z $PATH_TO_2 ] || [ $(basename $PATH_TO_2) != "doxygen" ] || [ ! -e $PATH_TO_2 ]; then + echo "No second path detected. Cloning Doxygen master branch..." + git clone https://github.com/doxygen/doxygen.git doxygen_master &> /dev/null + cd doxygen_master + MASTER_DESCRIBE=$(git describe --tags) + mkdir build + cd build + cmake .. &> /dev/null + make -j$NB_CORES &> /dev/null + cd ../.. #scripts + PATH_TO_2="$PWD/doxygen_master/build/bin/doxygen" + echo "done." +fi +#build doc with doxygen master +mv ./build_doc ./doc_dir +mkdir build_doc +cd ./build_doc +cmake -DCGAL_GENERATE_XML=ON -DDOXYGEN_EXECUTABLE="$PATH_TO_2" ../.. &> /dev/null +make -j$NB_CORES doc &> /dev/null +cd ../ #scripts +DOXYGEN_1=$($PATH_TO_1 --version) +DOXYGEN_2=$($PATH_TO_2 --version) +bash ./compare_testsuites.sh $PWD/build_doc/doc_output $PWD/doc_ref +#rebuild doc with Doxygen_1 to get the right doc_tags without GENERATE_XML because it ignores the EXCLUDE_SYMBOLS, +#which disrupts the logs +mv ./build_doc ./doc_master +rm -rf ./doc_dir +mkdir ./doc_dir +cd ./doc_dir +cmake -DCGAL_DOC_CREATE_LOGS=true -DDOXYGEN_EXECUTABLE="$PATH_TO_1" ../.. &> /dev/null +make -j$NB_CORES doc &> /dev/null +make -j$NB_CORES doc &> /dev/null +cd .. #scripts +#get VERSION's content +if [ $IS_RELEASE = 0 ]; then + cd $ROOT + mkdir -p ./build && cd ./build + cmake .. + CGAL_NAME="$(cat $PWD/VERSION)" + cd ..$ROOT + rm -rf ./build + cd $ROOT/Documentation/doc/scripts +else + CGAL_NAME="$(cat $ROOT/VERSION)" +fi + +#update overview + +python ./testsuite.py --output-dir $PWD/doc_dir/doc_output/ --doc-log-dir $PWD/doc_dir/doc_log/ \ + --publish $PUBLISH_DIR --diff $PWD/diff.txt --master-dir $PWD/doc_master/doc_output/ \ + --cgal-version "$CGAL_NAME" --do-copy-results --version-to-keep 10 --doxygen-version "$DOXYGEN_1" --master-describe "$MASTER_DESCRIBE" + +#clean-up +rm -rf ./build_doc +rm -rf ./doc_dir +rm -rf ./doc_master +rm -rf ./doc_ref +rm -rf ./doc_data +if [ -z $PATH_TO_2 ]; then + rm -rf ./doxygen_master +fi diff --git a/Documentation/doc/scripts/testsuite.py b/Documentation/doc/scripts/testsuite.py index ffc64689fb6..6c5af94a0eb 100755 --- a/Documentation/doc/scripts/testsuite.py +++ b/Documentation/doc/scripts/testsuite.py @@ -84,8 +84,15 @@ body {color: black; background-color: #C0C0D0; font-family: sans-serif;} ''' if args.publish and args.do_copy_results: - link="\nLink to this documentation\n" - d = pq(page_header+link+page_footer) + suffix='' + if args.doxygen_version: + suffix = "built with Doxygen "+args.doxygen_version + link="\nLink to this documentation {_suffix}\n".format(_suffix=suffix) + suffix = '' + if args.master_describe: + suffix=args.master_describe + link_master="\n
Link to documentation built with Doxygen Master {_suffix}\n".format(_suffix=suffix) + d = pq(page_header+link+" "+link_master+page_footer) else: d = pq(page_header+page_footer) logs=sorted(glob.glob('./*.log')) @@ -115,10 +122,14 @@ def main(): parser.add_argument('--publish', metavar='/path/to/publish', help='Specify this argument if the results should be published.') parser.add_argument('--doc-log-dir', default='.', metavar='/path/to/cgal/build/dir/doc_log', help='The path of the documentation logs.') + 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-dir', default='.', metavar='/path/to/cgal/build/dir/doc_output', help='The path to the build documentation') + parser.add_argument('--diff', metavar='/path/to/diff', help='The path to the 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-version', default ='', help='Specify this argument if you want to add a version number to the name of the link to the 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() @@ -135,7 +146,14 @@ def main(): title=d('#maintitle') title.text(title.text() + ' for ' + version_string) write_out_html(d, './index.html') - + + # does the diff exist ? + diff='n/a' + if args.diff: + diff_file=args.diff + if not os.path.isfile(diff_file): + sys.stderr.write('Diff file ' + diff_file + ' is not a file. Cannot diff.\n') + sys.exit(1) if args.publish: if args.publish.endswith('/'): publish_dir=args.publish @@ -150,12 +168,12 @@ def main(): 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.') + print('No index.html in the publish directory found. Writing a skeleton.') with open(publish_dir + 'index.html', 'w') as f: f.write('''