Skip to content

Commit

Permalink
Make auto-compiling of C# work with .NET Core
Browse files Browse the repository at this point in the history
CodeDom doesn't work in .NET Core (dotnet/runtime#18768) and Roslyn turns out to be too low level (just a compiler and not a building framework) so the best approach is to invoke msbuild as a process.

Signed-off-by: Dimitar Dobrev <dpldobrev@protonmail.com>
  • Loading branch information
ddobrev committed Dec 31, 2020
1 parent 8683546 commit 89159b2
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 107 deletions.
1 change: 0 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<Project>
<ItemGroup>
<PackageVersion Include="System.CodeDom" Version="4.7.0" />
<PackageVersion Include="Microsoft.Win32.Registry" Version="4.7.0" />
<PackageVersion Include="Microsoft.VisualStudio.Setup.Configuration.Interop" Version="2.3.2262-g94fae01e" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
Expand Down
4 changes: 3 additions & 1 deletion build/Tests.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ function SetupTestProjectsCSharp(name, depends, extraFiles, suffix)
str = "Std"
end

SetupExternalManagedTestProject(name .. ".CSharp")
if name ~= "NamespacesDerived" then
SetupExternalManagedTestProject(name .. ".CSharp")
end
SetupExternalManagedTestProject(name .. ".Tests.CSharp")
end

Expand Down
4 changes: 0 additions & 4 deletions src/Generator/CppSharp.Generator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,4 @@
<ProjectReference Include="..\Parser\CppSharp.Parser.csproj" />
<ProjectReference Include="$(NativeProjectsDir)Std-symbols.vcxproj" ReferenceOutputAssembly="false" Condition="$(IsWindows)" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.CodeDom" PrivateAssets="All" />
</ItemGroup>
</Project>
112 changes: 45 additions & 67 deletions src/Generator/Driver.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using CppSharp.AST;
using CppSharp.Generators;
using CppSharp.Generators.C;
using CppSharp.Generators.CLI;
using CppSharp.Generators.Cpp;
using CppSharp.Generators.CSharp;
using CppSharp.Parser;
using CppSharp.Passes;
using CppSharp.Utils;
using Microsoft.CSharp;
using CppSharp.Types;
using CppSharp.Generators.Cpp;
using CppSharp.Generators.C;

namespace CppSharp
{
public class Driver : IDisposable
{
public DriverOptions Options { get; private set; }
public DriverOptions Options { get; }
public ParserOptions ParserOptions { get; set; }
public BindingContext Context { get; private set; }
public Generator Generator { get; private set; }
Expand Down Expand Up @@ -353,66 +350,52 @@ private void WriteGeneratedCodeToFile(string file, string generatedCode)

private static readonly Dictionary<Module, string> libraryMappings = new Dictionary<Module, string>();

public void CompileCode(Module module)
public bool CompileCode(Module module)
{
var assemblyFile = Path.Combine(Options.OutputDir, module.LibraryName + ".dll");

var docFile = Path.ChangeExtension(assemblyFile, ".xml");

var compilerOptions = new StringBuilder();
compilerOptions.Append($" /doc:\"{docFile}\"");
compilerOptions.Append(" /debug:pdbonly");
compilerOptions.Append(" /unsafe");

var compilerParameters = new CompilerParameters
{
GenerateExecutable = false,
TreatWarningsAsErrors = false,
OutputAssembly = assemblyFile,
GenerateInMemory = false,
CompilerOptions = compilerOptions.ToString()
};

if (module != Options.SystemModule)
compilerParameters.ReferencedAssemblies.Add(
Path.Combine(Options.OutputDir, $"{Options.SystemModule.LibraryName}.dll"));
// add a reference to System.Core
compilerParameters.ReferencedAssemblies.Add(typeof(Enumerable).Assembly.Location);

var location = System.Reflection.Assembly.GetExecutingAssembly().Location;
var outputDir = Path.GetDirectoryName(location);
var locationRuntime = Path.Combine(outputDir, "CppSharp.Runtime.dll");
compilerParameters.ReferencedAssemblies.Add(locationRuntime);

compilerParameters.ReferencedAssemblies.AddRange(
(from dependency in module.Dependencies
where libraryMappings.ContainsKey(dependency)
select libraryMappings[dependency]).ToArray());

compilerParameters.ReferencedAssemblies.AddRange(module.ReferencedAssemblies.ToArray());

Diagnostics.Message($"Compiling {module.LibraryName}...");
CompilerResults compilerResults;
using (var codeProvider = new CSharpCodeProvider(
new Dictionary<string, string> {
{ "CompilerDirectoryPath", ManagedToolchain.FindCSharpCompilerDir() } }))
string csproj = Path.Combine(Options.OutputDir, $"{module.LibraryName}.csproj");
File.WriteAllText(Path.Combine(Options.OutputDir, "Directory.Build.props"), "<Project />");
File.WriteAllText(csproj,
$@"
<Project Sdk=""Microsoft.NET.Sdk"">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputPath>{Options.OutputDir}</OutputPath>
<Configuration>Release</Configuration>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableDefaultNoneItems>false</EnableDefaultNoneItems>
<EnableDefaultItems>false</EnableDefaultItems>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<ItemGroup>
{string.Join(Environment.NewLine, module.CodeFiles.Select(c => $"<Compile Include=\"{c}\" />"))}
</ItemGroup>
<ItemGroup>
{string.Join(Environment.NewLine,
new[] { Path.Combine(Path.GetDirectoryName(location), "CppSharp.Runtime.dll") }
.Union(from dependency in module.Dependencies
where libraryMappings.ContainsKey(dependency)
select libraryMappings[dependency])
.Select(reference =>
$@"<Reference Include=""{Path.GetFileNameWithoutExtension(reference)}"">
<HintPath>{reference}</HintPath>
</Reference>"))}
</ItemGroup>
</Project>".Trim());

string output = ProcessHelper.Run("dotnet", $"msbuild -restore {csproj}",
out int error, out string errorMessage);
if (error == 0)
{
compilerResults = codeProvider.CompileAssemblyFromFile(
compilerParameters, module.CodeFiles.ToArray());
Diagnostics.Message($@"Compilation succeeded: {
libraryMappings[module] = Path.Combine(Options.OutputDir, $"{module.LibraryName}.dll")}.");
return true;
}

var errors = compilerResults.Errors.Cast<CompilerError>().Where(e => !e.IsWarning &&
// HACK: auto-compiling on OS X produces "errors" which do not affect compilation so we ignore them
(!Platform.IsMacOS || !e.ErrorText.EndsWith("(Location of the symbol related to previous warning)", StringComparison.Ordinal))).ToList();
foreach (var error in errors)
Diagnostics.Error(error.ToString());

HasCompilationErrors = errors.Count > 0;
if (!HasCompilationErrors)
{
libraryMappings[module] = Path.Combine(outputDir, assemblyFile);
Diagnostics.Message("Compilation succeeded.");
}
Diagnostics.Error(output);
Diagnostics.Error(errorMessage);
return false;
}

public void AddTranslationUnitPass(TranslationUnitPass pass)
Expand Down Expand Up @@ -498,12 +481,7 @@ public static void Run(ILibrary library)

driver.SaveCode(outputs);
if (driver.Options.IsCSharpGenerator && driver.Options.CompileCode)
foreach (var module in driver.Options.Modules)
{
driver.CompileCode(module);
if (driver.HasCompilationErrors)
break;
}
driver.Options.Modules.Any(m => !driver.CompileCode(m));
}
}
}
Expand Down
12 changes: 0 additions & 12 deletions tests/NamespacesBase/NamespacesBase.CSharp.csproj

This file was deleted.

3 changes: 1 addition & 2 deletions tests/NamespacesBase/premake4.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ end

group "Tests/Namespaces"
SetupTestNativeProject("NamespacesBase")
targetdir (path.join(gendir, "NamespacesDerived"))
SetupWrapper("NamespacesBase")
targetdir (path.join(gendir, "NamespacesDerived"))
13 changes: 0 additions & 13 deletions tests/NamespacesDerived/NamespacesDerived.CSharp.csproj

This file was deleted.

2 changes: 1 addition & 1 deletion tests/NamespacesDerived/NamespacesDerived.Gen.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.IO;
using System.Reflection;
using CppSharp.AST;
using CppSharp.Generators;
using CppSharp.Utils;
Expand All @@ -18,6 +17,7 @@ public override void Setup(Driver driver)
base.Setup(driver);
driver.Options.GenerateDefaultValuesForArguments = true;
driver.Options.GenerateClassTemplates = true;
driver.Options.CompileCode = true;
driver.Options.DependentNameSpaces.Add("System.Runtime.CompilerServices");
driver.Options.Modules[1].IncludeDirs.Add(GetTestsDirectory("NamespacesDerived"));
var @base = "NamespacesBase";
Expand Down
14 changes: 13 additions & 1 deletion tests/NamespacesDerived/NamespacesDerived.Tests.CSharp.csproj
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk" />
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="NamespacesDerived.Gen.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<Reference Include="NamespacesBase">
<HintPath>..\..\build\gen\NamespacesDerived\NamespacesBase.dll</HintPath>
</Reference>
<Reference Include="NamespacesDerived">
<HintPath>..\..\build\gen\NamespacesDerived\NamespacesDerived.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
5 changes: 1 addition & 4 deletions tests/NamespacesDerived/premake4.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,4 @@ group "Tests/Namespaces"
end

SetupTestGeneratorProject("NamespacesDerived")
SetupTestProjectsCSharp("NamespacesDerived", "NamespacesBase")

project("NamespacesDerived.Tests.CSharp")
links { "NamespacesBase.CSharp" }
SetupTestProjectsCSharp("NamespacesDerived", "NamespacesBase")
2 changes: 1 addition & 1 deletion tests/Test.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="$(TestName).CSharp.csproj" Condition="$(MSBuildProjectName.EndsWith('CSharp'))" />
<ProjectReference Include="$(TestName).CSharp.csproj" Condition="$(MSBuildProjectName.EndsWith('CSharp')) AND $(TestName) != 'NamespacesDerived'" />
<ProjectReference Include="$(NativeProjectsDir)$(TestName).Native.vcxproj" Condition="$(IsWindows)" ReferenceOutputAssembly="false" />
<ProjectReference Include="$(NativeProjectsDir)$(TestName).CLI.vcxproj" Condition="$(MSBuildProjectName.EndsWith('CLI')) AND $(IsWindows)" />
</ItemGroup>
Expand Down

0 comments on commit 89159b2

Please sign in to comment.