Add enums.
authorRichard W.M. Jones <rjones@redhat.com>
Sun, 1 Jan 2012 20:01:15 +0000 (20:01 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Sun, 1 Jan 2012 20:53:04 +0000 (20:53 +0000)
16 files changed:
APIs/handle.api
examples/Makefile.am
examples/remote.c
generator-macros/pa_wrap.ml
generator/.depend
generator/Makefile.am
generator/wrappi_boilerplate.ml
generator/wrappi_boilerplate.mli
generator/wrappi_c.ml
generator/wrappi_c_impl.ml
generator/wrappi_enums.ml [new file with mode: 0644]
generator/wrappi_enums.mli [new file with mode: 0644]
generator/wrappi_main.ml
lib/.gitignore
lib/connect.c
lib/implementation_files.mk

index 3f22260..6803fe6 100644 (file)
@@ -18,6 +18,6 @@
 
 entry_point local void connect ()
 
-(* XXX Should scheme be an enum? *)
-entry_point local void set_scheme (string scheme)
+enum scheme ["local"; "ssh"] ;;
+entry_point local void set_scheme (enum scheme scheme)
 entry_point local void set_hostname (string hostname)
index 15b68bd..fa78ec7 100644 (file)
@@ -18,7 +18,9 @@
 noinst_PROGRAMS = remote
 
 remote_SOURCES = remote.c
-remote_CFLAGS = -I../lib
+remote_CFLAGS = \
+       -I../lib \
+       $(WARN_CFLAGS) $(WERROR_CFLAGS)
 remote_LDADD = ../lib/libwrappi.la
 
 CLEANFILES = *~
index 5e0d722..ae1d5b6 100644 (file)
@@ -31,7 +31,7 @@ main (int argc, char *argv[])
    * simply comment out the next two lines, then wrappi will run the
    * commands on the local machine.
    */
-  wrap_set_scheme (w, "ssh");
+  wrap_set_scheme (w, WRAP_SCHEME_SSH);
   wrap_set_hostname (w, hostname);
 
   /* Connect the handle.  Because we didn't set any error handler,
index 5ce6d08..a31497a 100644 (file)
@@ -86,6 +86,16 @@ let add_typedef _loc name t =
     Wrappi_accumulator.add_typedef td
   >>
 
+let add_enum _loc name identifiers =
+  let loc = expr_of_loc _loc _loc in
+
+  <:str_item<
+    let en = { Wrappi_types.en_loc = $loc$;
+               en_name = $str:name$;
+               en_identifiers = Array.of_list $identifiers$ } in
+    Wrappi_accumulator.add_enum en
+  >>
+
 let () =
   (* Quotation expander for C code. *)
   let c_quotation_expander _loc _ code =
@@ -112,7 +122,7 @@ EXTEND Gram
   ptype: [
     [ "bool" -> <:expr< Wrappi_types.TBool >> ]
   | [ "buffer" -> <:expr< Wrappi_types.TBuffer >> ]
-      (* enum XXX *)
+  | [ "enum"; name = LIDENT -> <:expr< Wrappi_types.TEnum $str:name$ >> ]
   | [ "file" -> <:expr< Wrappi_types.TFile >> ]
   | [ "hash"; "("; t = ptype; ")" -> <:expr< Wrappi_types.THash $t$ >> ]
   | [ "int" -> <:expr< Wrappi_types.TInt >> ]
@@ -124,7 +134,7 @@ EXTEND Gram
   | [ "struct"; name = LIDENT -> <:expr< Wrappi_types.TStruct $str:name$ >> ]
   | [ "uint32" -> <:expr< Wrappi_types.TUInt32 >> ]
   | [ "uint64" -> <:expr< Wrappi_types.TUInt64 >> ]
-      (* union XXX *)
+  | [ "union"; name = LIDENT -> <:expr< Wrappi_types.TUnion $str:name$ >> ]
   | [ name = LIDENT -> <:expr< Wrappi_types.TTypedef $str:name$ >> ]
   ];
 
@@ -148,6 +158,11 @@ EXTEND Gram
       ->
       add_entry_point _loc local name parameters rtype code includes
     ]
+
+  | [ "enum"; name = LIDENT; identifiers = expr ->
+      add_enum _loc name identifiers
+    ]
+
   | [ "typedef"; t = ptype; name = LIDENT ->
       add_typedef _loc name t
     ]
index 033feaf..2ab30d0 100644 (file)
@@ -7,8 +7,11 @@ wrappi_c.cmx: wrappi_pr.cmx wrappi_boilerplate.cmx wrappi_c.cmi
 wrappi_c_impl.cmi:
 wrappi_c_impl.cmo: wrappi_pr.cmi wrappi_boilerplate.cmi wrappi_c_impl.cmi
 wrappi_c_impl.cmx: wrappi_pr.cmx wrappi_boilerplate.cmx wrappi_c_impl.cmi
-wrappi_main.cmo: wrappi_pr.cmi wrappi_c_impl.cmi wrappi_c.cmi
-wrappi_main.cmx: wrappi_pr.cmx wrappi_c_impl.cmx wrappi_c.cmx
+wrappi_enums.cmi:
+wrappi_enums.cmo: wrappi_enums.cmi
+wrappi_enums.cmx: wrappi_enums.cmi
+wrappi_main.cmo: wrappi_pr.cmi wrappi_enums.cmi wrappi_c_impl.cmi wrappi_c.cmi
+wrappi_main.cmx: wrappi_pr.cmx wrappi_enums.cmx wrappi_c_impl.cmx wrappi_c.cmx
 wrappi_pr.cmi:
 wrappi_pr.cmo: wrappi_pr.cmi
 wrappi_pr.cmx: wrappi_pr.cmi
index 872d000..cecc7c2 100644 (file)
@@ -30,6 +30,8 @@ SOURCES = \
        wrappi_c_impl.ml \
        wrappi_c.mli \
        wrappi_c.ml \
+       wrappi_enums.mli \
+       wrappi_enums.ml \
        wrappi_main.ml \
        wrappi_pr.mli \
        wrappi_pr.ml
@@ -38,6 +40,7 @@ SOURCES = \
 OBJECTS = \
        wrappi_pr.cmo \
        wrappi_boilerplate.cmo \
+       wrappi_enums.cmo \
        wrappi_c_impl.cmo \
        wrappi_c.cmo \
        wrappi_main.cmo
index 5e39b31..1a6e18d 100644 (file)
@@ -31,8 +31,7 @@ type comment_style =
   | ErlangStyle
 type license = GPLv2plus | LGPLv2plus
 
-let generate_header comment license =
-  let inputs = [ "generator/wrappi_*.ml" ] in
+let generate_header inputs comment license =
   let c = match comment with
     | CStyle ->         pr "/* "; " *"
     | CPlusPlusStyle -> pr "// "; "//"
index c2fa59e..bc1f292 100644 (file)
@@ -21,4 +21,4 @@ type comment_style =
   | ErlangStyle
 type license = GPLv2plus | LGPLv2plus
 
-val generate_header : comment_style -> license -> unit
+val generate_header : string list -> comment_style -> license -> unit
index 7f9c1b4..80b832b 100644 (file)
@@ -23,10 +23,12 @@ open Wrappi_boilerplate
 
 open Printf
 
+let inputs = ["wrappi_c.ml"]
+
 let c_of_ptype ~param = function
   | TBool -> "int"
   | TBuffer -> assert false (* XXX not implemented *)
-  | TEnum name -> sprintf "enum wrap_%s" name
+  | TEnum name -> sprintf "wrap_%s_enum" name
   | TFile -> if param then "const char *" else "char *"
   | THash t -> if param then "char * const *" else "char **"
   | TInt -> "int" (* XXX not int, correct type depends on preconditions *)
@@ -69,7 +71,7 @@ let pr_extern_decl ep =
   pr ");\n"
 
 let generate_lib_wrappi_h api =
-  generate_header CStyle LGPLv2plus;
+  generate_header inputs CStyle LGPLv2plus;
 
   pr "\
 /* Please read the wrappi(1) man page for full documentation.  If you
@@ -89,6 +91,35 @@ extern \"C\" {
 /* The handle. */
 typedef struct wrap_h wrap_h;
 
+/* Types. */
+";
+
+  iter_enums api (
+    fun en ->
+      let name = en.en_name in
+
+      (* The C compiler may choose to declare the sizeof(enum) ==
+       * sizeof(char), and adding fields to such an enum later could
+       * cause ABI breakage.  (See the gcc --fshort-enums option for one
+       * example of this).  Therefore use the enum just to declare the
+       * values, and typedef the enum as an int.
+       *)
+      pr "enum {\n";
+
+      Array.iteri (
+        fun i id ->
+          pr "  WRAP_%s_%s = %d,\n"
+            (String.uppercase name) (String.uppercase id) i
+      ) en.en_identifiers;
+
+      pr "  WRAP_%s_ENUM_MAX = %d\n"
+        (String.uppercase name) (Array.length en.en_identifiers);
+      pr "};\n";
+      pr "typedef int wrap_%s_enum;\n" name;
+      pr "\n";
+  );
+
+  pr "\
 /* Connection management. */
 extern wrap_h *wrap_create (void);
 extern void wrap_close (wrap_h *w);
index c3e2ed7..2c3c143 100644 (file)
@@ -25,10 +25,12 @@ open Wrappi_pr
 
 open Printf
 
+let inputs = ["wrappi_c_impl.ml"]
+
 let c_of_ptype ~param = function
   | TBool -> "int"
   | TBuffer -> assert false (* XXX not implemented *)
-  | TEnum name -> sprintf "enum wrap_%s" name
+  | TEnum name -> sprintf "wrap_%s_enum" name
   | TFile -> if param then "const char *" else "char *"
   | THash t -> if param then "char * const *" else "char **"
   | TInt -> "int" (* XXX not int, correct type depends on preconditions *)
@@ -77,16 +79,18 @@ let pr_defn ?(impl = false) ep =
   pr ")\n"
 
 let generate_implementation ep =
-  generate_header CStyle LGPLv2plus;
+  generate_header inputs CStyle LGPLv2plus;
 
   pr "\
 /* Automatically generated implementation of '%s'.
- * This API was defined in '%s' at line %d.
- */
+" ep.ep_name;
 
-" ep.ep_name (Loc.file_name ep.ep_loc) (Loc.start_line ep.ep_loc);
+  if not (Loc.is_ghost ep.ep_loc) then
+    pr " * This API was defined in '%s' at line %d.\n"
+      (Loc.file_name ep.ep_loc) (Loc.start_line ep.ep_loc);
+
+  pr " */
 
-  pr "\
 #include <config.h>
 
 #include <stdio.h>
@@ -111,7 +115,8 @@ pr "\
   (match ep.ep_code with
   | None -> () (* XXX implicit code *)
   | Some { cc_loc = loc; cc_code = code } ->
-    pr "#line %d \"%s\"\n" (Loc.start_line loc) (Loc.file_name loc);
+    if not (Loc.is_ghost loc) then
+      pr "#line %d \"%s\"\n" (Loc.start_line loc) (Loc.file_name loc);
     pr "%s" code
   );
 
@@ -126,9 +131,9 @@ pr "\
     pr_defn ep;
 
     pr "{\n";
-    pr "  assert (w->scheme == NULL); /* XXX */;\n";
-    pr "\n";
-    pr "  ";
+    pr "  if (w->scheme == NULL) {\n";
+    pr "    /* Local connection. */\n";
+    pr "    ";
 
     let ret, req, opt = ep.ep_ftype in
 
@@ -149,10 +154,15 @@ pr "\
     pr ");\n";
 
     (match ret with
-    | RVoid -> pr "  return;\n"
+    | RVoid -> pr "    return;\n"
     | _ -> ()
     );
 
+    pr "  } else {\n";
+    pr "    /* Remote connection. */\n";
+    pr "    abort (); /* XXX */\n";
+    pr "  }\n";
+
     pr "}\n"
   )
 
@@ -167,7 +177,7 @@ let filename_of_ep ep =
   filename
 
 let generate_lib_implementation_files_mk api =
-  generate_header HashStyle GPLv2plus;
+  generate_header inputs HashStyle GPLv2plus;
 
   let eps = StringMap.bindings api.api_entry_points in
   let cmp (a, _) (b, _) = compare a b in
diff --git a/generator/wrappi_enums.ml b/generator/wrappi_enums.ml
new file mode 100644 (file)
index 0000000..3b33ec5
--- /dev/null
@@ -0,0 +1,92 @@
+(* wrappi
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Camlp4.PreCast
+
+open Wrappi_types
+open Wrappi_utils
+
+open Printf
+
+let extend_api api =
+  let eps = ref api.api_entry_points in
+
+  iter_enums api (
+    fun en ->
+      let name = en.en_name in
+
+      (* Add an entry point wrap_<name>_enum_to_string. *)
+      let fname = sprintf "%s_enum_to_string" name in
+      let code =
+        let b = Buffer.create 512 in
+        bprintf b "  switch (v) {\n";
+        Array.iter (
+          fun id ->
+            bprintf b "  case WRAP_%s_%s: return \"%s\";\n"
+              (String.uppercase name) (String.uppercase id) id;
+        ) en.en_identifiers;
+        bprintf b "  default:\n";
+        bprintf b "    set_error (\"unknown enum case %%d\", v);\n";
+        bprintf b "    return NULL;\n";
+        bprintf b "  }\n";
+        Buffer.contents b in
+      let c_code = {
+        cc_loc = Loc.ghost;
+        cc_code = code;
+      } in
+      let ep = {
+        ep_loc = Loc.ghost;
+        ep_local = true;
+        ep_name = fname;
+        ep_ftype = RStaticString, ["v", TEnum name, None], [];
+        ep_code = Some c_code;
+        ep_includes = []
+      } in
+
+      eps := StringMap.add fname ep !eps;
+
+      (* Add an entry point wrap_<name>_string_to_enum. *)
+      let fname = sprintf "%s_string_to_enum" name in
+      let code =
+        let b = Buffer.create 512 in
+        Array.iter (
+          fun id ->
+            bprintf b "  if (STREQ (v, \"%s\"))\n" id;
+            bprintf b "    return WRAP_%s_%s;\n"
+              (String.uppercase name) (String.uppercase id);
+        ) en.en_identifiers;
+        bprintf b "  set_error (\"unknown enum case %%s\", v);\n";
+        bprintf b "  return -1;\n";
+        Buffer.contents b in
+      let c_code = {
+        cc_loc = Loc.ghost;
+        cc_code = code;
+      } in
+      let ep = {
+        ep_loc = Loc.ghost;
+        ep_local = true;
+        ep_name = fname;
+        ep_ftype = Return (TEnum name), ["v", TString, None], [];
+        ep_code = Some c_code;
+        ep_includes = []
+      } in
+
+      eps := StringMap.add fname ep !eps
+  );
+
+  { api with api_entry_points = !eps }
diff --git a/generator/wrappi_enums.mli b/generator/wrappi_enums.mli
new file mode 100644 (file)
index 0000000..a5d36ec
--- /dev/null
@@ -0,0 +1,21 @@
+(* wrappi
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+(** Add some functions to the API for converting enums to/from strings. *)
+
+val extend_api : Wrappi_types.api -> Wrappi_types.api
index 8f92cb1..abe74ed 100644 (file)
@@ -30,6 +30,11 @@ let nr_sds = StringMap.cardinal api.api_structs
 let nr_uns = StringMap.cardinal api.api_unions
 let nr_eps = StringMap.cardinal api.api_entry_points
 
+(* Extend the API with some entry points which are purely
+ * generated from other things, eg. from enums.
+ *)
+let api = Wrappi_enums.extend_api api
+
 let dump_and_exit () =
   printf "typedefs (%d):\n" nr_tds;
   iter_typedefs api (fun td -> printf "  %s\n" (string_of_typedef td));
index 76d4e3e..2f8a6ba 100644 (file)
@@ -6,3 +6,5 @@
 /error-get_error_func.c
 /mkdir-mkdir.c
 /mknod-mknod_char.c
+/ghost-location-scheme_enum_to_string.c
+/ghost-location-scheme_string_to_enum.c
index a57fcfa..2062e08 100644 (file)
@@ -33,7 +33,7 @@ wrap_connect (wrap_h *w)
 }
 
 void
-wrap_set_scheme (wrap_h *w, const char *scheme)
+wrap_set_scheme (wrap_h *w, wrap_scheme_enum scheme)
 {
   /* XXX */
 }
index 04c4a17..8290343 100644 (file)
@@ -1,6 +1,6 @@
 # wrappi generated file
 # WARNING: THIS FILE IS GENERATED FROM:
-#   generator/wrappi_*.ml
+#   wrappi_c_impl.ml
 # ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.
 #
 # Copyright (C) 2011-2012 Red Hat Inc.
@@ -24,7 +24,9 @@ local_implementation_files := \
        error-error.c \
        error-get_errno.c \
        error-get_error.c \
-       error-get_error_func.c
+       error-get_error_func.c \
+       ghost-location-scheme_enum_to_string.c \
+       ghost-location-scheme_string_to_enum.c
 
 remote_implementation_files := \
        filesize-filesize.c \