We had a similar problem and were approaching it very much like @Toby Allen mentions in his answer, via client specs. However, in time this becomes very painful (setting up a new team member becomes more and more difficult as client specs become more and more convoluted; also automation is much more complicated because things are... "in flux" :-) ).
Eventually, we evolved our strategy to use a directory structure and branching instead. The directory structure is as follows:
//depot
/products
/(product_name)
/doc
/lib
/(3rd_party_libname)
(DLLs)
/(another_3rd_party_libname)
(DLLs)
/src
/Project1
(files, csproj, vbproj, etc)
/Project2
(files, csproj, vbproj, etc)
/Project3
(files, csproj, vbproj, etc)
Solution1.sln (includes src/Project1)
Solution2.sln (includes src/Project1, src/Project2)
Solution3.sln (includes src/Project1, src/Project3)
/(another_product_name)
/doc
/lib
/src
(solutions)
/shared
/(shared_lib_name)
/doc
/lib
/src
(solutions)
/(another_shared_lib_name)
/doc
/lib
/src
(solutions)
Note that the same structure is repeated throughout the structure (doc/lib/src/solutions). Lib contains "external" libraries - 3rd party libraries that are included in project references. Src contains a flat list of all projects that are part of a particular product. Solutions are then used to "combine" projects in any number of ways. I think of src directory as a container with "what is available", solutions are then picking from this container and combining projects (as needed).
Libraries that are shared among multiple products go into shared directory. Once in shared directory, they are treated as independent from products - they have their own release cycle and are never joined to products as source. Shared libraries are pulled into products by branching the shared library release assembly/assemblies into product's lib directory -> from product's perspective there is no difference between a 3rd party library and a shared library. This allows us to control what product is using what version of a shared library (when a product wants new features, it has to explicitely branch in newer release of a shared library, just like it would include a new release of a 3rd party library, with all pros and cons that go with it).
In summary, our structure has concept of two "types" of shared libraries:
- projects local to a product, used by multiple solutions (included in a flat list of projects in src directory, multiple solutions can reference them)
- projects used by multiple products (added to shared directory, treated as 3rd party libraries with releases independant from products)