Invokes static method 'Main' from the assembly.
Inheritance: System.MarshalByRefObject
        void Init(string fileNname, string domainName)
        {
            //difference comparing to InitLagacy:
            // CreateInstanceAndUnwrap instead of CreateInstanceFromAndUnwrap
            //
            // setup.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            // instead of setup.ApplicationBase = Path.GetDirectoryName(assemblyFileName); 
            //
            // In 2016 just discovered that InitLegacy doesn't longer work. May be because some changes in .NET versions...  
            // This is a low impact change as AssemblyExecutor is only used for cached vs. non-cached execution in stand alone 
            // hosting mode.

            assemblyFileName = fileNname;

            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
            setup.ApplicationName = Utils.GetAssemblyFileName(Assembly.GetExecutingAssembly());
            setup.ShadowCopyFiles = "true";
            setup.ShadowCopyDirectories = Path.GetDirectoryName(assemblyFileName);

            appDomain = AppDomain.CreateDomain(domainName, null, setup);
            remoteExecutor = (RemoteExecutor)appDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(RemoteExecutor).ToString());
            remoteExecutor.searchDirs = ExecuteOptions.options.searchDirs;
        }
Exemple #2
0
        public AssemblyExecutor(string fileNname, string domainName)
        {
            assemblyFileName = fileNname;
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationBase = Path.GetDirectoryName(assemblyFileName);
            setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
            setup.ApplicationName = Utils.GetAssemblyFileName(Assembly.GetExecutingAssembly());
            setup.ShadowCopyFiles = "true";
            setup.ShadowCopyDirectories = Path.GetDirectoryName(assemblyFileName);
            appDomain = AppDomain.CreateDomain(domainName, null, setup);

            remoteExecutor = (RemoteExecutor)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(RemoteExecutor).ToString());
            remoteExecutor.searchDirs = ExecuteOptions.options.searchDirs;
        }
        bool InitLegacy(string fileNname, string domainName)
        {
            try
            {
                assemblyFileName = fileNname;
                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationBase       = Path.GetDirectoryName(assemblyFileName);
                setup.PrivateBinPath        = AppDomain.CurrentDomain.BaseDirectory;
                setup.ApplicationName       = Path.GetFileName(Assembly.GetExecutingAssembly().Location());
                setup.ShadowCopyFiles       = Environment.GetEnvironmentVariable("AppDomainSetup.ShadowCopyFiles") ?? "true";
                setup.ShadowCopyDirectories = Path.GetDirectoryName(assemblyFileName);

                appDomain = AppDomain.CreateDomain(domainName, null, setup);
                // EXPECTED TO FAIL VERY OFTEN. IT'S NORMAL.
                remoteExecutor            = (RemoteExecutor)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(RemoteExecutor).ToString());
                remoteExecutor.searchDirs = ExecuteOptions.options.searchDirs;
                return(true);
            }
            catch
            {
                return(false);
            }
        }
Exemple #4
0
        /// <summary>
        /// This method implements compiling and execution of the script.
        /// </summary>
        void ExecuteImpl()
        {
            try
            {
                //System.Diagnostics.Debug.Assert(false);
                if (options.processFile)
                {
                    CSharpParser.InitInfo initInfo = options.initContext as CSharpParser.InitInfo;
                    if (initInfo != null && initInfo.CoInitializeSecurity)
                        ComInitSecurity(initInfo.RpcImpLevel, initInfo.EoAuthnCap);

                    if (options.local)
                        Environment.CurrentDirectory = Path.GetDirectoryName(Path.GetFullPath(options.scriptFileName));

                    if (!options.noLogo)
                    {
                        Console.WriteLine(AppInfo.appLogo);
                    }

                    if (Environment.GetEnvironmentVariable("EntryScript") == null)
                        Environment.SetEnvironmentVariable("EntryScript", Path.GetFullPath(options.scriptFileName));

                    {
                        CSSUtils.VerbosePrint("> ----------------", options);
                        CSSUtils.VerbosePrint("  TragetFramework: " + options.TargetFramework, options);
                        CSSUtils.VerbosePrint("  Provider: " + options.altCompiler, options);
                        try
                        {
                            CSSUtils.VerbosePrint("  Engine: " + Assembly.GetExecutingAssembly().Location, options);
                        }
                        catch { }

                        try
                        {
                            CSSUtils.VerbosePrint(string.Format("  Console Encoding: {0} ({1}) - {2}", Console.OutputEncoding.WebName, Console.OutputEncoding.EncodingName, (Utils.IsDefaultConsoleEncoding ? "system default" : "set by engine")), options);
                        }
                        catch { } //will fail for windows app

                        CSSUtils.VerbosePrint("  CurrentDirectory: " + Environment.CurrentDirectory, options);

                        if (!Utils.IsLinux() && options.verbose)
                        {
                            CSSUtils.VerbosePrint("  NuGet manager: " + NuGet.NuGetExeView, options);
                            CSSUtils.VerbosePrint("  NuGet cache: " + NuGet.NuGetCacheView, options);
                        }
                        CSSUtils.VerbosePrint("  Executing: " + Path.GetFullPath(options.scriptFileName), options);
                        CSSUtils.VerbosePrint("  Script arguments: ", options);
                        for (int i = 0; i < scriptArgs.Length; i++)
                            CSSUtils.VerbosePrint("    " + i + " - " + scriptArgs[i], options);
                        CSSUtils.VerbosePrint("  SearchDirectories: ", options);

                        {
                            int offset = 0;
                            if (Path.GetFullPath(options.searchDirs[0]) != Environment.CurrentDirectory)
                            {
                                CSSUtils.VerbosePrint("    0 - " + Environment.CurrentDirectory, options);
                                offset++;
                            }
                            for (int i = 0; i < options.searchDirs.Length; i++)
                                CSSUtils.VerbosePrint("    " + (i + offset) + " - " + options.searchDirs[i], options);
                        }
                        CSSUtils.VerbosePrint("> ----------------", options);
                        CSSUtils.VerbosePrint("", options);
                    }

                    // The following long comment is also reflected on Wiki

                    // Execution consist of multiple stages and some of them need to be atomic and need to be synchronized system wide. 
                    // Note: synchronization (concurrency control) may only be required for execution of a given script by two or more competing
                    // processes. If one process executes script_a.cs and another one executes script_b.cs then there is no need for any synchronization 
                    // as the script files are different and their executions do not collide with each other.

                    // ---
                    // VALIDATION
                    // First, script should be validated: assessed for having valid already compiled up to date assembly. Validation is done 
                    // by checking if the compiled assembly available at all and then comparing timestamps of the assembly and the script file.
                    // After all on checks are done all script dependencies (imports and ref assemblies) are also validated. Dependency validation is
                    // also timestamp based. For the script the dependencies are identified by parsing the script and for the assembly by extracting the 
                    // dependencies metadata injected into assembly during the last compilation by the script engine.
                    // The whole validation stage is atomic and it's synchronized system wide via SystemWideLock validatingFileLock. 
                    // SystemWideLock is a decorated Mutex-like system wide synchronization object: Mutex on Windows and file-lock on Linux. 
                    // This stage is very fast as there is no heavy lifting to be done just comparing timestamps.
                    // Timeout is infinite as there is very little chance for the stage to hang.
                    // ---
                    // COMPILATION
                    // Next, if assembly is valid (the script hasn't been changed since last compilation) the it is loaded for further execution without recompilation. 
                    // Otherwise it is compiled again. Compilation stage is also atomic, so concurrent compilations (if happen) do not try to build the assembly potentially 
                    // in the same location with the same name (e.g. caching). The whole validation stage is atomic and it's also synchronized system wide via SystemWideLock 
                    // 'compilingFileLock'. 
                    // This stage is potentially heavy. Some compilers, while being relatively fast may introduce significant startup overhead (like Roslyn). 
                    // That is why caching is a preferred execution approach.
                    // Timeout is fixed as there is a chance that the third party compiler can hang.
                    // ---
                    // EXECUTION
                    // Next, the script assembly needs to be loaded/executed.This stage is extremely heavy as the execution may take infinite time depending on business logic. 
                    // When the assembly file is loaded it is locked by CLR and none can delete/recreate it. Meaning that if any one is to recompile the assembly then this
                    // cannot be done until the execution is completed and CLR releases the assembly file. System wide synchronization doesn't make much sense in this case 
                    // as open end waiting is not practical at all. Thus it's more practical to let the compiler throw an informative locking (access denied) exception. 
                    //
                    // Note: if the assembly is loaded as in-memory file copy (options.inMemoryAsm) then the assembly locking is completely eliminated. This is in fact an extremely 
                    // attractive execution model as it eliminates any problems associated with the assembly looking during the execution. The only reason why it's not activated by 
                    // default is contradicts the traditional .NET loading model when the assembly loaded as a file. 
                    //
                    // While execution stage cannot benefit from synchronization it is still using executingFileLock synchronization object. Though the objective is not to wait 
                    // when file lock is detected but rather to detect unlocking and start compiling with a little delay. Reason for this is that (on Windows at least) CLR holds 
                    // the file lock a little bit longer event after the assembly execution is finished. It is completely undocumented CLR behaver, that is hard to catch and reproduce. 
                    // It has bee confirmed by the users that this work around helps in the intense concurrent "border line" scenarios. Though there is no warranty it will be still 
                    // valid with any future releases of CLR. There ware no reports about this behaver on Linux.
                    // ---
                    // Synchronizing the stages via the lock object that is based on the assembly file name seems like the natural and best option. However the actual assembly name 
                    // (unless caching is active) is only determined during the compilation, which needs to be synchronized (chicken egg problem). Thus script file is a more practical 
                    // (and more conservative) approach for basing synchronization objects identity. Though if the need arises the assembly-based approach can be attempted.
                    // ------------------------------------------------------
                    // The concurrency model described above was an unconditional behaver until v3.16
                    // Since v3.16 concurrency model can be chosen based on the user preferences:
                    // * ConcurrencyControl.HighResolution 
                    //      The model described above.
                    //
                    // * ConcurrencyControl.Standard 
                    //      Due to the limited choices with the system wide named synchronization objects on Linux both Validation and Compilations stages are treated as a single stage,
                    //      controlled by a single synch object compilingFileLock. 
                    //      This happens to be a good default choice for Windows as well.
                    //
                    // * ConcurrencyControl.None 
                    //      All synchronization is the responsibility of the hosting environment.

                    using (SystemWideLock validatingFileLock = new SystemWideLock(options.scriptFileName, "v"))
                    using (SystemWideLock compilingFileLock = new SystemWideLock(options.scriptFileName, null))
                    using (SystemWideLock executingFileLock = new SystemWideLock(options.scriptFileName, "e"))
                    {
                        bool lockByCSScriptEngine = false;
                        bool lockedByCompiler = false;

                        // --- VALIDATE ---
                        switch (options.concurrencyControl)
                        {
                            case ConcurrencyControl.Standard: lockedByCompiler = !compilingFileLock.Wait(3000); break;
                            case ConcurrencyControl.HighResolution: lockByCSScriptEngine = !validatingFileLock.Wait(-1); break;
                        }

                        //GetAvailableAssembly also checks timestamps
                        string assemblyFileName = options.useCompiled ? GetAvailableAssembly(options.scriptFileName) : null;

                        if (options.useCompiled && options.useSmartCaching)
                        {
                            if (assemblyFileName != null)
                            {
                                if (MetaDataItems.IsOutOfDate(options.scriptFileName, assemblyFileName))
                                {
                                    assemblyFileName = null;
                                }
                            }
                        }

                        if (options.forceCompile && assemblyFileName != null)
                        {
                            switch (options.concurrencyControl)
                            {
                                case ConcurrencyControl.Standard: /*we already acquired compiler lock*/ break;
                                case ConcurrencyControl.HighResolution: lockedByCompiler = !compilingFileLock.Wait(3000); break;
                            }

                            //If file is still locked (lockedByCompiler == true) FileDelete will throw the exception
                            Utils.FileDelete(assemblyFileName, true);
                            assemblyFileName = null;
                        }

                        if (assemblyFileName != null)
                        {
                            //OK, there is a compiled script file and it is current.
                            //Validating stage is over as we are not going to recompile the script 
                            //Release the lock immediately so other instances can do their validation if waiting.
                            validatingFileLock.Release();
                        }
                        else
                        {
                            //do not release validation lock as we will need to compile the script and any pending validations 
                            //will need to wait until we finish.
                        }

                        //add searchDirs to PATH to support search path for native dlls
                        //need to do this before compilation or execution
                        string path = Environment.GetEnvironmentVariable("PATH");
                        foreach (string s in options.searchDirs)
                            path += ";" + s;
#if net1
                            SetEnvironmentVariable("PATH", path);
#else
                        Environment.SetEnvironmentVariable("PATH", path);
#endif
                        //it is possible that there are fully compiled/cached and up to date script but no host compiled yet
                        string host = ScriptLauncherBuilder.GetLauncherName(assemblyFileName);
                        bool surrogateHostMissing = (options.useSurrogateHostingProcess &&
                                                    (!File.Exists(host) || !CSSUtils.HaveSameTimestamp(host, assemblyFileName)));


                        // --- COMPILE ---
                        if (options.buildExecutable || !options.useCompiled || (options.useCompiled && assemblyFileName == null) || options.forceCompile || surrogateHostMissing)
                        {
                            // Wait for other COMPILATION to complete(if any)

                            // infinite is not good here as it may block forever but continuing while the file is still locked will
                            // throw a nice informative exception
                            switch (options.concurrencyControl)
                            {
                                case ConcurrencyControl.Standard: /*we already acquired compiler lock*/ break;
                                case ConcurrencyControl.HighResolution: lockedByCompiler = !compilingFileLock.Wait(3000); break;
                            }

                            //no need to act on lockedByCompiler/lockedByHost as Compile(...) will throw the exception

                            if (!options.inMemoryAsm && !Utils.IsLinux())
                            {
                                // wait for other EXECUTION to complete (if any)
                                bool lockedByHost = !executingFileLock.Wait(1000);

                                if (!lockedByHost)
                                {
                                    //!lockedByHost means that if there is a host executing the assemblyFileName script it has finished the execution 
                                    //but the assemblyFileName file itself may still be locked by the IO because it's host process may be just exiting
                                    Utils.WaitForFileIdle(assemblyFileName, 1000);
                                }
                            }

                            try
                            {
                                CSSUtils.VerbosePrint("Compiling script...", options);
                                CSSUtils.VerbosePrint("", options);

                                TimeSpan initializationTime = Profiler.Stopwatch.Elapsed;
                                Profiler.Stopwatch.Reset();
                                Profiler.Stopwatch.Start();

                                assemblyFileName = Compile(options.scriptFileName);

                                if (Profiler.Stopwatch.IsRunning)
                                {
                                    Profiler.Stopwatch.Stop();
                                    TimeSpan compilationTime = Profiler.Stopwatch.Elapsed;
                                    CSSUtils.VerbosePrint("Initialization time: " + initializationTime.TotalMilliseconds + " msec", options);
                                    CSSUtils.VerbosePrint("Compilation time:    " + compilationTime.TotalMilliseconds + " msec", options);
                                    CSSUtils.VerbosePrint("> ----------------", options);
                                    CSSUtils.VerbosePrint("", options);
                                }
                            }
                            catch
                            {
                                if (!CSSUtils.IsRuntimeErrorReportingSupressed)
                                {
                                    print("Error: Specified file could not be compiled.\n");
                                    if (NuGet.newPackageWasInstalled)
                                    {
                                        print("> -----\nA new NuGet package has been installed. If some of it's components are not found you may need to restart the script again.\n> -----\n");
                                    }
                                }
                                throw;
                            }
                            finally
                            {
                                //release as soon as possible
                                validatingFileLock.Release();
                                compilingFileLock.Release();
                            }
                        }
                        else
                        {
                            Profiler.Stopwatch.Stop();
                            CSSUtils.VerbosePrint("  Loading script from cache...", options);
                            CSSUtils.VerbosePrint("", options);
                            CSSUtils.VerbosePrint("  Cache file: \n       " + assemblyFileName, options);
                            CSSUtils.VerbosePrint("> ----------------", options);
                            CSSUtils.VerbosePrint("Initialization time: " + Profiler.Stopwatch.Elapsed.TotalMilliseconds + " msec", options);
                            CSSUtils.VerbosePrint("> ----------------", options);
                        }

                        // --- EXECUTE ---
                        if (!options.supressExecution)
                        {
                            try
                            {
                                if (options.useSurrogateHostingProcess)
                                {
                                    throw new SurrogateHostProcessRequiredException(assemblyFileName, scriptArgs, options.startDebugger);
                                }

                                if (options.startDebugger)
                                {
                                    SaveDebuggingMetadata(options.scriptFileName);

                                    System.Diagnostics.Debugger.Launch();
                                    if (System.Diagnostics.Debugger.IsAttached)
                                    {
                                        System.Diagnostics.Debugger.Break();
                                    }
                                }


                                if (options.useCompiled || options.cleanupShellCommand != "")
                                {
                                    AssemblyResolver.CacheProbingResults = true; //it is reasonable safe to do the aggressive probing as we are executing only a single script (standalone execution not a script hosting model)

                                    //despite the name of the class the execution (assembly loading) will be in the current domain
                                    //I am just reusing some functionality of the RemoteExecutor class.

                                    RemoteExecutor executor = new RemoteExecutor(options.searchDirs);
                                    executor.ExecuteAssembly(assemblyFileName, scriptArgs, executingFileLock);
                                }
                                else
                                {
                                    //Load and execute assembly in a different domain to make it possible to unload assembly before clean up
                                    AssemblyExecutor executor = new AssemblyExecutor(assemblyFileName, "AsmExecution");
                                    executor.Execute(scriptArgs);
                                }
                            }
                            catch (SurrogateHostProcessRequiredException)
                            {
                                throw;
                            }
                            catch
                            {
                                if (!CSSUtils.IsRuntimeErrorReportingSupressed)
                                    print("Error: Specified file could not be executed.\n");
                                throw;
                            }

                            //cleanup
                            if (File.Exists(assemblyFileName) && !options.useCompiled && options.cleanupShellCommand == "")
                            {
                                Utils.ClearFile(assemblyFileName);
                            }

                            if (options.cleanupShellCommand != "")
                            {
                                try
                                {
                                    string counterFile = Path.Combine(GetScriptTempDir(), "counter.txt");
                                    int prevRuns = 0;

                                    if (File.Exists(counterFile))
                                        using (StreamReader sr = new StreamReader(counterFile))
                                        {
                                            prevRuns = int.Parse(sr.ReadToEnd());
                                        }

                                    if (prevRuns > options.doCleanupAfterNumberOfRuns)
                                    {
                                        prevRuns = 1;
                                        string[] cmd = options.ExtractShellCommand(options.cleanupShellCommand);
                                        if (cmd.Length > 1)
                                            Process.Start(cmd[0], cmd[1]);
                                        else
                                            Process.Start(cmd[0]);
                                    }
                                    else
                                        prevRuns++;

                                    using (StreamWriter sw = new StreamWriter(counterFile))
                                        sw.Write(prevRuns);
                                }
                                catch { }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Exception ex = e;
                if (e is System.Reflection.TargetInvocationException)
                    ex = e.InnerException;

                if (rethrow || e is SurrogateHostProcessRequiredException)
                {
                    lastException = ex;
                }
                else
                {
                    Environment.ExitCode = 1;
                    if (!CSSUtils.IsRuntimeErrorReportingSupressed)
                    {
                        if (options.reportDetailedErrorInfo)
                            print(ex.ToString());
                        else
                            print(ex.Message); //Mono friendly
                    }
                }
            }
        }
Exemple #5
0
		/// <summary>
		/// This method implements compiling and execution of the script.  
		/// </summary>
		private void ExecuteImpl()
		{
			try
			{
				if (options.processFile)
				{
					if (options.local)
						Environment.CurrentDirectory = Path.GetDirectoryName(Path.GetFullPath(options.scriptFileName));

					if (!options.noLogo)
					{
						Console.WriteLine(AppInfo.appLogo);
					}
					
					//compile
					string assemblyFileName = options.useCompiled ? GetAvailableAssembly(options.scriptFileName) : null;

					if (options.useCompiled && options.useSmartCaching)
					{
						if (assemblyFileName != null)
						{
							MetaDataItems depInfo = new MetaDataItems();
							
							if (depInfo.ReadFileStamp(assemblyFileName))
							{
								string dependencyFile = "";
								foreach (MetaDataItems.MetaDataItem item in depInfo.items)
								{
									if (item.assembly)
										dependencyFile = Path.Combine(Path.GetDirectoryName(options.scriptFileName), item.file); //assembly should be in the same directory with the script
									else
										dependencyFile = FileParser.ResolveFile(item.file, options.searchDirs, false);

									FileInfo info = new FileInfo(dependencyFile);
									if (!info.Exists || info.LastWriteTimeUtc != item.date)
									{
										assemblyFileName = null;
										break;
									}
								}
							}
							else
								assemblyFileName = null;
						}
					}

					if (options.forceCompile && assemblyFileName != null)
					{
						File.Delete(assemblyFileName);
						assemblyFileName = null;
					}


					if (options.buildExecutable || !options.useCompiled || (options.useCompiled && assemblyFileName == null) || options.forceCompile)
					{
						try
						{
							assemblyFileName = Compile(options.scriptFileName);
						}
						catch 
						{
							print("Error: Specified file could not be compiled.\n");
							throw;
						}
					}

					//execute
					if (!options.supressExecution) 
					{
						try 
						{
							if (options.startDebugger)
							{
								System.Diagnostics.Debugger.Launch();
								if (System.Diagnostics.Debugger.IsAttached)
									System.Diagnostics.Debugger.Break();
							}	
							if (options.useCompiled || options.cleanupShellCommand != "")
							{
								//despite the name of the class the execution will be in the current domain
								RemoteExecutor executor = new RemoteExecutor(options.searchDirs); 
								executor.ExecuteAssembly(assemblyFileName, scriptArgs);
							}
							else 
							{
								ExecuteAssembly(assemblyFileName);
							}
						}
						catch 
						{
							print("Error: Specified file could not be executed.\n");
							throw;
						}	

						//cleanup
						if (File.Exists(assemblyFileName) && !options.useCompiled && options.cleanupShellCommand == "") 
						{
							try
							{
								File.Delete(assemblyFileName);
							}
							catch { }
						}

						if (options.cleanupShellCommand != "")
						{
							string counterFile = Path.Combine(GetScriptTempDir(), "counter.txt");
							int prevRuns = 0;
							try
							{
								using (StreamReader sr = new StreamReader(counterFile))
								{
									prevRuns = int.Parse(sr.ReadToEnd());
								}
							}
							catch { }
				
							if (prevRuns > options.doCleanupAfterNumberOfRuns)
						   	{
								prevRuns = 1;
								string[] cmd = options.ExtractShellCommand(options.cleanupShellCommand);
								if (cmd.Length > 1)
									Process.Start(cmd[0], cmd[1]);
								else
									Process.Start(cmd[0]);
							}
							else
								prevRuns++;

							try
							{
								using (StreamWriter sw = new StreamWriter(counterFile))
									sw.Write(prevRuns);
							}
							catch { }
						}
					}
				}
			}
			catch (Exception e) 
			{
				if (rethrow)
				{
					lastException = e;
				}
				else
				{
					Environment.ExitCode = 1;
					if (options.reportDetailedErrorInfo)
						print(e.ToString());
					else	
						print(e.Message); //Mono friendly
				}
			}
		}
Exemple #6
0
        /// <summary>
        /// This method implements compiling and execution of the script.
        /// </summary>
        void ExecuteImpl()
        {
            try
            {
                //System.Diagnostics.Debug.Assert(false);
                if (options.processFile)
                {
                    CSharpParser.InitInfo initInfo = options.initContext as CSharpParser.InitInfo;
                    if (initInfo != null && initInfo.CoInitializeSecurity)
                        ComInitSecurity(initInfo.RpcImpLevel, initInfo.EoAuthnCap);

                    if (options.local)
                        Environment.CurrentDirectory = Path.GetDirectoryName(Path.GetFullPath(options.scriptFileName));

                    if (!options.noLogo)
                    {
                        Console.WriteLine(AppInfo.appLogo);
                    }

                    if (Environment.GetEnvironmentVariable("EntryScript") == null)
                        Environment.SetEnvironmentVariable("EntryScript", Path.GetFullPath(options.scriptFileName));

                    {
                        CSSUtils.VerbosePrint("> ----------------", options);
                        CSSUtils.VerbosePrint("  TragetFramework: " + options.TargetFramework, options);
                        try
                        {
                            CSSUtils.VerbosePrint(string.Format("  Console Encoding: {0} ({1}) - {2}", Console.OutputEncoding.WebName, Console.OutputEncoding.EncodingName, (Utils.IsDeraultConsoleEncoding ? "system default" : "set by engine")), options);
                        }
                        catch { } //will fail for windows app

                        CSSUtils.VerbosePrint("  CurrentDirectory: " + Environment.CurrentDirectory, options);
                        if (!Utils.IsLinux())
                        {
                            CSSUtils.VerbosePrint("  NuGet manager: " + NuGet.NuGetExeView, options);
                            CSSUtils.VerbosePrint("  NuGet cache: " + NuGet.NuGetCacheView, options);
                        }
                        CSSUtils.VerbosePrint("  Executing: " + Path.GetFullPath(options.scriptFileName), options);
                        CSSUtils.VerbosePrint("  Script arguments: ", options);
                        for (int i = 0; i < scriptArgs.Length; i++)
                            CSSUtils.VerbosePrint("    " + i + " - " + scriptArgs[i], options);
                        CSSUtils.VerbosePrint("  SearchDirectories: ", options);
                        for (int i = 0; i < options.searchDirs.Length; i++)
                            CSSUtils.VerbosePrint("    " + i + " - " + options.searchDirs[i], options);
                        CSSUtils.VerbosePrint("> ----------------", options);
                        CSSUtils.VerbosePrint("", options);
                    }

                    bool compilingFileUnlocked = false;
                    //Infinite timeout is not good choice here as it may block forever but continuing while the file is still locked will
                    //throw a nice informative exception.

                    using (Mutex validatingFileLock = Utils.FileLock(options.scriptFileName, "TimestampValidating"))
                    using (Mutex compilingFileLock = Utils.FileLock(options.scriptFileName, "Compiling"))
                    using (Mutex executingFileLock = Utils.FileLock(options.scriptFileName, "Executing")) //will need it in the future for more accurate concurrency control 
                        try
                        {
                            //validate
                            bool availableForChecking = validatingFileLock.WaitOne(-1, false);

                            //GetAvailableAssembly also checks timestamps
                            string assemblyFileName = options.useCompiled ? GetAvailableAssembly(options.scriptFileName) : null;

                            if (options.useCompiled && options.useSmartCaching)
                            {
                                if (assemblyFileName != null)
                                {
                                    if (MetaDataItems.IsOutOfDate(options.scriptFileName, assemblyFileName))
                                    {
                                        assemblyFileName = null;
                                    }
                                }
                            }

                            if (options.forceCompile && assemblyFileName != null)
                            {
                                bool lockedByCompiler = !compilingFileLock.WaitOne(3000, false);
                                //no need to act on lockedByCompiler as FileDelete will throw the exception

                                Utils.FileDelete(assemblyFileName, true);
                                assemblyFileName = null;
                            }

                            if (assemblyFileName != null)
                                Utils.ReleaseFileLock(validatingFileLock); //OK, there is a compiled script file and it is current 

                            //add searchDirs to PATH to support search path for native dlls
                            //need to do this before compilation or execution
                            string path = Environment.GetEnvironmentVariable("PATH");
                            foreach (string s in options.searchDirs)
                                path += ";" + s;
#if net1
                            SetEnvironmentVariable("PATH", path);
#else
                            Environment.SetEnvironmentVariable("PATH", path);
#endif
                            //it is possible that there are fully compiled/cached and up to date script but no host compiled yet
                            string host = ScriptLauncherBuilder.GetLauncherName(assemblyFileName);
                            bool surrogateHostMissing = (options.useSurrogateHostingProcess &&
                                (!File.Exists(host) || !CSSUtils.HaveSameTimestamp(host, assemblyFileName)));


                            //compile
                            if (options.buildExecutable || !options.useCompiled || (options.useCompiled && assemblyFileName == null) || options.forceCompile || surrogateHostMissing)
                            {
                                //infinite is not good here as it may block forever but continuing while the file is still locked will
                                //throw a nice informative exception
                                bool lockedByCompiler = !compilingFileLock.WaitOne(3000, false);
                                bool lockedByHost = !executingFileLock.WaitOne(3000, false);

                                if (!lockedByHost)
                                {
                                    //!lockedByHost means that if there is a host executing the assemblyFileName script it has finished the execution 
                                    //but the assemblyFileName file itself may still be locked by the IO because it's host process may be just exiting
                                    Utils.WaitForFileIdle(assemblyFileName, 1000);
                                }

                                //no need to act on lockedByCompiler/lockedByHost as Compile(...) will throw the exception

                                try
                                {
                                    CSSUtils.VerbosePrint("Compiling script...", options);
                                    CSSUtils.VerbosePrint("", options);

                                    TimeSpan initializationTime = Profiler.Stopwatch.Elapsed;
                                    Profiler.Stopwatch.Reset();
                                    Profiler.Stopwatch.Start();

                                    assemblyFileName = Compile(options.scriptFileName);


                                    Debug.WriteLine("Asm is compiled");

                                    if (Profiler.Stopwatch.IsRunning)
                                    {
                                        Profiler.Stopwatch.Stop();
                                        TimeSpan compilationTime = Profiler.Stopwatch.Elapsed;
                                        CSSUtils.VerbosePrint("Initialization time: " + initializationTime.TotalMilliseconds + " msec", options);
                                        CSSUtils.VerbosePrint("Compilation time:    " + compilationTime.TotalMilliseconds + " msec", options);
                                        CSSUtils.VerbosePrint("> ----------------", options);
                                        CSSUtils.VerbosePrint("", options);
                                    }
                                }
                                catch
                                {
                                    if (!CSSUtils.IsRuntimeErrorReportingSupressed)
                                        print("Error: Specified file could not be compiled.\n");
                                    throw;
                                }
                                finally
                                {
                                    Utils.ReleaseFileLock(validatingFileLock);
                                    Utils.ReleaseFileLock(compilingFileLock);
                                    compilingFileUnlocked = true;
                                }
                            }
                            else
                            {
                                Profiler.Stopwatch.Stop();
                                CSSUtils.VerbosePrint("  Loading script from cache...", options);
                                CSSUtils.VerbosePrint("", options);
                                CSSUtils.VerbosePrint("  Cache file: \n       " + assemblyFileName, options);
                                CSSUtils.VerbosePrint("> ----------------", options);
                                CSSUtils.VerbosePrint("Initialization time: " + Profiler.Stopwatch.Elapsed.TotalMilliseconds + " msec", options);
                                CSSUtils.VerbosePrint("> ----------------", options);
                            }

                            //execute
                            if (!options.supressExecution)
                            {
                                try
                                {
                                    if (options.useSurrogateHostingProcess)
                                    {
                                        throw new SurrogateHostProcessRequiredException(assemblyFileName, scriptArgs, options.startDebugger);
                                    }

                                    if (options.startDebugger)
                                    {
                                        SaveDebuggingMetadata(options.scriptFileName);

                                        System.Diagnostics.Debugger.Launch();
                                        if (System.Diagnostics.Debugger.IsAttached)
                                        {
                                            System.Diagnostics.Debugger.Break();
                                        }
                                    }


                                    if (options.useCompiled || options.cleanupShellCommand != "")
                                    {
                                        AssemblyResolver.CacheProbingResults = true; //it is reasonable safe to do the aggressive probing as we are executing only a single script (standalone execution not a script hosting model)

                                        //despite the name of the class the execution (assembly loading) will be in the current domain
                                        //I am just reusing some functionality of the RemoteExecutor class.

                                        RemoteExecutor executor = new RemoteExecutor(options.searchDirs);
                                        executor.ExecuteAssembly(assemblyFileName, scriptArgs, executingFileLock);
                                    }
                                    else
                                    {
                                        //Load and execute assembly in a different domain to make it possible to unload assembly before clean up
                                        AssemblyExecutor executor = new AssemblyExecutor(assemblyFileName, "AsmExecution");
                                        executor.Execute(scriptArgs);
                                    }
                                }
                                catch (SurrogateHostProcessRequiredException)
                                {
                                    throw;
                                }
                                catch
                                {
                                    if (!CSSUtils.IsRuntimeErrorReportingSupressed)
                                        print("Error: Specified file could not be executed.\n");
                                    throw;
                                }

                                //cleanup
                                if (File.Exists(assemblyFileName) && !options.useCompiled && options.cleanupShellCommand == "")
                                {
                                    Utils.FileDelete(assemblyFileName);
                                }

                                //Debug.Assert(false);
                                if (options.cleanupShellCommand != "")
                                {
                                    try
                                    {
                                        string counterFile = Path.Combine(GetScriptTempDir(), "counter.txt");
                                        int prevRuns = 0;

                                        if (File.Exists(counterFile))
                                            using (StreamReader sr = new StreamReader(counterFile))
                                            {
                                                prevRuns = int.Parse(sr.ReadToEnd());
                                            }

                                        if (prevRuns > options.doCleanupAfterNumberOfRuns)
                                        {
                                            prevRuns = 1;
                                            string[] cmd = options.ExtractShellCommand(options.cleanupShellCommand);
                                            if (cmd.Length > 1)
                                                Process.Start(cmd[0], cmd[1]);
                                            else
                                                Process.Start(cmd[0]);
                                        }
                                        else
                                            prevRuns++;

                                        using (StreamWriter sw = new StreamWriter(counterFile))
                                            sw.Write(prevRuns);
                                    }
                                    catch { }
                                }
                            }
                        }
                        finally
                        {
                            //strictly speaking releasing isn't needed as the 'using' block ensures the mutex is released
                            //though some .NET versions/implementations may not do this.

                            if (!compilingFileUnlocked) //avoid throwing unnecessary exception
                                Utils.ReleaseFileLock(compilingFileLock);
                            Utils.ReleaseFileLock(executingFileLock);
                        }
                }
            }
            catch (Exception e)
            {
                Exception ex = e;
                if (e is System.Reflection.TargetInvocationException)
                    ex = e.InnerException;

                if (rethrow || e is SurrogateHostProcessRequiredException)
                {
                    lastException = ex;
                }
                else
                {
                    Environment.ExitCode = 1;
                    if (!CSSUtils.IsRuntimeErrorReportingSupressed)
                    {
                        if (options.reportDetailedErrorInfo)
                            print(ex.ToString());
                        else
                            print(ex.Message); //Mono friendly
                    }
                }
            }
        }
Exemple #7
0
        /// <summary>
        /// This method implements compiling and execution of the script.
        /// </summary>
        private void ExecuteImpl()
        {
            try
            {
                //System.Diagnostics.Debug.Assert(false);
                if (options.processFile)
                {
                    if (options.local)
                        Environment.CurrentDirectory = Path.GetDirectoryName(Path.GetFullPath(options.scriptFileName));

                    if (!options.noLogo)
                    {
                        Console.WriteLine(AppInfo.appLogo);
                    }

                    if (Environment.GetEnvironmentVariable("EntryScript") == null)
                        Environment.SetEnvironmentVariable("EntryScript", Path.GetFullPath(options.scriptFileName));

                    {
                        CSSUtils.VerbosePrint("> ----------------", options);
                        CSSUtils.VerbosePrint("  TragetFramework: " + options.TargetFramework, options);
                        CSSUtils.VerbosePrint("  CurrentDirectory: " + Environment.CurrentDirectory, options);
                        CSSUtils.VerbosePrint("  Executing: " + Path.GetFullPath(options.scriptFileName), options);
                        CSSUtils.VerbosePrint("  Script arguments: ", options);
                        for (int i = 0; i < scriptArgs.Length; i++)
                            CSSUtils.VerbosePrint("    " + i + " - " + scriptArgs[i], options);
                        CSSUtils.VerbosePrint("  SearchDirectories: ", options);
                        for (int i = 0; i < options.searchDirs.Length; i++)
                            CSSUtils.VerbosePrint("    " + i + " - " + options.searchDirs[i], options);
                        CSSUtils.VerbosePrint("> ----------------", options);
                        CSSUtils.VerbosePrint("", options);
                    }

                    bool fileUnlocked = false;
                    using (Mutex fileLock = new Mutex(false, "Process." + options.scriptFileName.GetHashCode().ToString()))
                        try
                        {
                            int start = Environment.TickCount;
                            fileLock.WaitOne(3000, false); //let other thread/process (if any) to finish loading/compiling the same file; 3 seconds should be enough, if you need more use more sophisticated synchronization
                            //Trace.WriteLine(">>>  Waited  " + (Environment.TickCount - start));

                            //compile
                            string assemblyFileName = options.useCompiled ? GetAvailableAssembly(options.scriptFileName) : null;

                            if (options.useCompiled && options.useSmartCaching)
                            {
                                if (assemblyFileName != null)
                                {
                                    if (MetaDataItems.IsOutOfDate(options.scriptFileName, assemblyFileName))
                                    {
                                        assemblyFileName = null;
                                    }
                                }
                            }

                            if (options.forceCompile && assemblyFileName != null)
                            {
                                File.Delete(assemblyFileName);
                                assemblyFileName = null;
                            }

                            //add searchDirs to PATH to support search path for native dlls
                            //need to do this before compilation or execution
                            string path = Environment.GetEnvironmentVariable("PATH");
                            foreach (string s in options.searchDirs)
                                path += ";" + s;
#if net1
                            SetEnvironmentVariable("PATH", path);
#else
                            Environment.SetEnvironmentVariable("PATH", path);
#endif
                            //it is possible that there are fully compiled/cached and up to date script but no host compiled yet
                            string host = ScriptLauncherBuilder.GetLauncherName(assemblyFileName);
                            bool surrogateHostMissing = (options.useSurrogateHostingProcess &&
                                (!File.Exists(host) || !CSSUtils.HaveSameTimestamp(host, assemblyFileName)));

                            if (options.buildExecutable || !options.useCompiled || (options.useCompiled && assemblyFileName == null) || options.forceCompile || surrogateHostMissing)
                            {
                                try
                                {
                                    CSSUtils.VerbosePrint("Compiling script...", options);
                                    CSSUtils.VerbosePrint("", options);

                                    TimeSpan initializationTime = Profiler.Stopwatch.Elapsed;
                                    Profiler.Stopwatch.Reset();
                                    Profiler.Stopwatch.Start();

                                    assemblyFileName = Compile(options.scriptFileName);

                                    if (Profiler.Stopwatch.IsRunning)
                                    {
                                        Profiler.Stopwatch.Stop();
                                        TimeSpan compilationTime = Profiler.Stopwatch.Elapsed;
                                        CSSUtils.VerbosePrint("Initialization time: " + initializationTime.TotalMilliseconds + " msec", options);
                                        CSSUtils.VerbosePrint("Compilation time:    " + compilationTime.TotalMilliseconds + " msec", options);
                                        CSSUtils.VerbosePrint("> ----------------", options);
                                        CSSUtils.VerbosePrint("", options);
                                    }
                                }
                                catch
                                {
                                    print("Error: Specified file could not be compiled.\n");
                                    throw;
                                }
                                finally
                                {
                                    try { fileLock.ReleaseMutex(); }
                                    catch { }
                                    fileUnlocked = true;
                                }
                            }
                            else
                            {
                                Profiler.Stopwatch.Stop();
                                CSSUtils.VerbosePrint("  Loading script from cache...", options);
                                CSSUtils.VerbosePrint("", options);
                                CSSUtils.VerbosePrint("  Cache file: \n       " + assemblyFileName, options);
                                CSSUtils.VerbosePrint("> ----------------", options);
                                CSSUtils.VerbosePrint("Initialization time: " + Profiler.Stopwatch.Elapsed.TotalMilliseconds + " msec", options);
                                CSSUtils.VerbosePrint("> ----------------", options);
                            }

                            //execute
                            if (!options.supressExecution)
                            {
                                try
                                {
                                    if (options.useSurrogateHostingProcess)
                                    {
                                        throw new SurrogateHostProcessRequiredException(assemblyFileName, scriptArgs, options.startDebugger);
                                    }

                                    if (options.startDebugger)
                                    {
                                        System.Diagnostics.Debugger.Launch();
                                        if (System.Diagnostics.Debugger.IsAttached)
                                            System.Diagnostics.Debugger.Break();
                                    }

                                    if (options.useCompiled || options.cleanupShellCommand != "")
                                    {
                                        AssemblyResolver.CacheProbingResults = true; //it is reasonable safe to do the agressive probing as we are executing only a single script (standalone execution not a script hosting model)

                                        //despite the name of the class the execution (assembly loading) will be in the current domain
                                        //I am just reusing some functionality of the RemoteExecutor class.
                                        RemoteExecutor executor = new RemoteExecutor(options.searchDirs);
                                        executor.ExecuteAssembly(assemblyFileName, scriptArgs);
                                    }
                                    else
                                    {
                                        //Load and execute assembly in a different domain to make it possible to unload assembly before clean up
                                        AssemblyExecutor executor = new AssemblyExecutor(assemblyFileName, "AsmExecution");
                                        executor.Execute(scriptArgs);
                                    }
                                }
                                catch (SurrogateHostProcessRequiredException)
                                {
                                    throw;
                                }
                                catch
                                {
                                    print("Error: Specified file could not be executed.\n");
                                    throw;
                                }

                                //cleanup
                                if (File.Exists(assemblyFileName) && !options.useCompiled && options.cleanupShellCommand == "")
                                {
                                    try
                                    {
                                        File.Delete(assemblyFileName);
                                    }
                                    catch { }
                                }

                                if (options.cleanupShellCommand != "")
                                {
                                    try
                                    {
                                        string counterFile = Path.Combine(GetScriptTempDir(), "counter.txt");
                                        int prevRuns = 0;

                                        using (StreamReader sr = new StreamReader(counterFile))
                                        {
                                            prevRuns = int.Parse(sr.ReadToEnd());
                                        }

                                        if (prevRuns > options.doCleanupAfterNumberOfRuns)
                                        {
                                            prevRuns = 1;
                                            string[] cmd = options.ExtractShellCommand(options.cleanupShellCommand);
                                            if (cmd.Length > 1)
                                                Process.Start(cmd[0], cmd[1]);
                                            else
                                                Process.Start(cmd[0]);
                                        }
                                        else
                                            prevRuns++;

                                        using (StreamWriter sw = new StreamWriter(counterFile))
                                            sw.Write(prevRuns);
                                    }
                                    catch { }
                                }
                            }
                        }
                        finally
                        {
                            try { if (!fileUnlocked) fileLock.ReleaseMutex(); } //using fileUnlocked to avoid throwing unnecessary exception
                            catch { }
                        }
                }
            }
            catch (Exception e)
            {
                Exception ex = e;
                if (e is System.Reflection.TargetInvocationException)
                    ex = e.InnerException;

                if (rethrow || e is SurrogateHostProcessRequiredException)
                {
                    lastException = ex;
                }
                else
                {
                    Environment.ExitCode = 1;
                    if (options.reportDetailedErrorInfo)
                        print(ex.ToString());
                    else
                        print(ex.Message); //Mono friendly
                }
            }
        }