diff --git a/.gitignore b/.gitignore index 1054891197..21e992a12a 100644 --- a/.gitignore +++ b/.gitignore @@ -131,4 +131,6 @@ tags /icons/win32/msvc_resources.rc /tools/osx_packaging/Ardour/*.app +/tools/doxy2json/doxy2json + /MSVCvst_scan/vst_scan.rc diff --git a/tools/doxy2json/ardourdoc.sh b/tools/doxy2json/ardourdoc.sh new file mode 100755 index 0000000000..548d403e61 --- /dev/null +++ b/tools/doxy2json/ardourdoc.sh @@ -0,0 +1,55 @@ +#!/bin/bash +set -e +make +cd ../.. +test -f libs/ardour/ardour/ardour.h +LLVMINCLUDE="-I /usr/lib/llvm-3.6/include -I /usr/lib/llvm-3.6/lib/clang/3.6.2/include/" + +TMPFILE=`mktemp` +trap 'rm -f $TMPFILE' exit SIGINT SIGTERM + +echo "# analyzing source.. -> $TMPFILE" +./tools/doxy2json/doxy2json \ + `pkg-config --cflags glib-2.0 glibmm-2.4 cairomm-1.0 gtkmm-2.4 | sed 's/-std=c++11 //;s/-pthread //'` \ + $LLVMINCLUDE -I /usr/include/linux \ + -I libs/ardour -I libs/pbd -I libs/lua -I gtk2_ardour -I libs/timecode \ + -I libs/ltc -I libs/evoral \ + libs/ardour/ardour/* libs/pbd/pbd/* \ + gtk2_ardour/*.h \ + /usr/include/cairomm-1.0/cairomm/context.h \ +> $TMPFILE + +ls -lh $TMPFILE + +echo "# consolidating JSON" +php << EOF + \$a) { + \$jout[] = \$a; +} +file_put_contents('doc/ardourapi.json', json_encode (\$jout, JSON_PRETTY_PRINT)); +EOF + +ls -l doc/ardourapi.json diff --git a/tools/doxy2json/doxy2json.cc b/tools/doxy2json/doxy2json.cc new file mode 100644 index 0000000000..f8242d5d46 --- /dev/null +++ b/tools/doxy2json/doxy2json.cc @@ -0,0 +1,183 @@ +/* extract doxygen comments from C++ header files + * + * Copyright (C) 2016 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* +kind_to_txt (CXCursor cursor) +{ + CXCursorKind kind = clang_getCursorKind (cursor); + switch (kind) { + case CXCursor_StructDecl : return "Struct"; + case CXCursor_EnumDecl : return "Enum"; + case CXCursor_UnionDecl : return "Union"; + case CXCursor_FunctionDecl : return "C Function"; + case CXCursor_VarDecl : return "Variable"; + case CXCursor_ClassDecl : return "C++ Class"; + case CXCursor_CXXMethod : return "C++ Method"; + case CXCursor_Namespace : return "C++ Namespace"; + case CXCursor_Constructor : return "C++ Constructor"; + case CXCursor_Destructor : return "C++ Destructor"; + case CXCursor_FieldDecl : return "Data Member/Field"; + default: break; + } + return ""; +} + +static std::string +escape_json (const std::string &s) +{ + std::ostringstream o; + for (auto c = s.cbegin (); c != s.cend (); c++) { + switch (*c) { + case '"': o << "\\\""; break; + case '\\': o << "\\\\"; break; + case '\n': o << "\\n"; break; + case '\r': o << "\\r"; break; + case '\t': o << "\\t"; break; + default: + if ('\x00' <= *c && *c <= '\x1f') { + o << "\\u" << std::hex << std::setw (4) << std::setfill ('0') << (int)*c; + } else { + o << *c; + } + } + } + return o.str (); +} + +static void recurse_parents (CXCursor cr) { + CXCursor pc = clang_getCursorSemanticParent (cr); + if (CXCursor_TranslationUnit == clang_getCursorKind (pc)) { + return; + } + if (!clang_Cursor_isNull (pc)) { + recurse_parents (pc); + printf ("%s::", clang_getCString (clang_getCursorDisplayName (pc))); + } +} + +static enum CXChildVisitResult +traverse (CXCursor cr, CXCursor /*parent*/, CXClientData) +{ + CXComment c = clang_Cursor_getParsedComment (cr); + + if (clang_Comment_getKind (c) != CXComment_Null + && clang_isDeclaration (clang_getCursorKind (cr)) + && 0 != strlen (kind_to_txt (cr)) + ) { + + printf ("{ \"decl\" : \""); + recurse_parents (cr); + + // TODO: resolve typedef enum { .. } name; + // use clang_getCursorDefinition (clang_getCanonicalCursor (cr)) ?? + printf ("%s\",\n", clang_getCString (clang_getCursorDisplayName (cr))); + + if (clang_Cursor_isVariadic (cr)) { + printf (" \"variadic\" : true,\n"); + } + + printf (" \"kind\" : \"%s\",\n", kind_to_txt (cr)); + + CXSourceLocation loc = clang_getCursorLocation (cr); + CXFile file; unsigned line, col, off; + clang_getFileLocation (loc, &file, &line, &col, &off); + + printf (" \"src\" : \"%s:%d\",\n", + clang_getCString (clang_getFileName (file)), line); + + printf (" \"doc\" : \"%s\"\n", + escape_json (clang_getCString (clang_FullComment_getAsHTML (c))).c_str ()); + printf ("},\n"); + } + return CXChildVisit_Recurse; +} + +static void +process_file (int argc, char **args, const char *fn) +{ + CXIndex index = clang_createIndex (0, 0); + CXTranslationUnit tu = clang_createTranslationUnitFromSourceFile (index, fn, argc, args, 0, 0); + + if (tu == NULL) { + fprintf (stderr, "Cannot create translation unit for src: %s\n", fn); + return; + } + + clang_visitChildren (clang_getTranslationUnitCursor (tu), traverse, 0); + + clang_disposeTranslationUnit (tu); + clang_disposeIndex (index); +} + +static void +usage (int status) +{ + printf ("doxy2json - extract doxygen doc from C++ headers.\n\n"); + fprintf (stderr, "Usage: dox2json [-I path]* [filename]*\n"); + exit (status); +} + +int main (int argc, char **argv) +{ + int cnt = 2; + char **args = (char**) malloc (cnt * sizeof (char*)); + args[0] = strdup ("-x"); + args[1] = strdup ("c++"); + int c; + while (EOF != (c = getopt (argc, argv, "I:"))) { + switch (c) { + case 'I': + args = (char**) realloc (args, (cnt + 2) * sizeof (char*)); + args[cnt++] = strdup ("-I"); + args[cnt++] = strdup (optarg); + break; + case 'h': + usage (0); + default: + usage (EXIT_FAILURE); + break; + } + } + + if (optind >= argc) { + usage (EXIT_FAILURE); + } + + printf ("[\n"); + for (int i = optind; i < argc; ++i) { + process_file (cnt, args, argv[i]); + } + printf ("{} ]\n"); + + for (int i = 0; i < cnt; ++i) { + free (args[i]); + } + free (args); + + return 0; +}