dotnet / dotnet/project-system
Multi-repo solution with targets fails to build from Visual Studio; works from .NET CLI
- Dominant language
- C#
- Stars
- 1k
- Forks
- 415
- PR merge metrics
- No merged PRs in 30d
Description
### Versions
**Visual Studio**: 17.11.0 Preview 3
**.NET SDK**: 8.0.303
### Summary
We have a multi-repo solution at [LiveSplit](https://github.com/LiveSplit/LiveSplit). Projects are pulled in via git submodules and included in the main repo's solution file. These projects act as plugins (referred to as "components") for visualization of information on the app. Including them in the solution like this makes them easy to debug. When the app is built, these components must also be built, as there would be nothing to visualize otherwise. They must be placed in a `Components` sub-directory of the app's output directory.
Currently, we hard-code the output paths of the required main repo projects via `$(OutDir)` in a `Directory.Build.props` file (`.\bin\$(Configuration.ToLowerInvariant())`) and do the same for the projects in the `.\components` directory (`$(OutDir)\Components`). We would then build the entire solution and end up with the expected production output of the app and the components being placed in those specified destinations. The output looks something like this:
```
$(OutDir)
|-- LiveSplit.exe
|-- LiveSplit.csproj Dependencies
|-- LiveSplit.Register.exe
|-- LiveSplit.Register.csproj Dependencies
\-- Components
|-- **.dll
\-- **.csproj Dependencies
```
However, I've been wanting to remove those hard-coded paths, as they don't sit right with me and I would like to allow users to set `-o` from the command line to provide their own output path. Additionally, I want the production output to be produced only when the main executable project is built (`.\src\LiveSplit`). To achieve this, I've removed all of the `$(OutDir)` properties and added some `.targets` instead, which collect the `.\components` projects, restores them and builds them:
```xml
<_Components Include="$(ComponentsPath)\*\src\*\*.csproj" />
```
This is working fantastic from the command line, since it only builds the specified project. Visual Studio instead still builds the entire solution, even if only the project was marked for building. This results in conflicts and dependency files which seemingly cannot be found. I believe this is because Visual Studio builds the components into their own output directories first, but looks for them in `$(OutputPath)\Components` later in the target. This is further supported by the fact that the build succeeds when passing `"OutputPath=$(OutputPath)"` as the properties, since the required assemblies do exist there (they are dependencies of the main executable).
```
7>CSC : error CS0006: Metadata file '~\LiveSplit\artifacts\bin\LiveSplit\debug\Components\CustomFontDialog.dll' could not be found
7>CSC : error CS0006: Metadata file '~\LiveSplit\artifacts\bin\LiveSplit\debug\Components\LiveSplit.Core.dll' could not be found
7>CSC : error CS0006: Metadata file '~\LiveSplit\artifacts\bin\LiveSplit\debug\Components\UpdateManager.dll' could not be found
7>CSC : error CS0006: Metadata file '~\LiveSplit\artifacts\bin\LiveSplit\debug\Components\WinFormsColor.dll' could not be found
```
Unfortunately this has led to some frustration for me, since it means any user who wants to build the app from Visual Studio wouldn't be able to do so. This is obviously unacceptable, because Visual Studio provides a lot of valuable functionality for profiling and debugging.
If there's another approach we can take here, please let me know. I've provided some logs and relevant links below. Please ask any clarifying questions.
Logs: [LiveSplit-build-logs.zip](https://github.com/user-attachments/files/16174089/LiveSplit-build-logs.zip)
PR & discussion: https://github.com/LiveSplit/LiveSplit/pull/2484
Branch: https://github.com/just-ero/LiveSplit/tree/use-targets
### Steps to Reproduce
1. Clone https://github.com/just-ero/LiveSplit and enter the repo directory.
2. Switch to branch `use-targets`.
3. Run `dotnet build ./src/LiveSplit`, observe the build succeeding.
4. Open `LiveSplit.sln` in Visual studio.
5. Build or rebuild the `LiveSplit` project, observe the build failing.
I have to mention here that I have tried to create a sample project at https://github.com/just-ero/ProjectSystemIssueSample, but this one does succeed. I'm not sure what makes the one from LiveSplit fail. Perhaps it's the large amount of projects and references?
### Expected Behavior
Visual Studio only builds the selected project.
### Actual Behavior
Visual Studio appears to build the entire solution in a very jumbled order. The selected project isn't even the first one that builds. Comparing the binlogs of the .NET CLI build and the Visual Studio build makes this very obvious.
### User Impact
It is impossible for us to move forward without hard-coding the output paths of every single one of our projects.
Contributor guide
Assessment
This issue has not been assessed yet.