Skip to content
Merged
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
40 changes: 18 additions & 22 deletions Cesium.CodeGen.Tests/CodeGenNetInteropTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,57 @@
using System.Diagnostics.CodeAnalysis;
using Cesium.Compiler;
using Cesium.TestFramework;
using TruePath;
using Xunit.Abstractions;

namespace Cesium.CodeGen.Tests;

// TODO[#488]: Make them run in parallel, as all the integration tests
public class CodeGenNetInteropTests : CodeGenTestBase
public class CodeGenNetInteropTests(ITestOutputHelper output) : CodeGenTestBase
{
private readonly ITestOutputHelper _output;
public CodeGenNetInteropTests(ITestOutputHelper output)
{
_output = output;
}

private async Task DoTest(
TargetArchitectureSet architecture,
[StringSyntax("csharp")] string cSharpCode,
[StringSyntax("cpp")] string cCode)
{
var cSharpAssemblyPath = await CSharpCompilationUtil.CompileCSharpAssembly(
_output,
output,
CSharpCompilationUtil.DefaultRuntime,
cSharpCode);
var (cesiumAssembly, assemblyContents) = GenerateAssembly(runtime: null, arch: architecture, sources: new[]{cCode}, referencePaths: new[] { cSharpAssemblyPath });
var (cesiumAssembly, assemblyContents) = GenerateAssembly(
runtime: null,
arch: architecture,
sources: [cCode],
referencePaths: [cSharpAssemblyPath]);
await VerifyTypes(cesiumAssembly, architecture);
await VerifyAssemblyRuns(assemblyContents.ToArray(), cSharpAssemblyPath);
}

private async Task VerifyAssemblyRuns(byte[] assemblyContentToRun, string referencePath)
private async Task VerifyAssemblyRuns(byte[] assemblyContentToRun, AbsolutePath referencePath)
{
var testDirectoryPath = Path.GetTempFileName();
File.Delete(testDirectoryPath);
Directory.CreateDirectory(testDirectoryPath);

var testDirectory = Temporary.CreateTempFolder();
try
{
var assemblyPath = Path.Combine(testDirectoryPath, "EntryPoint.dll");
var runtimeConfigPath = Path.ChangeExtension(assemblyPath, ".runtimeconfig.json");
var assemblyPath = testDirectory / "EntryPoint.dll";
var runtimeConfigPath = Path.ChangeExtension(assemblyPath.Value, ".runtimeconfig.json");

await File.WriteAllBytesAsync(assemblyPath, assemblyContentToRun);
await File.WriteAllBytesAsync(assemblyPath.Value, assemblyContentToRun);
await File.WriteAllTextAsync(runtimeConfigPath, RuntimeConfig.EmitNet9());

DeployReferenceAssembly(CSharpCompilationUtil.CesiumRuntimeLibraryPath);
DeployReferenceAssembly(referencePath);

await ExecUtil.RunToSuccess(_output, "dotnet", testDirectoryPath, new[] { assemblyPath });
await ExecUtil.RunToSuccess(output, ExecUtil.DotNetHost, testDirectory, [assemblyPath.Value]);
}
finally
{
Directory.Delete(testDirectoryPath, recursive: true);
Directory.Delete(testDirectory.Value, recursive: true);
}

void DeployReferenceAssembly(string assemblyPath)
void DeployReferenceAssembly(AbsolutePath assemblyPath)
{
var targetFilePath = Path.Combine(testDirectoryPath, Path.GetFileName(assemblyPath));
File.Copy(assemblyPath, targetFilePath);
var targetFilePath = testDirectory / assemblyPath.FileName;
File.Copy(assemblyPath.Value, targetFilePath.Value);
}
}

Expand Down
7 changes: 4 additions & 3 deletions Cesium.CodeGen.Tests/CodeGenPInvokeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
// SPDX-License-Identifier: MIT

using Cesium.TestFramework;
using TruePath;

namespace Cesium.CodeGen.Tests;

public class CodeGenPInvokeTests : CodeGenTestBase
{
private const string _mainMockedFilePath = @"c:\a\b\c.c";
private static readonly string _mainMockedFilePath = OperatingSystem.IsWindows() ? @"C:\a\b\c.c" : "/a/b/c.c";

private static async Task DoTest(string source)
{
var processed = await PreprocessorUtil.DoPreprocess(_mainMockedFilePath, source);
var assembly = GenerateAssembly(default, processed);
var processed = await PreprocessorUtil.DoPreprocess(new AbsolutePath(_mainMockedFilePath), source);
var assembly = GenerateAssembly(null, processed);

var moduleType = assembly.Modules.Single().GetType("<Module>");
await VerifyMethods(moduleType);
Expand Down
28 changes: 17 additions & 11 deletions Cesium.CodeGen.Tests/CodeGenTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using JetBrains.Annotations;
using Mono.Cecil;
using Mono.Cecil.Cil;
using TruePath;
using Yoakke.Streams;
using Yoakke.SynKit.C.Syntax;

Expand All @@ -27,7 +28,7 @@ protected static AssemblyDefinition GenerateAssembly(TargetRuntimeDescriptor? ru
runtime,
@namespace: "",
globalTypeFqn: "",
referencePaths: Array.Empty<string>());
referencePaths: []);
return assembly;
}

Expand All @@ -37,7 +38,7 @@ protected static AssemblyDefinition GenerateAssembly(
string @namespace = "",
string globalTypeFqn = "", params string[] sources)
{
var (assembly, _) = GenerateAssembly(sources, runtime, arch, @namespace, globalTypeFqn, Array.Empty<string>());
var (assembly, _) = GenerateAssembly(sources, runtime, arch, @namespace, globalTypeFqn, []);
return assembly;
}

Expand All @@ -47,9 +48,14 @@ protected static (AssemblyDefinition, byte[]) GenerateAssembly(
TargetArchitectureSet arch = TargetArchitectureSet.Dynamic,
string @namespace = "",
string globalTypeFqn = "",
string[]? referencePaths = null)
AbsolutePath[]? referencePaths = null)
{
var context = CreateAssembly(runtime, arch, @namespace: @namespace, globalTypeFqn: globalTypeFqn, referencePaths);
var context = CreateAssembly(
runtime,
arch,
@namespace: @namespace,
globalTypeFqn: globalTypeFqn,
referencePaths?.Select(x => new LocalPath(x)).ToArray());
GenerateCode(context, sources);
return EmitAssembly(context);
}
Expand Down Expand Up @@ -82,22 +88,22 @@ private static AssemblyContext CreateAssembly(
TargetArchitectureSet targetArchitectureSet = TargetArchitectureSet.Dynamic,
string @namespace = "",
string globalTypeFqn = "",
string[]? referencePaths = null)
LocalPath[]? referencePaths = null)
{
var allReferences = (referencePaths ?? Array.Empty<string>()).ToList();
allReferences.Insert(0, typeof(Console).Assembly.Location);
var allReferences = (referencePaths ?? []).ToList();
allReferences.Insert(0, new LocalPath(typeof(Console).Assembly.Location));

CompilationOptions compilationOptions = new(
targetRuntime ?? CSharpCompilationUtil.DefaultRuntime,
targetArchitectureSet,
ModuleKind.Console,
typeof(Math).Assembly.Location,
typeof(RuntimeHelpers).Assembly.Location,
new LocalPath(typeof(Math).Assembly.Location),
new LocalPath(typeof(RuntimeHelpers).Assembly.Location),
allReferences,
@namespace,
globalTypeFqn,
Array.Empty<string>(),
Array.Empty<string>(),
[],
[],
ProducePreprocessedFile: false,
ProduceAstFile: false);
return AssemblyContext.Create(
Expand Down
1 change: 1 addition & 0 deletions Cesium.CodeGen/Cesium.CodeGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ SPDX-License-Identifier: MIT
<PackageReference Include="Mono.Cecil" />
<PackageReference Include="QuikGraph" />
<PackageReference Include="QuikGraph.Graphviz" />
<PackageReference Include="TruePath" />
</ItemGroup>

<ItemGroup>
Expand Down
57 changes: 52 additions & 5 deletions Cesium.CodeGen/CompilationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,66 @@
// SPDX-License-Identifier: MIT

using Mono.Cecil;
using TruePath;

namespace Cesium.CodeGen;

public record CompilationOptions(
TargetRuntimeDescriptor TargetRuntime,
TargetArchitectureSet TargetArchitectureSet,
ModuleKind ModuleKind,
string CorelibAssembly,
string CesiumRuntime,
IList<string> ImportAssemblies,
LocalPath CorelibAssembly,
LocalPath CesiumRuntime,
IList<LocalPath> ImportAssemblies,
string Namespace,
string GlobalClassFqn,
IList<string> DefineConstants,
IList<string> AdditionalIncludeDirectories,
IList<LocalPath> AdditionalIncludeDirectories,
bool ProducePreprocessedFile,
bool ProduceAstFile);
bool ProduceAstFile)
{
public virtual bool Equals(CompilationOptions? other)
{
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
return TargetRuntime.Equals(other.TargetRuntime)
&& TargetArchitectureSet == other.TargetArchitectureSet
&& ModuleKind == other.ModuleKind
&& CorelibAssembly.Equals(other.CorelibAssembly)
&& CesiumRuntime.Equals(other.CesiumRuntime)
&& ImportAssemblies.SequenceEqual(other.ImportAssemblies)
&& Namespace == other.Namespace
&& GlobalClassFqn == other.GlobalClassFqn
&& DefineConstants.SequenceEqual(other.DefineConstants)
&& AdditionalIncludeDirectories.SequenceEqual(other.AdditionalIncludeDirectories)
&& ProducePreprocessedFile == other.ProducePreprocessedFile
&& ProduceAstFile == other.ProduceAstFile;
}

public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(TargetRuntime);
hashCode.Add(TargetArchitectureSet);
hashCode.Add(ModuleKind);
hashCode.Add(CorelibAssembly);
hashCode.Add(CesiumRuntime);
foreach (var importAssembly in ImportAssemblies)
{
hashCode.Add(importAssembly);
}
hashCode.Add(Namespace);
hashCode.Add(GlobalClassFqn);
foreach (var defineConstant in DefineConstants)
{
hashCode.Add(defineConstant);
}
foreach (var additionalIncludeDirectory in AdditionalIncludeDirectories)
{
hashCode.Add(additionalIncludeDirectory);
}
hashCode.Add(ProducePreprocessedFile);
hashCode.Add(ProduceAstFile);
return hashCode.ToHashCode();
}
}
9 changes: 6 additions & 3 deletions Cesium.CodeGen/Contexts/AssemblyContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,12 @@ private AssemblyContext(
Module = module;
CompilationOptions = compilationOptions;

MscorlibAssembly = AssemblyDefinition.ReadAssembly(compilationOptions.CorelibAssembly);
CesiumRuntimeAssembly = AssemblyDefinition.ReadAssembly(compilationOptions.CesiumRuntime);
ImportAssemblies = compilationOptions.ImportAssemblies.Select(AssemblyDefinition.ReadAssembly).Union(new[] { MscorlibAssembly, CesiumRuntimeAssembly }).Distinct().ToArray();
MscorlibAssembly = AssemblyDefinition.ReadAssembly(compilationOptions.CorelibAssembly.Value);
CesiumRuntimeAssembly = AssemblyDefinition.ReadAssembly(compilationOptions.CesiumRuntime.Value);
ImportAssemblies = compilationOptions.ImportAssemblies
.Select(x => AssemblyDefinition.ReadAssembly(x.Value))
.Union([MscorlibAssembly, CesiumRuntimeAssembly])
.Distinct().ToArray();
_constantPool = new(
() =>
{
Expand Down
15 changes: 15 additions & 0 deletions Cesium.Compiler.Tests/AssemblyFileVerifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2025 Cesium contributors <https://github.com/ForNeVeR/Cesium>
//
// SPDX-License-Identifier: MIT

using System.Reflection;
using Cesium.TestFramework;

namespace Cesium.Compiler.Tests;

public class AssemblyFileVerifier
{
[Fact]
public void AssemblyHasNoUnusedTestFiles() =>
TestFileVerification.VerifyAllTestsFromAssembly(Assembly.GetExecutingAssembly());
}
1 change: 1 addition & 0 deletions Cesium.Compiler.Tests/Cesium.Compiler.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ SPDX-License-Identifier: MIT

<ItemGroup>
<ProjectReference Include="..\Cesium.Compiler\Cesium.Compiler.csproj" />
<ProjectReference Include="..\Cesium.TestFramework\Cesium.TestFramework.csproj" />
</ItemGroup>

</Project>
81 changes: 81 additions & 0 deletions Cesium.Compiler.Tests/JsonObjectFileTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2025 Cesium contributors <https://github.com/ForNeVeR/Cesium>
//
// SPDX-License-Identifier: MIT

using Cesium.CodeGen;
using Cesium.TestFramework;
using Mono.Cecil;
using TruePath;

namespace Cesium.Compiler.Tests;

public class JsonObjectFileTests : VerifyTestBase
{
[Theory, NoVerify]
[InlineData("file.json", false)]
[InlineData("file.obj", true)]
public void CorrectExtensions(string fileName, bool result) =>
Assert.Equal(result, JsonObjectFile.IsCorrectExtension(new LocalPath(fileName)));

private readonly LocalPath[] _inputFiles =
[
new("/nonexistent-folder/file1.c"),
new("file2.c")
];

private readonly CompilationOptions _options = new(
TargetRuntimeDescriptor.NetStandard20,
TargetArchitectureSet.Dynamic,
ModuleKind.Dll,
new("/corLib.dll"),
new("/cesiumRuntime.dll"),
[
new("ref1.dll"),
new("/nonexistent-folder/ref2.dll")
],
"My.Namespace",
"My.Global.Class",
["CONSTANT1", "CONSTANT2"],
[
new("/nonexistent-folder/include")
],
ProducePreprocessedFile: false,
ProduceAstFile: true
);

[Fact]
public async Task ObjectFileGetsDumpedCorrectly()
{
var outFile = Temporary.CreateTempFile();
try
{
await JsonObjectFile.Write(_inputFiles, _options, outFile);

var content = await File.ReadAllTextAsync(outFile.Value);
await Verify(Normalize(content), GetSettings());
}
finally
{
File.Delete(outFile.Value);
}

static string Normalize(string s) => s.Replace(@"\\", "/");
}

[Fact, NoVerify]
public async Task ObjectFileGetsReadCorrectly()
{
var objectFile = Temporary.CreateTempFile();
try
{
await JsonObjectFile.Write(_inputFiles, _options, objectFile);
var content = await JsonObjectFile.Read(objectFile);
Assert.Equal(_inputFiles, content.InputFilePaths.Select(x => new LocalPath(x)));
Assert.Equal(_options, content.CompilationOptions);
}
finally
{
File.Delete(objectFile.Value);
}
}
}
Loading
Loading