protected override byte[] Generate(string inputFilePath, string inputFileContents, string defaultNamespace, IVsGeneratorProgress progressCallback) { string oldCurDir = Environment.CurrentDirectory; try { string inputFolder = Path.GetDirectoryName(inputFilePath); Environment.CurrentDirectory = inputFolder; // --macros should be relative to file being processed // Originally I wrote a conversion from IVsGeneratorProgress to // IMessageSink so that errors could be reported immediately and // directly to Visual Studio. This broke in a bizarre way when I // added processing on a separate thread (in order to be able to // abort the thread if it runs too long); I got the following // InvalidCastException: "Unable to cast COM object of type 'System.__ComObject' // to interface type 'Microsoft.VisualStudio.Shell.Interop.IVsGeneratorProgress'. // This operation failed because the QueryInterface call on the COM component for // the interface with IID '{BED89B98-6EC9-43CB-B0A8-41D6E2D6669D}' failed due to // the following error: No such interface supported (Exception from HRESULT: // 0x80004002 (E_NOINTERFACE))." // // A simple solution is to store the messages rather than reporting // them immediately. I'll report the errors at the very end. MessageHolder sink = new MessageHolder(); var sourceFile = new InputOutput((UString)inputFileContents, inputFilePath); Compiler.KnownOptions["no-out-header"] = Pair.Create("", "Remove explanatory comment from output file"); Compiler.KnownOptions.Remove("parallel"); // not applicable to single file Compiler.KnownOptions.Remove("noparallel"); // not applicable to single file var c = new Compiler(sink, sourceFile) { AbortTimeout = TimeSpan.FromSeconds(10), Parallel = false // only one file, parallel doesn't help }; var argList = G.SplitCommandLineArguments(defaultNamespace); var options = c.ProcessArguments(argList, true, false); // Note: if default namespace is left blank, VS uses the namespace // from project settings. Don't show an error in that case. if (argList.Count > 1 || (argList.Count == 1 && options.Count > 0)) { sink.Write(Severity.Error, "Command line", "'{0}': expected options only (try --help).", argList[0]); } string _; if (options.TryGetValue("help", out _) || options.TryGetValue("?", out _)) { var ms = new MemoryStream(); LeMP.Compiler.ShowHelp(LeMP.Compiler.KnownOptions, new StreamWriter(ms), false); return(ms.GetBuffer()); } LeMP.Compiler.WarnAboutUnknownOptions(options, sink, LeMP.Compiler.KnownOptions); if (options.ContainsKey("no-out-header")) { c.NoOutHeader = true; } if (c.InLang == LesLanguageService.Value || inputFilePath.EndsWith(".les", StringComparison.OrdinalIgnoreCase)) { c.MacroProcessor.PreOpenedNamespaces.Add(GSymbol.Get("LeMP.Prelude.Les")); } Configure(c); _requestedExtension = c.OutExt; c.Run(); // Report errors foreach (var msg in sink.List) { ReportErrorToVS(progressCallback, msg.Severity, msg.Context, msg.Format, msg.Args); } return(Encoding.UTF8.GetBytes(c.Output.ToString())); } finally { Environment.CurrentDirectory = oldCurDir; } }
protected override byte[] Generate(string inputFilePath, string inputFileContents, string defaultNamespace, IVsGeneratorProgress progressCallback) { string oldCurDir = Environment.CurrentDirectory; try { string inputFolder = Path.GetDirectoryName(inputFilePath); Environment.CurrentDirectory = inputFolder; // --macros should be relative to file being processed // Originally I wrote a conversion from IVsGeneratorProgress to // IMessageSink so that errors could be reported immediately and // directly to Visual Studio. This broke in a bizarre way when I // added processing on a separate thread (in order to be able to // abort the thread if it runs too long); I got the following // InvalidCastException: "Unable to cast COM object of type 'System.__ComObject' // to interface type 'Microsoft.VisualStudio.Shell.Interop.IVsGeneratorProgress'. // This operation failed because the QueryInterface call on the COM component for // the interface with IID '{BED89B98-6EC9-43CB-B0A8-41D6E2D6669D}' failed due to // the following error: No such interface supported (Exception from HRESULT: // 0x80004002 (E_NOINTERFACE))." // // A simple solution is to store the messages rather than reporting // them immediately. I'll report the errors at the very end. MessageHolder sink = new MessageHolder(); var sourceFile = new InputOutput((UString)inputFileContents, inputFilePath); Compiler.KnownOptions["no-out-header"] = Pair.Create("", "Remove explanatory comment from output file"); Compiler.KnownOptions.Remove("parallel"); // not applicable to single file Compiler.KnownOptions.Remove("noparallel"); // not applicable to single file var c = new Compiler(sink, sourceFile) { AbortTimeout = TimeSpan.FromSeconds(10), Parallel = false // only one file, parallel doesn't help }; var argList = G.SplitCommandLineArguments(defaultNamespace); var options = c.ProcessArguments(argList, true, false); // Note: if default namespace is left blank, VS uses the namespace // from project settings. Don't show an error in that case. if (argList.Count > 1 || (argList.Count == 1 && options.Count > 0)) sink.Write(Severity.Error, "Command line", "'{0}': expected options only (try --help).", argList[0]); string _; if (options.TryGetValue("help", out _) || options.TryGetValue("?", out _)) { var ms = new MemoryStream(); LeMP.Compiler.ShowHelp(LeMP.Compiler.KnownOptions, new StreamWriter(ms), false); return ms.GetBuffer(); } LeMP.Compiler.WarnAboutUnknownOptions(options, sink, LeMP.Compiler.KnownOptions); if (options.ContainsKey("no-out-header")) c.NoOutHeader = true; if (c.InLang == LesLanguageService.Value || inputFilePath.EndsWith(".les", StringComparison.OrdinalIgnoreCase)) c.MacroProcessor.PreOpenedNamespaces.Add(GSymbol.Get("LeMP.Prelude.Les")); Configure(c); _requestedExtension = c.OutExt; c.Run(); // Report errors foreach (var msg in sink.List) ReportErrorToVS(progressCallback, msg.Severity, msg.Context, msg.Format, msg.Args); return Encoding.UTF8.GetBytes(c.Output.ToString()); } finally { Environment.CurrentDirectory = oldCurDir; } }