using System.Reflection;
using System.Collections;
using System.Diagnostics;
+using System.Text.RegularExpressions;
public class cilc
{
static CodeWriter C, H, Cindex, Hindex, Hdecls;
static string ns, dllname;
- static string cur_type, CurType;
+ static string cur_type, CurType, CurTypeClass;
static string target_dir;
static ArrayList funcs_done = new ArrayList ();
public static int Main (string[] args)
{
- if (args.Length < 1 || args.Length > 2) {
+ if (args.Length < 1 || args.Length > 4) {
Console.WriteLine ("Mono CIL-to-C binding generator");
- Console.WriteLine ("Usage: cilc [options] assembly target");
+ Console.WriteLine ("Usage: cilc assembly [target] [pkg ns[,ns...]]");
return 1;
}
ns = "Unnamed";
- //by default, generate in temporary directory and invoke make
if (args.Length == 1) {
- //Generate (args[0], Path.GetFileNameWithoutExtension (args[0]));
- string tmpdir = Path.GetTempPath () + Path.GetTempFileName ();
- string cwd = Directory.GetCurrentDirectory ();
- if (Directory.Exists (tmpdir) || File.Exists (tmpdir)) {
- Console.WriteLine ("Error: Temporary directory " + tmpdir + " already exists.");
- return 1;
- }
- Generate (args[0], tmpdir);
- Console.Write ("Compiling unmanaged binding");
- Run ("make", "-C \"" + tmpdir + "\" bundle=true");
- Console.WriteLine ();
- Console.Write ("Installing to current directory");
- Run ("make", "-C \"" + tmpdir + "\" install prefix=\"" + cwd + "\"");
- Directory.Delete (tmpdir, true);
- Console.WriteLine ();
- } else
+ SmartBind (args[0]);
+ } else if (args.Length == 2) {
Generate (args[0], args[1]);
+ } else if (args.Length == 3) {
+ RegisterPkg (args[1], args[2]);
+ SmartBind (args[0]);
+ } else if (args.Length == 4) {
+ RegisterPkg (args[2], args[3]);
+ Generate (args[0], args[1]);
+ }
return 0;
}
- public static void Run (string cmd, string args)
+
+ public static void SmartBind (string aname)
+ {
+ string tmpdir = Path.GetTempPath () + Path.GetTempFileName ();
+ string cwd = Directory.GetCurrentDirectory ();
+ if (Directory.Exists (tmpdir) || File.Exists (tmpdir)) {
+ Console.WriteLine ("Error: Temporary directory " + tmpdir + " already exists.");
+ return;
+ }
+ Generate (aname, tmpdir);
+ Console.Write ("Compiling unmanaged binding");
+ RunWithReport ("make", "-C \"" + tmpdir + "\" bundle=true");
+ Console.WriteLine ();
+ Console.Write ("Installing to current directory");
+ RunWithReport ("make", "-C \"" + tmpdir + "\" install prefix=\"" + cwd + "\"");
+ Directory.Delete (tmpdir, true);
+ Console.WriteLine ();
+ }
+
+ public static string Run (string cmd, string args)
+ {
+ ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
+ psi.UseShellExecute = false;
+ psi.RedirectStandardInput = true;
+ psi.RedirectStandardOutput = true;
+ psi.RedirectStandardError = true;
+
+ Process p = Process.Start (psi);
+
+ string line = p.StandardOutput.ReadLine ();
+
+ p.WaitForExit ();
+
+ return line;
+ }
+
+ public static int RunWithReport (string cmd, string args)
{
ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
psi.UseShellExecute = false;
Console.WriteLine ();
Console.Write (p.StandardError.ReadToEnd ());
+
+ p.WaitForExit ();
+
+ return p.ExitCode;
+ }
+
+ static string cflags;
+ static string extpkgs = "";
+ static string[] extsubpkgs = {};
+ static string[] extincludes = {};
+
+ public static void RegisterPkg (string pkg, string subpkgs)
+ {
+ extpkgs += " " + pkg;
+
+ string cflags = Run ("pkg-config", "--cflags-only-I " + pkg);
+
+ extsubpkgs = subpkgs.Trim ().Split (',');
+ extincludes = new string[extsubpkgs.Length];
+
+ for (int i = 0 ; i != extsubpkgs.Length ; i++)
+ extincludes[i] = extsubpkgs[i] + "/" + extsubpkgs[i] + ".h";
+
+ //string cmd = "gcc";
+ //string args = "-E " + cflags + " " + includedir + "/" + hname;
+
+ string cppincludes = "";
+ foreach (string include in extincludes)
+ cppincludes += " -include " + include;
+
+ string cmd = "cpp";
+ string args = cflags + cppincludes + " /dev/null";
+
+ ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
+ psi.UseShellExecute = false;
+ psi.RedirectStandardOutput = true;
+ psi.RedirectStandardError = true;
+
+ Process p = Process.Start (psi);
+
+ string line;
+
+ Regex re_type = new Regex (@"typedef struct (_\w+) (\w+);");
+ Regex re_enum = new Regex (@"} (\w+);");
+
+ while ((line = p.StandardOutput.ReadLine ()) != null) {
+ line = line.Trim ();
+
+ Match m;
+
+ m = re_type.Match (line);
+ if (m.Success) {
+ string G = m.Groups[2].Value;
+
+ bool dontgen = false;
+
+ if (!GIsValid (G))
+ continue;
+
+ if (G.EndsWith ("Class"))
+ continue;
+
+ RegisterG (G);
+ continue;
+ }
+
+ m = re_enum.Match (line);
+ if (m.Success) {
+ string G = m.Groups[1].Value;
+
+ if (!GIsValid (G))
+ continue;
+
+ RegisterG (G);
+ RegisterByVal (G);
+ continue;
+ }
+ }
+
+ p.WaitForExit ();
+ Console.Write (p.StandardError.ReadToEnd ());
+ Console.WriteLine ();
+ }
+
+ static bool GIsValid (string G) {
+ foreach (string extsubpkg in extsubpkgs)
+ if (G.ToLower ().StartsWith (extsubpkg))
+ return true;
+
+ return false;
}
public static void Generate (string assembly, string target)
Directory.CreateDirectory (target_dir);
- //register handled primitive types
- //TODO: sync these with the handled types
- RegisterCsType (typeof (string));
- RegisterCsType (typeof (int));
- RegisterCsType (typeof (bool));
- RegisterCsType (typeof (IntPtr));
-
- //TODO: parse given .h files here and register the type names
-
Assembly a = Assembly.LoadFrom (assembly);
Console.WriteLine ();
Console.WriteLine ("References (not followed):");
foreach (AssemblyName reference in a.GetReferencedAssemblies ())
- Console.WriteLine (" " + reference.Name);
+ Console.WriteLine (" " + reference.Name);
Console.WriteLine ();
dllname = Path.GetFileName (assembly);
makefile_defs.WriteLine ("ASSEMBLY = " + assembly);
makefile_defs.WriteLine ("SONAME = " + soname);
makefile_defs.WriteLine (@"OBJS = $(shell ls *.c | sed -e 's/\.c/.o/')");
+
+ if (extpkgs != "") {
+ makefile_defs.WriteLine ("EXTRAINCLUDES = $(shell pkg-config --cflags" + extpkgs + ")");
+ makefile_defs.WriteLine ("EXTRALIBS = $(shell pkg-config --libs" + extpkgs + ")");
+ }
makefile_defs.Close ();
Console.WriteLine ();
//identify hits on types that were registered too late
- foreach (Type rtype in registered_types) {
- string tname = rtype.FullName + " (type)";
- if (registry_hits.Contains (tname)) {
- Console.WriteLine ("Warning: " + tname + " was incorrectly registered after it was needed instead of before. Consider re-ordering.");
+ foreach (string tn in registered_types) {
+ if (registry_hits.Contains (tn)) {
+ Console.WriteLine ("Warning: " + tn + " was incorrectly registered after it was needed instead of before. Consider re-ordering.");
}
}
MakeReport (registry_hits, "Type registry missed hits", 20);
Console.WriteLine ();
- Console.WriteLine (registered_types.Count + " types generated in " + namespaces.Length + " namespaces; " + warnings_ignored + " types ignored");
+ //TODO: this count is now wrong
+ Console.WriteLine (registered_types.Count + " types generated/seen in " + namespaces.Length + " namespaces; " + warnings_ignored + " types ignored");
Console.WriteLine ();
}
continue;
}
- RegisterCsType (t);
+ RegisterType (t);
+
+ if (t.IsEnum)
+ RegisterByVal (t);
if (!ns_types.Contains (t.Namespace))
ns_types[t.Namespace] = new ArrayList ();
{
//ns = types[0].Namespace;
ns = given_ns;
-
+
Hindex = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + ".h");
- Hdecls = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + "-decls.h");
+ Hdecls = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + "types.h");
Cindex = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + ".c");
string Hindex_id = "__" + NsToFlat (ns).ToUpper () + "_H__";
Hindex.WriteLine ();
Hindex.WriteLine ("#endif /* " + Hindex_id + " */");
-
+
Hdecls.WriteLine ();
Hdecls.WriteLine ("#endif /* " + Hdecls_id + " */");
{
//TODO: we only handle ordinary classes for now
/*
- else if (t.IsSubclassOf (typeof (Delegate))) {
- Console.WriteLine ("Ignoring delegate: " + t.Name);
- return;
- }
- */
+ else if (t.IsSubclassOf (typeof (Delegate))) {
+ Console.WriteLine ("Ignoring delegate: " + t.Name);
+ return;
+ }
+ */
cur_type = NsToC (ns) + "_" + CamelToC (t.Name);
- CurType = NsToFlat (ns) + t.Name;
+ //CurType = NsToFlat (ns) + t.Name;
+ CurType = CsTypeToG (t);
+ CurTypeClass = GToGC (CurType);
//ns = t.Namespace;
string fname = NsToFlat (ns).ToLower () + t.Name.ToLower ();
H.WriteLine ("#include <glib.h>");
H.WriteLine ("#include <glib-object.h>");
+
+ foreach (string include in extincludes)
+ H.WriteLine ("#include <" + include + ">");
+
H.WriteLine ();
-
+
if (IsRegistered (t.BaseType))
H.WriteLine ("#include \"" + NsToFlat (t.BaseType.Namespace).ToLower () + t.BaseType.Name.ToLower () + ".h\"");
foreach (string ext_ns in namespaces)
- H.WriteLine ("#include \"" + NsToFlat (ext_ns).ToLower () + "-decls.h\"");
+ H.WriteLine ("#include \"" + NsToFlat (ext_ns).ToLower () + "types.h\"");
H.WriteLine ();
H.WriteLine ("G_BEGIN_DECLS");
H.WriteLine ();
-
+
{
string NS = NsToC (ns).ToUpper ();
string T = CamelToC (t.Name).ToUpper ();
H.WriteLine ("#define " + NSTT + " (" + cur_type + "_get_type ())");
H.WriteLine ("#define " + NST + "(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), " + NSTT + ", " + CurType + "))");
- H.WriteLine ("#define " + NST + "_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), " + NSTT + ", " + CurType + "Class))");
+ H.WriteLine ("#define " + NST + "_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), " + NSTT + ", " + CurTypeClass + "))");
H.WriteLine ("#define " + NS + "_IS_" + T + "(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), " + NSTT + "))");
H.WriteLine ("#define " + NS + "_IS_" + T + "_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), " + NSTT + "))");
- H.WriteLine ("#define " + NST + "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " + NSTT + ", " + CurType + "Class))");
+ H.WriteLine ("#define " + NST + "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " + NSTT + ", " + CurTypeClass + "))");
}
-
+
if (!C.IsDuplicate) {
Hdecls.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
- Hdecls.WriteLine ("typedef struct _" + CurType + "Class " + CurType + "Class;");
+ Hdecls.WriteLine ("typedef struct _" + CurTypeClass + " " + CurTypeClass + ";");
Hdecls.WriteLine ();
}
H.WriteLine ();
//H.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
-
+
//H.WriteLine ();
//H.WriteLine ("typedef struct _" + CurType + "Class " + CurType + "Class;");
H.WriteLine ("typedef struct _" + CurType + "Private " + CurType + "Private;");
H.WriteLine ("{");
string ParentName = CsTypeToG (t.BaseType);
+ string ParentNameClass = GToGC (ParentName);
H.WriteLine (ParentName + " parent_instance;");
H.WriteLine (CurType + "Private *priv;");
H.WriteLine ("};");
H.WriteLine ();
- H.WriteLine ("struct _" + CurType + "Class");
+ H.WriteLine ("struct _" + CurTypeClass);
H.WriteLine ("{");
- H.WriteLine (ParentName + "Class parent_class;" + " /* inherits " + t.BaseType.Namespace + " " + t.BaseType.Name + " */");
+ H.WriteLine (ParentNameClass + " parent_class;" + " /* inherits " + t.BaseType.Namespace + " " + t.BaseType.Name + " */");
H.WriteLine ();
-
+
//TODO: event arguments
foreach (EventInfo ei in events)
H.WriteLine ("void (* " + CamelToC (ei.Name) + ") (" + CurType + " *thiz" + ");");
-
+
H.WriteLine ("};");
H.WriteLine ();
//TODO: if the class inherits a known GLib Object, use its raw handle
C.WriteLine ("static gpointer parent_class;");
-
+
if (events.Length == 0)
C.WriteLine ("static guint signals[0];");
else
C.WriteLine ("}");
C.WriteLine ();
-
-
+
+
//generate the GObject class init function
- C.WriteLine ("static void " + cur_type + "_class_init (" + CurType + "Class *klass" + ")");
+ C.WriteLine ("static void " + cur_type + "_class_init (" + CurTypeClass + " *klass" + ")");
C.WriteLine ("{");
C.WriteLine ("GObjectClass *object_class = G_OBJECT_CLASS (klass);");
foreach (EventInfo ei in events)
EventGen (ei, t);
-
+
C.WriteLine ("}");
-
+
C.WriteLine ();
C.WriteLine ();
C.WriteLine ("static const GTypeInfo object_info =");
C.WriteLine ("{");
- C.WriteLine ("sizeof (" + CurType + "Class),");
+ C.WriteLine ("sizeof (" + CurTypeClass + "),");
C.WriteLine ("(GBaseInitFunc) NULL,");
C.WriteLine ("(GBaseFinalizeFunc) NULL,");
C.WriteLine ("(GClassInitFunc) " + cur_type + "_class_init,");
C.WriteLine ("(GInstanceInitFunc) " + cur_type + "_init,");
C.WriteLine ("};");
C.WriteLine ();
-
+
string parent_type = "G_TYPE_OBJECT";
- if (IsRegistered (t.BaseType))
+ if (IsRegistered (t.BaseType))
parent_type = NsToC (t.BaseType.Namespace).ToUpper () + "_TYPE_" + CamelToC (t.BaseType.Name).ToUpper ();
C.WriteLine ("object_type = g_type_register_static (" + parent_type + ", \"" + CurType + "\", &object_info, 0);");
methods = t.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (MethodInfo m in methods)
MethodGen (m, t);
-
+
H.WriteLine ();
H.WriteLine ("G_END_DECLS");
}
- //FIXME: this won't work in the general case. arraylist should contain just type names, not Types
+ //FIXME: clean up this mess with hits as the type registry uses strings
+
static ArrayList registered_types = new ArrayList ();
+ static ArrayList registered_byvals = new ArrayList ();
static Hashtable registry_hits = new Hashtable ();
+ static bool IsRegisteredByVal (Type t)
+ {
+ return registered_byvals.Contains (CsTypeToFlat (t));
+ }
+
+ static void RegisterByVal (string tn)
+ {
+ //TODO: warn on dupes
+ registered_byvals.Add (tn);
+ }
+
+ static void RegisterByVal (Type t)
+ {
+ RegisterByVal (CsTypeToFlat (t));
+ }
+
+ static bool IsRegistered (String tn)
+ {
+ return registered_types.Contains (tn);
+ }
+
static bool IsRegistered (Type t)
{
return IsRegistered (t, true);
static bool IsRegistered (Type t, bool log_hits)
{
- bool isreg = registered_types.Contains (t);
+ return IsRegistered (CsTypeToFlat (t), true);
+ }
- if (!isreg && log_hits) {
- if (t.Namespace != null && t.Namespace != "")
- HitRegistry (t.Namespace + " (namespace)");
+ static bool IsRegistered (string tn, bool log_hits)
+ {
+ //bool isreg = registered_types.Contains (t);
+ bool isreg = registered_types.Contains (tn);
- HitRegistry (t.FullName + " (type)");
+ if (!isreg && log_hits) {
+ HitRegistry (tn);
}
return isreg;
}
-
- static void HitRegistry (string tns)
+
+ static void HitRegistry (string tn)
{
- if (!registry_hits.Contains (tns)) {
+ //FIXME: ignore handled primitive types here
+
+ if (!registry_hits.Contains (tn)) {
int count = 0;
- registry_hits[tns] = count;
+ registry_hits[tn] = count;
}
- registry_hits[tns] = (int) registry_hits[tns] + 1;
+ registry_hits[tn] = (int) registry_hits[tn] + 1;
}
- static void RegisterCsType (Type t)
+ static bool RegisterG (string G)
{
- //if (t.IsPrimitive || IsRegistered (t))
- if (IsRegistered (t, false))
- return;
+ if (IsRegistered (G, false)) {
+ Console.WriteLine ("Warning: unmanaged type " + G + " already registered! Can't re-register.");
+ return false;
+ }
+
+ registered_types.Add (G);
+ return true;
+ }
+
+ static string NewG (string G)
+ {
+ if (IsRegistered (G, false))
+ {
+ Console.WriteLine ("Warning: type " + G + " already registered! Appending 'Extra' and trying again");
+ Console.WriteLine ();
+ return NewG (G + "Extra"); //FIXME: handle this properly
+ }
- registered_types.Add (t);
+ registered_types.Add (G);
+ return (G);
+ }
+
+ static string CsTypeToFlat (Type t) //TODO: use this everywhere
+ {
+ //TODO: check registry to see if t.Name's name has been changed during NewG.
+ //if it's not in the registry, continue as usual
+
+ return NsToFlat (t.Namespace) + t.Name;
+ }
+
+ static void RegisterType (Type t)
+ {
+ NewG (CsTypeToFlat (t));
+ }
+
+ static string NewG (Type t)
+ {
+ return NewG (CsTypeToFlat (t));
}
static string CsTypeToG (Type t)
{
if (IsRegistered (t))
- return NsToFlat (t.Namespace) + t.Name;
+ return CsTypeToFlat (t);
return "GObject";
}
+ //static string CsTypeToGC (String tn)
+ static string GToGC (string G)
+ {
+ string possGC = G + "Class";
+
+ if (IsRegistered (possGC))
+ return GToGC (G + "Object");
+ else
+ return possGC;
+ }
+
static string CsTypeToC (Type t)
{
//TODO: use this method everywhere
case "System.Int32":
return "gint ";
+ case "System.UInt32":
+ return "guint ";
+
case "System.Boolean":
return "gboolean ";
//questionable
case "System.EventHandler":
- case "System.MulticastDelegate":
+ case "System.MulticastDelegate":
return "GCallback ";
}
+ if (IsRegisteredByVal (t))
+ return CsTypeToFlat (t) + " ";
+
return CsTypeToG (t) + " *";
}
C.WriteLine ("\"" + name + "\",");
C.WriteLine ("G_OBJECT_CLASS_TYPE (object_class),");
C.WriteLine ("G_SIGNAL_RUN_LAST,");
- C.WriteLine ("G_STRUCT_OFFSET (" + CurType + "Class" + ", " + name + "),");
+ C.WriteLine ("G_STRUCT_OFFSET (" + CurTypeClass + ", " + name + "),");
C.WriteLine ("NULL, NULL,");
C.WriteLine ("g_cclosure_marshal_VOID__VOID,");
C.WriteLine ("G_TYPE_NONE, 0");
case "get_type":
return "retrieve_type";
default:
- return name;
+ return name;
}
}
else {
stat = m.IsStatic;
}
-
+
inst = !stat;
string mytype;
mytype = CurType + " *";
/*
- Console.WriteLine (ret_type);
- if (ret_type != null && ret_type != typeof (Void)) {
- has_return = true;
+ Console.WriteLine (ret_type);
+ if (ret_type != null && ret_type != typeof (Void)) {
+ has_return = true;
//TODO: return simple gint or gchar if possible
mytype = "MonoObject *";
}
- */
+ */
string params_arg = "NULL";
if (parameters.Length != 0)
//handle overloaded methods
//TODO: generate an alias function for the default ctor etc.
-
+
//TODO: how do we choose the default ctor/method overload? perhaps the
//first/shortest, but we need scope for this
//perhaps use DefaultMemberAttribute, Type.GetDefaultMembers
return;
funcs_done.Add (myname);
-
+
//handle the parameters
string param_assign = "";
string mycsargs = "";
C.WriteLine ("if (_mono_method == NULL) {");
- //if (ctor) C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor()\", FALSE);");
- if (ctor) C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor(" + mycsargs + ")\", FALSE);");
- else {
+ if (ctor)
+ C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor(" + mycsargs + ")\", FALSE);");
+ else
C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":" + m.Name + "(" + mycsargs + ")" + "\", FALSE);");
- }
+
C.WriteLine ("_mono_method = mono_method_desc_search_in_class (_mono_method_desc, " + cur_type + "_get_mono_class ());");
if (ctor) C.WriteLine (instance_arg + " = (MonoObject*) mono_object_new ((MonoDomain*) " + NsToC (ns) + "_get_mono_domain ()" + ", " + cur_type + "_get_mono_class ());");
- //invoke the method
-
- if (ctor || inst)
- C.WriteLine ("mono_runtime_invoke (_mono_method, " + instance_arg + ", " + params_arg + ", NULL);");
- else
- C.WriteLine ("mono_runtime_invoke (_mono_method, " + "NULL" + ", " + params_arg + ", NULL);");
+ //delegates are a special case as we want their constructor to take a function pointer
+ if (ctor && t.IsSubclassOf (typeof (MulticastDelegate))) {
+ C.WriteLine ("mono_delegate_ctor (" + instance_arg + ", object, method);");
+ } else {
+ //invoke the method
+
+ if (ctor || inst)
+ C.WriteLine ("mono_runtime_invoke (_mono_method, " + instance_arg + ", " + params_arg + ", NULL);");
+ else
+ C.WriteLine ("mono_runtime_invoke (_mono_method, " + "NULL" + ", " + params_arg + ", NULL);");
+ }
if (ctor) C.WriteLine ("return " + instance + ";");