/// <summary> /// Saves all packages and assets. /// </summary> /// <param name="log">The <see cref="LoggerResult"/> in which to report result.</param> public void Save(LoggerResult log, PackageSaveParameters saveParameters = null) { bool packagesSaved = false; //var clock = Stopwatch.StartNew(); using (var profile = Profiler.Begin(PackageSessionProfilingKeys.Saving)) { try { saveParameters = saveParameters ?? PackageSaveParameters.Default(); var assetsOrPackagesToRemove = BuildAssetsOrPackagesToRemove(); // Compute packages that have been renamed // TODO: Disable for now, as not sure if we want to delete a previous package //foreach (var package in packagesCopy) //{ // var newPackage = packages.Find(package.Id); // if (newPackage != null && package.PackagePath != null && newPackage.PackagePath != package.PackagePath) // { // assetsOrPackagesToRemove[package.PackagePath] = package; // } //} // Depending on saveParameters, select the list of source file operations to do List<SourceFileOperation> sourceFileOperations; switch (saveParameters.SaveSourceFileOperations) { case PackageSaveSourceFileOperations.All: sourceFileOperations = BuildSourceFileOperations(assetsOrPackagesToRemove); break; case PackageSaveSourceFileOperations.ReversibleOnly: sourceFileOperations = BuildSourceFileOperations(assetsOrPackagesToRemove).Where(x => !x.Irreversible).ToList(); break; case PackageSaveSourceFileOperations.None: sourceFileOperations = new List<SourceFileOperation>(); break; default: throw new ArgumentOutOfRangeException(); } // If package are not modified, return immediately if (!CheckModifiedPackages() && assetsOrPackagesToRemove.Count == 0 && sourceFileOperations.Count == 0) { return; } // Suspend tracking when saving as we don't want to receive // all notification events if (dependencies != null) { dependencies.BeginSavingSession(); } // Return immediately if there is any error if (log.HasErrors) { return; } // Perform source file operations foreach (var sourceFileOperation in sourceFileOperations) { switch (sourceFileOperation.Type) { case SourceFileOperationType.Move: try { // Move target already exists: try to copy and then delete if (File.Exists(sourceFileOperation.Destination)) { // Use upper try/catch File.Copy(sourceFileOperation.Source, sourceFileOperation.Destination, true); // Try to delete source try { File.Delete(sourceFileOperation.Source); } catch (Exception ex) { // File locked? log.Warning(sourceFileOperation.AssetItem.Package, sourceFileOperation.AssetItem.ToReference(), AssetMessageCode.AssetCannotDelete, ex, sourceFileOperation.Source); } } else { try { File.Move(sourceFileOperation.Source, sourceFileOperation.Destination); } catch (Exception ex) { // Could not Move, revert back to a Copy instead // Use upper try/catch File.Copy(sourceFileOperation.Source, sourceFileOperation.Destination, true); log.Warning(sourceFileOperation.AssetItem.Package, sourceFileOperation.AssetItem.ToReference(), AssetMessageCode.AssetCannotDelete, ex, sourceFileOperation.Source); } } // Update AssetItem var assetItem = sourceFileOperation.AssetItem; ((AssetImport)assetItem.Asset).Source = sourceFileOperation.Destination; assetItem.IsDirty = true; } catch (Exception ex) { log.Error(sourceFileOperation.AssetItem.Package, sourceFileOperation.AssetItem.ToReference(), AssetMessageCode.AssetCannotSave, ex, sourceFileOperation.Destination); } break; case SourceFileOperationType.Copy: try { File.Copy(sourceFileOperation.Source, sourceFileOperation.Destination, true); // Update AssetItem var assetItem = sourceFileOperation.AssetItem; ((AssetImport)assetItem.Asset).Source = sourceFileOperation.Destination; assetItem.IsDirty = true; } catch (Exception ex) { log.Error(sourceFileOperation.AssetItem.Package, sourceFileOperation.AssetItem.ToReference(), AssetMessageCode.AssetCannotSave, ex, sourceFileOperation.Destination); } break; case SourceFileOperationType.Delete: try { File.Delete(sourceFileOperation.Source); } catch (Exception ex) { // File locked? log.Warning(sourceFileOperation.AssetItem.Package, sourceFileOperation.AssetItem.ToReference(), AssetMessageCode.AssetCannotDelete, ex, sourceFileOperation.Source); } break; default: throw new ArgumentOutOfRangeException(); } } //batch projects var vsProjs = new Dictionary<string, Project>(); // Delete previous files foreach (var fileIt in assetsOrPackagesToRemove) { var assetPath = fileIt.Key; var assetItemOrPackage = fileIt.Value; var assetItem = assetItemOrPackage as AssetItem; if (File.Exists(assetPath)) { try { //If we are within a csproj we need to remove the file from there as well if (assetItem?.SourceProject != null) { var projectAsset = assetItem.Asset as ProjectSourceCodeAsset; if (projectAsset != null) { Project project; if (!vsProjs.TryGetValue(assetItem.SourceProject, out project)) { project = VSProjectHelper.LoadProject(assetItem.SourceProject); vsProjs.Add(assetItem.SourceProject, project); } var include = (new UFile(projectAsset.ProjectInclude)).ToWindowsPath(); var item = project.Items.FirstOrDefault(x => (x.ItemType == "Compile" || x.ItemType == "None") && x.EvaluatedInclude == include); if (item != null) { project.RemoveItem(item); } } } File.Delete(assetPath); } catch (Exception ex) { if (assetItem != null) { log.Error(assetItem.Package, assetItem.ToReference(), AssetMessageCode.AssetCannotDelete, ex, assetPath); } else { var package = assetItemOrPackage as Package; if (package != null) { log.Error(package, null, AssetMessageCode.AssetCannotDelete, ex, assetPath); } } } } } foreach (var project in vsProjs.Values) { project.Save(); project.ProjectCollection.UnloadAllProjects(); project.ProjectCollection.Dispose(); } // Save all dirty assets packagesCopy.Clear(); foreach (var package in LocalPackages) { // Save the package to disk and all its assets package.Save(log); // Clone the package (but not all assets inside, just the structure) var packageClone = package.Clone(false); packagesCopy.Add(packageClone); } packagesSaved = true; } finally { if (dependencies != null) { dependencies.EndSavingSession(); } // Once all packages and assets have been saved, we can save the solution (as we need to have fullpath to // be setup for the packages) if (packagesSaved) { PackageSessionHelper.SaveSolution(this, log); } } //System.Diagnostics.Trace.WriteLine("Elapsed saved: " + clock.ElapsedMilliseconds); IsDirty = false; } }
/// <summary> /// Saves this package and all dirty assets. See remarks. /// </summary> /// <param name="saveAllAssets">if set to <c>true</c> [save all assets].</param> /// <returns>LoggerResult.</returns> /// <remarks>When calling this method directly, it does not handle moving assets between packages. /// Call <see cref="PackageSession.Save"/> instead. /// </remarks> public LoggerResult Save(PackageSaveParameters saveParameters = null) { var result = new LoggerResult(); Save(result, saveParameters); return result; }
/// <summary> /// Saves this package and all dirty assets. See remarks. /// </summary> /// <param name="log">The log.</param> /// <exception cref="System.ArgumentNullException">log</exception> /// <remarks>When calling this method directly, it does not handle moving assets between packages. /// Call <see cref="PackageSession.Save" /> instead.</remarks> public void Save(ILogger log, PackageSaveParameters saveParameters = null) { if (log == null) throw new ArgumentNullException(nameof(log)); if (FullPath == null) { log.Error(this, null, AssetMessageCode.PackageCannotSave, "null"); return; } saveParameters = saveParameters ?? PackageSaveParameters.Default(); // Use relative paths when saving var analysis = new PackageAnalysis(this, new PackageAnalysisParameters() { SetDirtyFlagOnAssetWhenFixingUFile = false, ConvertUPathTo = UPathType.Relative, IsProcessingUPaths = true, }); analysis.Run(log); var assetsFiltered = false; try { // Update source folders UpdateSourceFolders(Assets); if (IsDirty) { List<UFile> filesToDeleteLocal; lock (filesToDelete) { filesToDeleteLocal = filesToDelete.ToList(); filesToDelete.Clear(); } try { AssetFileSerializer.Save(FullPath, this); // Move the package if the path has changed if (previousPackagePath != null && previousPackagePath != packagePath) { filesToDeleteLocal.Add(previousPackagePath); } previousPackagePath = packagePath; IsDirty = false; } catch (Exception ex) { log.Error(this, null, AssetMessageCode.PackageCannotSave, ex, FullPath); return; } // Delete obsolete files foreach (var file in filesToDeleteLocal) { if (File.Exists(file.FullPath)) { try { File.Delete(file.FullPath); } catch (Exception ex) { log.Error(this, null, AssetMessageCode.AssetCannotDelete, ex, file.FullPath); } } } } //batch projects var vsProjs = new Dictionary<string, Project>(); foreach (var asset in Assets) { var assetSaved = false; if (asset.IsDirty) { if (saveParameters.AssetFilter?.Invoke(asset) ?? true) { SaveSingleAsset_NoUpdateSourceFolder(asset, log); } else { assetsFiltered = true; } } // Add new files to .csproj var projectAsset = asset.Asset as IProjectAsset; if (projectAsset != null) { var projectFullPath = asset.SourceProject; var projectInclude = asset.GetProjectInclude(); Project project; if (!vsProjs.TryGetValue(projectFullPath, out project)) { project = VSProjectHelper.LoadProject(projectFullPath); vsProjs.Add(projectFullPath, project); } //check if the item is already there, this is possible when saving the first time when creating from a template if (project.Items.All(x => x.EvaluatedInclude != projectInclude)) { var generatorAsset = projectAsset as IProjectFileGeneratorAsset; if (generatorAsset != null) { var generatedInclude = asset.GetGeneratedInclude(); project.AddItem("None", projectInclude, new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("Generator", generatorAsset.Generator), new KeyValuePair<string, string>("LastGenOutput", new UFile(generatedInclude).GetFileNameWithExtension()) }); project.AddItem("Compile", generatedInclude, new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("AutoGen", "True"), new KeyValuePair<string, string>("DesignTime", "True"), new KeyValuePair<string, string>("DesignTimeSharedInput", "True"), new KeyValuePair<string, string>("DependentUpon", new UFile(projectInclude).GetFileNameWithExtension()) }); } else { project.AddItem("Compile", projectInclude); } } } } foreach (var project in vsProjs.Values) { project.Save(); project.ProjectCollection.UnloadAllProjects(); project.ProjectCollection.Dispose(); } // If some assets were filtered out, Assets is still dirty Assets.IsDirty = assetsFiltered; // Save properties like the Xenko version used PackageSessionHelper.SaveProperties(this); } finally { // Rollback all relative UFile to absolute paths analysis.Parameters.ConvertUPathTo = UPathType.Absolute; analysis.Run(); } }
/// <summary> /// Saves all packages and assets. /// </summary> /// <param name="log">The <see cref="LoggerResult"/> in which to report result.</param> /// <param name="saveParameters">The parameters for the save operation.</param> public void Save(LoggerResult log, PackageSaveParameters saveParameters = null) { bool packagesSaved = false; //var clock = Stopwatch.StartNew(); using (var profile = Profiler.Begin(PackageSessionProfilingKeys.Saving)) { var packagesDirty = false; try { saveParameters = saveParameters ?? PackageSaveParameters.Default(); var assetsOrPackagesToRemove = BuildAssetsOrPackagesToRemove(); // Compute packages that have been renamed // TODO: Disable for now, as not sure if we want to delete a previous package //foreach (var package in packagesCopy) //{ // var newPackage = packages.Find(package.Id); // if (newPackage != null && package.PackagePath != null && newPackage.PackagePath != package.PackagePath) // { // assetsOrPackagesToRemove[package.PackagePath] = package; // } //} // If package are not modified, return immediately if (!CheckModifiedPackages() && assetsOrPackagesToRemove.Count == 0) { return; } // Suspend tracking when saving as we don't want to receive // all notification events dependencies?.BeginSavingSession(); sourceTracker?.BeginSavingSession(); // Return immediately if there is any error if (log.HasErrors) return; //batch projects var vsProjs = new Dictionary<string, Project>(); // Delete previous files foreach (var fileIt in assetsOrPackagesToRemove) { var assetPath = fileIt.Key; var assetItemOrPackage = fileIt.Value; var assetItem = assetItemOrPackage as AssetItem; if (File.Exists(assetPath)) { try { //If we are within a csproj we need to remove the file from there as well if (assetItem?.SourceProject != null) { var projectAsset = assetItem.Asset as IProjectAsset; if (projectAsset != null) { var projectInclude = assetItem.GetProjectInclude(); Project project; if (!vsProjs.TryGetValue(assetItem.SourceProject, out project)) { project = VSProjectHelper.LoadProject(assetItem.SourceProject); vsProjs.Add(assetItem.SourceProject, project); } var projectItem = project.Items.FirstOrDefault(x => (x.ItemType == "Compile" || x.ItemType == "None") && x.EvaluatedInclude == projectInclude); if (projectItem != null) { project.RemoveItem(projectItem); } //delete any generated file as well var generatorAsset = assetItem.Asset as IProjectFileGeneratorAsset; if (generatorAsset != null) { var generatedAbsolutePath = assetItem.GetGeneratedAbsolutePath().ToWindowsPath(); File.Delete(generatedAbsolutePath); var generatedInclude = assetItem.GetGeneratedInclude(); var generatedItem = project.Items.FirstOrDefault(x => (x.ItemType == "Compile" || x.ItemType == "None") && x.EvaluatedInclude == generatedInclude); if (generatedItem != null) { project.RemoveItem(generatedItem); } } } } File.Delete(assetPath); } catch (Exception ex) { if (assetItem != null) { log.Error(assetItem.Package, assetItem.ToReference(), AssetMessageCode.AssetCannotDelete, ex, assetPath); } else { var package = assetItemOrPackage as Package; if (package != null) { log.Error(package, null, AssetMessageCode.AssetCannotDelete, ex, assetPath); } } } } } foreach (var project in vsProjs.Values) { project.Save(); project.ProjectCollection.UnloadAllProjects(); project.ProjectCollection.Dispose(); } // Save all dirty assets packagesCopy.Clear(); foreach (var package in LocalPackages) { // Save the package to disk and all its assets package.Save(log, saveParameters); // Check if everything was saved (might not be the case if things are filtered out) if (package.IsDirty || package.Assets.IsDirty) packagesDirty = true; // Clone the package (but not all assets inside, just the structure) var packageClone = package.Clone(); packagesCopy.Add(packageClone); } packagesSaved = true; } finally { sourceTracker?.EndSavingSession(); dependencies?.EndSavingSession(); // Once all packages and assets have been saved, we can save the solution (as we need to have fullpath to // be setup for the packages) if (packagesSaved) { PackageSessionHelper.SaveSolution(this, log); } } //System.Diagnostics.Trace.WriteLine("Elapsed saved: " + clock.ElapsedMilliseconds); IsDirty = packagesDirty; } }