Skip to content
Draft
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
17 changes: 17 additions & 0 deletions Akka.sln
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.TestKit.Xunit2.Tests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.TestKit.Xunit.Tests", "src\contrib\testkits\Akka.TestKit.Xunit.Tests\Akka.TestKit.Xunit.Tests.csproj", "{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AOT", "AOT", "{2E11BBFC-EA1F-4C20-8076-0D8542397264}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.AOT.App", "src\aot\Akka.AOT.App\Akka.AOT.App.csproj", "{6732F90D-59C9-45BA-801A-24BE2A12EB62}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1318,6 +1322,18 @@ Global
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|x64.Build.0 = Release|Any CPU
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|x86.ActiveCfg = Release|Any CPU
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|x86.Build.0 = Release|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Debug|x64.ActiveCfg = Debug|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Debug|x64.Build.0 = Debug|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Debug|x86.ActiveCfg = Debug|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Debug|x86.Build.0 = Debug|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Release|Any CPU.Build.0 = Release|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Release|x64.ActiveCfg = Release|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Release|x64.Build.0 = Release|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Release|x86.ActiveCfg = Release|Any CPU
{6732F90D-59C9-45BA-801A-24BE2A12EB62}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1441,6 +1457,7 @@ Global
{337A85B5-4A7C-4883-8634-46E7E52A765F} = {7735F35A-E7B7-44DE-B6FB-C770B53EB69C}
{95017C99-E960-44E5-83AD-BF21461DF06F} = {7625FD95-4B2C-4A5B-BDD5-94B1493FAC8E}
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68} = {7625FD95-4B2C-4A5B-BDD5-94B1493FAC8E}
{6732F90D-59C9-45BA-801A-24BE2A12EB62} = {2E11BBFC-EA1F-4C20-8076-0D8542397264}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {03AD8E21-7507-4E68-A4E9-F4A7E7273164}
Expand Down
33 changes: 33 additions & 0 deletions build-system/azure.aot.template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
parameters:
name: ''
displayName: ''
vmImage: ''
run_if: true
timeoutInMinutes: 10

jobs:
- job: ${{ parameters.name }}
condition: eq( ${{ parameters.run_if }}, true )
displayName: ${{ parameters.displayName }}
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
pool:
vmImage: ${{ parameters.vmImage }}
steps:
- task: UseDotNet@2
displayName: 'Use .NET'
inputs:
packageType: 'sdk'
useGlobalJson: true

# Option 1: Use inline script to handle paths properly
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
./scripts/test-aot-compatibility.ps1 net8.0
pwsh: true

- script: 'echo 1>&2'
failOnStderr: true
displayName: 'If above is partially succeeded, then fail'
condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues')
12 changes: 12 additions & 0 deletions build-system/pr-validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,15 @@ jobs:
command: "dotnet pack -c Release -o bin/nuget"
outputDirectory: "bin/nuget"
artifactName: "nuget_pack-$(Build.BuildId)"

- template: azure.aot.template.yaml
Copy link
Member Author

Choose a reason for hiding this comment

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

We do AOT on both Windows and Linux just in-case there are platform-specific issues. Should never happen, but you never know.

parameters:
name: "aot_linux"
displayName: "AOT Compilation (Linux)"
vmImage: "ubuntu-latest"

- template: azure.aot.template.yaml
parameters:
name: "aot_windows"
displayName: "AOT Compilation (Windows)"
vmImage: "windows-latest"
64 changes: 64 additions & 0 deletions scripts/test-aot-compatibility.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
param([string]$targetNetFramework = "net8.0")

$rootDirectory = Split-Path $PSScriptRoot -Parent
$publishOutput = dotnet publish $rootDirectory/src/aot/Akka.AOT.App/Akka.AOT.App.csproj -nodeReuse:false /p:UseSharedCompilation=false /p:ExposeExperimentalFeatures=true

$actualWarningCount = 0

foreach ($line in $($publishOutput -split "`r`n"))
{
if ($line -like "*analysis warning IL*")
{
Write-Host $line

$actualWarningCount += 1
}
}

# Determine the OS-specific folder
$osPlatform = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription
if ($osPlatform -match "Windows") {
$osFolder = "win-x64"
} else {
$osFolder = "linux-x64"
# Default to linux
}

$testAppPath = Join-Path -Path $rootDirectory/src/aot/Akka.AOT.App/bin/Release/$targetNetFramework -ChildPath $osFolder/publish

if (-Not (Test-Path $testAppPath)) {
Write-Error "Test App path does not exist: $testAppPath"
Exit 1
}

Write-Host $testAppPath
pushd $testAppPath

Write-Host "Executing test App..."
if ($osPlatform -match "Windows") {
./Akka.AOT.App.exe
} else {
# Default to linux
./Akka.AOT.App
}

Write-Host "Finished executing test App"

if ($LastExitCode -ne 0)
{
Write-Host "There was an error while executing AotCompatibility Test App. LastExitCode is:", $LastExitCode
}

popd

Write-Host "Actual warning count is:", $actualWarningCount
$expectedWarningCount = 0

$testPassed = 0
if ($actualWarningCount -ne $expectedWarningCount)
{
$testPassed = 1
Write-Host "Actual warning count:", $actualWarningCount, "is not as expected. Expected warning count is:", $expectedWarningCount
}

Exit $testPassed
18 changes: 18 additions & 0 deletions src/aot/Akka.AOT.App/Actors/AotReceiveActor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// -----------------------------------------------------------------------
// <copyright file="AotReceiveActor.cs" company="Akka.NET Project">
// Copyright (C) 2009-2025 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
// -----------------------------------------------------------------------

using Akka.Actor;

namespace Akka.AOT.App.Actors;

public class AotReceiveActor : ReceiveActor
{
public AotReceiveActor()
{
ReceiveAny(o => Sender.Tell(o));
Copy link
Member Author

Choose a reason for hiding this comment

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

We know ReceiveActors use the ExpressionCompiler in v1.5 and earlier, which should trip AOT warnings. I might need to add a stricter Receive<string> with a predicate prior to this one just to make sure that code gets exercised properly in the test,

}
}
18 changes: 18 additions & 0 deletions src/aot/Akka.AOT.App/Actors/AotUntypedActor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// -----------------------------------------------------------------------
// <copyright file="AotUntypedActor.cs" company="Akka.NET Project">
// Copyright (C) 2009-2025 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
// -----------------------------------------------------------------------

using Akka.Actor;

namespace Akka.AOT.App.Actors;

public class AotUntypedActor : UntypedActor
Copy link
Member Author

Choose a reason for hiding this comment

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

UntypedActor is already AOT-friendly, but we should still see that show up.

Copy link
Member Author

Choose a reason for hiding this comment

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

We probably need to add more cases to the canary over time, such as:

  1. IStash support
  2. IWithTimers support
  3. Custom dispatchers
  4. Custom mailboxes
  5. Routers

Just to gradually add full trim coverage to everything in the default Akka library, minus default serialization (which we know will not work)

{
protected override void OnReceive(object message)
{
Sender.Tell(message);
}
}
22 changes: 22 additions & 0 deletions src/aot/Akka.AOT.App/Akka.AOT.App.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(NetTestVersion)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<PropertyGroup Label="AotSettings">
Copy link
Member Author

Choose a reason for hiding this comment

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

These are all the MSBUILD settings needed to get the compiler to produce the AOT warnings we're looking for

<PublishAot>true</PublishAot>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
<SelfContained>true</SelfContained>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\core\Akka\Akka.csproj"/>
</ItemGroup>

</Project>
27 changes: 27 additions & 0 deletions src/aot/Akka.AOT.App/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Akka.Actor;
using Akka.AOT.App.Actors;

namespace Akka.AOT.App;

class Program
{
static async Task Main(string[] args)
{
var system = ActorSystem.Create("MySystem");
Copy link
Member Author

Choose a reason for hiding this comment

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

Does some end to end messaging and uses the default configuration loading, which currently triggers a number of AOT warnings due to how we load Types from HOCON strings - those are all issues on the #7246 epic we will have to gradually fix before this spec can pass.


// create AotUntypedActor
var untypedActorProps = Props.Create(() => new AotUntypedActor());
var untypedActor = system.ActorOf(untypedActorProps, "untyped-actor");

// create AotReceiveActor
var receiveActorProps = Props.Create(() => new AotReceiveActor());
var receiveActor = system.ActorOf(receiveActorProps, "receive-actor");

// send a message to both actors
Console.WriteLine(await untypedActor.Ask("Hello, untyped actor!"));
Console.WriteLine(await receiveActor.Ask("Hello, receive actor!"));

// terminate the actor system
await system.Terminate();
}
}