Click here to Skip to main content
15,887,214 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I am building 9 32bit Dlls under a Visual Studio solution.
For the sake of argument each one of them is dependent on all the others. In other words each one imports symbols from the other 8 and exports symbols imported by each of the other 8.

At the moment I do this by having 2 build configurations. The first tries to compile and link all the dlls.
All the compilations succeed but of course all the linking fails. The build however outputs .exp files and .lib files for each DLL

The second configuration is then run which compiles everything again and then ignores most of the files it's just made and links the libs and export files from the first configuration to build the actually Dlls.

This works but it is a horrible hack and takes twice as long as necessary.

What I need is a way to either get Visual Studio to sort this out for me or to stop the first configuration from trying to link and the second configuration form wasting its time compiling everything again.

I asked a similar question in the VS forum about a year ago and was told that this could be fixed by using the project dependency settings. It can't, as you can't create circular dependencies.

What I'm doing is the same as described here[^] but with Visual Studio.
Posted
Updated 23-Jul-13 3:47am
v2
Comments
nv3 23-Jul-13 9:28am    
Sorry for not having a solution, Matthew, but I have the impression you are fight windmills here. So far I have always tried to keep the DLL dependencies in strict tree order.
Matthew Faithfull 23-Jul-13 9:41am    
Normally I would too but there is a good reason for structuring things this way and it does 'work' in the sense that it eventually builds correct code.
I'm looking at whether I can continue to use Visual Studio for the build or I'll have to resort to custom make files which will be very costly in terms of time and effort.

I want to post this as article code here on CP but I don't want bad feedback because the 2 stage build process confuses people and produces thousands of spurious errors in the first stage which are not really a problem. That and the time it takes to build everything twice which is slowing down development.
pasztorpisti 23-Jul-13 9:52am    
Its a bit more relaxed: a directed graph, but the most important part is the "directed".
Matthew Faithfull 23-Jul-13 9:57am    
n this case it can be a DG but only a DAG if I can separate compilation from linking. Doing compile A, compile B, link AB, compile C, link AC, where A, B and C are whole modules not just compilation units. It's perfectly possible, just a matter of how to persuade VS to do things that way.
pasztorpisti 23-Jul-13 10:10am    
You are right. :-) The acyclic was missing from there.

What you are using is definitely an antipattern. Allowing cyclic dependency between DLLs is a bad idea not only in case of DLLs but also in case of static libs. If 2 DLLs reference each other then they keep each others reference count on 1 btw, that isn't the end of the world because you probably end their life with ExitProcess at the end.
Change your project this way: You have lib1 and lib2. Let only lib1 to depend on lib2. To communicate back from lib2 to lib1 do the following: Define an interface in your source code that is visible to both libs. In lib1 create an implementation of this interface. When lib 1 communicates to lib2 for example by calling lib2::Init() you pass an interface pointer to lib2 so lib2 will be able to call code inside lib1.
 
Share this answer
 
Comments
Matthew Faithfull 23-Jul-13 10:28am    
Thanks, that's a good idea in general, however in this case the interconnections are such I'm not sure it would work and it would mean a rewrite of ~30,000 lines of code. Forgive me if I look for another solution for a while.
pasztorpisti 23-Jul-13 10:48am    
Are there so many connections between the DLLs? I think wiping out some dirt is sometimes fruitful. Check out the number of calls between DLLs and find the direction in the bidirectional connection that contains the least calls - I would replace these with the interface. Then decide. :-) In my opinion this is a fairly easy refactorization without serious code modification. Creating an interface + interface implementation that calls the original exported functions inside lib1 is trivial. Besides this all you need is putting an Init(Interface*) method in the DLLs that is used by lib1 and you have to call the Init method of these DLLs at startup to store the interface pointer to a global variable inside other libs that want to call lib1. From now you call the interface pointer methods in other libs. If you are lazy and there are too many calls to the original lib1 functions in lib2 then you can create helper functions inside lib2 that have the same signature as the originally imported lib1 functions but these helpers call the methods of the interface pointer that is stored in lib2 as a global variable. Its work only around the interface without modification to the cores.
Matthew Faithfull 23-Jul-13 11:10am    
These are the link summaries from the Dlls in the first pass build:

1>D:\...\PosumQOR.dll : fatal error LNK1120: 257 unresolved externals
3>D:\...\stdQOR.dll : fatal error LNK1120: 125 unresolved externals
4>D:\...\SystemQOR.dll : fatal error LNK1120: 258 unresolved externals
5>D:\...\WinQL.dll : fatal error LNK1120: 503 unresolved externals
6>D:\...\WinQAPI.dll : fatal error LNK1120: 659 unresolved externals
7>D:\...\CQOR.dll : fatal error LNK1120: 218 unresolved externals
8>D:\...\ArchQOR.dll : fatal error LNK1120: 65 unresolved externals
9>D:\...\CompilerQOR.dll : fatal error LNK1120: 122 unresolved externals
10>D:\...\CodeQOR.dll : fatal error LNK1120: 119 unresolved externals

So yes I don't really fancy adding 2326 stubs or helper functions to get a cleaner build. This is a miniature version of the project, the full project figures would be much higher.

I like the idea though and I may do exactly as you've suggested for some of the ~70 interfaces.
pasztorpisti 23-Jul-13 11:17am    
I would put the interface into the central DLL you were talking about, this only one interface is needed. With this solution the central DLL would depend on all other DLLs but the other DLLs wouldn't depend on the central, they would just use the interface of it. This interface would contain all exported functions of the central DLL. With a lot of functions it can be boring work to do this but its a relatively low risk task. I would share a static lib between the non-central DLLs: This static lib would contain the Init(Interface*) exported symbol and the global variable, plus the helper functions that mimic the exported functions of the central DLL and call the methods of the stored global interface pointer. This way you have to write it only once for all non-central DLLs.

And don't misunderstand, I'm not forcing you to do this. :-) You have to weigh up the costs of your future actions with this codebase yourself. I'm just giving tips.
Matthew Faithfull 23-Jul-13 11:28am    
I'd essentially be creating a wrapper that exported the entire interface of my Dll network but only contained stubs. I have considered doing that despite it being a ridiculous amount of work. I already have static libraries that link into every Dll but I hadn't considered trying to but any import interface there because up till now no import interface was common between everything the lib got linked into.
I will think about it. Thank you.
I have done this type of thing before. It is a bit of a pain but it can be done. I will describe the linkage between 2 DLLs, which I think you can generalize. Set up the linkage so that A.dll compiles and links properly to B.dll. In B.dll, use function pointers to the A.dll APIs, and when you need to call A.dll from B.dll, have stub code for each API call do a LoadLibrary on A.dll and lookup the API(s) with GetProcAddress(). This bookkeeping only needs to be done once and can be cached in a static table. Use of extern "C" linkage in A.dll is not mandatory but will simplify the name lookup mess.
 
Share this answer
 
Comments
Matthew Faithfull 23-Jul-13 10:35am    
A good idea and I've done this before as well. I'm really not looking however for a way to rewrite the code though just to stop VS running off and wasting 10 minutes spewing spurious errors. One reason I can't adopt this strategy is that 7 of the 9 Dlls can't contain calls to LoadLibrary or GetProcAddress as they have to be source compatible with Linux, i.e. they also build as Linux shared objects
H.Brydon 23-Jul-13 10:50am    
I think you're going to have to break up the DLLs into more pieces so that everything of interest can look "down" instead of sideways.
Matthew Faithfull 23-Jul-13 11:15am    
That's an interesting possibility. I wonder if there's any way I could get some sort of automated analysis to produce a dependency tree? It would be horribly vast but it might give a clue as to where to begin unpicking the knot.
Matthew Faithfull 23-Jul-13 13:06pm    
Thanks guys for the very useful and interesting ideas on this. It's clear that there's not going to be any easy fix for VS. I guess I'll have to bite the bullet and move to CMake and look at restructuring dependencies in the longer term.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900