My current setup
I have a game (library) project already in development which already has a lot of features.
It contains a bunch of subprojects:
Utils
with utility code, likeIEnumerable
extensions, some data structures etc.Core
with the base library of the game. Contains essential game logic code.Meta
for code analysis and generation for the main project (Core
) and any mods.Shared
contains some stuff available for bothCore
andMeta
.TestContent
is a test mod.Tests
with tests for theCore
code and the code fromTestContent
. I'm usingnunit-3
for tests.Mine
as a playground for quickly running and debugging certain tests.
References between projects are achieved via ProjectReference
in the .csproj
files.
My Utils
, Core
, Shared
, TestContent
and Mine
subprojects do not reference any external projects, just each other (and no circular dependencies). All of these also have their AssemblyName
property in .csproj
files set to their name. For example, here is the Hopper.Core.csproj
file (others are similar):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>net4.8;netcoreapp3.1</TargetFrameworks>
<AssemblyName>Hopper.Core</AssemblyName>
<Name>Core</Name>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Utils\Hopper.Utils.csproj" />
<ProjectReference Include="..\Shared\Hopper.Shared.csproj" />
</ItemGroup>
</Project>
A problem with this is that I do not really care which version it exports for. I'm using only features from C# 7.X, so no default interface implementations, no nullable types and no a couple more things. C# 7.X is supported by basically any dotnet runtime (I guess?) there is, be it Core
, Framework
, Standart
, whatever. (I am still confused by these, even though I have read tons of documentation on this).
The base folder does not have a solution file. I personally do not understand the meaning behind solutions. I'd appreciate if a knowledgeable person explained it to me.
What I want to achieve
I'd like the particulars of the build system to be defined from an outer project, but with a default fallback. So, if I import this code as a git submodule
in another project, say a Godot project, I'd have my build system pick up on Godot's build system while all of the sub-projects kept working: I want to be able to generate code through Meta
, I want to be able to run my tests and I want to be able to even run some test code on just the included code, all while being a sub-project of the main project in Godot.
Also, I want to somehow clearly state to the outer project that Tests
are for tests and should not be in the build of the game, Meta
is a tool, and should not be in the game build as well. Maybe, these should even be built in separate folders, to avoid confusion.
I want the runtime version of the outer project (e.g. DotNet Framework 4.8
) to propagate to the sub-projects. I also want this version to be distinct from the version of the Meta
tool, which should be built with the runtime needed for Roslyn
to work. Ideally, this second "tool version" should be electable by the outer project, but should have a default value too.
I want my projects to build individual dll's, so we don't get all code compiled into a single dll.
I would like to avoid splitting it up into even more github projects. I want the Core projects to have both the code generation tool and the tests code.
Building into one directory?
I have only half-figured out how to force msbuild
to write output to a single folder. So, I have a build folder in the root of the project, where dll's of all subprojects end up. I have been able to achieve this via a Directory.Build.props
like this:
<Project>
<PropertyGroup>
<BaseOutputPath>..\build\bin</BaseOutputPath>
</PropertyGroup>
</Project>
There are a couple of problems with this solution:
- All of the subprojects, apparently, if they reference another subproject, even within the same project, seem to be recompiling these projects. E.g.
Tests
referenceCore
andUtils
,Core
referencesUtils
as well. WhenTests
are built, it buildsCore
, which buildsUtils
, but thenTests
buildsUtils
the second time. This is annoying. - All of the binaries end up mangled in that one folder, which is also annoying. I'd like them to be stored in separate folders, e.g. by the name of the project, in that build folder, but I could not find a way to get a reference to the project in
Directory.Build.props
. I must set this path in.props
, because if I set it in corresponding.csproj
files, where the name is known,Nuget
then messes it up by writing to the default path anyway (that is, a nested to the projectbin
).
I have not been able to figure out how to write the assembly info, I guess, generated in nested obj
folders, to somewhere else. Whichever setting/property I tried in msbuild
, it didn't end up working out.
Non-dotnet scripts
I have a couple of helper bat
scripts. One launches Meta
, the code generator, on the Core
subproject and on the TestContent
subproject, generating code for both, under their project folders. The other runs either all tests or a selection of tests. I would like to either convert these to tasks which I could run in an IDE or via dotnet whatever
commands, or make them decide what exactly to do depending on whether the project is included as a submodule or not.
For example, my test.bat
literally looks like this:
@echo off
cd .Tests
call dotnet build
cd ..
if %1==all (
nunit3-console build\bin\Debug\net4.8\Hopper.Tests.dll
) else (
nunit3-console build\bin\Debug\net4.8\Hopper.Tests.dll --test=Hopper.Tests.%1
)
So it literally builds the Tests
project and then goes ahead and runs the compiled dll by path. This is obviously no good if the dll ends up in a different folder.
Why not run tests through your IDE?
Oh, I was not able to run tests via VS Code. I have struggled for like 2 days, then ditched it. So, basically, if you write code for tests, VS Code automatically pick up on it. It displays Run Test
, Debug Test
and other buttons above your tests.
However, if you click on it, it always runs in the integrated terminal, which is really slow, annoying and doesn't color code correctly. I have searched for hours, but have not been able to find a solution to this. (I'm using ConEmu
as my console, which I like a lot more than the janky integrated terminal).
If you try debugging the test, the error Error processing 'configurationDone' request. Only 64-bit processes can be debugged.
pops up. I tried to find what exact command and in what way VS Code
actually tries debugging those tests with, but could not find any info whatsoever on this. This is why I have another project for debugging and quick tests: I am unable to debug tests. This is, too, extremely annoying.
How can you help me
I would like to get an advice from a more experienced developer on how to set up such projects in the most flexible, but at the same time least troublesome and annoying way possible. Partially breaking the existing setup is fine.
The most important thing is for this nested project to be totally independent of the parent-project, but at the same time to be customizable from the outside. Also, keep in mind that this project must be copied as a git submodule into the outer project.
I would appreciate examples of real projects, maybe blog posts that explain this in depth, any tricks or concepts I will need to learn to set this up, anything. I'm totally new to all this and have no idea where to start. I am too overwhelmed by the problems that have come up that I have no idea how to solve.
I would appreciate useful information on Godot build system and examples of any complex Godot projects that make use of dotnet, or, likewise, any articles on how to set it up properly or how to customize it.
Ideally, I would like to avoid reading 1000 pages of random documentation to hopefully fish some useful ideas. I would like to get this sorted in, ideally, a few days.
Actual code of the current setup
The Godot code is currently broken: the nested repo for first is not used in any way (it's just included as a submodule) and, for second, there is essentially no game yet. See the state of the godot outer project as of the day of writing.
Here is the state of the library project as of the day of writing. It is the repo described above, with the code generator and the Core
code.
Update
I have read a bunch of articles and github threads on msbuild and I hate it. The build system is so overly complicated. I have read e.g. this, this and I just don't get it.