I did this based on C ++ code, which has already been split into many libraries (which was a good start).
I had to train (or guess) which library most depended on, which no longer depended on the code base. Then I processed each library in turn.
In turn, I looked at each module (* .cpp files) and made sure that its own header was included first and commented out the rest, then I commented out all #includes in this header file and then recompiled simply this module allows the compiler to tell me what do you need. I would not comment on the first heading, which seemed to be needed, and considered it, if necessary, recursive. It was interesting to see how many headings turned out to be unnecessary.
If only a name is required (because you have a pointer or link), use class name; or struct name; which is called a forward declaration and avoids the #include header file.
The compiler is very useful in telling you what dependencies are when you comment on #includes (you need to recompile with all the compilers you need to support portability).
Sometimes I had to move modules between libraries so that no pairs or groups of libraries depended on each other.
source share