public void TestUpdateAssetUrl() { var projectDir = new UFile(Path.Combine(Environment.CurrentDirectory, "testxk")); // Create a project with an asset reference a raw file var project = new Package { FullPath = projectDir }; var assetItem = new AssetItem("test", new AssetObjectTest() { Reference = new AssetReference<AssetObjectTest>(Guid.Empty, "good/location")}); project.Assets.Add(assetItem); var goodAsset = new AssetObjectTest(); project.Assets.Add(new AssetItem("good/location", goodAsset)); // Add the project to the session to make sure analysis will run correctly var session = new PackageSession(project); // Create a session with this project var analysis = new PackageAnalysis(project, new PackageAnalysisParameters() { IsProcessingAssetReferences = true, ConvertUPathTo = UPathType.Absolute, IsProcessingUPaths = true }); var result = analysis.Run(); Assert.IsFalse(result.HasErrors); Assert.AreEqual(1, result.Messages.Count); Assert.IsTrue(result.Messages[0].ToString().Contains("changed")); var asset = (AssetObjectTest)assetItem.Asset; Assert.AreEqual(goodAsset.Id, asset.Reference.Id); Assert.AreEqual("good/location", asset.Reference.Location); }
/// <summary> /// Performs a wide package validation analysis. /// </summary> /// <param name="log">The log to output the result of the validation.</param> public override void Run(ILogger log) { if (log == null) throw new ArgumentNullException("log"); foreach (var package in Session.LocalPackages) { var analysis = new PackageAnalysis(package, parameters); analysis.Run(log); } }
/// <summary> /// Performs a wide package validation analysis. /// </summary> /// <param name="log">The log to output the result of the validation.</param> public override void Run(ILogger log) { if (log == null) { throw new ArgumentNullException("log"); } foreach (var package in Session.LocalPackages) { var analysis = new PackageAnalysis(package, parameters); analysis.Run(log); } }
public void TestMoveAssetWithUFile() { var projectDir = new UFile(Path.Combine(Environment.CurrentDirectory, "testxk")); var rawAssetPath = new UFile("../image.png"); var assetPath = new UFile("sub1/sub2/test"); // Create a project with an asset reference a raw file var project = new Package { FullPath = projectDir }; project.Profiles.Add(new PackageProfile("Shared", new AssetFolder("."))); var asset = new AssetObjectTest() { RawAsset = new UFile(rawAssetPath) }; var assetItem = new AssetItem(assetPath, asset); project.Assets.Add(assetItem); // Run an asset reference analysis on this project var analysis = new PackageAnalysis(project, new PackageAnalysisParameters() { ConvertUPathTo = UPathType.Absolute, IsProcessingUPaths = true }); var result = analysis.Run(); Assert.IsFalse(result.HasErrors); Assert.AreEqual(UPath.Combine(project.RootDirectory, new UFile("sub1/image.png")), asset.RawAsset); project.Assets.Remove(assetItem); assetItem = new AssetItem("sub1/test", asset); project.Assets.Add(assetItem); result = analysis.Run(); Assert.IsFalse(result.HasErrors); Assert.AreEqual(UPath.Combine(project.RootDirectory, new UFile("sub1/image.png")), asset.RawAsset); project.Assets.Remove(assetItem); assetItem = new AssetItem("test", asset); project.Assets.Add(assetItem); result = analysis.Run(); Assert.IsFalse(result.HasErrors); Assert.AreEqual(UPath.Combine(project.RootDirectory, new UFile("sub1/image.png")), asset.RawAsset); analysis.Parameters.ConvertUPathTo = UPathType.Relative; result = analysis.Run(); Assert.IsFalse(result.HasErrors); Assert.AreEqual(new UFile("sub1/image.png"), asset.RawAsset); }
/// <summary> /// Loads only the package description but not assets or plugins. /// </summary> /// <param name="log">The log to receive error messages.</param> /// <param name="filePath">The file path.</param> /// <param name="loadParametersArg">The load parameters argument.</param> /// <returns>A package.</returns> /// <exception cref="System.ArgumentNullException">log /// or /// filePath</exception> public static Package Load(ILogger log, string filePath, PackageLoadParameters loadParametersArg = null) { if (log == null) throw new ArgumentNullException("log"); if (filePath == null) throw new ArgumentNullException("filePath"); filePath = FileUtility.GetAbsolutePath(filePath); if (!File.Exists(filePath)) { log.Error("Package file [{0}] was not found", filePath); return null; } var loadParameters = loadParametersArg ?? PackageLoadParameters.Default(); try { var package = AssetSerializer.Load<Package>(filePath); package.FullPath = filePath; package.IsDirty = false; // Load assembly references if (loadParameters.LoadAssemblyReferences) { package.LoadAssemblyReferencesForPackage(log, loadParameters); } // Load assets if (loadParameters.AutoLoadTemporaryAssets) { package.LoadTemporaryAssets(log, loadParameters.CancelToken); } // Convert UPath to absolute if (loadParameters.ConvertUPathToAbsolute) { var analysis = new PackageAnalysis(package, new PackageAnalysisParameters() { ConvertUPathTo = UPathType.Absolute, IsProcessingUPaths = true, // This is done already by Package.Load SetDirtyFlagOnAssetWhenFixingAbsoluteUFile = true // When loading tag attributes that have an absolute file }); analysis.Run(log); } // Load templates package.LoadTemplates(log); return package; } catch (Exception ex) { log.Error("Error while pre-loading package [{0}]", ex, filePath); } return null; }
/// <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) { if (log == null) throw new ArgumentNullException("log"); if (FullPath == null) { log.Error(this, null, AssetMessageCode.PackageCannotSave, "null"); return; } // Use relative paths when saving var analysis = new PackageAnalysis(this, new PackageAnalysisParameters() { SetDirtyFlagOnAssetWhenFixingUFile = false, ConvertUPathTo = UPathType.Relative, IsProcessingUPaths = true }); analysis.Run(log); try { // Update source folders UpdateSourceFolders(); if (IsDirty) { try { // Notifies the dependency manager that a package with the specified path is being saved if (session != null && session.HasDependencyManager) { session.DependencyManager.AddFileBeingSaveDuringSessionSave(FullPath); } AssetSerializer.Save(FullPath, this); IsDirty = false; } catch (Exception ex) { log.Error(this, null, AssetMessageCode.PackageCannotSave, ex, FullPath); return; } } foreach (var asset in Assets) { if (asset.IsDirty) { var assetPath = asset.FullPath; try { // Notifies the dependency manager that an asset with the specified path is being saved if (session != null && session.HasDependencyManager) { session.DependencyManager.AddFileBeingSaveDuringSessionSave(assetPath); } // Incject a copy of the base into the current asset when saving var assetBase = asset.Asset.Base; if (assetBase != null && !assetBase.IsRootImport) { var assetBaseItem = session != null ? session.FindAsset(assetBase.Id) : Assets.Find(assetBase.Id); if (assetBaseItem != null) { var newBase = (Asset)AssetCloner.Clone(assetBaseItem.Asset); newBase.Base = null; asset.Asset.Base = new AssetBase(asset.Asset.Base.Location, newBase); } } AssetSerializer.Save(assetPath, asset.Asset); asset.IsDirty = false; } catch (Exception ex) { log.Error(this, asset.ToReference(), AssetMessageCode.AssetCannotSave, ex, assetPath); } } } Assets.IsDirty = false; // Save properties like the Paradox version used PackageSessionHelper.SaveProperties(this); } finally { // Rollback all relative UFile to absolute paths analysis.Parameters.ConvertUPathTo = UPathType.Absolute; analysis.Run(); } }
/// <summary> /// Load assets and perform package analysis. /// </summary> /// <param name="package">The package.</param> /// <param name="log">The log.</param> /// <param name="loadParametersArg">The load parameters argument.</param> /// <returns></returns> internal bool LoadAssets(ILogger log, PackageLoadParameters loadParametersArg) { var loadParameters = loadParametersArg ?? PackageLoadParameters.Default(); try { // Load assets if (loadParameters.AutoLoadTemporaryAssets) { LoadTemporaryAssets(log, loadParameters.AssetFiles, loadParameters.CancelToken, loadParameters.AssetFilter); } // Convert UPath to absolute if (loadParameters.ConvertUPathToAbsolute) { var analysis = new PackageAnalysis(this, new PackageAnalysisParameters() { ConvertUPathTo = UPathType.Absolute, IsProcessingUPaths = true, // This is done already by Package.Load SetDirtyFlagOnAssetWhenFixingAbsoluteUFile = true // When loading tag attributes that have an absolute file }); analysis.Run(log); } // Load templates LoadTemplates(log); return true; } catch (Exception ex) { log.Error("Error while pre-loading package [{0}]", ex, FullPath); return false; } }
/// <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) { if (log == null) throw new ArgumentNullException(nameof(log)); if (FullPath == null) { log.Error(this, null, AssetMessageCode.PackageCannotSave, "null"); return; } // Use relative paths when saving var analysis = new PackageAnalysis(this, new PackageAnalysisParameters() { SetDirtyFlagOnAssetWhenFixingUFile = false, ConvertUPathTo = UPathType.Relative, IsProcessingUPaths = true, AssetTemplatingRemoveUnusedBaseParts = true, }); analysis.Run(log); try { // Update source folders UpdateSourceFolders(); if (IsDirty) { List<UFile> filesToDeleteLocal; lock (filesToDelete) { filesToDeleteLocal = filesToDelete.ToList(); filesToDelete.Clear(); } try { AssetSerializer.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) { if (asset.IsDirty) { var assetPath = asset.FullPath; try { //Handle the ProjectSourceCodeAsset differently then regular assets in regards of Path var sourceCodeAsset = asset.Asset as ProjectSourceCodeAsset; if (sourceCodeAsset != null) { var profile = Profiles.FindSharedProfile(); var lib = profile?.ProjectReferences.FirstOrDefault(x => x.Type == ProjectType.Library && asset.Location.FullPath.StartsWith(x.Location.GetFileName())); if (lib == null) continue; var projectFullPath = UPath.Combine(RootDirectory, lib.Location); var fileFullPath = UPath.Combine(RootDirectory, asset.Location); var filePath = fileFullPath.MakeRelative(projectFullPath.GetFullDirectory()); var codeFile = new UFile(filePath + AssetRegistry.GetDefaultExtension(sourceCodeAsset.GetType())); Project project; if (!vsProjs.TryGetValue(projectFullPath, out project)) { project = VSProjectHelper.LoadProject(projectFullPath); vsProjs.Add(projectFullPath, project); } asset.SourceProject = projectFullPath; asset.SourceFolder = RootDirectory.GetFullDirectory(); sourceCodeAsset.ProjectInclude = codeFile; sourceCodeAsset.ProjectName = Path.GetFileNameWithoutExtension(projectFullPath.ToWindowsPath()); sourceCodeAsset.AbsoluteSourceLocation = UPath.Combine(projectFullPath.GetFullDirectory(), codeFile); sourceCodeAsset.AbsoluteProjectLocation = projectFullPath; assetPath = sourceCodeAsset.AbsoluteSourceLocation; //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 != codeFile.ToWindowsPath())) { var generatorAsset = sourceCodeAsset as ProjectCodeGeneratorAsset; if (generatorAsset != null) { generatorAsset.GeneratedAbsolutePath = new UFile(generatorAsset.AbsoluteSourceLocation).GetFullPathWithoutExtension() + ".cs"; generatorAsset.GeneratedInclude = new UFile(generatorAsset.ProjectInclude).GetFullPathWithoutExtension() + ".cs"; project.AddItem("None", codeFile.ToWindowsPath(), new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("Generator", generatorAsset.Generator), new KeyValuePair<string, string>("LastGenOutput", new UFile(generatorAsset.GeneratedInclude).GetFileNameWithExtension()) }); project.AddItem("Compile", new UFile(generatorAsset.GeneratedInclude).ToWindowsPath(), 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(generatorAsset.ProjectInclude).GetFileNameWithExtension()) }); } else { project.AddItem("Compile", codeFile.ToWindowsPath()); } } } // Inject a copy of the base into the current asset when saving var assetBase = asset.Asset.Base; if (assetBase != null && !assetBase.IsRootImport) { asset.Asset.Base = UpdateAssetBase(assetBase); } // Update base for BaseParts if (asset.Asset.BaseParts != null) { var baseParts = asset.Asset.BaseParts; for (int i = 0; i < baseParts.Count; i++) { var basePart = baseParts[i]; baseParts[i] = UpdateAssetBase(basePart); } } AssetSerializer.Save(assetPath, asset.Asset); asset.IsDirty = false; } catch (Exception ex) { log.Error(this, asset.ToReference(), AssetMessageCode.AssetCannotSave, ex, assetPath); } } } foreach (var project in vsProjs.Values) { project.Save(); project.ProjectCollection.UnloadAllProjects(); project.ProjectCollection.Dispose(); } Assets.IsDirty = false; // 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> /// Adds an existing package to the current session and runs the package analysis before adding it. /// </summary> /// <param name="package">The package to add</param> /// <param name="logger">The logger</param> public void AddExistingPackage(Package package, ILogger logger) { if (package == null) throw new ArgumentNullException(nameof(package)); if (logger == null) throw new ArgumentNullException(nameof(logger)); if (packages.Contains(package)) { return; } // Preset the session on the package to allow the session to look for existing asset this.Packages.Add(package); // Run analysis after var analysis = new PackageAnalysis(package, GetPackageAnalysisParametersForLoad()); analysis.Run(logger); }
/// <summary> /// Adds an existing package to the current session. /// </summary> /// <param name="packagePath">The package path.</param> /// <param name="logger">The session result.</param> /// <param name="loadParametersArg">The load parameters argument.</param> /// <exception cref="System.ArgumentNullException">packagePath</exception> /// <exception cref="System.ArgumentException">Invalid relative path. Expecting an absolute package path;packagePath</exception> /// <exception cref="System.IO.FileNotFoundException">Unable to find package</exception> public Package AddExistingPackage(UFile packagePath, ILogger logger, PackageLoadParameters loadParametersArg = null) { if (packagePath == null) throw new ArgumentNullException("packagePath"); if (logger == null) throw new ArgumentNullException("logger"); if (!packagePath.IsAbsolute) throw new ArgumentException("Invalid relative path. Expecting an absolute package path", "packagePath"); if (!File.Exists(packagePath)) throw new FileNotFoundException("Unable to find package", packagePath); var loadParameters = loadParametersArg ?? PackageLoadParameters.Default(); Package package; try { // Enable reference analysis caching during loading AssetReferenceAnalysis.EnableCaching = true; var packagesLoaded = new PackageCollection(); package = PreLoadPackage(this, logger, packagePath, false, packagesLoaded, loadParameters); // Load all missing references/dependencies LoadMissingReferences(logger, loadParameters); // Load assets TryLoadAssets(this, logger, package, loadParameters); // Run analysis after foreach (var packageToAdd in packagesLoaded) { var analysis = new PackageAnalysis(packageToAdd, GetPackageAnalysisParametersForLoad()); analysis.Run(logger); } } finally { // Disable reference analysis caching after loading AssetReferenceAnalysis.EnableCaching = false; } return package; }
/// <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) { if (log == null) throw new ArgumentNullException(nameof(log)); if (FullPath == null) { log.Error(this, null, AssetMessageCode.PackageCannotSave, "null"); return; } // Use relative paths when saving var analysis = new PackageAnalysis(this, new PackageAnalysisParameters() { SetDirtyFlagOnAssetWhenFixingUFile = false, ConvertUPathTo = UPathType.Relative, IsProcessingUPaths = true }); analysis.Run(log); try { // Update source folders UpdateSourceFolders(); if (IsDirty) { try { // Notifies the dependency manager that a package with the specified path is being saved if (session != null && session.HasDependencyManager) { session.DependencyManager.AddFileBeingSaveDuringSessionSave(FullPath); } AssetSerializer.Save(FullPath, this); // Move the package if the path has changed if (previousPackagePath != null && previousPackagePath != packagePath) { filesToDelete.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 filesToDelete) { if (File.Exists(file.FullPath)) { try { File.Delete(file.FullPath); } catch (Exception ex) { log.Error(this, null, AssetMessageCode.AssetCannotDelete, ex, file.FullPath); } } } filesToDelete.Clear(); } //batch projects var vsProjs = new Dictionary<string, Project>(); foreach (var asset in Assets) { if (asset.IsDirty) { var assetPath = asset.FullPath; try { //Handle the ProjectSourceCodeAsset differently then regular assets in regards of Path var sourceCodeAsset = asset.Asset as ProjectSourceCodeAsset; if (sourceCodeAsset != null) { var profile = Profiles.FindSharedProfile(); var lib = profile?.ProjectReferences.FirstOrDefault(x => x.Type == ProjectType.Library && asset.Location.FullPath.StartsWith(x.Location.GetFileName())); if (lib == null) continue; var projectFullPath = UPath.Combine(RootDirectory, lib.Location); var fileFullPath = UPath.Combine(RootDirectory, asset.Location); var filePath = fileFullPath.MakeRelative(projectFullPath.GetFullDirectory()); var codeFile = new UFile(filePath + AssetRegistry.GetDefaultExtension(sourceCodeAsset.GetType())); 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 != codeFile.ToWindowsPath())) { project.AddItem(AssetRegistry.GetDefaultExtension(sourceCodeAsset.GetType()) == ".cs" ? "Compile" : "None", codeFile.ToWindowsPath()); //todo None case needs Generator and LastGenOutput properties support! (eg xksl) } asset.SourceProject = projectFullPath; asset.SourceFolder = RootDirectory.GetFullDirectory(); sourceCodeAsset.ProjectInclude = codeFile; sourceCodeAsset.ProjectName = Path.GetFileNameWithoutExtension(projectFullPath.ToWindowsPath()); sourceCodeAsset.AbsoluteSourceLocation = UPath.Combine(projectFullPath.GetFullDirectory(), codeFile); sourceCodeAsset.AbsoluteProjectLocation = projectFullPath; assetPath = sourceCodeAsset.AbsoluteSourceLocation; } // Notifies the dependency manager that an asset with the specified path is being saved if (session != null && session.HasDependencyManager) { session.DependencyManager.AddFileBeingSaveDuringSessionSave(assetPath); } // Incject a copy of the base into the current asset when saving var assetBase = asset.Asset.Base; if (assetBase != null && !assetBase.IsRootImport) { var assetBaseItem = session != null ? session.FindAsset(assetBase.Id) : Assets.Find(assetBase.Id); if (assetBaseItem != null) { var newBase = (Asset)AssetCloner.Clone(assetBaseItem.Asset); newBase.Base = null; asset.Asset.Base = new AssetBase(asset.Asset.Base.Location, newBase); } } AssetSerializer.Save(assetPath, asset.Asset); asset.IsDirty = false; } catch (Exception ex) { log.Error(this, asset.ToReference(), AssetMessageCode.AssetCannotSave, ex, assetPath); } } } foreach (var project in vsProjs.Values) { project.Save(); project.ProjectCollection.UnloadAllProjects(); project.ProjectCollection.Dispose(); } Assets.IsDirty = false; // 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 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(); } }