public static void Watch(string sourcePath, OnRecompiledHandler callback) { if (syncronizedSources.ContainsKey(sourcePath)) { syncronizedSources[sourcePath].Callback = callback; return; } var watcher = GH_FileWatcher.CreateFileWatcher(sourcePath, GH_FileWatcherEvents.All, (p) => { var result = Create(sourcePath); syncronizedSources[sourcePath].Callback?.Invoke(result); }); syncronizedSources.Add(sourcePath, new SyncronizedSource(callback, watcher)); }
public static CompilationResult Create(string sourcePath, bool watch = false, OnRecompiledHandler callback = null) { var result = new CompilationResult() { Error = null, AssemblyLocation = null, Assembly = null }; try { if (!File.Exists(sourcePath)) { result.Error = $"File ${sourcePath} not found"; return(result); } if (watch) { Watch(sourcePath, callback); } var directory = Path.GetDirectoryName(sourcePath); var assemblyName = Path.GetFileNameWithoutExtension(sourcePath); var dllPath = Path.Combine(directory, assemblyName + ".dll"); var finfo = new FileInfo(dllPath); if (finfo.Exists && finfo.Length > 0 && File.GetLastWriteTime(sourcePath) <= finfo.LastWriteTime) { if (!s_assemblies.ContainsKey(sourcePath)) { s_assemblies.Add(sourcePath, Assembly.Load(File.ReadAllBytes(dllPath))); VerifyMemoryUsage(); } result.Assembly = s_assemblies[sourcePath]; result.AssemblyLocation = dllPath; return(result); } // // ParseSyntaxTree // var code = File.ReadAllText(sourcePath); var ParseOptions = new CSharpParseOptions() .WithKind(SourceCodeKind.Script) .WithDocumentationMode(DocumentationMode.Parse) .WithPreprocessorSymbols(new[] { "__DEMO__", "__DEMO_EXPERIMENTAL__", "TRACE", "DEBUG" }); var tree = SyntaxFactory.ParseSyntaxTree(code, ParseOptions, sourcePath, Encoding.UTF8); if (tree.GetDiagnostics().Count() != 0) { result.Error = String.Join("\n", from d in tree.GetDiagnostics() select $"{d.GetMessage()} ({d.Location.GetLineSpan ()})"); return(result); } // // CreateScriptCompilation // if (!Environment.Is64BitProcess) { result.Error = "Not x64 platform"; return(result); } var compilationOptions = new CSharpCompilationOptions( outputKind: OutputKind.DynamicallyLinkedLibrary, scriptClassName: Pascalize(assemblyName) + ".Program", usings: null, optimizationLevel: OptimizationLevel.Debug, platform: Platform.X64, sourceReferenceResolver: SourceFileResolver.Default, metadataReferenceResolver: ScriptMetadataResolver.Default, assemblyIdentityComparer: AssemblyIdentityComparer.Default, allowUnsafe: false ); var compilation = CSharpCompilation.CreateScriptCompilation( assemblyName, options: compilationOptions, references: GetInitialReferences() ); if (compilation.GetDiagnostics().Count() != 0) { result.Error = String.Join("\n", from d in tree.GetDiagnostics() select $"{d.GetMessage()} ({d.Location.GetLineSpan()})"); return(result); } compilation = compilation.AddSyntaxTrees(tree); IEnumerable <MetadataReference> GetInitialReferences() { var assemblies = new[] { typeof(Input).Assembly, typeof(RhinoApp).Assembly, typeof(GH_Document).Assembly }; var refs = from a in assemblies select MetadataReference.CreateFromFile(a.Location); var stdPath = Path.GetDirectoryName(typeof(object).Assembly.Location); return(new List <MetadataReference>() { MetadataReference.CreateFromFile(Path.Combine(stdPath, "mscorlib.dll")), MetadataReference.CreateFromFile(Path.Combine(stdPath, "System.dll")), MetadataReference.CreateFromFile(Path.Combine(stdPath, "System.Core.dll")), MetadataReference.CreateFromFile(Path.Combine(stdPath, "System.Runtime.dll")) }); } // // EmitAssembly // var pdbPath = Path.Combine(directory, assemblyName + ".pdb"); var xmlPath = Path.Combine(directory, assemblyName + ".xml"); var options = new EmitOptions( debugInformationFormat: DebugInformationFormat.PortablePdb ); using (var modFs = new FileStream(dllPath, FileMode.Create, FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous)) using (var pdbFs = new FileStream(pdbPath, FileMode.Create, FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous)) { // Can be changed with MemoryStream // And save the assembly files with the GH_Document in same directory EmitResult r = compilation.Emit(modFs, pdbFs, options: options); if (!r.Success) { result.Error = String.Join("\n", from d in r.Diagnostics select $"{d.GetMessage()} ({d.Location.GetLineSpan()})"); return(result); } } result.Assembly = Assembly.Load(File.ReadAllBytes(dllPath)); if (!s_assemblies.ContainsKey(sourcePath)) { s_assemblies.Add(sourcePath, result.Assembly); } else { s_assemblies[sourcePath] = result.Assembly; } result.AssemblyLocation = dllPath; VerifyMemoryUsage(); return(result); void VerifyMemoryUsage() { var size = Process.GetCurrentProcess().WorkingSet64; if (size > s_target_process_size) { //TODO: Alert user to reload Rhino } } string Pascalize(string str) { var r = new StringBuilder(str.Length); var i = 0; var chars = str.ToCharArray(); for (; i != chars.Length; ++i) { var c = chars[i]; if (!Char.IsLetter(c)) { continue; } if (Char.IsUpper(c)) { r.Append(c); } else { r.Append(Char.ToUpper(c)); } break; } var needupper = false; for (++i; i != chars.Length; ++i) { var c = chars[i]; if (!Char.IsLetterOrDigit(c)) { needupper = true; } else if (Char.IsUpper(c)) { needupper = false; r.Append(c); } else if (needupper) { needupper = false; r.Append(Char.ToUpper(c)); } else { r.Append(c); } } return(r.ToString()); } } catch (Exception e) { result.Error = e.Message; return(result); } }
public SyncronizedSource(OnRecompiledHandler callback, GH_FileWatcher watcher) { Watcher = watcher; Callback = callback; }