api: provide api definition over api

This patch allows a client to bootstrap itself by downloading the
JSON API definitions over the API itself.

This patch enables it for Python (probably need a dynamic language).
Call VPPApiClient with the new bootstrapapi=True parameter.

Example (Python):

from vpp_papi import VPPApiClient
vpp = VPPApiClient(bootstrapapi=True)
rv = vpp.connect("foobar")
assert rv == 0
print(f'SHOW VERSION: {vpp.api.show_version()}')
vpp.disconnect()

Type: feature
Change-Id: Id903fdccc82b2e22aa1994331d2c150253f2ccae
Signed-off-by: Ole Troan <otroan@employees.org>
diff --git a/src/tools/vppapigen/generate_json.py b/src/tools/vppapigen/generate_json.py
index 610f84f..dc5cf9c 100755
--- a/src/tools/vppapigen/generate_json.py
+++ b/src/tools/vppapigen/generate_json.py
@@ -110,6 +110,15 @@
                         ],
                         f.name,
                     ),
+                    "outputdir": "%s/%s/"
+                    % (
+                        output_path,
+                        output_dir_map[
+                            f.as_posix().split("/")[
+                                src_dir_depth + BASE_DIR.count("/") - 1
+                            ]
+                        ],
+                    ),
                     "input_file": f.as_posix(),
                     "includedir": [src_dir.as_posix()],
                     "output_module": "JSON",
diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py
index 6432788..fb7de0a 100755
--- a/src/tools/vppapigen/vppapigen_c.py
+++ b/src/tools/vppapigen/vppapigen_c.py
@@ -1574,6 +1574,7 @@
 #include "{module}.api.h"
 #undef vl_printfun
 
+#include "{module}.api_json.h"
 """
 
     write(hdr.format(module=module))
@@ -1586,6 +1587,7 @@
             '   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
             "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
         )
+        write(f"   vec_add1(am->json_api_repr, (u8 *)json_api_repr_{module});\n")
 
     for d in defines:
         write(
diff --git a/src/tools/vppapigen/vppapigen_json.py b/src/tools/vppapigen/vppapigen_json.py
index 0a1a3d3..7239d1e 100644
--- a/src/tools/vppapigen/vppapigen_json.py
+++ b/src/tools/vppapigen/vppapigen_json.py
@@ -1,5 +1,7 @@
 # JSON generation
 import json
+import sys
+import os
 
 process_imports = True
 
@@ -88,7 +90,26 @@
 #
 # Plugin entry point
 #
-def run(output_dir, filename, s):
+
+
+def contents_to_c_string(contents):
+    # Escape backslashes and double quotes
+    contents = contents.replace("\\", "\\\\").replace('"', '\\"')
+    # Replace newlines with \n
+    contents = contents.replace("\n", "\\n")
+    return '"' + contents + '"'
+
+
+def run(output_dir, apifilename, s):
+    if not output_dir:
+        sys.stderr.write("Missing --outputdir argument")
+        return None
+
+    basename = os.path.basename(apifilename)
+    filename_json_repr = os.path.join(output_dir + "/" + basename + "_json.h")
+    filename, _ = os.path.splitext(basename)
+    modulename = filename.replace(".", "_")
+
     j = {}
 
     j["types"] = walk_defs([o for o in s["types"] if o.__class__.__name__ == "Typedef"])
@@ -106,4 +127,9 @@
     j["vl_api_version"] = hex(s["file_crc"])
     j["imports"] = walk_imports(i for i in s["Import"])
     j["counters"], j["paths"] = walk_counters(s["Counters"], s["Paths"])
-    return json.dumps(j, indent=4, separators=(",", ": "))
+    r = json.dumps(j, indent=4, separators=(",", ": "))
+    c_string = contents_to_c_string(r)
+    with open(filename_json_repr, "w", encoding="UTF-8") as f:
+        print(f"const char *json_api_repr_{modulename} = {c_string};", file=f)
+    # return json.dumps(j, indent=4, separators=(",", ": "))
+    return r