/// <summary> /// Compiles and/or executes C# file or its project. /// If <paramref name="run"/> is false, returns 1 if compiled, 0 if failed to compile. /// Else returns: process id if started now, 0 if failed, (int)script.RunResult_.deferred if scheduled to run later, (int)script.RunResult_.editorThread if runs in editor thread. /// </summary> /// <param name="run">If true, compiles if need and executes. If false, always compiles and does not execute.</param> /// <param name="f">C# file. Does nothing if null or not C# file.</param> /// <param name="args">To pass to Main.</param> /// <param name="noDefer">Don't schedule to run later.</param> /// <param name="wrPipeName">Pipe name for script.writeResult.</param> /// <param name="runFromEditor">Starting from the Run button or menu Run command. Can restart etc.</param> /// <remarks> /// Saves editor text if need. /// Calls <see cref="Compiler.Compile"/>. /// Must be always called in the main UI thread (Environment.CurrentManagedThreadId == 1), because calls its file model functions. /// </remarks> public static int CompileAndRun(bool run, FileNode f, string[] args = null, bool noDefer = false, string wrPipeName = null, bool runFromEditor = false) { #if TEST_STARTUP_SPEED args = new string[] { perf.ms.ToString() }; //and in script use this code: print.it(perf.ms-Convert.ToInt64(args[0])); #endif App.Model.Save.TextNowIfNeed(onlyText: true); App.Model.Save.WorkspaceNowIfNeed(); //because the script may kill editor, eg if runs in editor thread if (f == null) { return(0); } if (f.FindProject(out var projFolder, out var projMain)) { f = projMain; } //can be set to run other script instead. // Useful for library projects. Single files have other alternatives - move to a script project or move code to a script file. if (run) { var f2 = f.TestScript; if (f2 != null) { if (!f2.FindProject(out projFolder, out projMain)) { f = f2; } else if (projMain != f) { f = projMain; } else { print.it($"<>The test script {f2.SciLink()} cannot be in the project folder {projFolder.SciLink()}"); return(0); } } } if (!f.IsCodeFile) { return(0); } bool ok = Compiler.Compile(run ? ECompReason.Run : ECompReason.CompileAlways, out var r, f, projFolder); if (run && (r.role == Au.Compiler.ERole.classFile || r.role == Au.Compiler.ERole.classLibrary)) //info: if classFile, compiler sets r.role and returns false (does not compile) { _OnRunClassFile(f, projFolder); return(0); } if (!ok) { return(0); } if (!run) { return(1); } if (r.role == Au.Compiler.ERole.editorExtension) { RunAssembly.Run(r.file, args, handleExceptions: true); return((int)script.RunResult_.editorThread); } return(App.Tasks.RunCompiled(f, r, args, noDefer, wrPipeName, runFromEditor: runFromEditor)); }
//[STAThread] //we use TrySetApartmentState instead static void Main(string[] args) { string asmFile, fullPathRefs; int pdbOffset, flags; if (args.Length != 1) { return; } string pipeName = args[0]; //if(!pipeName.Starts(@"\\.\pipe\Au.Task-")) return; int nr = 0; #if false //With NamedPipeClientStream faster by 1 ms, because don't need to JIT. But problems: //1. Loads System and System.Core immediately, making slower startup. //2. This process does not end when editor process ended, because then Connect spin-waits for server created. using (var pipe = new NamedPipeClientStream(".", pipeName.Substring(9), PipeDirection.In)) { pipe.Connect(); var b = new byte[10000]; nr = pipe.Read(b, 0, b.Length); } #else //APerf.First(); //_PrepareTest(); //APerf.NW(); for (int i = 0; i < 3; i++) { if (Api.WaitNamedPipe(pipeName, i == 2 ? -1 : 100)) { break; } if (Marshal.GetLastWin32Error() != Api.ERROR_SEM_TIMEOUT) { return; } //APerf.First(); switch (i) { case 0: _Prepare1(); break; //~25 ms with cold CPU //case 1: _Prepare2(); break; //~15 ms with cold CPU } //APerf.NW(); } //APerf.First(); using (var pipe = Api.CreateFile(pipeName, Api.GENERIC_READ, 0, default, Api.OPEN_EXISTING, 0)) { if (pipe.Is0) { ADebug.PrintNativeError_(); return; } //APerf.Next(); int size; if (!Api.ReadFile(pipe, &size, 4, out nr, default) || nr != 4) { return; } //APerf.Next(); if (!Api.ReadFileArr(pipe, out var b, size, out nr) || nr != size) { return; } //APerf.Next(); //ADebug.PrintLoadedAssemblies(true, true); //APerf.First(); var a = Au.Util.Serializer_.Deserialize(b); ATask.Init_(ATRole.MiniProgram, a[0]); asmFile = a[1]; pdbOffset = a[2]; flags = a[3]; args = a[4]; fullPathRefs = a[5]; string wrp = a[6]; if (wrp != null) { Environment.SetEnvironmentVariable("ATask.WriteResult.pipe", wrp); } AFolders.Workspace = (string)a[7]; } #endif //APerf.Next(); bool mtaThread = 0 != (flags & 2); //app without [STAThread] if (mtaThread == s_isSTA) { _SetComApartment(mtaThread ? ApartmentState.MTA : ApartmentState.STA); } if (0 != (flags & 4)) { Api.AllocConsole(); //meta console true } //if(0 != (flags & 1)) { //hasConfig // var config = asmFile + ".config"; // if(AFile.ExistsAsFile(config, true)) AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", config); //} if (s_hook == null) { _Hook(); } //APerf.Next(); try { RunAssembly.Run(asmFile, args, pdbOffset, fullPathRefs: fullPathRefs); } catch (Exception ex) { AOutput.Write(ex); } finally { s_hook?.Dispose(); } }