Antlr4 C# Application Tutorial/Example - c#

I want to use Antlr4 to parse some files in my C# application. I have been able to generate the parser and lexer files so far given my grammer. Now I would like to use read in the files and apply the parser and lexer to them. I have been searching for documentation on how to do that but I am coming up short. I have found some old examples using previous versions of Antlr but they don't appear to work for Antlr4. Any help would be appreciated. Thanks.

In Visual Studio, go to Tools -> Extensions and Updates and search the Online section for "ANTLR Language Support" by Sam Harwell. More information can be found on the GitHub project site
This does a few things:
Adds Templates for the combined grammars.
Adds Syntax Highlighting
Adds an MSBuild target for the grammar to generate the parser.
In your solution, set up your project structure like this:
Solution
GrammarProject
Calculator.g4
ImplementationProject
GeneratedFiles (All files in this folder are added as Links to files located in GrammarProject\obj\Debug)
CalculatorBaseListener.cs
CalculatorBaseVisitor.cs
CalculatorLexer.cs
CalculatorListener.cs
CalculatorParser.cs
CalculatorVistor.cs
MyCalcualtorImplementation.cs
Write and Compile your grammar.
In your folder for the Links to Generated Files, Right-Click the folder and click Add -> Existing Item
Browse to Grammar Project\obj\Debug and select all the generated parser files.
This next step is important. On the Add button there is a little drop-down arrow. Click the drop-down arrow and click "Add As Link".
This will add the generated files to the implementation project using a symbolic link instead of a direct copy.
This gives the added benefit of not having to remove and re-add the parser files if you have to change your grammar later.
Once you have gotten this far, then you can inherit from GrammarProject.CalculatorBaseListener or GrammarProject.CalculatorBaseParser depending on what development pattern you have decided to use.
As a side note, "The Definitive ANTLR 4 Reference" by Terence Parr is an excellent resource to understand how ANTLR4 works and the difference development patterns. All the examples are in java, but the concepts apply to both Java and C#.

try with
using (StreamReader fileStream = new StreamReader(fileName)) {
AntlrInputStream inputStream = new AntlrInputStream(fileStream);
SearchLexer lexer = new SearchLexer(inputStream);
CommonTokenStream commonTokenStream = new CommonTokenStream(lexer);
SearchParser parser = new SearchParser(commonTokenStream);
parser.RemoveErrorListeners();
parser.AddErrorListener(new ErrorListener()); // add ours
parser.root();
}

I am using Visual Studio 2019 Professional (latest version 16.7.3)
As for now ANTLR Language Support are not available for VS 2019. There is an unofficial version available https://github.com/tunnelvisionlabs/antlr4cs/issues/353 but it not complaint with VS2019 extension API
(more info here: https://devblogs.microsoft.com/visualstudio/updates-to-synchronous-autoload-of-extensions-in-visual-studio-2019/)
You may try the following (steps for .net standard library)
Install VS extension AntlrVSIX 8.0 (using the Extension Manager)
create a .NET Standard Library project (MyLib.Parser.Grammar)
Created a dummy (.cs) class - not sure if its still necessary, there were some issues in the past if the project contained only grammar files
Reference the following packages (using Nuget)
Antlr4.Runtime.Standard
Antlr4BuildTasks
Add grammar files (.g4) e.g. you can used the grammar repository available here
https://github.com/antlr/grammars-v4
Let's say you would like to parse TSQL (https://github.com/antlr/grammars-v4/tree/master/sql/tsql) - add TSqlParser.g4 and TSqllexer.g4 to your project
Edit the project file MyLib.Parser.Grammar.csproj, it should look something like
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Antlr4 Include="TSqlLexer.g4">
<Package>MyLib.Parser</Package>
<Visitor>true</Visitor>
<Error>false</Error>
<Listener>true</Listener>
</Antlr4>
<Antlr4 Include="TSqlParser.g4">
<Package>MyLib.Parser</Package>
<Visitor>true</Visitor>
<Error>false</Error>
<Listener>true</Listener>
</Antlr4>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Antlr4.Runtime.Standard" Version="4.8.0" />
<PackageReference Include="Antlr4BuildTasks" Version="8.3.0" />
</ItemGroup>
</Project>
At this point when you build the MyLib.Parser.Grammar project the Antlr4BuildTasks tools will create the parser .cs files, but they will be available in the project bin folder (e.g. \MyLib.Parser.Grammar\bin\Debug\netstandard2.1)
Create another library project MyLib.Parser
Create project dependency so MyLib.Parser.Grammar is build before MyLib.Parser
Direct the output files from MyLib.Parser.Grammar to MyLib.Parser project by using the AntOutDir attribute and relative paths in the project definition. Now the Antlr4 section in the project file should like something like:
<ItemGroup>
<Antlr4 Include="TSqlLexer.g4">
<Package>MyLib.Parser</Package>
<Visitor>true</Visitor>
<Error>false</Error>
<Listener>true</Listener>
<AntOutDir>..\MyLib.Parser</AntOutDir>
</Antlr4>
<Antlr4 Include="TSqlParser.g4">
<Package>MyLib.Parser</Package>
<Visitor>true</Visitor>
<Error>false</Error>
<Listener>true</Listener>
<AntOutDir>..\MyLib.Parser</AntOutDir>
</Antlr4>
</ItemGroup>
Now after rebuild, the generated .cs files should be added to the MyLib.Parser project automatically

here are a sample of ErrorListener
public class ErrorListener : BaseErrorListener
{
public void SyntaxError(IRecognizer recognizer, int offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
{
Console.WriteLine("{0}: line {1}/column {2} {3}", e, line, charPositionInLine, msg);
}
}

Related

Using Grpc.Tools with Protoc plug-in to generate additional C# files

I am using Grpc.Tools (2.38.1) to generate C# types and gRPC stubs from a Test.proto file containing some service definitions.
To do this I have the following in my project's .csproj file:
<ItemGroup>
<Protobuf Include="**/*.proto" />
</ItemGroup>
This is all working fine: my Test.proto gets compiled to Test.cs and TestGrpc.cs in the obj/Debug folder of my project. The types within them can be referenced from within other types in the project.
But I need to create a WCF interface for the service too, so I thought I could generate this using a custom Protoc plug-in. So I wrote a simple Protoc plug-in that writes out a TestWcf.cs file containing an interface. I then placed this plug-in executable on my path named protoc-gen-blah.exe and updated the entry in the .csproj file to this:
<ItemGroup>
<Protobuf Include="**/*.proto" AdditionalProtocArguments="--blah_out=obj\Debug" />
</ItemGroup>
This correctly creates the C# file, TestWcf.cs, with my interface in: fantastic.
The problem is that my interface within TestWcf.cs cannot be referenced from other types in the project unless I manually include the generated file in the project: something I do not have to do with the other generated files.
Whilst none of the files are included in the project by default―I have to enable 'Show All Files' to see them―Test.cs and TestGrpc.cs have arrows beside them in the Solution Explorer that allow them to be expanded to reveal the types inside. TestWcf.cs does not have this arrow. So Visual Studio is somehow aware that Test.cs and TestGrpc.cs are source code files.
Does anyone know what I need to do for my generated file to be automatically recognised by Visual Studio like the other two files are?
I suspect it has something to do with this part of the Grpc.Tools build target, as I noticed my TestWcf.cs file is not included in the files deleted by the Grpc.Tools clean either, but I can't see why it does not consider my generated file to be C#.
When I build, this is the Protoc call:
D:\...\Src\packages\Grpc.Tools.2.38.1\tools\windows_x86\protoc.exe --csharp_out=obj\Debug ⤶
--plugin=protoc-gen-grpc=D:\...\Src\packages\Grpc.Tools.2.38.1\tools\windows_x86\grpc_csharp_plugin.exe ⤶
--grpc_out=obj\Debug --proto_path=D:\...\Src\packages\Grpc.Tools.2.38.1\build\native\include ⤶
--proto_path=. --dependency_out=obj\Debug\xxxx_Test.protodep --error_format=msvs --blah_out=obj\Debug ⤶
Test.proto
The dependency file looks like this:
obj\Debug/Test.cs \
obj\Debug/TestGrpc.cs \
obj\Debug/TestWcf.cs: Test.proto
Thanks.
I believe the problem is caused some logic in Grpc.Tools that informs MSBuild of the files that have been generated:
public override string[] GetPossibleOutputs(ITaskItem protoItem)
{
...
var outputs = new string[doGrpc ? 2 : 1];
...
outputs[0] = Path.Combine(outdir, filename) + ".cs";
if (doGrpc)
{
...
outputs[1] = Path.Combine(grpcdir, filename) + "Grpc.cs";
}
return outputs;
}
This code only caters for two files being generated from a Protocol Buffer source (name.proto): the Protocol Buffers code generation (name.cs) and the gRPC code generation (nameGrpc.cs). It is not picking up the additional file and informing MSBuild that it exists, hence Visual Studio does not consider it to be code.
There is no away around this short of changing the Grpc.Tools code.

Add VSIX features to C# Class Library

I have an existing Single-File Generator (housed in a C# Class Library). How do you add the VSIX project-level features to this project? The end goal is to compile my class library project and get a VSIX.
(I'm actually answering my own question. This is in relation to SIngle-file generator changes in Visual Studio 2017 - but that question wasn't asking what I'm answering here.)
First off,
your Single-File Generator class needs to have the appropriate class-level attributes:
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
using VSLangProj80;
[ComVisible(true)]
[Guid("your-unique-identifier")]
[CodeGeneratorRegistration(
typeof(MyCustomTool),
"MyCustomTool",
vsContextGuids.vsContextGuidVCSProject,
GeneratesDesignTimeSource = true,
GeneratorRegKeyName = "MyCustomTool"
)]
[ProvideObject(
typeof(MyCustomTool),
RegisterUsing = RegistrationMethod.CodeBase
)]
public sealed class MyCustomTool : IVsSingleFileGenerator {
All of these attributes will ensure that a .pkgdef file is correctly generated within your VSIX. The .pkgdef file contains the registry entries that are used to register your single-file generator with Visual Studio.
Second,
add a text file "source.extension.vsixmanifest" to your project. Its "Build Action" should be "None." Give it some default text of:
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="MyCustomTool.MyCompany.another-random-guid" Version="1.0" Language="en-US" Publisher="MyCompany" />
<DisplayName>MyCustomTool</DisplayName>
<Description>Helpful information</Description>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[15.0]" />
</Installation>
<Dependencies>
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" />
</Dependencies>
<Prerequisites>
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[15.0,16.0)" DisplayName="Visual Studio core editor" />
</Prerequisites>
</PackageManifest>
Most of this stuff is pretty esoteric. In the next step, we'll make it so you can edit this file with a designer.
Third
(and to answer the original question), you need to manhandle the .csproj file (your C# Class Library file). Specifically, you need to add the following:
<PropertyGroup>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<ItemGroup>
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<PropertyGroup>
<UseCodebase>true</UseCodebase>
</PropertyGroup>
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
So, what have we done here? Let's break it down.
First, we setup a path to the location of the Visual Studio toolset. Afterall, this is now an "extensions project." So, it needs to know where the VS-SDK is located.
Then, we changed the "ProjectTypeGuids" (which probably wasn't in your project file to begin with). Originally, it just included the guid for C# (which is the "FAE04EC0-..." guid). Now, it also includes the guid for VSIX (which is the "82b43b9b-..." guid).
We also made sure the "source.extension.vsixmanifest" file uses its new, fancy designer (instead of editing the file by hand).
The "UseCodeBase" element is important. This element prevents you from being forced to register your Generator with the system's COM registry. Instead, Visual Studio will simply load up your Generator from its installation location.
At the bottom, we import the MsBuild stuff for the VS-SDK.
Fourth,
Load your project back up. Go to the Project Properties screen and you'll see a new "VSIX" section at the bottom. Open that section and check the "Create VSIX Container during build" checkbox.
At this point, you can also double-check the "source.extension.vsixmanifest" file. Depending on how fancy your Generator is, you can change whatever you need. (The contents of the file that I pasted above is pretty much exactly what I used for my project.)
And finally,
you can compile your project. In the bin folder, you'll find MyCustomTool.dll and MyCustomTool.vsix. The .vsix file is simply a zip file. Inside the .vsix, you'll find MyCustomTool.pkgdef.
If we've done everything correctly, the .pkgdef file should look something like this:
[$RootKey$\Generators\{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}\MyCustomTool]
#="MyCustomTool"
"CLSID"="{your-unique-identifier}"
"GeneratesDesignTimeSource"=dword:00000001
[$RootKey$\CLSID\{your-unique-identifier}]
#="MyCustomTool"
"InprocServer32"="$WinDir$\SYSTEM32\MSCOREE.DLL"
"Class"="MyCustomTool"
"CodeBase"="$PackageFolder$\MyCustomTool.dll"
"ThreadingModel"="Both"
And, I think this is the longest SO answer I've written. And probably, only 5 people will ever read this :)
If, instead of implementing the IVsSingleFileGenerator on your class, your class inherits from the abstract class BaseCodeGeneratorWithSite that inherits from the abstract class BaseCodeGenerator that implements IVsSingleFileGenerator you need to add the following atribute to your class, to avoid the error message "Cannot find custom tool '...' on this system":
[ClassInterface(ClassInterfaceType.None)]
The reason is that the abstract class BaseCodeGeneratorWithSite is not COM visible.

SonarQube with C# plugin with MSBuild Runner does not take exclusions

Currently I have an instance of SonarQube 5.1.2 with C# plugin and MSBuild runner in order to analyze a 1.200.000 LOC project. I intend to reduce the classes that are analyzed, I created a sonar.properties file with the line
sonar.exclusions=**/Databases/**/*.*
but after reading the log from the analysis, files inside the Databases folder were analyzed. following the instructions from Eric Starr, I set this simple exclusion rule in the call of the runner:
"C:\sonarqube-5.1.2\bin\MSBuild.SonarQube.Runner.exe" begin /k:MyProject /n:MyProject /v:2 /d:sonar.exclusions="file:C:\codesource\Databases/**/*.*" /d:sonar.scm.provider=tfvc /d:sonar.tfvc.username=************* /d:sonar.tfvc.password.secured={aes}*************************** "/d:sonar.cs.vscoveragexml.reportsPaths=C:\codesource\CodeCoverage\Results.coveragexml"
I found that the runner creates a sonar-project.properties file, and it contains a lot of files located in the databases folder:
BC78C8C4-8ECD-47CB-9781-F621AE109FE4.sonar.projectName=myDatabase
BC78C8C4-8ECD-47CB-9781-F621AE109FE4.sonar.projectBaseDir=BC78C8C4-8ECD-47CB-9781-F621AE109FE4.sonar.projectName=myDatabase
BC78C8C4-8ECD-47CB-9781-F621AE109FE4.sonar.projectBaseDir=C:\\codesource\\Databases\\myDatabase
BC78C8C4-8ECD-47CB-9781-F621AE109FE4.sonar.sources=\
C:\\codesource\\Databases\\myDatabase\\Scripts\\PreDeployment\\PATCH_20150527_01.sql,\
C:\\codesource\\Databases\\myDatabase\\Scripts\\PreDeployment\\ROCOMMON.DBVERSION.sql,\
,\.....
as I understood, there should be no files in the databases folder. Am I wrong?
You are using the SonarQube Scanner for MSBuild which is very different from the regular SonarQube Scanner used for all other languages.
The sonar.exclude line that you are trying to use would only work if you would use the regular SonarQube scanner, because that takes in the Sonar-project.properties file. The SonarQube Scanner for MSBuild only has a SonarQube.Analysis.Xml file that contains project-related settings that you can tweak.
You can use couple of overwriting strategies for the SonarQube.Analysis.Xml file:
A project-specific property defined in the MSBuild *.*proj file (corresponding to a SonarQube module) can override:
A property defined in the command line (/d:propertyName=value) has which can override:
A property defined in the SonarQube.Analysis.xml configuration file
A property defined in the SonarQube User Interface at project level which can override everything
A property defined in the SonarQube User Interface at global level which can't override anything
To exclude specific folders or extensions from your Solution:
You need to add the excludes into each individual projects' .csproj file. Here's the syntax which you should use within the main root node, called <Project...> and into one of the targets, preferably <Target Name="BeforeBuild">. Hope the syntax below is self-explanetory enough, but in case it isn't, please leave a comment under this answer and I'll update it right away.
<Target Name="BeforeBuild">
<ItemGroup>
<SonarQubeSetting Include="sonar.exclusions">
<Value>**/Databases/**/*</Value>
</SonarQubeSetting>
</ItemGroup>
</Target>
Hope it helps!
Source

ANTLR4 with Unity3D

I am currently trying to use my ANTLR4 parser/lexer in an Unity project.
The steps I have taken are:
Generate the parser and lexer from the grammar in a separate project and copying the Lexer, Parser and the visitors/listeners into the Unity project
Since obviously the Antlr4 runtime is missing I added the Antlr4.Runtime.v3.5.dll since Unity uses .NET 3.5
I fixed an error in the parser since IReadOnlyList could not be resolved. I just changed it to an IList
After all these steps, Unity presents me with the following error:
-----CompilerOutput:-stdout--exitcode:
-1073741819--compilationhadfailure: True
--outfile: Temp/Assembly-CSharp.dll
compute_class_bitmap: Invalid type 13 for field
Antlr4.Runtime.Recognizer`2[Symbol,ATNInterpreter]:_interp
can anyone help me with this matter? It is very important, that I get this parser up and running because otherwise I'd need to write the parser on my own which would be extremely annoying...
Make sure you are using the C# runtime for ANTLR, which can be found on github here:
https://github.com/tunnelvisionlabs/antlr4cs
The Readme file at that link has a lot of information about installing the runtime. I will refer to this Readme a couple of times as I give you a step-by-step list.
I am assuming you are using Windows. You cannot currently compile an ANTLR4 parser for C# using Mac.
1) Install Visual Studio Community (if you haven't already) and at install time make sure to install:
Core Visual Studio editor (default)
.NET desktop development (under Workloads)
NuGet Package manager (under Individual Components -> Code tools)
2) Create a new Project.
Select Windows Classic Desktop
Select a .NET Framework app
Once Project is created, right-click project name and select Properties and take note of your version of .NET Framework (mine is 4.6.1)
3) Use NuGet to install ANTLR4. Install a recent stable version of ANTLR4 (mine is 4.6.4)
Right click the top-level solution node in the Solution Explorer window and select Manage NuGet Packages for Solution...
Select Browse and in the search box type Antlr4
In the search results there will be several options. Click on the one whose name is exactly Antlr4
In the right-hand pane, choose the version to be the latest stable version (4.6.4) and then click Install
4) Add your grammar file (.g4) to your Project by clicking Project -> Add Existing Item... and navigating to your .g4 file.
5) From the github Readme at the link posted above:
If you have the ANTLR Language Support extension installed, this step is automatically performed. Otherwise, you will need to right click the grammar file in Solution Explorer and select Properties. In the properties window, configure the following items.
Build Action: Antlr4 (If Antlr4 doesn't appear as an option in the dropdown list, restart Visual Studio then try again)
Custom Tool: MSBuild:Compile
Custom Tool Namespace: The complete name of the namespace you want the generated classes to be located within
6) Restart Visual Studio and reload Project
7) Select Build -> Build Solution
8) Look in ~/repos/projectName/projectName/obj/Debug to find the generated files
copy *.tokens and *.cs to a new folder
also copy repos/projectName/projectName/Properties/AssemblyInfo.cs to that new folder
Open AssemblyInfo.cs and add the line "using System;" at the top and the line “[assembly: CLSCompliant(false)]” at the bottom
9) Move to Unity and import the folder you created in the previous step.
10) Open directory where you've installed Antlr4, and find subdirectory Antlr4.Runtime.4.6.4/lib (where you may need to replace 4.6.4 with your version of ANTLR4) and then choose the folder of the corresponding .NET version as used in step 2 (in my case the closest one is net4.5). Drag Antlr4.Runtime.dll from that subdirectory into your Unity project.
And you should be good to go for using ANTLR4 inside Unity.
Assuming that you've already created your parser in another project, and only want to use generated classes with Unity, it's as simple as putting Antlr4.Runtime.Standard.dll file into your Unity folder Assets/Plugins. This worked for us in Unity 2019.3.9f1.

Generate C# project using CMake

I'm trying to generate a C# project within an existing C++ CMake code base on Windows. After some research, I could find only two projects that built their own CSharp compilers for CMake:
gdcm and kde.
I tried both of them. Unfortunately, the first one failed to generate a C# project. Instead it created a VS C++ project with cs files in it, and because of C++ flags set for linker, the build always failed with errors. After experimenting with the sample project they provided, I wonder whether this could be due to a limitation of the "Visual Studio 8 2005" code generator?
The second project was primarily aimed at Mono, so I wasn't successful with it either.
Has anyone had a positive experience with building C# projects using one of those CMake modules or something else?
As of CMake 3.8.2, CSharp project generation is officially supported by CMake.
To build the default Visual Studio 2017 generated C#/WPF project using CMake, create a CMakeList.txt file as follows.
Project Declaration
project(Example VERSION 0.1.0 LANGUAGES CSharp)
Include CMake CSharpUtilities if you are planning on using WPF or other designer properties.
include(CSharpUtilities)
Add all cs, xaml, settings, properties
add_executable(Example
App.config
App.xaml
App.xaml.cs
MainWindow.xaml
MainWindow.xaml.cs
Properties/AssemblyInfo.cs
Properties/Resources.Designer.cs
Properties/Resources.resx
Properties/Settings.Designer.cs
Properties/Settings.settings)
Link designer files, xaml files, and other properties files with their corresponding cs files
csharp_set_designer_cs_properties(
Properties/AssemblyInfo.cs
Properties/Resources.Designer.cs
Properties/Resources.resx
Properties/Settings.Designer.cs
Properties/Settings.settings)
csharp_set_xaml_cs_properties(
App.xaml
App.xaml.cs
MainWindow.xaml
MainWindow.xaml.cs)
Set app App.xaml properties file as program entry point (if project is a WPF project)
set_property(SOURCE App.xaml PROPERTY VS_XAML_TYPE "ApplicationDefinition")
Set other csproj file flags
set_property(TARGET Example PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.6.1")
set_property(TARGET Example PROPERTY WIN32_EXECUTABLE TRUE)
// ...
Add libraries
set_property(TARGET Example PROPERTY VS_DOTNET_REFERENCES
"Microsoft.CSharp"
"PresentationCore"
"PresentationFramework"
"System"
"System.Core"
"System.Data"
"System.Data.DataSetExtensions"
"System.Net.Http"
"System.Xaml"
"System.Xml"
"System.Xml.Linq"
"WindowsBase")
For a working WPF example, see https://github.com/bemehiser/cmake_csharp_example
For a WinForms example, see this answer.
CMake 2.8.9 and up add a TYPE parameter to include_external_msproject like so:
include_external_msproject(
MyProject MyProject.csproj
TYPE FAE04EC0-301F-11D3-BF4B-00C04F79EFBC)
This lets you specify that the project is C# (the magic GUID above), otherwise things struggle (see docs).
You will probably still want to use the configure_file template approach mentioned elsewhere with your .csproj file to get the right paths into it, unless you're building straight into your source tree.
The good news is that you can wildcard your C# files in the .csproj.template file like so:
<ItemGroup>
<Compile Include="${DOS_STYLE_SOURCE_DIR}\**\*.cs" />
</ItemGroup>
And you'll need something like this in your CMakeLists.txt to convert CMake's unix-style forwardslash path separators into Windows-style backslashes, otherwise it will compile the files but they won't show up as links in the project in Visual Studio:
FILE(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" DOS_STYLE_SOURCE_DIR)
Then it's just:
CONFIGURE_FILE(MyProject.csproj.template MyProject.csproj)
In your CMakeLists.txt to configure the template file into a real project file with the right wildcard path.
HTH.
Just in case anyone is still looking for information about this, there is really no reason to generate C# projects with CMake, they are cross platform by design. On Linux, C# projects are generally managed with MonoDevelop which can read .csproj files from visual studio just fine. This should enable cross-platform development of C# projects.
The only potential issue would be if you had native c++ projects mixed with c# projects (like a backend written in c++ with a GUI in c#), in this case just have cmake copy over your .csproj files as though they were data and you should be good to go.
CMake is intended to set up your build environment to be cross platform. This comes in really handy with c++ where code is built in a very different way on linux than on windows (or other OSs if youre into that), but is kind of unnecessary for c# which can execute cross-platform, and, thanks to design decisions by the mono team, can build cross platform. CMake does provide some great tools to automate things, but much of this functionality can be recovered with a properly configured .csproj file.
Anyway I know this question is over a year old but it's one of the top search results I stumbled on when I was looking up how to do this. I've since made this realization.
To piggy-back on the answer provided by #the_storyteller, CMake v3.8 and greater indeed supports C# as a first-class language. Because a WPF example was already provided, here is a complete CMake example for a simple Windows Forms application. I've provided the optional commands for linking in other libraries built locally in the source tree, and linking 3rd party library dependencies.
Note that Windows Forms applications require the use of the csharp_set_windows_forms_properties CMake command, whereas WPF projects use csharp_set_designer_cs_properties and csharp_set_xaml_cs_properties.
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(MyWinFormApp LANGUAGES CSharp)
# Include CMake utilities for CSharp, for WinForm and WPF application support.
include(CSharpUtilities)
# Define the executable, including any .cs files.
# The .resx and other Properties files are optional here, but including them makes them visible in the VS solution for easy editing.
add_executable(MyWinFormApp
App.config
Form1.cs
Form1.Designer.cs
Form1.resx
Program.cs
Properties/AssemblyInfo.cs
Properties/Resources.Designer.cs
Properties/Resources.resx
Properties/Settings.Designer.cs
Properties/Settings.settings
)
# Set the .NET Framework version for the executable.
set_property(TARGET MyWinFormApp PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.6.1")
# Set the executable to be 32-bit.
set_property(TARGET MyWinFormApp PROPERTY WIN32_EXECUTABLE TRUE)
# Set the C# language version (defaults to 3.0).
set(CMAKE_CSharp_FLAGS "/langversion:latest")
# Set the source file properties for Windows Forms use.
csharp_set_windows_forms_properties(
Form1.cs
Form1.Designer.cs
Form1.resx
Program.cs
Properties/AssemblyInfo.cs
Properties/Resources.Designer.cs
Properties/Resources.resx
Properties/Settings.Designer.cs
Properties/Settings.settings
)
# If necessary, link in other library dependencies that were built locally in this source tree.
target_link_libraries(MyWinFormApp MyLocalLib)
# If necessary, link in other library/DLL references, such as 3rd party libraries.
set_property(TARGET MyWinFormApp PROPERTY
VS_DOTNET_REFERENCE_MyThirdPartyLib /path/to/libs/MyThirdPartyLib.dll)
# Add in the .NET reference libraries.
set_property(TARGET MyWinFormApp PROPERTY VS_DOTNET_REFERENCES
"Microsoft.CSharp"
"System"
"System.Core"
"System.Data"
"System.Drawing"
"System.Windows.Forms"
)
I was finally able to generate a valid solution using the second c# module - kde.
Although, cmake created a number of .vcproj files while I expected to get .csproj, but I guess that is the only form "Visual Studio 8 2005" generator can offer.
Nevertheless I was able to successfully build this solution and produce executables and dll libraries.
You can create a project with Visual Studio, than take it apart and make CMake write it using configure_file command (you'll have to generate a bit of XML with the list of sources) and add it to the solution with include_external_msproject (for other generators you'll need to create the custom target to run msbuild on it manually; cmake does not seem to support doing that yet). The project is rather simple, so it should be possible to do that.
I wanted to point out that it is now possible to generate C# Projects using the SDK-Style project XML format using CMake 3.23.
Although it is still a release candidate version, it works pretty well with CMake 3.23 rc2. To create C# SDK-style projects use DOTNET_SDK target property.
For example one could use C# SDK-style project with this line
set_target_properties(myTarget PROPERTIES DOTNET_SDK "Microsoft.NET.Sdk")
Also remember that you don't need a project() command when creating C# Projects with CMake. The project() command always creates a new solution which could be useful but not necessary. One can use enable_language(CSharp) to declare a Target being a C# Project.
To test C# SDK-style projects I used Visual Studio 2019 with Visual Studio 16 Generator on Windows.

Categories