// // (c) 2008-2009, Sigbjorn Finne // // A Haskell .NET wrapper generator tool; it started out as a small throw-away tool.. // grew more features...more code...became kind of useful...but hacky... // using System; using System.IO; using System.Collections; namespace HsWrap { class App { static bool createDir = false; static String initWrapperModule(String tyName) { Int32 modIdx = tyName.LastIndexOf('.'); String fileStem = ""; String outDir = (Config.OutputDir == "" ? "" : (Config.OutputDir + "\\")); if ( modIdx == 0) { fileStem = tyName; } else if ( createDir ) { String dir = outDir; if ( Config.WithTypeModule ) { dir += HsType.TypeToModuleFile(tyName); } else { dir += HsType.TypeToModuleFile(tyName.Substring(0,modIdx)); } if ( !Config.DryRunMode ) { Console.WriteLine("IO=>creating directory: {0}", dir); System.IO.Directory.CreateDirectory(dir); } fileStem = outDir + HsType.TypeToModuleFile(tyName); } else { fileStem = outDir + tyName.Substring(modIdx+1); } return String.Concat(fileStem, ".hs"); } static void Usage() { String usageMsg = @" hswrap - creating Haskell modules that wraps up .NET classes Usage: hswrap classname [outfile] where options is zero or more of the following: --help print out this usage message and exit. -v be verbose about actions performed. -q generate wrapper modules without making a song and dance about it on stdout/stderr. -d output into a dir hierarchy; creating it if needed. (e.g., System.Object.Futon will be emitted as System/Object/Futon.hs) -n don't output the Haskell type into a separate module (default is to do so.) --interactive have user verify the generation of wrappers --non-interactive don't ask for permission; just go. --no-chase if uses of non-bound types are encountered, don't try to generate wrappers for these --dry-run disable generation of wrapper modules, just print out what they are/would be. --ignore= don't generate wrappers for types matching PAT -- --ignore=System.Xml.* turns off wrapper generation for the System.Xml namespace. --ignore-file= don't generate wrappers for type patterns in FILE. --file= generate wrappers for types listed in FILE; one per-line. --outdir= emit the generated wrappers to DIR, i.e., the type System.X will be generated as DIR/System/X.hs --module-dir= add DIR to the wrapper module search path. DIR will then be probed when the tool tries to determine if a referenced type already has a wrapper module (and generating another one isn't needed.) Notice that DIR is assumed to be at the toplevel of the module hierarchy, i.e., a DIR of 'Foo' will match 'System.X' if 'Foo/System/X.hs' is present. The 'hswrap' assumes that the _module name_ in that X.hs is 'System.X'. "; Console.WriteLine(usageMsg); } private static bool HasHelp(String s) { return (s=="--help"); } private static Int32 ProcessOptions(WrapQ wq, string[] args) { Int32 idx = 0; bool processingOptions = true; char[] spc={' ','\t'}; while (processingOptions) { if (idx >= args.Length) { processingOptions=false; break; } if ( args[idx] == "--dry-run" ) { Config.DryRunMode=true; idx++; continue; } if ( args[idx] == "--interactive" ) { Config.InteractiveMode=true; idx++; continue; } if ( args[idx] == "--non-interactive" ) { Config.InteractiveMode=false; idx++; continue; } if ( args[idx] == "-d" ) { createDir = true; idx++; continue; } if ( args[idx].StartsWith("--file=") ) { String fn=args[idx].Substring("--file=".Length); try { using (StreamReader rdr = new StreamReader(fn)) { String ln; int ix; while ( (ln=rdr.ReadLine()) != null ) { if ( ln.TrimStart(spc) == "" || ln.StartsWith("#") ) { continue; } if ( (ix = ln.IndexOf('=')) > 0 ) { String t = ln.Substring(0,ix-1); String f = ln.Substring(ix+1); wq.AddType(t.TrimStart(spc),f.TrimEnd(spc)); } else { wq.AddType(ln.TrimEnd(spc)); } } } } catch (Exception) { Console.WriteLine("Problem reading 'file' " + fn); } idx++; continue; } if ( args[idx].StartsWith("--ignore=") ) { String pat=args[idx].Substring("--ignore=".Length); wq.AddException(pat); idx++; continue; } if ( args[idx].StartsWith("--outdir=") ) { String fn=args[idx].Substring("--outdir=".Length); Config.OutputDir=fn; Config.AddSearchDir(fn); wq.Blacklist.AddTypePath(fn); idx++; continue; } if ( args[idx].StartsWith("--module-dir=") ) { String d=args[idx].Substring("--module-dir=".Length); Config.AddSearchDir(d); wq.Blacklist.AddTypePath(d); idx++; continue; } if ( args[idx].StartsWith("--ignore-file=") ) { String fn=args[idx].Substring("--ignore-file=".Length); try { using (StreamReader rdr = new StreamReader(fn)) { String ln; while ( (ln=rdr.ReadLine()) != null ) { if ( ln.TrimStart(spc) == "" || ln.StartsWith("#") ) { continue; } wq.AddException(ln); } } } catch (Exception) { Console.WriteLine("Problem reading 'ignore-file' " + fn); } idx++; continue; } if ( args[idx] == "-n" ) { Config.WithTypeModule = false; idx++; continue; } if ( args[idx] == "-v" ) { Config.Verbose = true; idx++; continue; } if ( args[idx] == "-q" ) { Config.Verbose = false; idx++; continue; } if ( args[idx][0] == '-' ) { Console.WriteLine("WARNING: unrecognized option " + args[idx] + "; ignoring.."); idx++; continue; } else { processingOptions=false; } } return idx; } [STAThread] static void Main(string[] args) { if ( args.Length == 0 || Array.Exists(args,App.HasHelp) ) { Usage(); return; } Queue modQueue = new Queue(); WrapQ wq = new WrapQ(); String tyName = ""; String outFile = ""; bool useBlackList = false; Int32 idx = ProcessOptions(wq,args); if ( idx < args.Length ) { tyName = args[idx]; if (args.Length > (idx+1)) { outFile = args[idx+1]; } else { outFile = initWrapperModule(args[idx]); } wq.AddType(tyName,outFile); } // test.. wq.AddException("System\\.Xml\\..*"); while ( (tyName = wq.GetNextModule(useBlackList,out outFile)) != null ) { useBlackList=true; if ( Config.DryRunMode ) { Console.WriteLine(tyName); } else if ( Config.Verbose ) { Console.WriteLine("Processing: {0}", tyName); } HsTypeDetails hst = HsType.ToTypeDetails(tyName); TypeInfo ti = new TypeInfo(hst.HsOrigName); if ( ti.Type == null ) { Console.WriteLine("Unable to locate class {0}", tyName); continue; } HsOutput hs = new HsOutput(ti.Type,ti.Members,wq,false); if ( !Config.DryRunMode ) { outFile = initWrapperModule(hst.HsTypeName); } hs.OutputToFile(outFile); if ( !Config.DryRunMode ) { if ( Config.WithTypeModule ) { if ( !createDir ) { Console.WriteLine("ERROR: can't create type module without '-d' enabled"); } else { String outTyFile = outFile.Replace(".hs","\\Type.hs"); Console.WriteLine("generating type: {0}", outTyFile); HsOutput hsTy = new HsOutput(ti.Type,ti.Members,wq,true); Console.WriteLine(outTyFile); hsTy.OutputToFile(outTyFile); } } } } } }; }