# Support for \`dotnet\` CLI and ninja generator for C#

**URL:** https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304
**Category:** Development
**Tags:** lang:csharp
**Created:** [October 18, 2021, 8:37pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304 "2021-10-18T20:37:25Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 18, 2021, 8:37pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/1 "2021-10-18T20:37:25Z")

</div>

CMake supports C# using VS generator with the project files in line with .Net Framework projects. .Net also has [.Net Project SDKs](https://docs.microsoft.com/en-us/dotnet/core/project-sdk/overview) which enables building and running the projects on multiple platforms using [.Net CLI](https://docs.microsoft.com/en-us/dotnet/core/tools/).

Currently, CMake doesn’t support generating SDK-style projects for C#. CMake MR 6634 is focused on converting project style to use .Net Project SDK, but is limited to VS generator.

To support C# cross-platform, I propose the following:

- Introduce a new language `dotnet`.
- CMake will validate the toolset for `dotnet` using .Net CLI which enables using with Ninja generator in addition to the current VS generator.
- Since .Net CLI is a command line, we can also write build.ninja where the first step would be generating SDK-style projects and second step would be `dotnet build`.

This will be backward-compatible as we wouldn’t be impacting the existing project generation using `CSharp` language.

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 18, 2021, 8:37pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/2 "2021-10-18T20:37:59Z")

</div>

Since I am a new user, I can only add two links in the original post. Here is the link to WIP MR 6634: [https://gitlab.kitware.com/cmake/cmake/-/merge\_requests/6634](https://gitlab.kitware.com/cmake/cmake/-/merge_requests/6634)

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [October 18, 2021, 10:40pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/3 "2021-10-18T22:40:25Z")

</div>

> [@bhardwajs](#):
>
> This will be backward-compatible as we wouldn’t be impacting the existing project generation using `CSharp` language.

While it would be backwards compatible, we would still need to define the behavior with both `dotnet` and `CSharp` languages being enabled. Which would get “precedence” for `.cs` files? Note that embedding other projects is a thing, so a project might have the other language enabled outside of its control at a higher level.

(Note: I don’t do C# development, so I have no predisposition as to how to answer this.)

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 19, 2021, 6:07am UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/4 "2021-10-19T06:07:16Z")

</div>

@ben.boeckel: That’s a good point. What I want to achieve is a feature flag that allows CMake user to choose between using C# with the current approach (approach 1) and with dotnet tools (approach 2).

Approach 1 will be available only on VS generators. I am positive that Approach 2 could be extended to Ninja generator and to Linux. Unfortunately, there are still some use cases where C# developers need Approach 1 and therefore, it can’t be deprecated.

What’s the best way to do this in CMake? And, would the feature flag selection in an embedded project be controlled by that project or the outer project embedding it?

Once we have a feature flag, default should be `dotnet` and we can warn CMake user when they upgrade CMake version.

Thoughts?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [October 19, 2021, 2:04pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/5 "2021-10-19T14:04:26Z")

</div>

I would say it would have to be a per-target property that defaults to `CSharp`. Eventually, if `dotnet` covers all of the `CSharp` use cases, there can be a policy to switch over.

> [@bhardwajs](#):
>
> Once we have a feature flag, default should be `dotnet` and we can warn CMake user when they upgrade CMake version.

Alas, the feature flag cannot be the new feature by default because otherwise projects that work today would have different behavior with a new CMake. Such a default flip would require a policy to handle.

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 19, 2021, 8:54pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/6 "2021-10-19T20:54:04Z")

</div>

My thinking was to have the process of generating the project files be a per-target process. In the draft MR, I have the variable `IsDotNetSdkTarget` a member variable of `cmGeneratorTarget`. What I am not sure is how do we get there?

My thinking of introducing the language was to enable the switch at global level. That doesn’t seem a good idea given your comments. How about we do the following?

- Introduce a flag `CMAKE_CSHARP_DOTNET` which when set will prefer using .Net CLI for identifying and testing C# toolchain.
- When `CMAKE_CSHARP_DOTNET` is `TRUE`, modify `CMakeDetermineCSharpCompiler.cmake` to see if `dotnet build`, `dotnet run`, and `dotnet test` succeed.
- If `dotnet build` succeeds, Ninja generator can be used.
- If `dotnet build` doesn’t succeed, Ninja generator can’t be used. CMake will see if `csc` is available and if so, use that for VS generator.
- Introduce a new per-target property `DOTNET_SDK_STYLE_PROJECT` which will generate `SDK-style` project for C# files.

Open question:

- How do we set `DOTNET_SDK_STYLE_PROJECT` to true for all projects?
- Do we need the policy introduced in MR 6634 at all?

Thoughts/comments?

From implementation perspective, I can modify MR 6634 to use a per-target property and the rest of the code there shouldn’t need to be changed. Then, the next change would be introducing `CMAKE_CSHARP_DOTNET` (or other names that people want to use 🙂 ).

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [October 19, 2021, 9:10pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/7 "2021-10-19T21:10:36Z")

</div>

Unfortunately, this is probably out of my depths on C# support in CMake.

A per-target flag is fine, though CMake still only supports a single toolchain per language which makes it still a project-wide setting.

@brad.king Thoughts?

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [October 20, 2021, 1:59pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/8 "2021-10-20T13:59:46Z")

</div>

> choose between using C# with the current approach (approach 1) and with dotnet tools (approach 2).

Is there any reason to prefer the old approach, or can the new approach cover all use cases equally well? If CMake didn’t currently have any C# support and you were implementing new support from scratch, would there be any reason to have the proposed switch?

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 20, 2021, 2:52pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/9 "2021-10-20T14:52:36Z")

</div>

The new approach requires defining a .Net project SDK and not all types of projects have that defined. So, there might be a gap.

Having said that, if C# support was being implemented from scratch, I think it would be simpler and cleaner to only support SDK-style projects.

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [October 21, 2021, 2:06pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/10 "2021-10-21T14:06:18Z")

</div>

Thanks.

> Introduce a new language `dotnet`.

IIUC, dotnet is a platform and not a language. C# just happens to be a common choice to program for that platform. I don’t think we should have anything like `enable_language(dotnet)`.

> Introduce a flag CMAKE\_CSHARP\_DOTNET which when set will prefer using .Net CLI

Every variable that defines something fundamental about the target platform and tooling needs to be propagated through `try_compile` and several other places that run separate configure/generate steps. For the Visual Studio generators, we have generator-wide variables like [CMAKE\_GENERATOR\_TOOLSET](https://cmake.org/cmake/help/v3.22/variable/CMAKE_GENERATOR_TOOLSET.html) and [CMAKE\_GENERATOR\_PLATFORM](https://cmake.org/cmake/help/v3.22/variable/CMAKE_GENERATOR_PLATFORM.html) for that. They are already passed to other generator contexts automatically. The `,`-separated `key=value` field syntax used in `CMAKE_GENERATOR_TOOLSET` could be used to encode tooling preferences for this use case too.

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 21, 2021, 7:30pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/11 "2021-10-21T19:30:07Z")

</div>

> [@](#):
>
> IIUC, dotnet is a platform and not a language. C# just happens to be a common choice to program for that platform. I don’t think we should have anything like `enable_language(dotnet)`.

Yes, .Net is a platform that CMake can use with VS and Ninja generators.

> [@](#):
>
> Every variable that defines something fundamental about the target platform and tooling needs to be propagated through `try_compile` and several other places that run separate configure/generate steps. For the Visual Studio generators, we have generator-wide variables like [CMAKE\_GENERATOR\_TOOLSET](https://cmake.org/cmake/help/v3.22/variable/CMAKE_GENERATOR_TOOLSET.html) and [CMAKE\_GENERATOR\_PLATFORM](https://cmake.org/cmake/help/v3.22/variable/CMAKE_GENERATOR_PLATFORM.html) for that. They are already passed to other generator contexts automatically. The `,`-separated `key=value` field syntax used in `CMAKE_GENERATOR_TOOLSET` could be used to encode tooling preferences for this use case too.

Using toolset selection sounds like a good idea. The modified proposal then would be to introduce a new key for Toolset with name `dotnet` and any non-empty value as signal to try using `dotnet` tools. Does that sound good?

How would the toolset work for Ninja generator?

Thanks for all the comments Brad and Ben. Appreciated!

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [October 26, 2021, 2:32pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/12 "2021-10-26T14:32:52Z")

</div>

> The modified proposal then would be to introduce a new key for Toolset with name dotnet and any non-empty value as signal to try using dotnet tools.

Let’s not accept more values than necessary. Maybe just `tools=dotnet`? And reject other values.

> How would the toolset work for Ninja generator?

It wouldn’t work for that. The Ninja generator expects a command-line environment and uses environment variables and/or explicit settings like `-DCMAKE_CSharp_COMPILER=...`.

How does one compile `.cs` source files by hand using `dotnet` at the command prompt?

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 26, 2021, 9:03pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/13 "2021-10-26T21:03:56Z")

</div>

> [@](#):
>
> Let’s not accept more values than necessary. Maybe just `tools=dotnet`? And reject other values.

Sounds good to me.

> [@](#):
>
> It wouldn’t work for that. The Ninja generator expects a command-line environment and uses environment variables and/or explicit settings like `-DCMAKE_CSharp_COMPILER=...`.
> 
> How does one compile `.cs` source files by hand using `dotnet` at the command prompt?

Currently, there isn’t a way to compile a `.cs` source file using `dotnet` at the command prompt. The documentations on [`dotnet build`](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build) and `dotnet msbuild` only refer to project files (.csproj etc.).

For testing out the toolchain, can we try something like the examples in [Get Started](https://docs.microsoft.com/en-us/dotnet/core/get-started)?

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [October 27, 2021, 12:52pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/14 "2021-10-27T12:52:26Z")

</div>

If `.csproj` files are the only way to compile using the dotnet tools, then we will only be able to support them through the Visual Studio generators.

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 28, 2021, 5:46pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/15 "2021-10-28T17:46:13Z")

</div>

Why can’t `CMake` use a barebones `HelloWorld.csproj` to test compilation of a file? If `CMake` can use the bare-bones `HelloWorld.csproj` and `Program.cs` to test that `dotnet` tools are on a build environment, the rest of the workflow - generating `csproj`, using `dotnet build`, etc. would be generator-agnostic and work on multiple platforms.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [October 28, 2021, 6:58pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/16 "2021-10-28T18:58:11Z")

</div>

The problem with that (for me) is listing dependencies properly. How is `ninja` (or `make`) supposed to know that some external change could require a recompile of the C# project (basically, the depfiles for the `dotnet build` command)? For C or C++ this would be a header changing, but it is basically any file incidentally used by `dotnet build` _anywhere_ in its process execution tree. Incremental builds would cause this to be partial. So I can see this working _if_ `dotnet build` can output a list of files that matter to it (even in an incremental build of itself).

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [October 28, 2021, 7:56pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/17 "2021-10-28T19:56:34Z")

</div>

With the SDK-style projects, there is a component of watching directories to see if there are any changes in `.cs` files or any new `.cs` files. This is controlled by the property `EnableDefaultItems` which is `true` by default.

In my draft MR, I had this set to `false` and then list out the files that the user specifies in `CMakeLists.txt`. With that configuration, the depfiles for `dotnet build` would be the generated `.csproj` and the `.cs` files. And, the depfile for generating `.csproj` would be `CMakeLists.txt`.

@ben.boeckel: Does that feel like a solution to your problem?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [October 28, 2021, 11:04pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/18 "2021-10-28T23:04:57Z")

</div>

> [@bhardwajs](#):
>
> With that configuration, the depfiles for `dotnet build` would be the generated `.csproj` and the `.cs` files. And, the depfile for generating `.csproj` would be `CMakeLists.txt`.

There’s no possibility of user props, external files that are read during `dotnet build` (linking, imports, etc.), or other such things? If not, then it could be possible in principle I suppose. I’m not sure how much it works with things like the sub-targets of the makefiles generators (`/all` and friends) either.

---

<div class="post-metadata">

### Author: ![bhardwajs](https://discourse.cmake.org/user_avatar/discourse.cmake.org/bhardwajs/32/1886_2.png) [@bhardwajs](https://discourse.cmake.org/u/bhardwajs)
#### Post date: [November 2, 2021, 9:20pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/19 "2021-11-02T21:20:50Z")

</div>

> [@ben.boeckel](#):
>
> There’s no possibility of user props, external files that are read during `dotnet build` (linking, imports, etc.), or other such things? If not, then it could be possible in principle I suppose. I’m not sure how much it works with things like the sub-targets of the makefiles generators (`/all` and friends) either.

User props, external files etc. will be used as specified in the project file. Behavior doesn’t change between what CMake does now using command line `msbuild` and what CMake will do with SDK-style project using `dotnet build`.

To go forward, should I create a new issue in `CMake` repo and send merge request for `dotnet` `CMAKE_GENERATOR_TOOLSET`? I will work on finalizing my MR to generate SDK-style projects (6634).

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [November 3, 2021, 5:00pm UTC](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304/20 "2021-11-03T17:00:08Z")

</div>

I think we’ve concluded here that we’ll only be able to support the `dotnet` tooling in the Visual Studio generators for now, as is the case currently with `C#` in general anyway.

Moving forward on the `CMAKE_GENERATOR_TOOLSET` approach sounds good to me, though some other things may need to be worked out first:

- See [CMake Issue 22849](https://gitlab.kitware.com/cmake/cmake/-/issues/22849) on target framework selection for the VS generators.
- Your [CMake MR 6634](https://gitlab.kitware.com/cmake/cmake/-/merge_requests/6634) can probably be finished independently first.

[Next page](https://discourse.cmake.org/t/support-for-dotnet-cli-and-ninja-generator-for-c/4304.md?page=2)
