Add small llvm/libclang util to exctract doxygen comments
authorRobin Gareus <robin@gareus.org>
Thu, 24 Mar 2016 18:53:27 +0000 (19:53 +0100)
committerRobin Gareus <robin@gareus.org>
Thu, 24 Mar 2016 21:54:20 +0000 (22:54 +0100)
.gitignore
tools/doxy2json/ardourdoc.sh [new file with mode: 0755]
tools/doxy2json/doxy2json.cc [new file with mode: 0644]

index 1054891197d8f748ca9e577b766a92c7bdacf8a6..21e992a12ad685418a274fc427cb4cfa1772d66d 100644 (file)
@@ -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 (executable)
index 0000000..548d403
--- /dev/null
@@ -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
+<?php
+\$json = file_get_contents ('$TMPFILE');
+\$api = array ();
+foreach (json_decode (\$json, true) as \$a) {
+       if (!isset (\$a['decl'])) { continue; }
+       if (empty (\$a['decl'])) { continue; }
+       if (\$a['decl'] == '::') { continue; }
+       if (substr (\$a['decl'], 0, 1) == '_') { continue; }
+       if (substr (\$a['decl'], 0, 2) == '::') { continue; }
+       if (substr (\$a['decl'], 0, 4) == 'sigc') { continue; }
+       if (substr (\$a['decl'], 0, 5) == 'Atk::') { continue; }
+       if (substr (\$a['decl'], 0, 5) == 'Gdk::') { continue; }
+       if (substr (\$a['decl'], 0, 5) == 'Gtk::') { continue; }
+       if (substr (\$a['decl'], 0, 5) == 'Gio::') { continue; }
+       if (substr (\$a['decl'], 0, 6) == 'Glib::') { continue; }
+       if (substr (\$a['decl'], 0, 7) == 'Pango::') { continue; }
+       if (substr (\$a['decl'], 0, 11) == 'luabridge::') { continue; }
+
+       \$a['decl'] = str_replace ('size_t', 'unsigned long', \$a['decl']);
+       \$canon = str_replace (' *', '*', \$a['decl']);
+       \$api[\$canon] = \$a;
+       }
+\$jout = array ();
+foreach (\$api as \$k => \$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 (file)
index 0000000..f8242d5
--- /dev/null
@@ -0,0 +1,183 @@
+/* extract doxygen comments from C++ header files
+ *
+ * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <cstdio>
+#include <cstring>
+#include <sstream>
+#include <iomanip>
+#include <clang-c/Index.h>
+#include <clang-c/Documentation.h>
+
+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> [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;
+}