[PATCH v2 0/7] Add symbol versioning (now working).
This rather more complex patch series adds symbol versioning (7/7 shows it in action). This works for me, tested by running old and new virt-inspector binaries against the new library. Rich.
From: "Richard W.M. Jones" <rjones@redhat.com> This is just code motion. --- .gitignore | 1 + generator/c.ml | 69 +++++++++++++++++++++++++++++++++---------------------- generator/main.ml | 1 + po/POTFILES | 1 + src/Makefile.am | 2 ++ 5 files changed, 47 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 5b91286..b3c9ef7 100644 --- a/.gitignore +++ b/.gitignore @@ -346,6 +346,7 @@ Makefile.in /src/errnostring-gperf.c /src/errnostring-gperf.gperf /src/errnostring.h +/src/free-structs.c /src/guestfs.3 /src/guestfs-actions.pod /src/guestfs-availability.pod diff --git a/generator/c.ml b/generator/c.ml index ac8fd5e..484b4f9 100644 --- a/generator/c.ml +++ b/generator/c.ml @@ -687,6 +687,48 @@ and generate_internal_actions_h () = c_name style ) non_daemon_functions +(* Functions to free structures. *) +and generate_client_free_structs () = + generate_header CStyle LGPLv2plus; + + pr "\ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include \"guestfs.h\" +#include \"guestfs-internal.h\" +#include \"guestfs_protocol.h\" + +"; + + pr "/* Structure-freeing functions. These rely on the fact that the\n"; + pr " * structure format is identical to the XDR format. See note in\n"; + pr " * generator.ml.\n"; + pr " */\n"; + pr "\n"; + + List.iter ( + fun (typ, _) -> + pr "void\n"; + pr "guestfs_free_%s (struct guestfs_%s *x)\n" typ typ; + pr "{\n"; + pr " xdr_free ((xdrproc_t) xdr_guestfs_int_%s, (char *) x);\n" typ; + pr " free (x);\n"; + pr "}\n"; + pr "\n"; + + pr "void\n"; + pr "guestfs_free_%s_list (struct guestfs_%s_list *x)\n" typ typ; + pr "{\n"; + pr " xdr_free ((xdrproc_t) xdr_guestfs_int_%s_list, (char *) x);\n" typ; + pr " free (x);\n"; + pr "}\n"; + pr "\n"; + + ) structs + (* Generate the client-side dispatch stubs. *) and generate_client_actions () = generate_header CStyle LGPLv2plus; @@ -1425,33 +1467,6 @@ trace_send_line (guestfs_h *g) fun f -> generate_daemon_stub f ) daemon_functions; - (* Functions to free structures. *) - pr "/* Structure-freeing functions. These rely on the fact that the\n"; - pr " * structure format is identical to the XDR format. See note in\n"; - pr " * generator.ml.\n"; - pr " */\n"; - pr "\n"; - - List.iter ( - fun (typ, _) -> - pr "void\n"; - pr "guestfs_free_%s (struct guestfs_%s *x)\n" typ typ; - pr "{\n"; - pr " xdr_free ((xdrproc_t) xdr_guestfs_int_%s, (char *) x);\n" typ; - pr " free (x);\n"; - pr "}\n"; - pr "\n"; - - pr "void\n"; - pr "guestfs_free_%s_list (struct guestfs_%s_list *x)\n" typ typ; - pr "{\n"; - pr " xdr_free ((xdrproc_t) xdr_guestfs_int_%s_list, (char *) x);\n" typ; - pr " free (x);\n"; - pr "}\n"; - pr "\n"; - - ) structs; - (* Functions which have optional arguments have two or three * generated variants. *) diff --git a/generator/main.ml b/generator/main.ml index 4cb3c7c..8d60850 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -84,6 +84,7 @@ Run it from the top source directory using the command output_to "src/guestfs.h" generate_guestfs_h; output_to "src/guestfs-internal-actions.h" generate_internal_actions_h; output_to "src/actions.c" generate_client_actions; + output_to "src/free-structs.c" generate_client_free_structs; output_to "src/bindtests.c" generate_bindtests; output_to "src/guestfs-structs.pod" generate_structs_pod; output_to "src/guestfs-actions.pod" generate_actions_pod; diff --git a/po/POTFILES b/po/POTFILES index a73377d..3f49a05 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -225,6 +225,7 @@ src/errnostring.c src/events.c src/file.c src/filearch.c +src/free-structs.c src/fuse.c src/guestfs.c src/info.c diff --git a/src/Makefile.am b/src/Makefile.am index 5f80bc1..082c122 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,6 +28,7 @@ generator_built = \ errnostring-gperf.gperf \ errnostring.c \ errnostring.h \ + free-structs.c \ guestfs-actions.pod \ guestfs-availability.pod \ guestfs-structs.pod \ @@ -130,6 +131,7 @@ libguestfs_la_SOURCES = \ events.c \ file.c \ filearch.c \ + free-structs.c \ fuse.c \ info.c \ inspect.c \ -- 1.7.11.4
From: "Richard W.M. Jones" <rjones@redhat.com> This is currently done implicitly because of the linker script. However in order to do symbol versioning, we will have to do this explicitly at each definition instead. --- generator/c.ml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/generator/c.ml b/generator/c.ml index 484b4f9..798a3a3 100644 --- a/generator/c.ml +++ b/generator/c.ml @@ -711,7 +711,7 @@ and generate_client_free_structs () = List.iter ( fun (typ, _) -> - pr "void\n"; + pr "GUESTFS_DLL_PUBLIC void\n"; pr "guestfs_free_%s (struct guestfs_%s *x)\n" typ typ; pr "{\n"; pr " xdr_free ((xdrproc_t) xdr_guestfs_int_%s, (char *) x);\n" typ; @@ -719,7 +719,7 @@ and generate_client_free_structs () = pr "}\n"; pr "\n"; - pr "void\n"; + pr "GUESTFS_DLL_PUBLIC void\n"; pr "guestfs_free_%s_list (struct guestfs_%s_list *x)\n" typ typ; pr "{\n"; pr " xdr_free ((xdrproc_t) xdr_guestfs_int_%s_list, (char *) x);\n" typ; @@ -1115,10 +1115,12 @@ trace_send_line (guestfs_h *g) if optargs = [] then generate_prototype ~extern:false ~semicolon:false ~newline:true ~handle:"g" ~prefix:"guestfs_" + ~dll_public:true c_name style else generate_prototype ~extern:false ~semicolon:false ~newline:true ~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv" ~optarg_proto:Argv + ~dll_public:true c_name style; pr "{\n"; @@ -1191,11 +1193,15 @@ trace_send_line (guestfs_h *g) (* Generate the action stub. *) if optargs = [] then generate_prototype ~extern:false ~semicolon:false ~newline:true - ~handle:"g" ~prefix:"guestfs_" c_name style + ~handle:"g" ~prefix:"guestfs_" + ~dll_public:true + c_name style else generate_prototype ~extern:false ~semicolon:false ~newline:true ~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv" - ~optarg_proto:Argv c_name style; + ~optarg_proto:Argv + ~dll_public:true + c_name style; pr "{\n"; -- 1.7.11.4
From: "Richard W.M. Jones" <rjones@redhat.com> --- generator/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/Makefile.am b/generator/Makefile.am index e218ffa..276c2cf 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -57,7 +57,7 @@ OBJECTS = $(SOURCES_ML:.ml=.cmo) EXTRA_DIST = $(SOURCES) files-generated.txt -OCAMLCFLAGS = -warn-error CDEFLMPSUVYZX +OCAMLCFLAGS = -warn-error CDEFLMPSUVYZ+14+15+16+17+18+19+20+21+22+24+25+30-23 OCAMLCLIBS = unix.cma str.cma noinst_PROGRAM = generator -- 1.7.11.4
From: "Richard W.M. Jones" <rjones@redhat.com> This just makes it simpler to add extra fields to each struct. This is code motion, although the output of the generator changes very slightly: the camel-cased structs are no longer stored sorted, so the generated lists in java/Makefile.in and java/com/redhat/et/libguestfs/.gitignore change order. --- generator/c.ml | 8 +- generator/csharp.ml | 2 +- generator/erlang.ml | 2 +- generator/fish.ml | 2 +- generator/gobject.ml | 2 +- generator/java.ml | 6 +- generator/main.ml | 6 +- generator/ocaml.ml | 4 +- generator/python.ml | 2 +- generator/structs.ml | 165 ++++++++++++++++++------------- generator/structs.mli | 18 ++-- generator/xdr.ml | 3 +- java/Makefile.inc | 24 ++--- java/com/redhat/et/libguestfs/.gitignore | 24 ++--- 14 files changed, 153 insertions(+), 115 deletions(-) diff --git a/generator/c.ml b/generator/c.ml index 798a3a3..54f35b5 100644 --- a/generator/c.ml +++ b/generator/c.ml @@ -311,7 +311,7 @@ and generate_actions_pod_back_compat_entry { name = name; and generate_structs_pod () = (* Structs documentation. *) List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> pr "=head2 guestfs_%s\n" typ; pr "\n"; pr " struct guestfs_%s {\n" typ; @@ -542,7 +542,7 @@ extern GUESTFS_DLL_PUBLIC void *guestfs_next_private (guestfs_h *g, const char * (* Public structures. *) List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> pr "struct guestfs_%s {\n" typ; List.iter ( function @@ -710,7 +710,7 @@ and generate_client_free_structs () = pr "\n"; List.iter ( - fun (typ, _) -> + fun { s_name = typ } -> pr "GUESTFS_DLL_PUBLIC void\n"; pr "guestfs_free_%s (struct guestfs_%s *x)\n" typ typ; pr "{\n"; @@ -1661,7 +1661,7 @@ and generate_linker_script () = ) in let structs = List.concat ( - List.map (fun (typ, _) -> + List.map (fun { s_name = typ } -> ["guestfs_free_" ^ typ; "guestfs_free_" ^ typ ^ "_list"]) structs ) in diff --git a/generator/csharp.ml b/generator/csharp.ml index 9f86f48..803a4a5 100644 --- a/generator/csharp.ml +++ b/generator/csharp.ml @@ -110,7 +110,7 @@ namespace Guestfs * method names (eg. "class stat" and "stat"). *) List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> pr " [StructLayout (LayoutKind.Sequential)]\n"; pr " public class _%s {\n" typ; List.iter ( diff --git a/generator/erlang.ml b/generator/erlang.ml index 673394e..6c7eb70 100644 --- a/generator/erlang.ml +++ b/generator/erlang.ml @@ -237,7 +237,7 @@ extern void free_strings (char **r); in List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> pr "static ETERM *\n"; pr "make_%s (const struct guestfs_%s *%s)\n" typ typ typ; pr "{\n"; diff --git a/generator/fish.ml b/generator/fish.ml index 76377a2..59c1114 100644 --- a/generator/fish.ml +++ b/generator/fish.ml @@ -227,7 +227,7 @@ Guestfish will prompt for these separately." (* print_* functions *) List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> let needs_i = List.exists (function (_, (FUUID|FBuffer)) -> true | _ -> false) cols in diff --git a/generator/gobject.ml b/generator/gobject.ml index 2cd1809..6c35e02 100644 --- a/generator/gobject.ml +++ b/generator/gobject.ml @@ -107,7 +107,7 @@ let filenames = "session" :: "tristate" :: (* structs *) - List.map (function typ, cols -> "struct-" ^ typ) structs @ + List.map (fun { s_name = typ } -> "struct-" ^ typ) structs @ (* optargs *) List.map (function { name = name } -> "optargs-" ^ name) ( diff --git a/generator/java.ml b/generator/java.ml index 1e06c62..bffe077 100644 --- a/generator/java.ml +++ b/generator/java.ml @@ -875,10 +875,10 @@ and generate_java_makefile_inc () = pr "java_built_sources = \\\n"; List.iter ( - fun (typ, jtyp) -> + fun { s_name = typ; s_camel_name = jtyp } -> pr "\tcom/redhat/et/libguestfs/%s.java \\\n" jtyp; - ) camel_structs; + ) structs; pr "\tcom/redhat/et/libguestfs/GuestFS.java\n" and generate_java_gitignore () = - List.iter (fun (_, jtyp) -> pr "%s.java\n" jtyp) camel_structs + List.iter (fun { s_camel_name = jtyp } -> pr "%s.java\n" jtyp) structs diff --git a/generator/main.ml b/generator/main.ml index 8d60850..98f4b31 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -124,11 +124,11 @@ Run it from the top source directory using the command output_to "java/com/redhat/et/libguestfs/GuestFS.java" generate_java_java; List.iter ( - fun (typ, jtyp) -> + fun { s_name = typ; s_camel_name = jtyp } -> let cols = cols_of_struct typ in let filename = sprintf "java/com/redhat/et/libguestfs/%s.java" jtyp in output_to filename (generate_java_struct jtyp cols) - ) camel_structs; + ) structs; delete_except_generated ~skip:["java/com/redhat/et/libguestfs/LibGuestFSException.java"] "java/com/redhat/et/libguestfs/*.java"; @@ -152,7 +152,7 @@ Run it from the top source directory using the command output_to "gobject/docs/guestfs-title.sgml" generate_gobject_doc_title; List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> let short = sprintf "struct-%s" typ in let filename = sprintf "gobject/include/guestfs-gobject/%s.h" short in diff --git a/generator/ocaml.ml b/generator/ocaml.ml index b90aeb8..f9ac5ca 100644 --- a/generator/ocaml.ml +++ b/generator/ocaml.ml @@ -377,7 +377,7 @@ copy_table (char * const * argv) in List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> let has_optpercent_col = List.exists (function (_, FOptPercent) -> true | _ -> false) cols in @@ -672,7 +672,7 @@ copy_table (char * const * argv) and generate_ocaml_structure_decls () = List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> pr "type %s = {\n" typ; List.iter ( function diff --git a/generator/python.ml b/generator/python.ml index 3975055..0e754db 100644 --- a/generator/python.ml +++ b/generator/python.ml @@ -160,7 +160,7 @@ free_strings (char **argv) (* Structures, turned into Python dictionaries. *) List.iter ( - fun (typ, cols) -> + fun { s_name = typ; s_cols = cols } -> pr "static PyObject *\n"; pr "put_%s (struct guestfs_%s *%s)\n" typ typ typ; pr "{\n"; diff --git a/generator/structs.ml b/generator/structs.ml index d62fcc5..4c8e2ef 100644 --- a/generator/structs.ml +++ b/generator/structs.ml @@ -22,6 +22,11 @@ open Types open Utils type cols = (string * field) list +type struc = { + s_name : string; + s_cols : cols; + s_camel_name : string; +} (* Because we generate extra parsing code for LVM command line tools, * we have to pull out the LVM columns separately here. @@ -88,6 +93,8 @@ let lvm_lv_cols = [ "modules", FString; ] +let defaults = { s_name = ""; s_cols = []; s_camel_name = "" } + (* Names and fields in all structures (in RStruct and RStructList) * that we support. *) @@ -95,21 +102,29 @@ let structs = [ (* The old RIntBool return type, only ever used for aug_defnode. Do * not use this struct in any new code. *) - "int_bool", [ + { defaults with + s_name = "int_bool"; + s_cols = [ "i", FInt32; (* for historical compatibility *) "b", FInt32; (* for historical compatibility *) - ]; + ]; + s_camel_name = "IntBool" }; (* LVM PVs, VGs, LVs. *) - "lvm_pv", lvm_pv_cols; - "lvm_vg", lvm_vg_cols; - "lvm_lv", lvm_lv_cols; + { defaults with + s_name = "lvm_pv"; s_cols = lvm_pv_cols; s_camel_name = "PV" }; + { defaults with + s_name = "lvm_vg"; s_cols = lvm_vg_cols; s_camel_name = "VG" }; + { defaults with + s_name = "lvm_lv"; s_cols = lvm_lv_cols; s_camel_name = "LV" }; (* Column names and types from stat structures. * NB. Can't use things like 'st_atime' because glibc header files * define some of these as macros. Ugh. *) - "stat", [ + { defaults with + s_name = "stat"; + s_cols = [ "dev", FInt64; "ino", FInt64; "mode", FInt64; @@ -123,8 +138,11 @@ let structs = [ "atime", FInt64; "mtime", FInt64; "ctime", FInt64; - ]; - "statvfs", [ + ]; + s_camel_name = "Stat" }; + { defaults with + s_name = "statvfs"; + s_cols = [ "bsize", FInt64; "frsize", FInt64; "blocks", FInt64; @@ -136,48 +154,66 @@ let structs = [ "fsid", FInt64; "flag", FInt64; "namemax", FInt64; - ]; + ]; + s_camel_name = "StatVFS" }; (* Column names in dirent structure. *) - "dirent", [ + { defaults with + s_name = "dirent"; + s_cols = [ "ino", FInt64; (* 'b' 'c' 'd' 'f' (FIFO) 'l' 'r' (regular file) 's' 'u' '?' *) "ftyp", FChar; "name", FString; - ]; + ]; + s_camel_name = "Dirent" }; (* Version numbers. *) - "version", [ + { defaults with + s_name = "version"; + s_cols = [ "major", FInt64; "minor", FInt64; "release", FInt64; "extra", FString; - ]; + ]; + s_camel_name = "Version" }; (* Extended attribute. *) - "xattr", [ + { defaults with + s_name = "xattr"; + s_cols = [ "attrname", FString; "attrval", FBuffer; - ]; + ]; + s_camel_name = "XAttr" }; (* Inotify events. *) - "inotify_event", [ + { defaults with + s_name = "inotify_event"; + s_cols = [ "in_wd", FInt64; "in_mask", FUInt32; "in_cookie", FUInt32; "in_name", FString; - ]; + ]; + s_camel_name = "INotifyEvent" }; (* Partition table entry. *) - "partition", [ + { defaults with + s_name = "partition"; + s_cols = [ "part_num", FInt32; "part_start", FBytes; "part_end", FBytes; "part_size", FBytes; - ]; + ]; + s_camel_name = "Partition" }; (* Application. *) - "application", [ + { defaults with + s_name = "application"; + s_cols = [ "app_name", FString; "app_display_name", FString; "app_epoch", FInt32; @@ -190,10 +226,13 @@ let structs = [ "app_source_package", FString; "app_summary", FString; "app_description", FString; - ]; + ]; + s_camel_name = "Application" }; (* ISO primary volume descriptor. *) - "isoinfo", [ + { defaults with + s_name = "isoinfo"; + s_cols = [ "iso_system_id", FString; "iso_volume_id", FString; "iso_volume_space_size", FUInt32; @@ -211,24 +250,33 @@ let structs = [ "iso_volume_modification_t", FInt64; "iso_volume_expiration_t", FInt64; "iso_volume_effective_t", FInt64; - ]; + ]; + s_camel_name = "ISOInfo" }; (* /proc/mdstat information. See linux.git/drivers/md/md.c *) - "mdstat", [ + { defaults with + s_name = "mdstat"; + s_cols = [ "mdstat_device", FString; "mdstat_index", FInt32; "mdstat_flags", FString; - ]; + ]; + s_camel_name = "MDStat" }; (* btrfs subvolume list output *) - "btrfssubvolume", [ + { defaults with + s_name = "btrfssubvolume"; + s_cols = [ "btrfssubvolume_id", FUInt64; "btrfssubvolume_top_level_id", FUInt64; "btrfssubvolume_path", FString; - ]; + ]; + s_camel_name = "BTRFSSubvolume" }; (* XFS info descriptor. *) - "xfsinfo", [ + { defaults with + s_name = "xfsinfo"; + s_cols = [ "xfs_mntpoint", FString; "xfs_inodesize", FUInt32; "xfs_agcount", FUInt32; @@ -254,60 +302,45 @@ let structs = [ "xfs_rtextsize", FUInt32; "xfs_rtblocks", FUInt64; "xfs_rtextents", FUInt64; - ]; + ]; + s_camel_name = "XFSInfo" }; (* utsname *) - "utsname", [ + { defaults with + s_name = "utsname"; + s_cols = [ "uts_sysname", FString; "uts_release", FString; "uts_version", FString; "uts_machine", FString; - ]; + ]; + s_camel_name = "UTSName" }; (* Used by hivex_* APIs to return a list of int64 handles (node * handles and value handles). Note that we can't add a putative * 'RInt64List' type to the generator because we need to return * length and size, and RStructList does this already. *) - "hivex_node", [ + { defaults with + s_name = "hivex_node"; + s_cols = [ "hivex_node_h", FInt64; - ]; - "hivex_value", [ + ]; + s_camel_name = "HivexNode" }; + { defaults with + s_name = "hivex_value"; + s_cols = [ "hivex_value_h", FInt64; - ]; + ]; + s_camel_name = "HivexValue" }; ] (* end of structs *) -(* For bindings which want camel case *) -let camel_structs = [ - "int_bool", "IntBool"; - "lvm_pv", "PV"; - "lvm_vg", "VG"; - "lvm_lv", "LV"; - "stat", "Stat"; - "statvfs", "StatVFS"; - "dirent", "Dirent"; - "version", "Version"; - "xattr", "XAttr"; - "inotify_event", "INotifyEvent"; - "partition", "Partition"; - "application", "Application"; - "isoinfo", "ISOInfo"; - "xfsinfo", "XFSInfo"; - "mdstat", "MDStat"; - "btrfssubvolume", "BTRFSSubvolume"; - "utsname", "UTSName"; - "hivex_node", "HivexNode"; - "hivex_value", "HivexValue"; -] -let camel_structs = List.sort (fun (_,a) (_,b) -> compare a b) camel_structs - -let camel_name_of_struct typ = - try List.assoc typ camel_structs +let lookup_struct name = + try List.find (fun { s_name = n } -> n = name) structs with Not_found -> failwithf - "camel_name_of_struct: no camel_structs entry corresponding to %s" typ + "lookup_struct: no structs entry corresponding to %s" name -let cols_of_struct typ = - try List.assoc typ structs - with Not_found -> - failwithf "cols_of_struct: unknown struct %s" typ +let camel_name_of_struct name = (lookup_struct name).s_camel_name + +let cols_of_struct name = (lookup_struct name).s_cols diff --git a/generator/structs.mli b/generator/structs.mli index 56987e0..05df115 100644 --- a/generator/structs.mli +++ b/generator/structs.mli @@ -23,11 +23,14 @@ type cols = (string * Types.field) list (** List of structure fields (called "columns"). *) -val structs : (string * cols) list -(** List of structures. *) +type struc = { + s_name : string; (** Regular name. *) + s_cols : cols; (** Columns. *) + s_camel_name : string; (** Camel-cased name. *) +} -val camel_structs : (string * string) list -(** For bindings which want camel case struct names *) +val structs : struc list +(** List of structures. *) val lvm_pv_cols : cols val lvm_vg_cols : cols @@ -36,8 +39,11 @@ val lvm_lv_cols : cols used to generate code for parsing the output of commands like [lvs]. One day replace this with liblvm API calls. *) +val lookup_struct : string -> struc +(** Lookup a struct by name. *) + val camel_name_of_struct : string -> string -(** Camel case name of struct. *) +(** Lookup struct by name, return the s_camel_name field. *) val cols_of_struct : string -> cols -(** Extract columns of a struct. *) +(** Lookup struct by name, return the s_cols field. *) diff --git a/generator/xdr.ml b/generator/xdr.ml index b45f2c4..46b1c90 100644 --- a/generator/xdr.ml +++ b/generator/xdr.ml @@ -70,8 +70,7 @@ let generate_xdr () = pr "/* Internal structures. */\n"; pr "\n"; List.iter ( - function - | typ, cols -> + fun { s_name = typ; s_cols = cols } -> pr "struct guestfs_int_%s {\n" typ; List.iter (function | name, FChar -> pr " char %s;\n" name diff --git a/java/Makefile.inc b/java/Makefile.inc index 64b533a..aa9f96c 100644 --- a/java/Makefile.inc +++ b/java/Makefile.inc @@ -20,23 +20,23 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. java_built_sources = \ - com/redhat/et/libguestfs/Application.java \ - com/redhat/et/libguestfs/BTRFSSubvolume.java \ - com/redhat/et/libguestfs/Dirent.java \ - com/redhat/et/libguestfs/HivexNode.java \ - com/redhat/et/libguestfs/HivexValue.java \ - com/redhat/et/libguestfs/INotifyEvent.java \ - com/redhat/et/libguestfs/ISOInfo.java \ com/redhat/et/libguestfs/IntBool.java \ - com/redhat/et/libguestfs/LV.java \ - com/redhat/et/libguestfs/MDStat.java \ com/redhat/et/libguestfs/PV.java \ - com/redhat/et/libguestfs/Partition.java \ + com/redhat/et/libguestfs/VG.java \ + com/redhat/et/libguestfs/LV.java \ com/redhat/et/libguestfs/Stat.java \ com/redhat/et/libguestfs/StatVFS.java \ - com/redhat/et/libguestfs/UTSName.java \ - com/redhat/et/libguestfs/VG.java \ + com/redhat/et/libguestfs/Dirent.java \ com/redhat/et/libguestfs/Version.java \ com/redhat/et/libguestfs/XAttr.java \ + com/redhat/et/libguestfs/INotifyEvent.java \ + com/redhat/et/libguestfs/Partition.java \ + com/redhat/et/libguestfs/Application.java \ + com/redhat/et/libguestfs/ISOInfo.java \ + com/redhat/et/libguestfs/MDStat.java \ + com/redhat/et/libguestfs/BTRFSSubvolume.java \ com/redhat/et/libguestfs/XFSInfo.java \ + com/redhat/et/libguestfs/UTSName.java \ + com/redhat/et/libguestfs/HivexNode.java \ + com/redhat/et/libguestfs/HivexValue.java \ com/redhat/et/libguestfs/GuestFS.java diff --git a/java/com/redhat/et/libguestfs/.gitignore b/java/com/redhat/et/libguestfs/.gitignore index 72a1144..eb332a8 100644 --- a/java/com/redhat/et/libguestfs/.gitignore +++ b/java/com/redhat/et/libguestfs/.gitignore @@ -1,19 +1,19 @@ -Application.java -BTRFSSubvolume.java -Dirent.java -HivexNode.java -HivexValue.java -INotifyEvent.java -ISOInfo.java IntBool.java -LV.java -MDStat.java PV.java -Partition.java +VG.java +LV.java Stat.java StatVFS.java -UTSName.java -VG.java +Dirent.java Version.java XAttr.java +INotifyEvent.java +Partition.java +Application.java +ISOInfo.java +MDStat.java +BTRFSSubvolume.java XFSInfo.java +UTSName.java +HivexNode.java +HivexValue.java -- 1.7.11.4
From: "Richard W.M. Jones" <rjones@redhat.com> --- generator/actions.ml | 1 + generator/c.ml | 132 +++++++++++++++++++++++++++++++++++++++++++++----- generator/checks.ml | 10 ++++ generator/structs.ml | 4 +- generator/structs.mli | 2 + generator/types.ml | 2 + po/POTFILES | 1 + src/Makefile.am | 3 ++ src/compat.c | 26 ++++++++++ src/guestfs.pod | 26 ++++++++++ 10 files changed, 195 insertions(+), 12 deletions(-) create mode 100644 src/compat.c diff --git a/generator/actions.ml b/generator/actions.ml index 71aee37..e1db3db 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -32,6 +32,7 @@ let defaults = { name = ""; style = RErr, [], []; proc_nr = None; progress = false; camel_name = ""; cancellable = false; config_only = false; once_had_no_optargs = false; blocking = true; + symbol_version = None; c_name = ""; c_function = ""; c_optarg_prefix = ""; non_c_aliases = [] } diff --git a/generator/c.ml b/generator/c.ml index 54f35b5..5e75be1 100644 --- a/generator/c.ml +++ b/generator/c.ml @@ -710,7 +710,8 @@ and generate_client_free_structs () = pr "\n"; List.iter ( - fun { s_name = typ } -> + function + | { s_name = typ; s_symbol_version = None } -> pr "GUESTFS_DLL_PUBLIC void\n"; pr "guestfs_free_%s (struct guestfs_%s *x)\n" typ typ; pr "{\n"; @@ -727,7 +728,38 @@ and generate_client_free_structs () = pr "}\n"; pr "\n"; - ) structs + | { s_name = typ; s_symbol_version = Some v } -> + let suffix = "__" ^ replace_char v '.' '_' in + + pr "extern GUESTFS_DLL_PUBLIC void guestfs_free_%s%s (struct guestfs_%s *x);\n" + typ suffix typ; + pr "\n"; + pr "GUESTFS_DLL_PUBLIC void\n"; + pr "guestfs_free_%s%s (struct guestfs_%s *x)\n" typ suffix typ; + pr "{\n"; + pr " xdr_free ((xdrproc_t) xdr_guestfs_int_%s, (char *) x);\n" typ; + pr " free (x);\n"; + pr "}\n"; + pr "\n"; + + pr "extern GUESTFS_DLL_PUBLIC void guestfs_free_%s_list%s (struct guestfs_%s_list *x);\n" + typ suffix typ; + pr "\n"; + pr "GUESTFS_DLL_PUBLIC void\n"; + pr "guestfs_free_%s_list%s (struct guestfs_%s_list *x)\n" typ suffix typ; + pr "{\n"; + pr " xdr_free ((xdrproc_t) xdr_guestfs_int_%s_list, (char *) x);\n" typ; + pr " free (x);\n"; + pr "}\n"; + pr "\n"; + + ) structs; + + pr "/* Include backwards compatibility code for old ABIs. */\n"; + pr "#define COMPAT_STRUCTS 1\n"; + pr "#include \"compat.c\"\n"; + pr "\n"; + pr "/* EOF */\n" (* Generate the client-side dispatch stubs. *) and generate_client_actions () = @@ -1108,18 +1140,53 @@ trace_send_line (guestfs_h *g) ) in + (* GCC symver won't let us just use the external name of the + * function if the function has multiple versions. Instead we + * have to give the function a unique, non-exported name + * ('guestfs_...__<VERSION>'). + *) + let suffix_for_symbol_versions c_name style = function + | None -> "" + | Some v -> + let suffix = "__" ^ replace_char v '.' '_' in + + pr "/* This symbol has multiple versions, resolved by the linker\n"; + pr " * at run time. This is the latest version used for new code.\n"; + pr " * See src/compat.c for old compatibility versions.\n"; + pr " */\n"; + let _, _, optargs = style in + if optargs = [] then + generate_prototype ~extern:true ~single_line:true ~newline:true + ~handle:"g" ~prefix:"guestfs_" ~suffix + ~dll_public:true + c_name style + else + generate_prototype ~extern:true ~single_line:true ~newline:true + ~handle:"g" ~prefix:"guestfs_" ~suffix:("_argv" ^ suffix) + ~optarg_proto:Argv + ~dll_public:true + c_name style; + pr "\n"; + + suffix + in + (* For non-daemon functions, generate a wrapper around each function. *) let generate_non_daemon_wrapper { name = name; c_name = c_name; style = ret, _, optargs as style; - config_only = config_only } = + config_only = config_only; + symbol_version = symbol_version } = + let suffix = suffix_for_symbol_versions c_name style symbol_version in + if optargs = [] then generate_prototype ~extern:false ~semicolon:false ~newline:true - ~handle:"g" ~prefix:"guestfs_" + ~handle:"g" ~prefix:"guestfs_" ~suffix ~dll_public:true c_name style else generate_prototype ~extern:false ~semicolon:false ~newline:true - ~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv" ~optarg_proto:Argv + ~handle:"g" ~prefix:"guestfs_" ~suffix:("_argv" ^ suffix) + ~optarg_proto:Argv ~dll_public:true c_name style; pr "{\n"; @@ -1184,21 +1251,24 @@ trace_send_line (guestfs_h *g) (* Client-side stubs for each function. *) let generate_daemon_stub { name = name; c_name = c_name; - style = ret, args, optargs as style } = + style = ret, args, optargs as style; + symbol_version = symbol_version } = let errcode = match errcode_of_ret ret with | `CannotReturnError -> assert false | (`ErrorIsMinusOne | `ErrorIsNULL) as e -> e in + let suffix = suffix_for_symbol_versions c_name style symbol_version in + (* Generate the action stub. *) if optargs = [] then generate_prototype ~extern:false ~semicolon:false ~newline:true - ~handle:"g" ~prefix:"guestfs_" + ~handle:"g" ~prefix:"guestfs_" ~suffix ~dll_public:true c_name style else generate_prototype ~extern:false ~semicolon:false ~newline:true - ~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv" + ~handle:"g" ~prefix:"guestfs_" ~suffix:("_argv" ^ suffix) ~optarg_proto:Argv ~dll_public:true c_name style; @@ -1599,7 +1669,13 @@ trace_send_line (guestfs_h *g) | ({ style = _, _, (_::_); once_had_no_optargs = true } as f) -> generate_va_variants f; generate_back_compat_wrapper f - ) all_functions_sorted + ) all_functions_sorted; + + pr "/* Include backwards compatibility code for old ABIs. */\n"; + pr "#define COMPAT_ACTIONS 1\n"; + pr "#include \"compat.c\"\n"; + pr "\n"; + pr "/* EOF */\n" (* Generate the linker script which controls the visibility of * symbols in the public ABI and ensures no other symbols get @@ -1667,14 +1743,48 @@ and generate_linker_script () = ) in let globals = List.sort compare (globals @ functions @ structs) in - pr "{\n"; + pr "GUESTFS_0.0 {\n"; pr " global:\n"; List.iter (pr " %s;\n") globals; pr "\n"; pr " local:\n"; pr " *;\n"; - pr "};\n" + pr "};\n"; + + (* Explicitly versioned symbols. *) + let h = Hashtbl.create 13 in + List.iter ( + function + | { symbol_version = None } -> () + | { name = name; symbol_version = Some ver } -> + let names = try Hashtbl.find h ver with Not_found -> [] in + Hashtbl.replace h ver (name :: names) + ) all_functions; + List.iter ( + function + | { s_symbol_version = None } -> () + | { s_name = name; s_symbol_version = Some ver } -> + let names = try Hashtbl.find h ver with Not_found -> [] in + Hashtbl.replace h ver + (sprintf "free_%s" name :: sprintf "free_%s_list" name :: names) + ) Structs.structs; + let vers = Hashtbl.fold (fun k _ ks -> k :: ks) h [] in + let vers = List.sort compare vers in + let prev = ref "GUESTFS_0.0" in + List.iter ( + fun ver -> + let names = Hashtbl.find h ver in + let names = List.sort compare names in + + pr "\n"; + pr "%s {\n" ver; + pr " global:\n"; + List.iter (pr " guestfs_%s;\n") names; + pr "} %s;\n" !prev; + + prev := ver + ) vers and generate_max_proc_nr () = pr "%d\n" max_proc_nr diff --git a/generator/checks.ml b/generator/checks.ml index 2cccf26..f813fbf 100644 --- a/generator/checks.ml +++ b/generator/checks.ml @@ -243,6 +243,16 @@ let () = | { blocking = true } -> () ) daemon_functions; + (* Check symbol version string is sane. *) + List.iter ( + function + | { name = name; symbol_version = Some ver } -> + let len = String.length ver in + if len < 12 || String.sub ver 0 10 <> "GUESTFS_1." then + failwithf "%s: invalid symbol_version (%s)" name ver + | { symbol_version = None } -> () + ) all_functions; + (* Non-fish functions must have correct camel_name. *) List.iter ( fun { name = name; camel_name = camel_name } -> diff --git a/generator/structs.ml b/generator/structs.ml index 4c8e2ef..499f825 100644 --- a/generator/structs.ml +++ b/generator/structs.ml @@ -26,6 +26,7 @@ type struc = { s_name : string; s_cols : cols; s_camel_name : string; + s_symbol_version : string option; } (* Because we generate extra parsing code for LVM command line tools, @@ -93,7 +94,8 @@ let lvm_lv_cols = [ "modules", FString; ] -let defaults = { s_name = ""; s_cols = []; s_camel_name = "" } +let defaults = { s_name = ""; s_cols = []; s_camel_name = ""; + s_symbol_version = None } (* Names and fields in all structures (in RStruct and RStructList) * that we support. diff --git a/generator/structs.mli b/generator/structs.mli index 05df115..4e1044d 100644 --- a/generator/structs.mli +++ b/generator/structs.mli @@ -27,6 +27,8 @@ type struc = { s_name : string; (** Regular name. *) s_cols : cols; (** Columns. *) s_camel_name : string; (** Camel-cased name. *) + s_symbol_version : string option; (** C symbol version. + See guestfs(3)/SYMBOL VERSIONING *) } val structs : struc list diff --git a/generator/types.ml b/generator/types.ml index cff3c6d..df019e8 100644 --- a/generator/types.ml +++ b/generator/types.ml @@ -399,6 +399,8 @@ type action = { set flags in the handle are marked non-blocking so that we don't add machinery in various bindings. *) + symbol_version : string option; (* C symbol version. + See guestfs(3)/SYMBOL VERSIONING *) (* "Internal" data attached by the generator at various stages. This * doesn't need to (and shouldn't) be set when defining actions. diff --git a/po/POTFILES b/po/POTFILES index 3f49a05..8cc71fe 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -219,6 +219,7 @@ src/actions.c src/appliance.c src/bindtests.c src/command.c +src/compat.c src/dbdump.c src/errnostring-gperf.c src/errnostring.c diff --git a/src/Makefile.am b/src/Makefile.am index 082c122..c567fe2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,6 +46,7 @@ EXTRA_DIST = \ libguestfs.3 \ libguestfs.pc libguestfs.pc.in \ guestfs.pod \ + compat.c \ api-support/added \ api-support/README \ api-support/update-from-tarballs.sh @@ -152,6 +153,8 @@ libguestfs_la_SOURCES = \ proto.c \ libguestfs.syms +actions.c: compat.c + libguestfs_la_LIBADD = \ $(PCRE_LIBS) $(MAGIC_LIBS) \ $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ diff --git a/src/compat.c b/src/compat.c new file mode 100644 index 0000000..27ffd05 --- /dev/null +++ b/src/compat.c @@ -0,0 +1,26 @@ +/* libguestfs + * Copyright (C) 2012 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This is used for symbol versioning. See guestfs(3)/SYMBOL VERSIONING. + * + * XXX Symbol versioning only seems to work if all the related + * functions are compiled into a single file, so this file is + * #included directly into src/actions.c and src/free-structs.c, + * instead of being compiled as a separate unit. The cpp symbols + * COMPAT_ACTIONS or COMPAT_STRUCTS are defined as appropriate. + */ diff --git a/src/guestfs.pod b/src/guestfs.pod index 60664e7..04d8b6b 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -3499,6 +3499,32 @@ into the appliance. Debugging messages are never translated, since they are intended for the programmers. +=head2 SYMBOL VERSIONING + +The generator supports symbol versioning. This is used +B<as a last resort only> when we need to modify an API and we +cannot possibly make the change ABI compatible. Using symbol +versioning allows us to get older applications to transparently use a +compatibility function (preserving ABI) while newly compiled +applications get the new API. + +First, familiarize yourself with symbol versioning by reading the +relevant sections of the GNU ld documentation and this document by +Ulrich Drepper: L<http://www.akkadia.org/drepper/dsohowto.pdf> + +The modified API should have a C<symbol_version> added. This has the +form C<GUESTFS_1.X> where C<1.X> is the first stable version of +libguestfs where the new, incompatible API will appear. + +Next edit C<src/compat.c> and add the relevant C<.symver> directives +so that old libraries call a translation function that is backwards +compatible with the old ABI, and to make the new symbol the default +for newly compiled code. There are examples in this file. + +Finally check that old binaries do not crash and still return the same +data. It's a good idea to add a C<debug> call to the translation +function so you can be sure it is being called. + =head2 SOURCE CODE SUBDIRECTORIES =over 4 -- 1.7.11.4
Slightly modified version of 5/7 patch attached. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming blog: http://rwmj.wordpress.com Fedora now supports 80 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora
From: "Richard W.M. Jones" <rjones@redhat.com> --- generator/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/Makefile.am b/generator/Makefile.am index 276c2cf..e218ffa 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -57,7 +57,7 @@ OBJECTS = $(SOURCES_ML:.ml=.cmo) EXTRA_DIST = $(SOURCES) files-generated.txt -OCAMLCFLAGS = -warn-error CDEFLMPSUVYZ+14+15+16+17+18+19+20+21+22+24+25+30-23 +OCAMLCFLAGS = -warn-error CDEFLMPSUVYZX OCAMLCLIBS = unix.cma str.cma noinst_PROGRAM = generator -- 1.7.11.4
From: "Richard W.M. Jones" <rjones@redhat.com> This breaks the ABI, but by using symbol versioning existing callers will not be affected. Also this change is source compatible, so existing code does not need to be modified. --- generator/actions.ml | 1 + generator/structs.ml | 11 ++++-- src/compat.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/inspect-apps.c | 5 +++ 4 files changed, 118 insertions(+), 3 deletions(-) diff --git a/generator/actions.ml b/generator/actions.ml index e1db3db..3b0806a 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -1522,6 +1522,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_list_applications"; style = RStructList ("applications", "application"), [Device "root"], []; + symbol_version = Some "GUESTFS_1.20"; shortdesc = "get list of applications installed in the operating system"; longdesc = "\ Return the list of applications installed in the operating system. diff --git a/generator/structs.ml b/generator/structs.ml index 499f825..fd77654 100644 --- a/generator/structs.ml +++ b/generator/structs.ml @@ -213,14 +213,14 @@ let structs = [ s_camel_name = "Partition" }; (* Application. *) - { defaults with - s_name = "application"; + { s_name = "application"; s_cols = [ "app_name", FString; "app_display_name", FString; "app_epoch", FInt32; "app_version", FString; "app_release", FString; + "app_arch", FString; "app_install_path", FString; "app_trans_path", FString; "app_publisher", FString; @@ -228,8 +228,13 @@ let structs = [ "app_source_package", FString; "app_summary", FString; "app_description", FString; + "app_spare1", FString; + "app_spare2", FString; + "app_spare3", FString; + "app_spare4", FString; ]; - s_camel_name = "Application" }; + s_camel_name = "Application"; + s_symbol_version = Some "GUESTFS_1.20" }; (* ISO primary volume descriptor. *) { defaults with diff --git a/src/compat.c b/src/compat.c index 27ffd05..520400b 100644 --- a/src/compat.c +++ b/src/compat.c @@ -24,3 +24,107 @@ * instead of being compiled as a separate unit. The cpp symbols * COMPAT_ACTIONS or COMPAT_STRUCTS are defined as appropriate. */ + +#define DEBUG_COMPAT debug (g, "%s: compatibility wrapper invoked", __func__) + +/* guestfs_inspect_list_applications did not return the C<app_arch> + * field in libguestfs < 1.20. We cannot add fields to structs + * without breaking compatibility. + */ +struct guestfs_application__v1 { + char *app_name; + char *app_display_name; + int32_t app_epoch; + char *app_version; + char *app_release; + char *app_install_path; + char *app_trans_path; + char *app_publisher; + char *app_url; + char *app_source_package; + char *app_summary; + char *app_description; +}; + +struct guestfs_application_list__v1 { + uint32_t len; + struct guestfs_application__v1 *val; +}; + +#if COMPAT_ACTIONS + +/* Declare a prototype to make GCC happy. */ +GUESTFS_DLL_PUBLIC struct guestfs_application_list__v1 *guestfs_inspect_list_applications__v1 (guestfs_h *g, const char *root); + +GUESTFS_DLL_PUBLIC +struct guestfs_application_list__v1 * +guestfs_inspect_list_applications__v1 (guestfs_h *g, const char *root) +{ + struct guestfs_application_list__v1 *ret; + struct guestfs_application_list *r; + size_t i; + + DEBUG_COMPAT; + + /* Call the new function. */ + r = guestfs_inspect_list_applications (g, root); + if (!r) + return NULL; + + /* Translate the structures from the new format to the old format. */ + ret = safe_malloc (g, sizeof (struct guestfs_application_list__v1)); + ret->len = r->len; + ret->val = + safe_malloc (g, sizeof (struct guestfs_application__v1) * r->len); + for (i = 0; i < r->len; ++i) { + ret->val[i].app_name = r->val[i].app_name; + ret->val[i].app_display_name = r->val[i].app_display_name; + ret->val[i].app_epoch = r->val[i].app_epoch; + ret->val[i].app_version = r->val[i].app_version; + ret->val[i].app_release = r->val[i].app_release; + ret->val[i].app_install_path = r->val[i].app_install_path; + ret->val[i].app_trans_path = r->val[i].app_trans_path; + ret->val[i].app_publisher = r->val[i].app_publisher; + ret->val[i].app_url = r->val[i].app_url; + ret->val[i].app_source_package = r->val[i].app_source_package; + ret->val[i].app_summary = r->val[i].app_summary; + ret->val[i].app_description = r->val[i].app_description; + } + free (r->val); /* Must not free the strings. */ + free (r); + + return ret; +} + +__asm__(".symver guestfs_inspect_list_applications__v1,guestfs_inspect_list_applications@"); +__asm__(".symver guestfs_inspect_list_applications__GUESTFS_1_20,guestfs_inspect_list_applications@@GUESTFS_1.20"); + +#endif /* COMPAT_ACTIONS */ + +#if COMPAT_STRUCTS + +GUESTFS_DLL_PUBLIC void guestfs_free_application__v1 (struct guestfs_application__v1 *x); + +GUESTFS_DLL_PUBLIC void +guestfs_free_application__v1 (struct guestfs_application__v1 *x) +{ + /* XXX */ + free (x); +} + +__asm__(".symver guestfs_free_application__v1,guestfs_free_application@"); +__asm__(".symver guestfs_free_application__GUESTFS_1_20,guestfs_free_application@@GUESTFS_1.20"); + +GUESTFS_DLL_PUBLIC void guestfs_free_application_list__v1 (struct guestfs_application_list__v1 *x); + +GUESTFS_DLL_PUBLIC void +guestfs_free_application_list__v1 (struct guestfs_application_list__v1 *x) +{ + /* XXX */ + free (x); +} + +__asm__(".symver guestfs_free_application_list__v1,guestfs_free_application_list@"); +__asm__(".symver guestfs_free_application_list__GUESTFS_1_20,guestfs_free_application_list@@GUESTFS_1.20"); + +#endif /* COMPAT_STRUCTS */ diff --git a/src/inspect-apps.c b/src/inspect-apps.c index 23e68e4..e5db4b3 100644 --- a/src/inspect-apps.c +++ b/src/inspect-apps.c @@ -580,6 +580,7 @@ add_application (guestfs_h *g, struct guestfs_application_list *apps, apps->val[apps->len-1].app_epoch = epoch; apps->val[apps->len-1].app_version = safe_strdup (g, version); apps->val[apps->len-1].app_release = safe_strdup (g, release); + apps->val[apps->len-1].app_arch = safe_strdup (g, ""); apps->val[apps->len-1].app_install_path = safe_strdup (g, install_path); /* XXX Translated path is not implemented yet. */ apps->val[apps->len-1].app_trans_path = safe_strdup (g, ""); @@ -591,6 +592,10 @@ add_application (guestfs_h *g, struct guestfs_application_list *apps, apps->val[apps->len-1].app_source_package = safe_strdup (g, ""); apps->val[apps->len-1].app_summary = safe_strdup (g, ""); apps->val[apps->len-1].app_description = safe_strdup (g, description); + apps->val[apps->len-1].app_spare1 = safe_strdup (g, ""); + apps->val[apps->len-1].app_spare2 = safe_strdup (g, ""); + apps->val[apps->len-1].app_spare3 = safe_strdup (g, ""); + apps->val[apps->len-1].app_spare4 = safe_strdup (g, ""); } /* Sort applications by name before returning the list. */ -- 1.7.11.4
On Tue, Oct 30, 2012 at 11:57:15AM +0000, Richard W.M. Jones wrote:
From: "Richard W.M. Jones" <rjones@redhat.com>
This breaks the ABI, but by using symbol versioning existing callers will not be affected. Also this change is source compatible, so existing code does not need to be modified.
Personally I wouldn't do this, I'd add a new API using the new ABI, leaving the original APIs intact. While I think symbol versioning is good in general, I'm not a fan of using it for hiding ABI breakage. Be aware that while many UNIX have symbol versioning, most do not allow for replacement of symbols with the same name. This was a Linux enhancement over the original Solaris scheme. So making use of this feature will mean you will suffer ABI breakage on any non-Linux platform. Daniel
participants (2)
-
Daniel P. Berrange -
Richard W.M. Jones