I read your question as saying that dependencies are in .Net or C++ ; and your final output builds are .Net. You are aware that NuGet is 'the' standard .Net tool for all things dependency related.
Now. It depends on what exactly you want with your thought of easily 'switching' between source and binary?
You don't distinguish on the build server from on the developer machine. You surely want both to build from binaries so as to be as fast as possible, but I can't make sense of wanting source code on the build server: source code is for humans, not machines.
Option 1. You mean 'get the build speed of binary but the debugging experience of source'
You can achieve this with NuGet. You are using Azure so you can add tasks to your library builds to publish straight to a private Azure NuGet feed. Your starting point for that is
Which talks you through
- Set up the feed
- Publish to the feed
- Consume the feed
Nuget can give you the full “source code when debugging” experience because you can publish to Nuget including source symbols. This is covered on the same page at the anchor,
On the developer machine you would then replace dependencies on projects in the solution with dependencies on the NuGet package. You then get the normal nuget experience of:
- the nuget feed compiled .dlls will be cached on your machine
- NuGet will tell you when updates are available
- You choose when to get latest ; OR you can enforce always update on build ; OR you can enforce always update on checkout.
Force update-on-checkout will give you the source-code-style experience of always looking at the (nearly) latest code whilst keeping the fastest possibly build from binary whilst you are developing. You can force update-on-git-checkout by adding a git post-checkout hook to run nuget update, so that every git get latest will simultaneously get-latest from nuget. https://ddg.gg/example%20git%20oncheckout%20hook%20windows.
( It may help to enforce a standard checkout path for libraries on developer machines, because .Net symbol files will include the path used for the last build. However this takes some effort and windows-know-how to reproduce paths on dev machines used by the build server. )
Option 2. You mean 'be able to easily switch between building from source and building from binary'
There is no out-of-the-box solution for this because it is an unusual requirement. Instead, ask again what exactly are you trying to achieve?
If you merely want to be confident that you are building from the very latest sources, then Option 1 already resolves this -- give or take a few seconds -- for both build server and dev machines, because your Azure nuget feeds will always contain the very latest builds. In fact, if you have automated tests, Option 1 is better than build from source, because it used the 'most recent source that builds and passes tests' rather than 'most recent thing someone accidentally checked in'.
If you want 'full source in my project but fast builds', this can happen automatically: msbuild, like cmake, will not rebuild a project if it's already up-to-date. However, visual studio developers habitually remember the keystroke for rebuild (= clean + build ) instead of build. If you have a ton of dependencies, learning to change this one keystroke can save you all the waiting time. The catch is, if other developers are making lots of changes there is no avoiding the get-latest-source-and-rebuild time.
On a buildserver, you always do clean rebuilds. (Azure does allow you to break this rule and use non-clean work environments. But Reproducibility frowns on this approach. People have learned the hard way that not having reproducible builds can cause so much pain you'll be mentally scarred for life)
You could have different project files for building dev machine vs build server. Maintaining that will be an error-prone drag, so you should script the auto-generation of the build server csproj files from the standard ones. The build server can then get from nuget, whilst the dev machine references source and also pushes updates of successfully built libraries to the nuget feed.
Comment : How close can we get to best of both worlds?
There is no avoiding the fact that having large projects with many dependencies introduces a cost that you don't have with small projects. You pay the price either in having a large projects which are slow to open and slow to build; or in breaking up the builds (via nuget or some other means) and losing the instant simple access to all the source code.
NuGet feeds with symbols offers the most-mainstream and probably most-simple solution. Probably because that nuget capability has been designed to solve what may be your problem: give me the fastest possible builds but also access to source code when debugging.