1

I have a Visual Studio solution with 12 C++ projects. Half of the projects build static libraries, and the other half build native desktop executables.

When I rebuild the entire solution from Visual Studio 2019's menu (Build > Rebuild Solution), it appears to work--I get functional, debuggable executables. However, some of the PDBs for some of the static libraries are missing. When I dig into the build log, I see a lot of linker warnings like this:

wingui.lib(text.obj) : warning LNK4099: PDB 'wingui.pdb' was not found with 'wingui.lib(text.obj)' or at 'D:\code\aid\apps\vcell\x64\Debug\wingui.pdb'; linking object as if no debug info

Indeed, wingui.pdb is usually missing, but occasionally it's generated right where it should be. And if I turn off the parallelism in the build, it's always there. Race condition?

Updated 2021-07-24

Using procmon from the Sysinternals toolset, I logged all the operations to wingui.pdb during a clean rebuild. Here's a summary:

  1. MSBuild looks for wingui.pdb and learns it doesn't exist.
  2. CL creates it, writes it, reads some of it back, writes some more, and then closes it.
  3. MSBuild opens wingui.pdb, sets FILE_DISPOSITION_DELETE, and then closes the file. Huh?
  4. mspdbsrv tries to open wingui.pdb several times. Each attempt fails, of course, because MSBuild deleted it.

Can anyone explain step 3? Why would MSBuild mark a freshly made PDB file for deletion?

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175

1 Answers1

1

I think this boils down to the Difference between Rebuild and Clean + Build, which is also mentioned here.

Edited 2021-07-25

Rebuild works project-by-project. It first does the "clean" step for the project and then a build. Cleaning a project means deleting the build artifacts from the project's intermediate directory $(IntDir). While each project has its own intermediate directory, final targets (like the PDB files) are written to the same output directory--the output directory of the main project.

In my solutions, each static library X is referenced by at least two other projects: X_test and the application A. Because of these dependencies, project X will be scheduled before both projects X_test and A. First, project X is cleaned by purging artifacts from previous builds, and then it produces fresh object files in its intermediate directory. Project X's build finishes by producing its outputs--the LIB and PDB files--in the common output directory.

Let's say project X_test is scheduled next. It begins by purging its intermediate directory, rebuilding its objects, and then linking against X's LIB and PDB files from the common output directory. So far, so good.

Finally project A begins by purging its build artifacts. This does not mean it deletes every file in its intermediate directory. Instead, there are a set of patterns, including a project's specific output (e.g., A.exe) and some wildcards for others, including *.pdb. These wild card patterns are applied to the intermediate directory (at least).

And here's the kicker: my solutions are set up so that A's intermediate directory is also the common output directory. So as project A begins, it deletes A.exe and *.pdb (among others). That leaves the static library X.lib where it's expected, but its companion X.pdb is gone.

And if the Rebuild is done in parallel, the A and X_test projects can race, so A's purge might cause linker warnings for X_test as well.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175