Skip to content

Commit

Permalink
Decouple Orleans server dependency from base CloudActors
Browse files Browse the repository at this point in the history
We already had a separate assembly for the Orleans server extensions. By moving the grain generator to its own assembly, we can fully decouple the server/hosting interface and types from the bare business logic implemented via cloud actors. The only dependency would be the serializer generation which needs to be based on annotating the actual types, so it's unavoidable. But this is just the abstractions package.
  • Loading branch information
kzu committed Jul 29, 2024
1 parent c1c1de2 commit 9ec33eb
Show file tree
Hide file tree
Showing 24 changed files with 164 additions and 102 deletions.
8 changes: 7 additions & 1 deletion CloudActors.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudActors", "src\CloudAct
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "src\Tests\Tests.csproj", "{1F95CDB9-4030-4F16-A20D-12AE362D00DB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudActors.CodeAnaysis", "src\CloudActors.CodeAnaysis\CloudActors.CodeAnaysis.csproj", "{A610D1AC-9F46-4CC7-90AC-4B2DFBF4D5C2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudActors.CodeAnalysis", "src\CloudActors.CodeAnalysis\CloudActors.CodeAnalysis.csproj", "{A610D1AC-9F46-4CC7-90AC-4B2DFBF4D5C2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudActors.CodeFix", "src\CloudActors.CodeFix\CloudActors.CodeFix.csproj", "{03D8620C-6745-476A-B097-7C62D076F9A7}"
EndProject
Expand All @@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestDomain", "src\TestDomain\TestDomain.csproj", "{1D75C6E9-C4E9-44D8-B598-3A2D45C25EE6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudActors.Orleans.CodeAnalysis", "src\CloudActors.Orleans.CodeAnalysis\CloudActors.Orleans.CodeAnalysis.csproj", "{68823E7F-269E-4947-8E2B-9905F163C96B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -64,6 +66,10 @@ Global
{1D75C6E9-C4E9-44D8-B598-3A2D45C25EE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D75C6E9-C4E9-44D8-B598-3A2D45C25EE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D75C6E9-C4E9-44D8-B598-3A2D45C25EE6}.Release|Any CPU.Build.0 = Release|Any CPU
{68823E7F-269E-4947-8E2B-9905F163C96B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68823E7F-269E-4947-8E2B-9905F163C96B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68823E7F-269E-4947-8E2B-9905F163C96B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68823E7F-269E-4947-8E2B-9905F163C96B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using static Devlooped.CloudActors.Diagnostics;
using static Devlooped.CloudActors.AnalysisExtensions;

namespace Devlooped.CloudActors;

Expand All @@ -10,7 +10,7 @@ public class ActorBusOverloadGenerator : IIncrementalGenerator
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var source = context.CompilationProvider.SelectMany((x, _) => x.Assembly.GetAllTypes().OfType<INamedTypeSymbol>())
.Where(IsActorMessage)
.Where(t => t.IsActorMessage())
.Combine(context.CompilationProvider
.Select((c, _) => new
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using static Devlooped.CloudActors.Diagnostics;

namespace Devlooped.CloudActors;

Expand All @@ -13,7 +12,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var options = context.GetOrleansOptions();
var messages = context.CompilationProvider
.SelectMany((x, _) => x.Assembly.GetAllTypes().OfType<INamedTypeSymbol>())
.Where(IsActorMessage)
.Where(t => t.IsActorMessage())
.Where(t => t.IsPartial());

var additionalTypes = messages.SelectMany((x, _) =>
Expand All @@ -31,7 +30,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.Select(p => p.Type)
.OfType<INamedTypeSymbol>()))
// We already generate separately for actor messages.
.Where(t => !IsActorMessage(t) && t.IsPartial())
.Where(t => !t.IsActorMessage() && t.IsPartial())
.Collect();

context.RegisterImplementationSourceOutput(messages.Combine(options), GenerateCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Scriban;
using static Devlooped.CloudActors.Diagnostics;
using static Devlooped.CloudActors.AnalysisExtensions;

namespace Devlooped.CloudActors;

Expand All @@ -22,7 +22,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

var actors = context.CompilationProvider
.SelectMany((x, _) => x.Assembly.GetAllTypes().OfType<INamedTypeSymbol>())
.Where(x => x.GetAttributes().Any(IsActor));
.Where(x => x.GetAttributes().Any(x => x.IsActor()));

context.RegisterImplementationSourceOutput(actors.Combine(options), (ctx, source) =>
{
Expand Down
66 changes: 66 additions & 0 deletions src/CloudActors.CodeAnalysis/AnalysisExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Devlooped.CloudActors;

public static class AnalysisExtensions
{
public static SymbolDisplayFormat FullName { get; } = new(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable);

public static IEnumerable<ITypeSymbol> GetAllTypes(this IAssemblySymbol assembly)
=> GetAllTypes(assembly.GlobalNamespace);

static IEnumerable<ITypeSymbol> GetAllTypes(INamespaceSymbol namespaceSymbol)
{
foreach (var typeSymbol in namespaceSymbol.GetTypeMembers())
{
yield return typeSymbol;
}

foreach (var childNamespace in namespaceSymbol.GetNamespaceMembers())
{
foreach (var typeSymbol in GetAllTypes(childNamespace))
{
yield return typeSymbol;
}
}
}

public static string GetTypeName(this ITypeSymbol type, string containingNamespace)
{
var typeName = type.ToDisplayString(FullName);
if (typeName.StartsWith(containingNamespace + "."))
return typeName.Substring(containingNamespace.Length + 1);

return typeName;
}

public static string ToFileName(this ITypeSymbol type) => type.ToDisplayString(FullName).Replace('+', '.');

public static bool IsActorMessage(this ITypeSymbol type) => type.AllInterfaces.Any(x =>
x.ToDisplayString(FullName).Equals("Devlooped.CloudActors.IActorMessage"));

public static bool IsActorCommand(this ITypeSymbol type) => type.AllInterfaces.Any(x =>
x.ToDisplayString(FullName).StartsWith("Devlooped.CloudActors.IActorCommand") && x.IsGenericType);

public static bool IsActorVoidCommand(this ITypeSymbol type) => type.AllInterfaces.Any(x =>
x.ToDisplayString(FullName).StartsWith("Devlooped.CloudActors.IActorCommand") && !x.IsGenericType);

public static bool IsActorQuery(this ITypeSymbol type) => type.AllInterfaces.Any(x =>
x.ToDisplayString(FullName).StartsWith("Devlooped.CloudActors.IActorQuery") && x.IsGenericType);

public static bool IsActor(this ITypeSymbol type) => type.GetAttributes().Any(a => a.IsActor());

public static bool IsActor(this AttributeData attr) =>
attr.AttributeClass?.ToDisplayString(FullName) == "Devlooped.CloudActors.ActorAttribute";

public static bool IsPartial(this ITypeSymbol node) => node.DeclaringSyntaxReferences.Any(
r => r.GetSyntax() is TypeDeclarationSyntax { Modifiers: { } modifiers } &&
modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)));
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,34 +50,4 @@ public static class Diagnostics
"Build",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static SymbolDisplayFormat FullName { get; } = new(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable);

public static string ToFileName(this ITypeSymbol type) => type.ToDisplayString(FullName).Replace('+', '.');

public static bool IsActorMessage(ITypeSymbol type) => type.AllInterfaces.Any(x =>
x.ToDisplayString(FullName).Equals("Devlooped.CloudActors.IActorMessage"));

public static bool IsActorCommand(this ITypeSymbol type) => type.AllInterfaces.Any(x =>
x.ToDisplayString(FullName).StartsWith("Devlooped.CloudActors.IActorCommand") && x.IsGenericType);

public static bool IsActorVoidCommand(this ITypeSymbol type) => type.AllInterfaces.Any(x =>
x.ToDisplayString(FullName).StartsWith("Devlooped.CloudActors.IActorCommand") && !x.IsGenericType);

public static bool IsActorQuery(this ITypeSymbol type) => type.AllInterfaces.Any(x =>
x.ToDisplayString(FullName).StartsWith("Devlooped.CloudActors.IActorQuery") && x.IsGenericType);

public static bool IsActor(AttributeData attr) =>
attr.AttributeClass?.ToDisplayString(FullName) == "Devlooped.CloudActors.ActorAttribute";

public static bool IsPartial(ITypeSymbol node) =>
node.DeclaringSyntaxReferences.Any(r =>
r.GetSyntax() is ClassDeclarationSyntax { Modifiers: { } modifiers } &&
modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) ||
node.DeclaringSyntaxReferences.Any(r =>
r.GetSyntax() is RecordDeclarationSyntax { Modifiers: { } modifiers } &&
modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)));
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Scriban;
using static Devlooped.CloudActors.Diagnostics;
using static Devlooped.CloudActors.AnalysisExtensions;

namespace Devlooped.CloudActors;

Expand All @@ -18,7 +18,7 @@ public class EventSourcedGenerator : IIncrementalGenerator
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var actors = context.CompilationProvider.SelectMany((x, _) => x.Assembly.GetAllTypes().OfType<INamedTypeSymbol>())
.Where(x => x.GetAttributes().Any(IsActor) && x.AllInterfaces.Any(i => i.ToDisplayString(FullName) == "Devlooped.CloudActors.IEventSourced"))
.Where(x => x.GetAttributes().Any(a => a.IsActor()) && x.AllInterfaces.Any(i => i.ToDisplayString(FullName) == "Devlooped.CloudActors.IEventSourced"))
.Combine(context.CompilationProvider.Select((c, _) => c.GetTypeByMetadataName("Devlooped.CloudActors.IEventSourced")))
.Where(x => x.Right != null && !x.Right
// Only if users haven't already implemented *any* members of the interface
Expand Down
10 changes: 10 additions & 0 deletions src/CloudActors.CodeAnalysis/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Devlooped.CloudActors;

static class Extensions
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static void Analyze(SyntaxNodeAnalysisContext context)
return;

// Only require this for actor and message types
if (!symbol.GetAttributes().Any(IsActor) &&
if (!symbol.GetAttributes().Any(x => x.IsActor()) &&
// symbol implements IActorMessage
!symbol.AllInterfaces.Contains(messageType, SymbolEqualityComparer.Default))
return;
Expand Down Expand Up @@ -69,7 +69,7 @@ static void Analyze(CompilationAnalysisContext context)
// where the type is not partial
//.Where(t => !t.GetAttributes().Any(a => generateAttr.Equals(a.AttributeClass, SymbolEqualityComparer.Default))),
.Where(t =>
!t.GetAttributes().Any(IsActor) &&
!t.GetAttributes().Any(x => x.IsActor()) &&
!t.AllInterfaces.Contains(messageType, SymbolEqualityComparer.Default) &&
!t.IsPartial() &&
t.Locations.Any(l => l.IsInSource)),
Expand Down
49 changes: 0 additions & 49 deletions src/CloudActors.CodeAnaysis/Extensions.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/CloudActors.CodeFix/CloudActors.CodeFix.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CloudActors.CodeAnaysis\CloudActors.CodeAnaysis.csproj" />
<ProjectReference Include="..\CloudActors.CodeAnalysis\CloudActors.CodeAnalysis.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Scriban;
using static Devlooped.CloudActors.Diagnostics;
using static Devlooped.CloudActors.AnalysisExtensions;

namespace Devlooped.CloudActors;

Expand All @@ -16,14 +17,24 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var options = context.GetOrleansOptions();

var actors = context.CompilationProvider
.SelectMany((x, _) => x.Assembly.GetAllTypes().OfType<INamedTypeSymbol>())
.Where(x => x.GetAttributes().Any(IsActor));
.SelectMany((x, _) => x.Assembly
.GetAllTypes()
.OfType<INamedTypeSymbol>()
.Where(t => t.IsActor())
.Concat(x.GetUsedAssemblyReferences()
.SelectMany(r =>
{
if (x.GetAssemblyOrModuleSymbol(r) is IAssemblySymbol asm)
return asm.GetAllTypes().OfType<INamedTypeSymbol>().Where(t => t.IsActor());

return [];
})));

context.RegisterImplementationSourceOutput(actors.Combine(options), (ctx, source) =>
{
var (actor, options) = source;

var attribute = actor.GetAttributes().First(IsActor);
var attribute = actor.GetAttributes().First(x => x.IsActor());
var state = default(string);
var storage = default(string);
if (attribute.ConstructorArguments.Length >= 1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AssemblyName>Devlooped.CloudActors.Orleans.CodeAnalysis</AssemblyName>
<TargetFramework>netstandard2.0</TargetFramework>

<IsRoslynComponent>true</IsRoslynComponent>
<!-- CS8785: ignoring Orleans source generator being run due to https://github.com/NuGet/Home/issues/6279 -->
<NoWarn>RS1036;CS8785</NoWarn>

<PackFolder>analyzers/dotnet/roslyn4.5/cs</PackFolder>
<!-- See https://github.com/scriban/scriban#source-embedding -->
<PackageScribanIncludeSource>true</PackageScribanIncludeSource>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\CloudActors.CodeAnalysis\AnalysisExtensions.cs" Link="AnalysisExtensions.cs" />
<Compile Include="..\CloudActors.CodeAnalysis\OrleansGenerator.cs" Link="OrleansGenerator.cs" />
</ItemGroup>

<ItemGroup>
<!-- See: https://github.com/NuGet/Home/issues/6279. For now, ExcludeAssets doesn't work for analyzers/generators, so we ARE running
the Orleans source generator in this analyzer project itself, even if we don't want/need to. -->
<PackageReference Include="Microsoft.Orleans.CodeGenerator" Version="8.2.0" ExcludeAssets="all" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="NuGetizer" Version="1.2.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Pack="false" Version="4.5.0" />
<PackageReference Include="PolySharp" PrivateAssets="All" Version="1.14.1" />

<PackageReference Include="Scriban" Version="5.10.0" Pack="false" IncludeAssets="build" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" PrivateAssets="all" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" PrivateAssets="all" />
<PackageReference Include="ThisAssembly.AssemblyInfo" Version="1.4.3" PrivateAssets="all" />
<PackageReference Include="ThisAssembly.Resources" Version="1.4.3" PrivateAssets="all" Pack="false" />
</ItemGroup>

<ItemGroup>
<Reference Include="$(PkgMicrosoft_Orleans_CodeGenerator)\analyzers\dotnet\cs\Orleans.CodeGenerator.dll" />
<PackageFile Include="$(PkgMicrosoft_Orleans_CodeGenerator)\analyzers\dotnet\cs\Orleans.CodeGenerator.dll" PackagePath="$(PackFolder)" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="@(None -&gt; WithMetadataValue('Extension', '.sbntxt'))" Kind="text" />
</ItemGroup>

</Project>
Loading

0 comments on commit 9ec33eb

Please sign in to comment.