void WriteFileInternal (string file, string sourceFile, Solution solution, bool saveProjects, ProgressMonitor monitor)
		{
			if (saveProjects) {
				var items = solution.GetAllSolutionItems ().ToArray ();
				monitor.BeginTask (items.Length + 1);
				foreach (var item in items) {
					try {
						monitor.BeginStep ();
						item.SavingSolution = true;
						item.SaveAsync (monitor).Wait ();
					} finally {
						item.SavingSolution = false;
					}
				}
			} else {
				monitor.BeginTask (1);
				monitor.BeginStep ();
			}

			SlnFile sln = new SlnFile ();
			sln.FileName = file;
			if (File.Exists (sourceFile)) {
				try {
					sln.Read (sourceFile);
				} catch (Exception ex) {
					LoggingService.LogError ("Existing solution can't be updated since it can't be read", ex);
				}
			}

			sln.FormatVersion = format.SlnVersion;

			// Don't modify the product description if it already has a value
			if (string.IsNullOrEmpty (sln.ProductDescription))
				sln.ProductDescription = format.ProductDescription;

			solution.WriteSolution (monitor, sln);

			sln.Write (file);
			monitor.EndTask ();
		}
		void WriteProjects (SolutionFolder folder, SlnFile sln, ProgressMonitor monitor, HashSet<string> unknownProjects)
		{
			monitor.BeginTask (folder.Items.Count); 
			foreach (SolutionFolderItem ce in folder.Items.ToArray ())
			{
				monitor.BeginStep ();
				if (ce is SolutionItem) {
					
					SolutionItem item = (SolutionItem) ce;

					var proj = sln.Projects.GetOrCreateProject (ce.ItemId);
					proj.TypeGuid = item.TypeGuid;
					proj.Name = item.Name;
					proj.FilePath = FileService.NormalizeRelativePath (FileService.AbsoluteToRelativePath (sln.BaseDirectory, item.FileName)).Replace ('/', '\\');

					var sec = proj.Sections.GetOrCreateSection ("MonoDevelopProperties", SlnSectionType.PreProcess);
					sec.SkipIfEmpty = true;
					folder.ParentSolution.WriteSolutionFolderItemData (monitor, sec.Properties, ce);

					if (item.ItemDependencies.Count > 0) {
						sec = proj.Sections.GetOrCreateSection ("ProjectDependencies", SlnSectionType.PostProcess);
						sec.Properties.ClearExcept (unknownProjects);
						foreach (var dep in item.ItemDependencies)
							sec.Properties.SetValue (dep.ItemId, dep.ItemId);
					} else
						proj.Sections.RemoveSection ("ProjectDependencies");
				} else if (ce is SolutionFolder) {
					var proj = sln.Projects.GetOrCreateProject (ce.ItemId);
					proj.TypeGuid = MSBuildProjectService.FolderTypeGuid;
					proj.Name = ce.Name;
					proj.FilePath = ce.Name;

					// Folder files
					WriteFolderFiles (proj, (SolutionFolder) ce);
					
					//Write custom properties
					var sec = proj.Sections.GetOrCreateSection ("MonoDevelopProperties", SlnSectionType.PreProcess);
					sec.SkipIfEmpty = true;
					folder.ParentSolution.WriteSolutionFolderItemData (monitor, sec.Properties, ce);
				}
				if (ce is SolutionFolder)
					WriteProjects (ce as SolutionFolder, sln, monitor, unknownProjects);
			}
			monitor.EndTask ();
		}
		//Reader
		public async Task<object> ReadFile (string fileName, ProgressMonitor monitor)
		{
			if (fileName == null || monitor == null)
				return null;

			var sol = new Solution (true);
			sol.FileName = fileName;
			sol.FileFormat = format;

			try {
				monitor.BeginTask (string.Format (GettextCatalog.GetString ("Loading solution: {0}"), fileName), 1);
				monitor.BeginStep ();
				await sol.OnBeginLoad ();
				var projectLoadMonitor = monitor as ProjectLoadProgressMonitor;
				if (projectLoadMonitor != null)
					projectLoadMonitor.CurrentSolution = sol;
				await Task.Factory.StartNew (() => {
					sol.ReadSolution (monitor);
				});
			} catch (Exception ex) {
				monitor.ReportError (GettextCatalog.GetString ("Could not load solution: {0}", fileName), ex);
				sol.OnEndLoad ().Wait ();
				sol.NotifyItemReady ();
				monitor.EndTask ();
				throw;
			}
			await sol.OnEndLoad ();
			sol.NotifyItemReady ();
			monitor.EndTask ();
			return sol;
		}
		internal void WriteFileInternal (SlnFile sln, Solution solution, ProgressMonitor monitor)
		{
			SolutionFolder c = solution.RootFolder;

			// Delete data for projects that have been removed from the solution

			var currentProjects = new HashSet<string> (solution.GetAllItems<SolutionFolderItem> ().Select (it => it.ItemId));
			var removedProjects = new HashSet<string> ();
			if (solution.LoadedProjects != null)
				removedProjects.UnionWith (solution.LoadedProjects.Except (currentProjects));
			var unknownProjects = new HashSet<string> (sln.Projects.Select (p => p.Id).Except (removedProjects).Except (currentProjects));

			foreach (var p in removedProjects) {
				var ps = sln.Projects.GetProject (p);
				if (ps != null)
					sln.Projects.Remove (ps);
				var pc = sln.ProjectConfigurationsSection.GetPropertySet (p, true);
				if (pc != null)
					sln.ProjectConfigurationsSection.Remove (pc);
			}
			var secNested = sln.Sections.GetSection ("NestedProjects");
			if (secNested != null) {
				foreach (var np in secNested.Properties.ToArray ()) {
					if (removedProjects.Contains (np.Key) || removedProjects.Contains (np.Value))
						secNested.Properties.Remove (np.Key);
				}
			}
			solution.LoadedProjects = currentProjects;

			//Write the projects
			using (monitor.BeginTask (GettextCatalog.GetString ("Saving projects"), 1)) {
				monitor.BeginStep ();
				WriteProjects (c, sln, monitor, unknownProjects);
			}

			//FIXME: SolutionConfigurations?

			var pset = sln.SolutionConfigurationsSection;
			foreach (SolutionConfiguration config in solution.Configurations) {
				var cid = ToSlnConfigurationId (config);
				pset.SetValue (cid, cid);
			}

			WriteProjectConfigurations (solution, sln);

			//Write Nested Projects
			ICollection<SolutionFolder> folders = solution.RootFolder.GetAllItems<SolutionFolder> ().ToList ();
			if (folders.Count > 1) {
				// If folders ==1, that's the root folder
				var sec = sln.Sections.GetOrCreateSection ("NestedProjects", SlnSectionType.PreProcess);
				foreach (SolutionFolder folder in folders) {
					if (folder.IsRoot)
						continue;
					WriteNestedProjects (folder, solution.RootFolder, sec);
				}
				// Remove items which don't have a parent folder
				var toRemove = solution.GetAllItems<SolutionFolderItem> ().Where (it => it.ParentFolder == solution.RootFolder);
				foreach (var it in toRemove)
					sec.Properties.Remove (it.ItemId);
			}

			// Write custom properties for configurations
			foreach (SolutionConfiguration conf in solution.Configurations) {
				string secId = "MonoDevelopProperties." + conf.Id;
				var sec = sln.Sections.GetOrCreateSection (secId, SlnSectionType.PreProcess);
				solution.WriteConfigurationData (monitor, sec.Properties, conf);
				if (sec.IsEmpty)
					sln.Sections.Remove (sec);
			}
		}
		async Task<BuildResult> RebuildAsync (IBuildTarget entry, ProgressMonitor monitor, OperationContext operationContext)
		{
			ITimeTracker tt = Counters.BuildItemTimer.BeginTiming ("Rebuilding " + entry.Name);
			try {
				OnStartClean (monitor, tt);

				monitor.BeginTask (GettextCatalog.GetString ("Rebuilding..."), 2);
				monitor.BeginStep (GettextCatalog.GetString ("Rebuilding... (Clean)"));

				var res = await CleanAsync (entry, monitor, tt, true, operationContext);
				monitor.EndStep ();
				if (res.HasErrors) {
					tt.End ();
					monitor.Dispose ();
					return res;
				}
				if (StartBuild != null) {
					BeginBuild (monitor, tt, true);
				}
				monitor.BeginStep (GettextCatalog.GetString ("Rebuilding... (Build)"));
				return await BuildSolutionItemAsync (entry, monitor, tt, operationContext:operationContext);
			} finally {
				tt.End ();
			}
		}
        private async Task EnsureProjectsAreGeneratedForPlatform(string platform, ProgressMonitor monitor)
        {
            var timestamp = DateTime.UtcNow;
            lock (generateRequestLock) {
                generateRequestTime[platform] = timestamp;
            }

            await Task.Run(() => 
            {
                lock (generateLock) {
                    if (!shadowSolutions.ContainsKey (platform)) {

                        lock (generateRequestLock) {
                            var generateAt = generateRequestTime.ContainsKey (platform)
                                ? generateRequestTime[platform]
                                : (DateTime?) null;
                            if (generateAt == null || timestamp < generateAt) {
                                // Request is not needed any more.
                                return;
                            }
                            generateRequestTime.Remove (platform);
                        }

                        try {
                            monitor.BeginTask ("Generating .NET projects for " + platform + "...", 1);
								monitor.BeginStep();
								appDomain.RunExecutableWithArguments (latestModuleInfo, "--generate " + platform, x => {
									monitor.EndTask ();
									monitor.BeginTask (x, 1);
									monitor.BeginStep();
								});
                        }
                        catch (Exception ex) {
                            monitor.ReportError ("Failed to generate projects", ex);
                            throw;
                        }
                        finally {
                            monitor.EndTask ();
                        }

                        try {
								monitor.BeginTask ("Caching .NET solution for " + platform + "...", 1);
								monitor.BeginStep();
                            var path = Path.Combine (latestModuleInfo.Path,
                                latestModuleInfo.Name + "." + platform + ".sln");
								shadowSolutions[platform] = (Solution)Services.ProjectService.ReadWorkspaceItem(new ProgressMonitor(), path).Result;
                        }
                        catch (Exception ex) {
                            monitor.ReportError ("Failed to cache solution", ex);
                            throw;
                        }
                        finally {
                            monitor.EndTask ();
                        }
                    }
                }
            });
	    }
        public void SaveModule(FilePath file, ProgressMonitor monitor)
        {
            try {
                monitor.BeginTask ("Saving module " + latestModuleInfo.Name + "...", 1);
				monitor.BeginStep();

                if (this.SingleStartup) {
                    latestModuleInfo.DefaultStartupProject = this.StartupItem.Name;
                }

                appDomain.SaveModule (latestModuleInfo);
            }
            catch (Exception ex) {
                monitor.ReportError ("Failed to save module " + latestModuleInfo.Name, ex);
                throw;
            }
            finally {
                monitor.ReportSuccess ("Saved module " + latestModuleInfo.Name);
                monitor.EndTask ();
            }

			DefinitionOrModuleSaved();
        }
	    public async Task Load(string path, ProgressMonitor monitor)
	    {
			if (appDomain != null)
			{
                monitor.BeginTask ("Stopping Protobuild...", 1);
				monitor.BeginStep();
				appDomain.UnloadAppDomain();
				appDomain.ProtobuildChangedEvent -= HandleProtobuildChangedEvent;
				appDomain = null;
				monitor.EndTask();
			}

            monitor.BeginTask("Starting Protobuild...", 1);
			monitor.BeginStep();
			appDomain = new ProtobuildAppDomain(path);
			appDomain.ProtobuildChangedEvent += HandleProtobuildChangedEvent;
            latestModuleInfo = appDomain.LoadModule(monitor);

            FileName = path;

            // We have to perform an initial generation on load so we have a set of
            // projects that can be used for the type system.  Also we generate before
            // loading projects because generation might trigger package resolution.
	        //ActiveConfiguration = appDomain.HostPlatform;
			//IdeApp.Workspace.ActiveConfigurationId = appDomain.HostPlatform;
            //await EnsureProjectsAreGeneratedForPlatform(appDomain.HostPlatform, monitor);

            //monitor.BeginStepTask("Loading projects...", 1, 1);
		    ReloadProjects ();

			monitor.EndTask();
		}