| File: Workspace\Solution\SourceGeneratorExecutionVersion.cs | Web Access |
| Project: src\roslyn\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; /// <summary> /// Represents the version of source generator execution that a project is at. Source generator results are kept around /// as long as this version stays the same and we are in <see cref="SourceGeneratorExecutionPreference.Balanced"/> /// mode. This has no effect when in <see cref="SourceGeneratorExecutionPreference.Automatic"/> mode (as we always rerun /// generators on any change). This should effectively be used as a monotonically increasing value. /// </summary> /// <param name="MajorVersion">Controls the major version of source generation execution. When this changes the /// generator driver should be dropped and all generation should be rerun.</param> /// <param name="MinorVersion">Controls the minor version of source generation execution. When this changes the /// generator driver can be reused and should incrementally determine what the new generated documents should be. /// </param> [DataContract] internal readonly record struct SourceGeneratorExecutionVersion( [property: DataMember(Order = 0)] int MajorVersion, [property: DataMember(Order = 1)] int MinorVersion) { public SourceGeneratorExecutionVersion IncrementMajorVersion() => new(MajorVersion + 1, MinorVersion: 0); public SourceGeneratorExecutionVersion IncrementMinorVersion() => new(MajorVersion, MinorVersion + 1); public void WriteTo(ObjectWriter writer) { writer.WriteInt32(MajorVersion); writer.WriteInt32(MinorVersion); } public static SourceGeneratorExecutionVersion ReadFrom(ObjectReader reader) => new(reader.ReadInt32(), reader.ReadInt32()); public override string ToString() => $"{MajorVersion}.{MinorVersion}"; public Checksum Checksum => new(MajorVersion, MinorVersion); public static bool operator >(SourceGeneratorExecutionVersion left, SourceGeneratorExecutionVersion right) => left.MajorVersion > right.MajorVersion || (left.MajorVersion == right.MajorVersion && left.MinorVersion > right.MinorVersion); public static bool operator <(SourceGeneratorExecutionVersion left, SourceGeneratorExecutionVersion right) => !(left == right || left > right); } /// <summary> /// Helper construct to allow a mapping from <see cref="ProjectId"/>s to <see cref="SourceGeneratorExecutionVersion"/>. /// Limited to just the surface area the workspace needs. /// </summary> internal sealed class SourceGeneratorExecutionVersionMap(ImmutableSortedDictionary<ProjectId, SourceGeneratorExecutionVersion> map) { public static readonly SourceGeneratorExecutionVersionMap Empty = new(); public ImmutableSortedDictionary<ProjectId, SourceGeneratorExecutionVersion> Map { get; } = map; public SourceGeneratorExecutionVersionMap() : this(ImmutableSortedDictionary<ProjectId, SourceGeneratorExecutionVersion>.Empty) { } public SourceGeneratorExecutionVersion this[ProjectId projectId] => Map[projectId]; public static bool operator ==(SourceGeneratorExecutionVersionMap map1, SourceGeneratorExecutionVersionMap map2) => map1.Map == map2.Map; public static bool operator !=(SourceGeneratorExecutionVersionMap map1, SourceGeneratorExecutionVersionMap map2) => !(map1 == map2); public override int GetHashCode() => throw new InvalidOperationException(); public override bool Equals([NotNullWhen(true)] object? obj) => obj is SourceGeneratorExecutionVersionMap map && this == map; public void WriteTo(ObjectWriter writer) { // Writing out the dictionary in order is fine. That's because it's a sorted dictionary, and ProjectIds are // naturally comparable. writer.WriteInt32(Map.Count); foreach (var (projectId, version) in Map) { projectId.WriteTo(writer); version.WriteTo(writer); } } public static SourceGeneratorExecutionVersionMap Deserialize(ObjectReader reader) { var count = reader.ReadInt32(); var builder = ImmutableSortedDictionary.CreateBuilder<ProjectId, SourceGeneratorExecutionVersion>(); for (var i = 0; i < count; i++) { var projectId = ProjectId.ReadFrom(reader); var version = SourceGeneratorExecutionVersion.ReadFrom(reader); builder.Add(projectId, version); } return new(builder.ToImmutable()); } public Checksum GetChecksum() { using var _ = ArrayBuilder<Checksum>.GetInstance(this.Map.Count * 2, out var checksums); foreach (var (projectId, version) in this.Map) { checksums.Add(projectId.Checksum); checksums.Add(Checksum.Create(version, static (v, w) => v.WriteTo(w))); } return Checksum.Create(checksums); } public override string ToString() { using var _ = PooledStringBuilder.GetInstance(out var builder); builder.AppendLine(nameof(SourceGeneratorExecutionVersionMap)); foreach (var (projectId, version) in Map) builder.AppendLine($" {projectId}: {version}"); return builder.ToString(); } }