Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions UnitsNet.Xp.SrcGen/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Project>
<!-- Disable Directory.Build.props in repo root. -->
</Project>
6 changes: 6 additions & 0 deletions UnitsNet.Xp.SrcGen/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<!-- Disable Directory.Packages.props in repo root. -->
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>
</Project>
34 changes: 34 additions & 0 deletions UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGen.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitsNet.Xp.SrcGenDemo", "UnitsNet.Xp.SrcGenDemo\UnitsNet.Xp.SrcGenDemo.csproj", "{B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitsNet.Xp.SrcGen", "UnitsNet.Xp.SrcGen\UnitsNet.Xp.SrcGen.csproj", "{D635D0D1-D745-43A7-A04A-75995801753E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Files", "_Files", "{8775A1CE-645B-45F2-82B2-1C94D56FA134}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitsNet.Xp.SrcGenDummySource", "UnitsNet.Xp.SrcGenDummySource\UnitsNet.Xp.SrcGenDummySource.csproj", "{8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}.Release|Any CPU.Build.0 = Release|Any CPU
{D635D0D1-D745-43A7-A04A-75995801753E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D635D0D1-D745-43A7-A04A-75995801753E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D635D0D1-D745-43A7-A04A-75995801753E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D635D0D1-D745-43A7-A04A-75995801753E}.Release|Any CPU.Build.0 = Release|Any CPU
{8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
10 changes: 10 additions & 0 deletions UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGen/IsExternalInit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// using System.ComponentModel;
//
// // ReSharper disable once CheckNamespace
// namespace System.Runtime.CompilerServices;
//
// /// <summary>
// /// Workaround for netstandard2.0 and record types and init properties: https://stackoverflow.com/a/62656145/134761
// /// </summary>
// [EditorBrowsable(EditorBrowsableState.Never)]
// internal class IsExternalInit{}
134 changes: 134 additions & 0 deletions UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGen/QuantitySourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace UnitsNet.Xp.SrcGen
{
[Generator]
public class QuantitySourceGenerator : ISourceGenerator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ISourceGenerator has been deprecated in favor of IIncrementalGenerator

{
private const string SrcQuantitySource =
"""
namespace UnitsNetSrcGen
{
public interface ISrcQuantity<TUnitEnum> where TUnitEnum : System.Enum
{
double Value { get; }
TUnitEnum Unit { get; }
}
}
""";

private const string LengthSource = """
namespace UnitsNetSrcGen
{
public enum LengthUnit
{
Centimeter,
Meter,
}

public struct Length : ISrcQuantity<LengthUnit>
{
public Length(double value, LengthUnit unit)
{
Value = value;
Unit = unit;
}

public required double Value { get; init; }
public required LengthUnit Unit { get; init; }
}
}
""";

private const string MassSource = """
namespace UnitsNetSrcGen
{
public enum MassUnit
{
Gram,
Kilogram,
}

public struct Mass : ISrcQuantity<MassUnit>
{
public Mass(double value, MassUnit unit)
{
Value = value;
Unit = unit;
}

public required double Value { get; init; }
public required MassUnit Unit { get; init; }
}
}
""";

private const string AttributeSource =
"""
[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = false)]
internal sealed class UnitsNetSrcGenInitAttribute : System.Attribute
{
public string[] QuantityNames { get; }

public UnitsNetSrcGenInitAttribute(string quantityNames)
=> (QuantityNames) = (quantityNames.Split(',').Select(str => str.Trim()).ToArray());
}
""";

public void Execute(GeneratorExecutionContext context)
{
SyntaxReceiver rx = (SyntaxReceiver)context.SyntaxContextReceiver!;
foreach (string quantityName in rx.QuantityNames)
{
string source = quantityName switch
{
"Length" => LengthSource,
"Mass" => MassSource,
_ => "",
};

if (source != "")
{
context.AddSource($"UnitsNetSrc_{quantityName}.g.cs", source);
}
}
}

public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForPostInitialization((pi) =>
{
pi.AddSource("UnitsNetSrcGenInit.g.cs", AttributeSource);
pi.AddSource("UnitsNetSrc_ISrcQuantity.g.cs", SrcQuantitySource);
});
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
}

private class SyntaxReceiver : ISyntaxContextReceiver
{
public List<string> QuantityNames = new();

public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
// find all valid mustache attributes
if (context.Node is AttributeSyntax attrib
&& context.SemanticModel.GetTypeInfo(attrib).Type?.ToDisplayString() == "UnitsNetSrcGenInitAttribute")
{
// string[] quantityNames = (string[])context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[0].Expression).Value;
IList<string> quantityNames = ((string)context.SemanticModel.GetConstantValue(attrib.ArgumentList!.Arguments[0].Expression).Value).Split(',').Select(str => str.Trim()).ToList();

// string template = context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[1].Expression).ToString();
// string hash = context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[2].Expression).ToString();

QuantityNames.AddRange(quantityNames);
}
}
}
}
}
18 changes: 18 additions & 0 deletions UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGen.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>

<!-- RS1036: Specify analyzer banned API enforcement setting: A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. -->
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>

</Project>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[assembly: UnitsNetSrcGenInit("Length,Mass")]
7 changes: 7 additions & 0 deletions UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGenDemo/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using UnitsNetSrcGen;

var l = new Length {Value = 10, Unit = LengthUnit.Centimeter};
var m = new Mass { Value = 20, Unit = MassUnit.Kilogram };

Console.WriteLine($"Length: {l.Value} {l.Unit}");
Console.WriteLine($"Mass: {m.Value} {m.Unit}");
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<!-- Persist the source generator (and other) files to disk -->
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<!-- 👇 The "base" path for the source generators -->
<GeneratedFolder>Generated</GeneratedFolder>
<!-- 👇 Write the output for each target framework to a different sub-folder -->
<CompilerGeneratedFilesOutputPath>$(GeneratedFolder)\$(TargetFramework)</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGen.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
</ItemGroup>

</Project>
52 changes: 52 additions & 0 deletions UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGenDummySource/DummySources.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

/*
* Dummy sources for copy & paste into c# source generator strings in UnitsNet.Xp.SrcGen/QuantitySourceGenerator.cs.
*/
// ReSharper disable once CheckNamespace
namespace UnitsNet.Xp.SrcGen;

/// <summary>
/// Represents a quantity.
/// </summary>
public interface ISrcQuantity<TUnitEnum> where TUnitEnum : System.Enum
{
double Value { get; }
TUnitEnum Unit { get; }
}

public enum LengthUnit
{
Centimeter,
Meter,
}

public struct Length : ISrcQuantity<LengthUnit>
{
public Length(double value, LengthUnit unit)
{
Value = value;
Unit = unit;
}

public required double Value { get; init; }
public required LengthUnit Unit { get; init; }
}

public enum MassUnit
{
Gram,
Kilogram,
}

public struct Mass : ISrcQuantity<MassUnit>
{
public Mass(double value, MassUnit unit)
{
Value = value;
Unit = unit;
}

public required double Value { get; init; }
public required MassUnit Unit { get; init; }
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>