diff --git a/src/BasicModelInterface.Tests/BasicModelInterface.Tests.csproj b/src/BasicModelInterface.Tests/BasicModelInterface.Tests.csproj index 19b6fd2..cf1dfa5 100644 --- a/src/BasicModelInterface.Tests/BasicModelInterface.Tests.csproj +++ b/src/BasicModelInterface.Tests/BasicModelInterface.Tests.csproj @@ -1,5 +1,5 @@  - + Debug @@ -21,6 +21,11 @@ prompt 4 true + + + + + pdbonly @@ -29,6 +34,7 @@ TRACE prompt 4 + true @@ -48,10 +54,11 @@ + - {b973019e-5674-4e23-9b1e-c4ca5eac381b} + {B973019E-5674-4E23-9B1E-C4CA5EAC381B} BasicModelInterface diff --git a/src/BasicModelInterface.Tests/BasicModelInterfaceLibraryTest.cs b/src/BasicModelInterface.Tests/BasicModelInterfaceLibraryTest.cs index 170c3f7..b80ae4d 100644 --- a/src/BasicModelInterface.Tests/BasicModelInterfaceLibraryTest.cs +++ b/src/BasicModelInterface.Tests/BasicModelInterfaceLibraryTest.cs @@ -1,26 +1,28 @@ using System; using NUnit.Framework; +using System.Runtime.InteropServices; namespace BasicModelInterface.Tests { [TestFixture] public class BasicModelInterfaceLibraryTest { - private const string LibraryC = @"..\..\..\..\bin\Debug\model-c.dll"; - + private const string EngineName = @"modelc"; + private string libraryPath; + private const string LibraryC = @"..\..\..\..\bin\Debug\model-c.dll"; private IBasicModelInterface library; [SetUp] public void SetUp() { - library = new BasicModelInterfaceLibrary(LibraryC); + libraryPath = BasicModelInterfaceLibrary.GetLibraryPath(EngineName); + library = new BasicModelInterfaceLibrary(libraryPath); } [Test] public void InitializeAndFinish() { const string configFilePath = "empty"; - library.Initialize(configFilePath); library.Finish(); } @@ -45,7 +47,7 @@ public void GetVariableNames() [Test] public void Run() { - BasicModelInterfaceLibrary.Run(LibraryC, "test.config"); + BasicModelInterfaceLibrary.Run(libraryPath, "test.config"); } [Test] @@ -111,5 +113,16 @@ public void SetLogger() library.Initialize(string.Empty); library.Finish(); } + + [Test] + public void InitializeDirect() + { + initialize (""); + } + + [DllImport ("modelc", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public static extern int initialize (string configPath); + + } } \ No newline at end of file diff --git a/src/BasicModelInterface.Tests/app.config b/src/BasicModelInterface.Tests/app.config new file mode 100644 index 0000000..9885737 --- /dev/null +++ b/src/BasicModelInterface.Tests/app.config @@ -0,0 +1,4 @@ + + + + diff --git a/src/BasicModelInterface.Tests/packages.config b/src/BasicModelInterface.Tests/packages.config index ad37a52..d4e241a 100644 --- a/src/BasicModelInterface.Tests/packages.config +++ b/src/BasicModelInterface.Tests/packages.config @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/src/BasicModelInterface/BasicModelInterface.csproj b/src/BasicModelInterface/BasicModelInterface.csproj index 2bbd8b1..a3256c0 100644 --- a/src/BasicModelInterface/BasicModelInterface.csproj +++ b/src/BasicModelInterface/BasicModelInterface.csproj @@ -1,5 +1,5 @@  - + Debug @@ -38,6 +38,9 @@ + + ..\..\..\..\.local\lib\libmodelc-csharp.dll + diff --git a/src/BasicModelInterface/BasicModelInterfaceLibrary.cs b/src/BasicModelInterface/BasicModelInterfaceLibrary.cs index fbb6531..356ad2f 100644 --- a/src/BasicModelInterface/BasicModelInterfaceLibrary.cs +++ b/src/BasicModelInterface/BasicModelInterfaceLibrary.cs @@ -1,6 +1,8 @@ using System; using System.Diagnostics; using System.IO; +using System.Collections; +using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -32,6 +34,67 @@ public BasicModelInterfaceLibrary(string libraryPath, CallingConvention callingC lib.ReturnTypes["finalize"] = typeof(int); } + public static string LibraryName(string engine) + { + string extension = ".dll"; + string prefix = ""; + var p = Environment.OSVersion.Platform; + if (p == PlatformID.Unix || p == PlatformID.MacOSX) + { + prefix = "lib"; + extension = ".so"; + + // HACK: Mono implements Platform incorrectly + if (IsRunningOnMac ()) { + extension = ".dylib"; + extension = ""; + } + } + else { + prefix = ""; + extension = ".dll"; + }; + return prefix + engine + extension; + } + + //From Managed.Windows.Forms/XplatUI + [DllImport ("libc")] + static extern int uname (IntPtr buf); + + static bool IsRunningOnMac () + { + IntPtr buf = IntPtr.Zero; + try { + buf = Marshal.AllocHGlobal (8192); + // This is a hacktastic way of getting sysname from uname () + if (uname (buf) == 0) { + string os = Marshal.PtrToStringAnsi (buf); + if (os == "Darwin") + return true; + } + } catch { + } finally { + if (buf != IntPtr.Zero) + Marshal.FreeHGlobal (buf); + } + return false; + } + + public static string GetLibraryPath(string engine) + { + string[] knownPaths = { + "~/.local/lib", + "/usr/local/lib", + "/usr/lib" + }; + // TODO loop over paths to find the library. + + string expandedPath = knownPaths[0].Replace("~", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); + var fullPath = Path.GetFullPath (Path.Combine (expandedPath, LibraryName (engine))); + Debug.Assert (new FileInfo (fullPath).Exists); + return fullPath; + } + public DateTime StartTime { get diff --git a/src/BasicModelInterface/Interop/DynamicDllImportMetaObject.cs b/src/BasicModelInterface/Interop/DynamicDllImportMetaObject.cs index c260fd2..a7ed1d8 100644 --- a/src/BasicModelInterface/Interop/DynamicDllImportMetaObject.cs +++ b/src/BasicModelInterface/Interop/DynamicDllImportMetaObject.cs @@ -54,12 +54,14 @@ public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, Dy private Type GetMethodReturnType(InvokeMemberBinder binder) { + /* var types = binder.GetType().GetField("m_typeArguments", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(binder) as IList; if ((types != null) && (types.Count > 0)) { return types[0]; } + */ Type type; lib.ReturnTypes.TryGetValue(binder.Name, out type); diff --git a/src/BasicModelInterfaceRunner/BasicModelInterfaceRunner.csproj b/src/BasicModelInterfaceRunner/BasicModelInterfaceRunner.csproj index 813abf9..eb64f3f 100644 --- a/src/BasicModelInterfaceRunner/BasicModelInterfaceRunner.csproj +++ b/src/BasicModelInterfaceRunner/BasicModelInterfaceRunner.csproj @@ -1,5 +1,5 @@  - + Debug @@ -32,9 +32,6 @@ 4 - - ..\packages\docopt.net.0.6.1.5\lib\net40\DocoptNet.dll - @@ -42,6 +39,9 @@ + + ..\..\packages\docopt.net.0.6.1.5\lib\net40\DocoptNet.dll + @@ -49,20 +49,21 @@ + + + + - {b973019e-5674-4e23-9b1e-c4ca5eac381b} + {B973019E-5674-4E23-9B1E-C4CA5EAC381B} BasicModelInterface - - - diff --git a/src/BasicModelInterfaceRunner/Main.usage.txt b/src/BasicModelInterfaceRunner/Main.usage.txt index 6a68397..777bf5a 100644 --- a/src/BasicModelInterfaceRunner/Main.usage.txt +++ b/src/BasicModelInterfaceRunner/Main.usage.txt @@ -1,39 +1,39 @@ -Example usage for T4 Docopt.NET - -Usage: - prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch] - prog files FILE... - -Options: - -o Short switch. - -s= Short option with arg. - --long=ARG Long option with arg. - --swith Long switch. - -Explanation: - This is an example usage file that needs to be customized. - Every time you change this file, run the Custom Tool command - on T4DocoptNet.tt to re-generate the MainArgs class - (defined in T4DocoptNet.cs). - You can then use the MainArgs classed as follows: - - class Program - { - - static void DoStuff(string arg, bool flagO, string longValue) - { - // ... - } - - static void Main(string[] argv) - { - // Automatically exit(1) if invalid arguments - var args = new MainArgs(argv, exit: true); - if (args.CmdCommand) - { - Console.WriteLine("First command"); - DoStuff(args.ArgArg, args.OptO, args.OptLong); - } - } - } - +Example usage for T4 Docopt.NET + +Usage: + prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch] + prog files FILE... + +Options: + -o Short switch. + -s= Short option with arg. + --long=ARG Long option with arg. + --swith Long switch. + +Explanation: + This is an example usage file that needs to be customized. + Every time you change this file, run the Custom Tool command + on T4DocoptNet.tt to re-generate the MainArgs class + (defined in T4DocoptNet.cs). + You can then use the MainArgs classed as follows: + + class Program + { + + static void DoStuff(string arg, bool flagO, string longValue) + { + // ... + } + + static void Main(string[] argv) + { + // Automatically exit(1) if invalid arguments + var args = new MainArgs(argv, exit: true); + if (args.CmdCommand) + { + Console.WriteLine("First command"); + DoStuff(args.ArgArg, args.OptO, args.OptLong); + } + } + } + diff --git a/src/BasicModelInterfaceRunner/T4DocoptNet.tt b/src/BasicModelInterfaceRunner/T4DocoptNet.tt new file mode 100644 index 0000000..871fa2b --- /dev/null +++ b/src/BasicModelInterfaceRunner/T4DocoptNet.tt @@ -0,0 +1,112 @@ +<# +/* +T4DocopNet Version 0.1.0 +*/ +#> +<#@ template language="C#" debug="true" hostspecific="true" #> +<#@ assembly name="System.Core" #> +<#@ assembly name="Microsoft.VisualStudio.Shell.Interop" #> +<#@ assembly name="EnvDTE" #> +<#@ assembly name="EnvDTE80" #> +<#@ assembly name="VSLangProj" #> +<#@ assembly name="System.Xml" #> +<#@ assembly name="System.Xml.Linq" #> +<# +// This directive will be updated at package install time +// by install.ps1. It can also be set manually to point +// to the right path. +#> +<#@ assembly name="$(ProjectDir)$(OutDir)DocoptNet.dll" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="DocoptNet" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Text.RegularExpressions" #> +<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #> +<#@ import namespace="EnvDTE" #> +<#@ import namespace="EnvDTE80" #> +<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #> + +using System.Collections; +using System.Collections.Generic; +using DocoptNet; + +namespace <#= System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint") #> +{ +<# + var templateDir = Path.GetDirectoryName(Host.TemplateFile); + var files = Directory.EnumerateFiles(templateDir).Where(f => f.EndsWith(".usage.txt")); + foreach (var file in files) + { + var entry = new UsageFileEntry(file); +#> + // Generated class for <#= Path.GetFileName(entry.FileName) #> + public class <#= entry.Name #>Args + { + public const string USAGE = @"<#= entry.Usage #>"; + private readonly IDictionary _args; + public <#= entry.Name #>Args(ICollection argv, bool help = true, + object version = null, bool optionsFirst = false, bool exit = false) + { + _args = new Docopt().Apply(USAGE, argv, help, version, optionsFirst, exit); + } + + public IDictionary Args + { + get { return _args; } + } + +<# + var s = new Docopt().GenerateCode(entry.Usage); + PushIndent(" "); + Write(s); + ClearIndent(); +#> + } + +<# + } + +#> +} + +<#+ + class UsageFileEntry + { + public string FileName; + public string Name; + public string Usage; + public UsageFileEntry(string fileName) + { + FileName = fileName; + Name = ExtractName(fileName); + Usage = File.ReadAllText(fileName).Replace("\"", "\"\""); + } + + private static string ExtractName(string fileName) + { + + var s = Path.GetFileName(fileName).Replace(".usage.txt", ""); + var res = ""; + var first = true; + foreach (char c in s) + { + if (char.IsLetterOrDigit(c)) + { + res += first ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c); + first = false; + } + else + { + res += "_"; + } + } + return res; + } + } + +/* IMPORTANT: Do not add blanks after this last tag as it would break the T4 generation + * See http://stackoverflow.com/questions/11379471/error-a-template-containing-a-class-feature-must-end-with-a-class-feature + */ +#> \ No newline at end of file diff --git a/src/BasicModelInterfaceRunner/T4DocoptNet.tt.hooks.t4 b/src/BasicModelInterfaceRunner/T4DocoptNet.tt.hooks.t4 new file mode 100644 index 0000000..8a7e209 --- /dev/null +++ b/src/BasicModelInterfaceRunner/T4DocoptNet.tt.hooks.t4 @@ -0,0 +1,18 @@ +<#+ +/* + +This file contains hooks and extra code used by T4DocoptNet.tt. The main goal is to avoid the need for users +to fork the 'official' template in order to achieve what they want. + +*/ + +void RenderAdditionalCode() { +#> +[GeneratedCode("T4DocoptNet", "2.0"), DebuggerNonUserCode] +internal static class T4DocoptNetHelpers { +// TODO +} + +<#+ +} +#> diff --git a/src/BasicModelInterfaceRunner/T4DocoptNet.tt.settings.xml b/src/BasicModelInterfaceRunner/T4DocoptNet.tt.settings.xml new file mode 100644 index 0000000..225229f --- /dev/null +++ b/src/BasicModelInterfaceRunner/T4DocoptNet.tt.settings.xml @@ -0,0 +1,11 @@ + + + + T4DocoptNet + + Args + + + + + \ No newline at end of file diff --git a/src/BasicModelInterfaceRunner/packages.config b/src/BasicModelInterfaceRunner/packages.config index 11f8d4f..962e876 100644 --- a/src/BasicModelInterfaceRunner/packages.config +++ b/src/BasicModelInterfaceRunner/packages.config @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file