Пример #1
0
        /// <summary>
        /// Serializes an <see cref="Asset" /> to the specified stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="asset">The asset object.</param>
        /// <param name="yamlMetadata"></param>
        /// <param name="log">The logger.</param>
        /// <exception cref="System.ArgumentNullException">
        /// stream
        /// or
        /// assetFileExtension
        /// </exception>
        public static void Save(Stream stream, object asset, AttachedYamlAssetMetadata yamlMetadata, ILogger log = null)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }
            if (asset == null)
            {
                return;
            }

            var assetFileExtension = AssetRegistry.GetDefaultExtension(asset.GetType());

            if (assetFileExtension == null)
            {
                throw new ArgumentException("Unable to find a serializer for the specified asset. No asset file extension registered to AssetRegistry");
            }

            var serializer = FindSerializer(assetFileExtension);

            if (serializer == null)
            {
                throw new InvalidOperationException($"Unable to find a serializer for [{assetFileExtension}]");
            }
            serializer.Save(stream, asset, yamlMetadata, log);
        }
Пример #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Asset"/> class.
        /// </summary>
        protected Asset()
        {
            Id   = AssetId.New();
            Tags = new TagCollection();

            // Initializse asset with default versions (same code as in Package..ctor())
            var defaultPackageVersion = AssetRegistry.GetCurrentFormatVersions(GetType());

            if (defaultPackageVersion != null)
            {
                SerializedVersion = new Dictionary <string, PackageVersion>(defaultPackageVersion);
            }
        }
Пример #3
0
        public static bool MigrateAssetIfNeeded(AssetMigrationContext context, PackageLoadingAssetFile loadAsset, string dependencyName, PackageVersion untilVersion = null)
        {
            var assetFullPath = loadAsset.FilePath.FullPath;

            // Determine if asset was Yaml or not
            var assetFileExtension = Path.GetExtension(assetFullPath);

            if (assetFileExtension == null)
            {
                return(false);
            }

            assetFileExtension = assetFileExtension.ToLowerInvariant();

            var serializer = AssetFileSerializer.FindSerializer(assetFileExtension);

            if (!(serializer is YamlAssetSerializer))
            {
                return(false);
            }

            // We've got a Yaml asset, let's get expected and serialized versions
            var            serializedVersion = PackageVersion.Zero;
            PackageVersion expectedVersion;
            Type           assetType;

            // Read from Yaml file the asset version and its type (to get expected version)
            // Note: It tries to read as few as possible (SerializedVersion is expected to be right after Id, so it shouldn't try to read further than that)
            using (var assetStream = loadAsset.OpenStream())
                using (var streamReader = new StreamReader(assetStream))
                {
                    var yamlEventReader = new EventReader(new Parser(streamReader));

                    // Skip header
                    yamlEventReader.Expect <StreamStart>();
                    yamlEventReader.Expect <DocumentStart>();
                    var mappingStart = yamlEventReader.Expect <MappingStart>();

                    var  tagTypeRegistry = AssetYamlSerializer.Default.GetSerializerSettings().TagTypeRegistry;
                    bool typeAliased;
                    assetType = tagTypeRegistry.TypeFromTag(mappingStart.Tag, out typeAliased);

                    var expectedVersions = AssetRegistry.GetCurrentFormatVersions(assetType);
                    expectedVersion = expectedVersions?.FirstOrDefault(x => x.Key == dependencyName).Value ?? PackageVersion.Zero;

                    Scalar assetKey;
                    while ((assetKey = yamlEventReader.Allow <Scalar>()) != null)
                    {
                        // Only allow Id before SerializedVersion
                        if (assetKey.Value == nameof(Asset.Id))
                        {
                            yamlEventReader.Skip();
                        }
                        else if (assetKey.Value == nameof(Asset.SerializedVersion))
                        {
                            // Check for old format: only a scalar
                            var scalarVersion = yamlEventReader.Allow <Scalar>();
                            if (scalarVersion != null)
                            {
                                serializedVersion = PackageVersion.Parse("0.0." + Convert.ToInt32(scalarVersion.Value, CultureInfo.InvariantCulture));

                                // Let's update to new format
                                using (var yamlAsset = loadAsset.AsYamlAsset())
                                {
                                    yamlAsset.DynamicRootNode.RemoveChild(nameof(Asset.SerializedVersion));
                                    AssetUpgraderBase.SetSerializableVersion(yamlAsset.DynamicRootNode, dependencyName, serializedVersion);

                                    var baseBranch = yamlAsset.DynamicRootNode["~Base"];
                                    if (baseBranch != null)
                                    {
                                        var baseAsset = baseBranch["Asset"];
                                        if (baseAsset != null)
                                        {
                                            baseAsset.RemoveChild(nameof(Asset.SerializedVersion));
                                            AssetUpgraderBase.SetSerializableVersion(baseAsset, dependencyName, serializedVersion);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // New format: package => version mapping
                                yamlEventReader.Expect <MappingStart>();

                                while (!yamlEventReader.Accept <MappingEnd>())
                                {
                                    var packageName    = yamlEventReader.Expect <Scalar>().Value;
                                    var packageVersion = PackageVersion.Parse(yamlEventReader.Expect <Scalar>().Value);

                                    // For now, we handle only one dependency at a time
                                    if (packageName == dependencyName)
                                    {
                                        serializedVersion = packageVersion;
                                    }
                                }

                                yamlEventReader.Expect <MappingEnd>();
                            }
                            break;
                        }
                        else
                        {
                            // If anything else than Id or SerializedVersion, let's stop
                            break;
                        }
                    }
                }

            if (serializedVersion > expectedVersion)
            {
                // Try to open an asset newer than what we support (probably generated by a newer Xenko)
                throw new InvalidOperationException($"Asset of type {assetType} has been serialized with newer version {serializedVersion}, but only version {expectedVersion} is supported. Was this asset created with a newer version of Xenko?");
            }

            if (serializedVersion < expectedVersion)
            {
                // Perform asset upgrade
                context.Log.Verbose($"{Path.GetFullPath(assetFullPath)} needs update, from version {serializedVersion} to version {expectedVersion}");

                using (var yamlAsset = loadAsset.AsYamlAsset())
                {
                    var yamlRootNode = yamlAsset.RootNode;

                    // Check if there is any asset updater
                    var assetUpgraders = AssetRegistry.GetAssetUpgraders(assetType, dependencyName);
                    if (assetUpgraders == null)
                    {
                        throw new InvalidOperationException($"Asset of type {assetType} should be updated from version {serializedVersion} to {expectedVersion}, but no asset migration path was found");
                    }

                    // Instantiate asset updaters
                    var currentVersion = serializedVersion;
                    while (currentVersion != expectedVersion)
                    {
                        PackageVersion targetVersion;
                        // This will throw an exception if no upgrader is available for the given version, exiting the loop in case of error.
                        var upgrader = assetUpgraders.GetUpgrader(currentVersion, out targetVersion);

                        // Stop if the next version would be higher than what is expected
                        if (untilVersion != null && targetVersion > untilVersion)
                        {
                            break;
                        }

                        upgrader.Upgrade(context, dependencyName, currentVersion, targetVersion, yamlRootNode, loadAsset);
                        currentVersion = targetVersion;
                    }

                    // Make sure asset is updated to latest version
                    YamlNode       serializedVersionNode;
                    PackageVersion newSerializedVersion = null;
                    if (yamlRootNode.Children.TryGetValue(new YamlScalarNode(nameof(Asset.SerializedVersion)), out serializedVersionNode))
                    {
                        var newSerializedVersionForDefaultPackage = ((YamlMappingNode)serializedVersionNode).Children[new YamlScalarNode(dependencyName)];
                        newSerializedVersion = PackageVersion.Parse(((YamlScalarNode)newSerializedVersionForDefaultPackage).Value);
                    }

                    if (untilVersion == null && newSerializedVersion != expectedVersion)
                    {
                        throw new InvalidOperationException($"Asset of type {assetType} was migrated, but still its new version {newSerializedVersion} doesn't match expected version {expectedVersion}.");
                    }

                    context.Log.Verbose($"{Path.GetFullPath(assetFullPath)} updated from version {serializedVersion} to version {expectedVersion}");
                }

                return(true);
            }

            return(false);
        }
Пример #4
0
 public AssetUpgraderCollection(Type assetType, PackageVersion currentVersion)
 {
     this.currentVersion = currentVersion;
     AssetRegistry.IsAssetOrPackageType(assetType, true);
     AssetType = assetType;
 }
Пример #5
0
 public static bool IsPackageFile(string filePath)
 {
     return(AssetRegistry.GetAssetTypeFromFileExtension(Path.GetExtension(filePath)) == typeof(Package));
 }
Пример #6
0
        private async Task PreLoadPackageDependencies(ILogger log, SolutionProject project, PackageLoadParameters loadParameters)
        {
            if (log == null)
            {
                throw new ArgumentNullException(nameof(log));
            }
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }
            if (loadParameters == null)
            {
                throw new ArgumentNullException(nameof(loadParameters));
            }

            bool packageDependencyErrors = false;

            var package = project.Package;

            // TODO: Remove and recheck Dependencies Ready if some secondary packages are removed?
            if (package.State >= PackageState.DependenciesReady)
            {
                return;
            }

            log.Verbose($"Process dependencies for {project.Name}...");

            var packageReferences = new Dictionary <string, PackageVersionRange>();

            // Load some informations about the project
            try
            {
                var msProject = VSProjectHelper.LoadProject(project.FullPath, loadParameters.BuildConfiguration, extraProperties: new Dictionary <string, string> {
                    { "SkipInvalidConfigurations", "true" }
                });
                try
                {
                    var packageVersion = msProject.GetPropertyValue("PackageVersion");

                    if (!string.IsNullOrEmpty(packageVersion))
                    {
                        package.Meta.Version = new PackageVersion(packageVersion);
                    }

                    project.TargetPath = msProject.GetPropertyValue("TargetPath");
                    package.Meta.Name  = msProject.GetPropertyValue("PackageId") ?? msProject.GetPropertyValue("AssemblyName") ?? package.Meta.Name;

                    var outputType = msProject.GetPropertyValue("OutputType");

                    project.Type = outputType.ToLowerInvariant() == "winexe" || outputType.ToLowerInvariant() == "exe" ||
                                   outputType.ToLowerInvariant() == "appcontainerexe" || // UWP
                                   msProject.GetPropertyValue("AndroidApplication").ToLowerInvariant() == "true" // Android
                        ? ProjectType.Executable
                        : ProjectType.Library;

                    // Note: Platform might be incorrect if Xenko is not restored yet (it won't include Xenko targets)
                    // Also, if already set, don't try to query it again
                    if (project.Type == ProjectType.Executable && project.Platform == PlatformType.Shared)
                    {
                        project.Platform = VSProjectHelper.GetPlatformTypeFromProject(msProject) ?? PlatformType.Shared;
                    }

                    foreach (var packageReference in msProject.GetItems("PackageReference").ToList())
                    {
                        if (packageReference.HasMetadata("Version") && PackageVersionRange.TryParse(packageReference.GetMetadataValue("Version"), out var packageRange))
                        {
                            packageReferences[packageReference.EvaluatedInclude] = packageRange;
                        }
                    }

                    // Need to go recursively
                    foreach (var projectReference in msProject.GetItems("ProjectReference").ToList())
                    {
                        var projectFile = new UFile(Path.Combine(Path.GetDirectoryName(project.FullPath), projectReference.EvaluatedInclude));
                        if (File.Exists(projectFile))
                        {
                            var referencedProject = Projects.OfType <SolutionProject>().FirstOrDefault(x => x.FullPath == new UFile(projectFile));
                            if (referencedProject != null)
                            {
                                await PreLoadPackageDependencies(log, referencedProject, loadParameters);
                            }
                        }
                    }
                }
                finally
                {
                    msProject.ProjectCollection.UnloadAllProjects();
                    msProject.ProjectCollection.Dispose();
                }
            }
            catch (Exception ex)
            {
                log.Error($"Unexpected exception while loading project [{project.FullPath.ToWindowsPath()}]", ex);
            }

            // Check if there is any package upgrade to do
            var pendingPackageUpgrades = new List <PendingPackageUpgrade>();

            pendingPackageUpgradesPerPackage.Add(package, pendingPackageUpgrades);

            foreach (var packageReference in packageReferences)
            {
                var dependencyName    = packageReference.Key;
                var dependencyVersion = packageReference.Value;

                var packageUpgrader = AssetRegistry.GetPackageUpgrader(dependencyName);
                if (packageUpgrader != null)
                {
                    // Check if this upgrader has already been added due to another package reference
                    if (pendingPackageUpgrades.Any(pendingPackageUpgrade => pendingPackageUpgrade.PackageUpgrader == packageUpgrader))
                    {
                        continue;
                    }

                    // Check if upgrade is necessary
                    if (dependencyVersion.MinVersion >= packageUpgrader.Attribute.UpdatedVersionRange.MinVersion)
                    {
                        continue;
                    }

                    // Check if upgrade is allowed
                    if (dependencyVersion.MinVersion < packageUpgrader.Attribute.PackageMinimumVersion)
                    {
                        // Throw an exception, because the package update is not allowed and can't be done
                        throw new InvalidOperationException($"Upgrading project [{project.Name}] to use [{dependencyName}] from version [{dependencyVersion}] to [{packageUpgrader.Attribute.UpdatedVersionRange.MinVersion}] is not supported (supported only from version [{packageUpgrader.Attribute.PackageMinimumVersion}]");
                    }

                    log.Info($"Upgrading project [{project.Name}] to use [{dependencyName}] from version [{dependencyVersion}] to [{packageUpgrader.Attribute.UpdatedVersionRange.MinVersion}] will be required");

                    pendingPackageUpgrades.Add(new PendingPackageUpgrade(packageUpgrader, new PackageDependency(dependencyName, dependencyVersion), null));
                }
            }

            if (pendingPackageUpgrades.Count > 0)
            {
                var upgradeAllowed = packageUpgradeAllowed != false ? PackageUpgradeRequestedAnswer.Upgrade : PackageUpgradeRequestedAnswer.DoNotUpgrade;

                // Need upgrades, let's ask user confirmation
                if (loadParameters.PackageUpgradeRequested != null && !packageUpgradeAllowed.HasValue)
                {
                    upgradeAllowed = loadParameters.PackageUpgradeRequested(package, pendingPackageUpgrades);
                    if (upgradeAllowed == PackageUpgradeRequestedAnswer.UpgradeAll)
                    {
                        packageUpgradeAllowed = true;
                    }
                    if (upgradeAllowed == PackageUpgradeRequestedAnswer.DoNotUpgradeAny)
                    {
                        packageUpgradeAllowed = false;
                    }
                }

                if (!PackageLoadParameters.ShouldUpgrade(upgradeAllowed))
                {
                    log.Error($"Necessary package migration for [{package.Meta.Name}] has not been allowed");
                    return;
                }

                // Perform pre assembly load upgrade
                foreach (var pendingPackageUpgrade in pendingPackageUpgrades)
                {
                    var expectedVersion = pendingPackageUpgrade.PackageUpgrader.Attribute.UpdatedVersionRange.MinVersion.ToString();

                    // Update NuGet references
                    try
                    {
                        var projectFile    = project.FullPath;
                        var msbuildProject = VSProjectHelper.LoadProject(projectFile.ToWindowsPath());
                        var isProjectDirty = false;

                        foreach (var packageReference in msbuildProject.GetItems("PackageReference").ToList())
                        {
                            if (packageReference.EvaluatedInclude == pendingPackageUpgrade.Dependency.Name && packageReference.GetMetadataValue("Version") != expectedVersion)
                            {
                                packageReference.SetMetadataValue("Version", expectedVersion);
                                isProjectDirty = true;
                            }
                        }

                        if (isProjectDirty)
                        {
                            msbuildProject.Save();
                        }

                        msbuildProject.ProjectCollection.UnloadAllProjects();
                        msbuildProject.ProjectCollection.Dispose();
                    }
                    catch (Exception e)
                    {
                        log.Warning($"Unable to load project [{project.FullPath.GetFileName()}]", e);
                    }

                    var packageUpgrader   = pendingPackageUpgrade.PackageUpgrader;
                    var dependencyPackage = pendingPackageUpgrade.DependencyPackage;
                    if (!packageUpgrader.UpgradeBeforeAssembliesLoaded(loadParameters, package.Session, log, package, pendingPackageUpgrade.Dependency, dependencyPackage))
                    {
                        log.Error($"Error while upgrading package [{package.Meta.Name}] for [{dependencyPackage.Meta.Name}] from version [{pendingPackageUpgrade.Dependency.Version}] to [{dependencyPackage.Meta.Version}]");
                        return;
                    }
                }
            }

            // Now that our references are upgraded, let's do a real nuget restore (download files)
            log.Verbose($"Restore NuGet packages for {project.Name}...");
            if (loadParameters.AutoCompileProjects)
            {
                await VSProjectHelper.RestoreNugetPackages(log, project.FullPath);
            }

            // If platform was unknown (due to missing nuget packages during first pass), check it again
            if (project.Type == ProjectType.Executable && project.Platform == PlatformType.Shared)
            {
                try
                {
                    var msProject = VSProjectHelper.LoadProject(project.FullPath, extraProperties: new Dictionary <string, string> {
                        { "SkipInvalidConfigurations", "true" }
                    });
                    try
                    {
                        project.Platform = VSProjectHelper.GetPlatformTypeFromProject(msProject) ?? PlatformType.Shared;
                    }
                    finally
                    {
                        msProject.ProjectCollection.UnloadAllProjects();
                        msProject.ProjectCollection.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    log.Error($"Unexpected exception while loading project [{project.FullPath.ToWindowsPath()}]", ex);
                }
            }

            project.FlattenedDependencies.Clear();
            project.DirectDependencies.Clear();
            var projectAssetsJsonPath = Path.Combine(project.FullPath.GetFullDirectory(), @"obj", LockFileFormat.AssetsFileName);

            if (File.Exists(projectAssetsJsonPath))
            {
                var format        = new LockFileFormat();
                var projectAssets = format.Read(projectAssetsJsonPath);

                // Update dependencies
                foreach (var library in projectAssets.Libraries)
                {
                    project.FlattenedDependencies.Add(new Dependency(library.Name, library.Version.ToPackageVersion(), library.Type == "project" ? DependencyType.Project : DependencyType.Package)
                    {
                        MSBuildProject = library.Type == "project" ? library.MSBuildProject : null
                    });
                }

                foreach (var projectReference in projectAssets.PackageSpec.RestoreMetadata.TargetFrameworks.First().ProjectReferences)
                {
                    var projectName = new UFile(projectReference.ProjectUniqueName).GetFileNameWithoutExtension();
                    project.DirectDependencies.Add(new DependencyRange(projectName, null, DependencyType.Project)
                    {
                        MSBuildProject = projectReference.ProjectPath
                    });
                }

                foreach (var dependency in projectAssets.PackageSpec.TargetFrameworks.First().Dependencies)
                {
                    if (dependency.AutoReferenced)
                    {
                        continue;
                    }
                    project.DirectDependencies.Add(new DependencyRange(dependency.Name, dependency.LibraryRange.VersionRange.ToPackageVersionRange(), DependencyType.Package));
                }

                // Load dependency (if external)

                // Compute output path
            }

            // 1. Load store package
            foreach (var projectDependency in project.FlattenedDependencies)
            {
                var loadedPackage = packages.Find(projectDependency);
                if (loadedPackage == null)
                {
                    string file = null;
                    switch (projectDependency.Type)
                    {
                    case DependencyType.Project:
                        if (Path.GetExtension(projectDependency.MSBuildProject).ToLowerInvariant() == ".csproj")
                        {
                            file = UPath.Combine(project.FullPath.GetFullDirectory(), (UFile)projectDependency.MSBuildProject);
                        }
                        break;

                    case DependencyType.Package:
                        file = PackageStore.Instance.GetPackageFileName(projectDependency.Name, new PackageVersionRange(projectDependency.Version), constraintProvider);
                        break;
                    }

                    if (file != null && File.Exists(file))
                    {
                        // Load package
                        var loadedProject = LoadProject(log, file, true, loadParameters);
                        loadedProject.Package.Meta.Name    = projectDependency.Name;
                        loadedProject.Package.Meta.Version = projectDependency.Version;
                        Projects.Add(loadedProject);

                        loadedPackage = loadedProject.Package;
                    }
                }

                if (loadedPackage != null)
                {
                    projectDependency.Package = loadedPackage;
                }
            }

            // 2. Load local packages

            /*foreach (var packageReference in package.LocalDependencies)
             * {
             *  // Check that the package was not already loaded, otherwise return the same instance
             *  if (Packages.ContainsById(packageReference.Id))
             *  {
             *      continue;
             *  }
             *
             *  // Expand the string of the location
             *  var newLocation = packageReference.Location;
             *
             *  var subPackageFilePath = package.RootDirectory != null ? UPath.Combine(package.RootDirectory, newLocation) : newLocation;
             *
             *  // Recursive load
             *  var loadedPackage = PreLoadPackage(log, subPackageFilePath.FullPath, false, loadedPackages, loadParameters);
             *
             *  if (loadedPackage == null || loadedPackage.State < PackageState.DependenciesReady)
             *      packageDependencyErrors = true;
             * }*/

            // 3. Update package state
            if (!packageDependencyErrors)
            {
                package.State = PackageState.DependenciesReady;
            }
        }
Пример #7
0
 protected override bool CanVisit(object obj)
 {
     return(!AssetRegistry.IsContentType(obj?.GetType()) && base.CanVisit(obj));
 }