4 using System.Collections.Generic;
5 using System.ComponentModel;
6 using System.Diagnostics;
9 using System.Threading;
11 using SiliconStudio.Assets.Analysis;
12 using SiliconStudio.Assets.Diagnostics;
13 using SiliconStudio.Assets.Templates;
14 using SiliconStudio.Core;
15 using SiliconStudio.Core.Diagnostics;
16 using SiliconStudio.Core.IO;
17 using SiliconStudio.Core.Reflection;
18 using SiliconStudio.Core.Storage;
20 namespace SiliconStudio.Assets
25 [DataContract(
"Package")]
26 [AssetFileExtension(PackageFileExtension)]
27 [DebuggerDisplay(
"Id: {Id}, Name: {Meta.Name}, Version: {Meta.Version}, Assets [{Assets.Count}]")]
34 private readonly List<PackageReference> localDependencies;
36 private readonly List<UDirectory> explicitFolders;
40 private UFile packagePath;
46 public const string PackageFileExtension =
".pdxpkg";
58 localDependencies =
new List<PackageReference>();
61 explicitFolders =
new List<UDirectory>();
64 TemplateFolders =
new List<TemplateFolder>();
65 Templates =
new List<TemplateDescription>();
75 public bool IsSystem {
get;
internal set; }
90 public List<PackageReference> LocalDependencies
94 return localDependencies;
109 public List<UDirectory> ExplicitFolders
113 return explicitFolders;
129 public List<TemplateFolder> TemplateFolders {
get;
private set; }
136 public List<TemplateDescription> Templates {
get;
private set; }
160 return temporaryAssets;
169 public UFile FullPath
177 SetPackagePath(value,
true);
195 OnAssetDirtyChanged(
this);
208 return FullPath != null ? FullPath.GetParent() : null;
226 if (value != null && session != null && !ReferenceEquals(session, value))
228 throw new InvalidOperationException(
"Cannot attach a package to more than one session");
231 IsIdLocked = (session != null);
243 AddExitingProject(pathToMsproj, logger);
254 if (pathToMsproj == null)
throw new ArgumentNullException(
"pathToMsproj");
255 if (logger == null)
throw new ArgumentNullException(
"logger");
256 if (!pathToMsproj.
IsAbsolute)
throw new ArgumentException(
"Expecting relative path",
"pathToMsproj");
261 var msProject = VSProjectHelper.LoadProject(pathToMsproj, platform:
"NoPlatform");
263 var projectType = VSProjectHelper.GetProjectTypeFromProject(msProject);
264 if (!projectType.HasValue)
266 logger.Error(
"This project is not a project created with the editor");
270 var platformType = VSProjectHelper.GetPlatformTypeFromProject(msProject) ??
PlatformType.Shared;
274 Id = VSProjectHelper.GetProjectGuid(msProject),
275 Location = pathToMsproj.MakeRelative(RootDirectory),
276 Type = projectType.Value
280 foreach (var profile
in Profiles.Where(profile => platformType == profile.Platform))
282 profile.ProjectReferences.Add(projectReference);
288 logger.Error(
"Unexpected exception while loading project [{0}]", ex, pathToMsproj);
296 var sharedProfile = Profiles[PlatformType.Shared];
297 var folder = sharedProfile.AssetFolders.FirstOrDefault();
298 if (folder != null && folder.Path != null)
304 return "Assets/" + PackageProfile.SharedName;
317 var
package = (Package)AssetCloner.Clone(this);
318 package.FullPath = FullPath;
319 foreach (var asset
in Assets)
322 var assetItem =
new AssetItem(asset.Location, newAsset);
323 package.Assets.Add(assetItem);
335 var previousPath = packagePath;
336 var previousRootDirectory = RootDirectory;
337 packagePath = newPath;
338 if (packagePath != null && !packagePath.IsAbsolute)
340 packagePath = UPath.Combine(Environment.CurrentDirectory, packagePath);
343 if (copyAssets && packagePath != previousPath)
346 var currentRootDirectory = RootDirectory;
347 if (previousRootDirectory != null && currentRootDirectory != null)
349 foreach (var profile
in Profiles)
351 foreach (var sourceFolder
in profile.AssetFolders)
353 if (sourceFolder.Path.IsAbsolute)
355 var relativePath = sourceFolder.Path.MakeRelative(previousRootDirectory);
356 sourceFolder.Path = UPath.Combine(currentRootDirectory, relativePath);
362 foreach (var asset
in Assets)
364 asset.IsDirty =
true;
370 internal void OnAssetDirtyChanged(
Asset asset)
372 Action<Asset> handler = AssetDirtyChanged;
373 if (handler != null) handler(asset);
400 if (log == null)
throw new ArgumentNullException(
"log");
402 if (FullPath == null)
404 log.Error(
this, null, AssetMessageCode.PackageCannotSave,
"null");
411 SetDirtyFlagOnAssetWhenFixingUFile =
false,
412 ConvertUPathTo = UPathType.Relative,
413 IsProcessingUPaths =
true
420 UpdateSourceFolders();
427 if (session != null && session.HasDependencyManager)
429 session.DependencyManager.AddFileBeingSaveDuringSessionSave(FullPath);
432 AssetSerializer.Save(FullPath,
this);
438 log.Error(
this, null, AssetMessageCode.PackageCannotSave, ex, FullPath);
443 foreach (var asset
in Assets)
447 var assetPath = asset.FullPath;
451 if (session != null && session.HasDependencyManager)
453 session.DependencyManager.AddFileBeingSaveDuringSessionSave(assetPath);
457 var assetBase = asset.Asset.Base;
458 if (assetBase != null && !assetBase.IsRootImport)
460 var assetBaseItem = session != null ? session.FindAsset(assetBase.Id) : Assets.Find(assetBase.Id);
461 if (assetBaseItem != null)
465 asset.Asset.Base =
new AssetBase(asset.Asset.Base.Location, newBase);
469 AssetSerializer.Save(assetPath, asset.Asset);
470 asset.IsDirty =
false;
474 log.Error(
this, asset.ToReference(),
AssetMessageCode.AssetCannotSave, ex, assetPath);
479 Assets.IsDirty =
false;
484 analysis.Parameters.ConvertUPathTo = UPathType.Absolute;
501 if (filePath == null)
throw new ArgumentNullException(
"filePath");
502 return AssetSerializer.Load<
Package>(filePath).Id;
517 if (log == null)
throw new ArgumentNullException(
"log");
518 if (filePath == null)
throw new ArgumentNullException(
"filePath");
520 filePath = FileUtility.GetAbsolutePath(filePath);
522 if (!
File.Exists(filePath))
524 log.Error(
"Package file [{0}] was not found", filePath);
528 var loadParameters = loadParametersArg ?? PackageLoadParameters.Default();
532 var
package = AssetSerializer.Load<Package>(filePath);
533 package.FullPath = filePath;
534 package.IsDirty =
false;
537 if (loadParameters.LoadAssemblyReferences)
539 package.LoadAssemblyReferencesForPackage(log, loadParameters);
543 if (loadParameters.AutoLoadTemporaryAssets)
545 package.LoadTemporaryAssets(log, loadParameters.CancelToken);
549 if (loadParameters.ConvertUPathToAbsolute)
553 ConvertUPathTo = UPathType.Absolute,
554 IsProcessingUPaths =
true,
555 SetDirtyFlagOnAssetWhenFixingAbsoluteUFile =
true
561 package.LoadTemplates(log);
567 log.Error(
"Error while pre-loading package [{0}]", ex, filePath);
575 if (TemporaryAssets.Count == 0)
583 Assets.SuspendCollectionChanged();
591 var resolver = AssetResolver.FromPackage(
this);
592 resolver.AlwaysCreateNewId = alwaysGenerateNewAssetId;
595 AssetCollision.Clean(TemporaryAssets, outputItems, resolver,
false);
598 foreach (var item
in outputItems)
603 TemporaryAssets.Clear();
608 Assets.ResumeCollectionChanged();
623 if (log == null)
throw new ArgumentNullException(
"log");
626 if (FullPath == null)
628 log.Warning(
"Fullpath not set on this package");
633 TemporaryAssets.Clear();
636 var listFiles = ListAssetFiles(log,
this, cancelToken);
638 var progressMessage = String.Format(
"Loading Assets from Package [{0}]", FullPath.GetFileNameWithExtension());
642 if (loggerResult == null || !loggerResult.IsLoggingProgressAsInfo)
644 log.Info(progressMessage);
648 for (
int i = 0; i < listFiles.Count; i++)
650 var fileUPath = listFiles[i].Item1;
651 var sourceFolder = listFiles[i].Item2;
652 if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
654 log.Warning(
"Skipping loading assets. PackageSession.Load cancelled");
659 if (loggerResult != null)
661 loggerResult.Progress(progressMessage, i, listFiles.Count);
665 var assetPath = fileUPath.MakeRelative(sourceFolder).GetDirectoryAndFileName();
670 var assetFullPath = fileUPath.FullPath;
671 var asset = LoadAsset(log, assetFullPath, assetPath, fileUPath);
674 var assetItem =
new AssetItem(assetPath, asset)
678 SourceFolder = sourceFolder.MakeRelative(RootDirectory)
681 assetItem.ModifiedTime = File.GetLastWriteTime(assetFullPath);
683 FixAssetImport(assetItem);
686 TemporaryAssets.Add(assetItem);
692 var yamlException = ex as YamlException;
693 if (yamlException != null)
695 row = yamlException.Start.Line + 1;
696 column = yamlException.Start.Column;
699 var module = log.Module;
704 if (loggerResult != null)
706 loggerResult.Module =
"{0}({1},{2})".ToFormat(Path.GetFullPath(fileUPath.FullPath), row, column);
709 log.Error(
this, assetReference, AssetMessageCode.AssetLoadingFailed, ex, fileUPath, ex.Message);
711 if (loggerResult != null)
713 loggerResult.Module = module;
719 private static Asset LoadAsset(
ILogger log,
string assetFullPath,
string assetPath,
UFile fileUPath)
721 AssetMigration.MigrateAssetIfNeeded(log, assetFullPath);
723 var asset = AssetSerializer.Load<
Asset>(assetFullPath);
727 if (sourceCodeAsset != null)
730 sourceCodeAsset.Id = SourceCodeAsset.GenerateGuidFromLocation(assetPath);
731 sourceCodeAsset.AbsoluteSourceLocation = fileUPath;
739 if (log == null)
throw new ArgumentNullException(
"log");
740 if (loadParameters == null)
throw new ArgumentNullException(
"loadParameters");
741 var assemblyContainer = loadParameters.AssemblyContainer ?? AssemblyContainer.Default;
742 foreach (var profile
in Profiles)
744 foreach (var projectReference
in profile.ProjectReferences.Where(projectRef => projectRef.Type ==
ProjectType.Plugin || projectRef.Type ==
ProjectType.Library))
746 string assemblyPath = null;
747 var fullProjectLocation = UPath.Combine(RootDirectory, projectReference.Location);
750 assemblyPath = VSProjectHelper.GetOrCompileProjectAssembly(fullProjectLocation, log, loadParameters.AutoCompileProjects, extraProperties: loadParameters.ExtraCompileProperties, onlyErrors:
true);
752 if (String.IsNullOrWhiteSpace(assemblyPath))
754 log.Error(
"Unable to locate assembly reference for project [{0}]", fullProjectLocation);
758 if (!
File.Exists(assemblyPath))
760 log.Error(
"Unable to build assembly reference [{0}]", assemblyPath);
764 var assembly = assemblyContainer.LoadAssemblyFromPath(assemblyPath, log);
765 if (assembly == null)
767 log.Error(
"Unable to load assembly reference [{0}]", assemblyPath);
772 log.Error(
"Unexpected error while loading project [{0}] or assembly reference [{1}]", ex, fullProjectLocation, assemblyPath);
778 private void UpdateSourceFolders()
781 if (Assets.Count == 0)
790 sharedProfile = PackageProfile.NewShared();
791 Profiles.Add(sharedProfile);
795 sharedProfile = Profiles[PlatformType.Shared];
799 var defaultFolder = sharedProfile.AssetFolders.Count > 0 ? sharedProfile.AssetFolders.First().Path :
UDirectory.
This;
800 var assetFolders =
new HashSet<UDirectory>(GetDistinctAssetFolderPaths());
801 foreach (var asset
in Assets)
803 if (asset.SourceFolder == null)
805 asset.SourceFolder = defaultFolder.IsAbsolute ? defaultFolder.MakeRelative(RootDirectory) : defaultFolder;
806 asset.IsDirty =
true;
809 var assetFolderAbsolute = UPath.Combine(RootDirectory, asset.SourceFolder);
810 if (!assetFolders.Contains(assetFolderAbsolute))
812 assetFolders.Add(assetFolderAbsolute);
813 sharedProfile.AssetFolders.Add(
new AssetFolder(assetFolderAbsolute));
823 private void LoadTemplates(
ILogger log)
825 foreach (var templateDir
in TemplateFolders)
827 foreach (var filePath
in templateDir.Files)
831 var file =
new FileInfo(filePath);
834 log.Warning(
"Template [{0}] does not exist ", file);
839 templateDescription.FullPath = file.FullName;
840 Templates.Add(templateDescription);
844 log.Error(
"Error while loading template from [{0}]", ex, filePath);
850 private List<UDirectory> GetDistinctAssetFolderPaths()
852 var existingAssetFolders =
new List<UDirectory>();
853 foreach (var profile
in Profiles)
855 foreach (var folder
in profile.AssetFolders)
857 var folderPath = RootDirectory != null ? UPath.Combine(RootDirectory, folder.Path) : folder.Path;
858 if (!existingAssetFolders.Contains(folderPath))
860 existingAssetFolders.Add(folderPath);
864 return existingAssetFolders;
867 private static List<Tuple<UFile, UDirectory>> ListAssetFiles(
ILogger log,
Package package, CancellationToken? cancelToken)
869 var listFiles =
new List<Tuple<UFile, UDirectory>>();
874 throw new InvalidOperationException(
"Package RootDirectory is null");
879 throw new InvalidOperationException(
"Package RootDirectory [{0}] does not exist".ToFormat(package.
RootDirectory));
883 foreach (var sourceFolder
in package.GetDistinctAssetFolderPaths())
888 var files = directory.GetFiles();
890 foreach (var filePath
in files)
893 if (filePath.FullName.EndsWith(PackageFileExtension))
899 var fileUPath =
new UFile(filePath.FullName);
900 if (fileUPath.GetFileExtension() == null)
911 listFiles.Add(
new Tuple<UFile, UDirectory>(fileUPath, sourceFolder));
923 private static void FixAssetImport(
AssetItem item)
928 if (assetImport == null || assetImport.Source == null)
934 if (assetImport.Base == null)
936 var fileExtension = assetImport.Source.GetFileExtension();
939 assetImportBase.SetAsRootImport();
940 assetImportBase.SetDefaults();
943 if (!String.IsNullOrEmpty(fileExtension))
945 var importerId = AssetRegistry.FindImporterByExtension(fileExtension).FirstOrDefault();
946 if (importerId != null)
948 assetImport.ImporterId = importerId.Id;
952 if (assetImportTracked != null)
954 assetImportTracked.SourceHash = ObjectId.Empty;
957 assetImport.Base =
new AssetBase(assetImportBase);
Package()
Initializes a new instance of the Package class.
static bool IsAssetFileExtension(string extension)
Determines whether the extension is an asset file type.
PlatformType
Describes the platform operating system.
static Package Load(ILogger log, string filePath, PackageLoadParameters loadParametersArg=null)
Loads only the package description but not assets or plugins.
Package Clone(bool deepCloneAsset)
Deep clone this package.
SearchDirection
A direction to search for files in directories
A location relative to a package from where assets will be loaded
A collection of AssetItem that contains only absolute location without any drive information. This class cannot be inherited.
void AddExitingProject(UFile pathToMsproj, LoggerResult logger)
Adds an exiting project to this package.
SiliconStudio.Core.Diagnostics.LoggerResult LoggerResult
A logger that stores messages locally useful for internal log scenarios.
A registry for file extensions, IAssetImporter, IAssetFactory and aliases associated with assets...
An asset item part of a Package accessible through Package.Assets.
AssetMessageCode
A message code used by AssetLogMessage to identify an error/warning.
Class PackageAnalysisParameters. This class cannot be inherited.
static readonly UDirectory This
A this '.' directory.
A collection of AssetItem that contains only absolute location without any drive information. This class cannot be inherited.
An analysis to check the validity of a Package, convert UFile or UDirectory references to absolute/re...
An asset item part of a Package accessible through SiliconStudio.Assets.Package.Assets.
A session for editing a package.
Description of a template generator that can be displayed in the GameStudio.
void LoadTemporaryAssets(ILogger log, CancellationToken?cancelToken=null)
Refreshes this package from the disk by loading or reloading all assets.
Defines a normalized directory path. See UPath for details. This class cannot be inherited.
A collection of PackageProfile.
bool IsAbsolute
Determines whether this instance is absolute.
UDirectory RootDirectory
Gets the top directory of this package on the local disk.
LoggerResult AddExitingProject(UFile pathToMsproj)
Adds an exiting project to this package.
An importable asset with a content that need to be tracked if original asset is changing.
ProjectType
Type of the project.
Action< Asset > AssetDirtyChanged
Occurs when an asset dirty changed occured.
LoggerResult Save()
Saves this package and all dirty assets. See remarks.
void Save(ILogger log)
Saves this package and all dirty assets. See remarks.
object Clone()
Clones the current value of this cloner with the specified new shadow registry (optional) ...
Identify an object that is associated with an anchor file on the disk where all the UPath members of ...
static IEnumerable< DirectoryInfo > EnumerateDirectories(string rootDirectory, SearchDirection direction)
A reference to a Visual Studio project that is part of a Package
static Guid GetPackageIdFromFile(string filePath)
Gets the package identifier from file.
void SetPackagePath(UFile newPath, bool copyAssets=true)
Sets the package path.
Describes buld parameters used when building assets.
void ValidateAssets(bool alwaysGenerateNewAssetId=false)
The template can be applied to an existing PackageSession.
A package managing assets.
Parameters used for loading a package.
Allows to clone an asset or values stored in an asset.
Defines a normalized file path. See UPath for details. This class cannot be inherited.