public void Remove (RemoteBuildEngine builder) { foreach (var p in builders) { if (p.Value.Remove (builder)) { if (p.Value.Count == 0) builders.Remove (p.Key); return; } } }
internal static async Task<RemoteProjectBuilder> GetProjectBuilder (TargetRuntime runtime, string minToolsVersion, string file, string solutionFile, int customId, bool lockBuilder = false) { using (await buildersLock.EnterAsync ()) { //attempt to use 14.0 builder first if available string toolsVersion = "14.0"; string binDir = runtime.GetMSBuildBinPath ("14.0"); if (binDir == null) { toolsVersion = "12.0"; binDir = runtime.GetMSBuildBinPath ("12.0"); if (binDir == null) { //fall back to 4.0, we know it's always available toolsVersion = "4.0"; } } // Check the ToolsVersion we found can handle the project // The check is only done for the .NET framework since Mono doesn't really have the concept of ToolsVersion. // On Mono we'll just try to build with whatever is installed. Version tv, mtv; if (runtime is MsNetTargetRuntime && Version.TryParse (toolsVersion, out tv) && Version.TryParse (minToolsVersion, out mtv) && tv < mtv) { string error = null; if (minToolsVersion == "12.0") error = "MSBuild 2013 is not installed. Please download and install it from " + "http://www.microsoft.com/en-us/download/details.aspx?id=40760"; throw new InvalidOperationException (error ?? string.Format ( "Runtime '{0}' does not have MSBuild '{1}' ToolsVersion installed", runtime.Id, toolsVersion) ); } //one builder per solution string builderKey = runtime.Id + " # " + solutionFile + " # " + customId; RemoteBuildEngine builder = null; if (lockBuilder) { foreach (var b in builders.GetBuilders (builderKey)) { if (b.Lock ()) { builder = b; break; } b.Unlock (); } } else builder = builders.GetBuilders (builderKey).FirstOrDefault (); if (builder != null) { builder.ReferenceCount++; return new RemoteProjectBuilder (file, builder); } return await Task.Run (() => { //always start the remote process explicitly, even if it's using the current runtime and fx //else it won't pick up the assembly redirects from the builder exe var exe = GetExeLocation (runtime, toolsVersion); MonoDevelop.Core.Execution.RemotingService.RegisterRemotingChannel (); var pinfo = new ProcessStartInfo (exe) { UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardInput = true, }; runtime.GetToolsExecutionEnvironment ().MergeTo (pinfo); Process p = null; try { IBuildEngine engine; if (!runLocal) { p = runtime.ExecuteAssembly (pinfo); // The builder app will write the build engine reference // after reading the process id from the standard input ManualResetEvent ev = new ManualResetEvent (false); string responseKey = "[MonoDevelop]"; string sref = null; p.ErrorDataReceived += (sender, e) => { if (e.Data == null) { if (string.IsNullOrEmpty (sref)) LoggingService.LogError ("The MSBuild builder exited before initializing"); return; } if (e.Data.StartsWith (responseKey, StringComparison.Ordinal)) { sref = e.Data.Substring (responseKey.Length); ev.Set (); } else Console.WriteLine (e.Data); }; p.BeginErrorReadLine (); p.StandardInput.WriteLine (Process.GetCurrentProcess ().Id.ToString ()); if (!ev.WaitOne (TimeSpan.FromSeconds (5))) throw new Exception ("MSBuild process could not be started"); byte [] data = Convert.FromBase64String (sref); MemoryStream ms = new MemoryStream (data); BinaryFormatter bf = new BinaryFormatter (); engine = (IBuildEngine)bf.Deserialize (ms); } else { var asm = System.Reflection.Assembly.LoadFrom (exe); var t = asm.GetType ("MonoDevelop.Projects.MSBuild.BuildEngine"); engine = (IBuildEngine)Activator.CreateInstance (t); } engine.SetCulture (GettextCatalog.UICulture); engine.SetGlobalProperties (GetCoreGlobalProperties (solutionFile)); foreach (var gpp in globalPropertyProviders) engine.SetGlobalProperties (gpp.GetGlobalProperties ()); builder = new RemoteBuildEngine (p, engine); } catch { if (p != null) { try { p.Kill (); } catch { } } throw; } builders.Add (builderKey, builder); builder.ReferenceCount = 1; builder.Disconnected += async delegate { using (await buildersLock.EnterAsync ()) builders.Remove (builder); }; if (lockBuilder) builder.Lock (); return new RemoteProjectBuilder (file, builder); }); } }
internal static async void ReleaseProjectBuilder (RemoteBuildEngine engine) { using (await buildersLock.EnterAsync ()) { if (--engine.ReferenceCount != 0) return; builders.Remove (engine); } engine.Dispose (); }
public void Add (string key, RemoteBuildEngine builder) { List<RemoteBuildEngine> list; if (!builders.TryGetValue (key, out list)) builders [key] = list = new List<RemoteBuildEngine> (); list.Add (builder); }
internal static async Task<RemoteProjectBuilder> GetProjectBuilder (TargetRuntime runtime, string minToolsVersion, string file, string solutionFile, int customId, bool requiresMicrosoftBuild, bool lockBuilder = false) { Version mtv = Version.Parse (minToolsVersion); if (mtv >= new Version (15,0)) requiresMicrosoftBuild = true; using (await buildersLock.EnterAsync ()) { string binDir; var toolsVersion = GetNewestInstalledToolsVersion (runtime, requiresMicrosoftBuild, out binDir); Version tv; if (Version.TryParse (toolsVersion, out tv) && Version.TryParse (minToolsVersion, out mtv) && tv < mtv) { throw new InvalidOperationException (string.Format ( "Project requires MSBuild ToolsVersion '{0}' which is not supported by runtime '{1}'", toolsVersion, runtime.Id) ); } //one builder per solution string builderKey = runtime.Id + " # " + solutionFile + " # " + customId + " # " + requiresMicrosoftBuild; RemoteBuildEngine builder = null; if (lockBuilder) { foreach (var b in builders.GetBuilders (builderKey)) { if (b.Lock ()) { builder = b; break; } b.Unlock (); } } else builder = builders.GetBuilders (builderKey).FirstOrDefault (); if (builder != null) { builder.ReferenceCount++; return new RemoteProjectBuilder (file, builder); } return await Task.Run (async () => { //always start the remote process explicitly, even if it's using the current runtime and fx //else it won't pick up the assembly redirects from the builder exe var exe = GetExeLocation (runtime, toolsVersion, requiresMicrosoftBuild); MonoDevelop.Core.Execution.RemotingService.RegisterRemotingChannel (); var pinfo = new ProcessStartInfo (exe) { WorkingDirectory = binDir, UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardInput = true, }; runtime.GetToolsExecutionEnvironment ().MergeTo (pinfo); Process p = null; try { IBuildEngine engine; if (!runLocal) { p = runtime.ExecuteAssembly (pinfo); // The builder app will write the build engine reference // after reading the process id from the standard input var processStartedSignal = new TaskCompletionSource<bool> (); string responseKey = "[MonoDevelop]"; string sref = null; p.ErrorDataReceived += (sender, e) => { if (e.Data == null) { if (string.IsNullOrEmpty (sref)) LoggingService.LogError ("The MSBuild builder exited before initializing"); return; } if (e.Data.StartsWith (responseKey, StringComparison.Ordinal)) { sref = e.Data.Substring (responseKey.Length); processStartedSignal.SetResult (true); } else Console.WriteLine (e.Data); }; p.BeginErrorReadLine (); p.StandardInput.WriteLine (binDir); p.StandardInput.WriteLine (Process.GetCurrentProcess ().Id.ToString ()); if (await Task.WhenAny (processStartedSignal.Task, Task.Delay (5000)) != processStartedSignal.Task) throw new Exception ("MSBuild process could not be started"); byte [] data = Convert.FromBase64String (sref); MemoryStream ms = new MemoryStream (data); BinaryFormatter bf = new BinaryFormatter (); engine = (IBuildEngine)bf.Deserialize (ms); } else { var asm = System.Reflection.Assembly.LoadFrom (exe); var t = asm.GetType ("MonoDevelop.Projects.MSBuild.BuildEngine"); engine = (IBuildEngine)Activator.CreateInstance (t); } engine.SetCulture (GettextCatalog.UICulture); engine.SetGlobalProperties (GetCoreGlobalProperties (solutionFile)); foreach (var gpp in globalPropertyProviders) engine.SetGlobalProperties (gpp.GetGlobalProperties ()); builder = new RemoteBuildEngine (p, engine); } catch { if (p != null) { try { p.Kill (); } catch { } } throw; } builders.Add (builderKey, builder); builder.ReferenceCount = 1; builder.Disconnected += async delegate { using (await buildersLock.EnterAsync ()) builders.Remove (builder); }; if (lockBuilder) builder.Lock (); return new RemoteProjectBuilder (file, builder); }); } }
public void Dispose () { if (!MSBuildProjectService.ShutDown && engine != null) { try { if (builder != null) engine.UnloadProject (builder); MSBuildProjectService.ReleaseProjectBuilder (engine); } catch { // Ignore } GC.SuppressFinalize (this); engine = null; builder = null; } }
internal RemoteProjectBuilder (string file, RemoteBuildEngine engine) { this.file = file; this.engine = engine; builder = engine.LoadProject (file); referenceCache = new Dictionary<string, AssemblyReference[]> (); }
/// <summary> /// Gets or creates a remote build engine /// </summary> async static Task <RemoteBuildEngine> GetBuildEngine(TargetRuntime runtime, string minToolsVersion, string solutionFile, string group, object buildSessionId, bool setBusy = false, bool allowBusy = true) { var binDir = MSBuildProjectService.GetMSBuildBinPath(runtime); Version tv = Version.Parse(MSBuildProjectService.ToolsVersion); if (Version.TryParse(minToolsVersion, out Version mtv) && tv < mtv) { throw new InvalidOperationException(string.Format( "Project requires MSBuild ToolsVersion '{0}' which is not supported by runtime '{1}'", minToolsVersion, runtime.Id) ); } // One builder per solution string builderKey = runtime.Id + " # " + solutionFile + " # " + group; RemoteBuildEngine builder = null; // Find builders which are not being shut down var candiateBuilders = builders.GetBuilders(builderKey).Where(b => !b.IsShuttingDown && (!b.IsBusy || allowBusy)); if (buildSessionId != null) { // Look for a builder that already started the session. // If there isn't one, pick builders which don't have any session assigned, so a new one // can be started. var sessionBuilders = candiateBuilders.Where(b => b.BuildSessionId == buildSessionId); if (!sessionBuilders.Any()) { sessionBuilders = candiateBuilders.Where(b => b.BuildSessionId == null); } candiateBuilders = sessionBuilders; } else { // Pick builders which are not bound to any session candiateBuilders = candiateBuilders.Where(b => b.BuildSessionId == null); } // Prefer non-busy builders builder = candiateBuilders.FirstOrDefault(b => !b.IsBusy) ?? candiateBuilders.FirstOrDefault(); if (builder != null) { if (setBusy) { builder.SetBusy(); } if (builder.BuildSessionId == null && buildSessionId != null) { // If a new session is being assigned, signal the session start builder.BuildSessionId = buildSessionId; var si = (SessionInfo)buildSessionId; await builder.BeginBuildOperation(si.Monitor, si.Logger, si.Verbosity, si.Configurations).ConfigureAwait(false); } builder.CancelScheduledDisposal(); return(builder); } // No builder available with the required constraints. Start a new builder return(await Task.Run(async() => { // Always start the remote process explicitly, even if it's using the current runtime and fx // else it won't pick up the assembly redirects from the builder exe var exe = GetExeLocation(runtime); RemoteProcessConnection connection = null; try { connection = new RemoteProcessConnection(exe, runtime.GetExecutionHandler()); await connection.Connect().ConfigureAwait(false); var props = GetCoreGlobalProperties(solutionFile, binDir, MSBuildProjectService.ToolsVersion); foreach (var gpp in MSBuildProjectService.GlobalPropertyProviders) { foreach (var e in gpp.GetGlobalProperties()) { props [e.Key] = e.Value; } } await connection.SendMessage(new InitializeRequest { IdeProcessId = Process.GetCurrentProcess().Id, BinDir = binDir, CultureName = GettextCatalog.UICulture.Name, GlobalProperties = props }).ConfigureAwait(false); builder = new RemoteBuildEngine(connection, solutionFile); } catch { if (connection != null) { try { connection.Dispose(); } catch { } } throw; } builders.Add(builderKey, builder); builder.ReferenceCount = 0; builder.BuildSessionId = buildSessionId; builder.Disconnected += async delegate { using (await buildersLock.EnterAsync().ConfigureAwait(false)) builders.Remove(builder); }; if (setBusy) { builder.SetBusy(); } if (buildSessionId != null) { var si = (SessionInfo)buildSessionId; await builder.BeginBuildOperation(si.Monitor, si.Logger, si.Verbosity, si.Configurations); } return builder; })); }
internal static async Task ReleaseProjectBuilder(RemoteBuildEngine engine) { using (await buildersLock.EnterAsync().ConfigureAwait(false)) { await ReleaseProjectBuilderNoLock(engine); } }