Rich Jahn
Rich Jahn

Reputation: 443

Should makefile prerequisites list shared objects?

I understand that with many processes using a common shared library, the binary code in the shared library can be updated without necessarily having to update all those processes. It's fine as long as it is an internal implementation change, and does not remove exported functions, or change the arguments of exported functions.

I'm not sure how to reconcile this though with Makefile dependencies. The simple guideline is if the file is referenced by any of the commands, then it is a dependency and should be listed as a prerequisite in the Makefile rule.

When linking an executable, it definitely can reference a shared object, and so depend on that shared object. However, it doesn't seem like a relink is always necessary just because the shared object changed.

So I'm wondering about listing library as dependency:

all: process

# listing library.so as dependency
process: file.o library.so
    gcc -o process file.o library.so

library.so: library.o
    gcc -shared -o library.so library.o

library.o: library.h

vs. not listing it:

all: library.so process

# not listing library.so as dependency
process: file.o
    gcc -o process file.o library.so

library.so: library.o
    gcc -shared -o library.so library.o

file.o: library.h

library.o: library.h

Suppose library.h contains declarations for all exported functions from library.so.

If library.so is a dependency for process, then it seems if I only change the implementation of a function (no change to exported function signatures), then I don't need to relink process, but a make command would relink library.so, and then in turn relink process.

However, if library.so is not a dependency for process, then make would still relink library.so for an implementation change via the all target, but not relink process. If I change the data type of a function argument, add/remove a function argument, or add/remove a function, then library.h will have a change. This will in turn trigger recompiling file.o, and in turn trigger relinking process.

Use of library.h to control recompile of file.o, and so indirectly control relink of process is still imprecise though, because file.o might not actually reference any of the exported functions from library.so that changed.

I reviewed this question, but I couldn't find anything specific about shared object dependencies for a process: Makefile Dependencies, What Should Be a Dependency?

Upvotes: 0

Views: 451

Answers (1)

Luis Colorado
Luis Colorado

Reputation: 12698

If you don't list library.so as a dependency of process, if you modify one of its sources (the source files of library.so), you'll end, most probably, with an incompatible library.so with the program process and your program will probably crash when you try to execute it.

Think you change the library, eliminating a function f() from it, because you don't need it more... but your program process still calls it in its main() function. As you have not modified process.o (you didn't touch any source of it) it is not to be compiled, and no new link command will be done (because process depends only on process.o).

Last time you linked process the linker put a call to f() in main and resolved to put it at address x (corresponding to the address that existed in library.so). But as you have not stated that process needs to be linked if library.so changes, it will not be linked... and when you execute it, it will call to f() at address x, and crash.

Try this, and you'll see that if you leave the dependency on library.so, then your program will be linked against the shared library and will complaint about f() not existent. So you should modify process.c and repeat.

Always think this... Is the file library.so needed to link process? If it is, then you should depend on it. And it is, because the functions you wrote in library.so are called from process. You will see that the dependencies to .h files are put on .o targets, and never on .c files. This is because the .c and all the included .h files are needed to build the .o object. But the .c files don't depend on the .h files... what depends is the result of the compilation (and this is the .o file).

The second file, has a fake dependency on target all. That's what is called a .PHONY: target (a target that is always executed, and doesn't require a file to be created) With it you try to create both, library.so and process. If library.so is present when you link process, then it will be included (but without knowing if it is a recent buid or an old one) but if it's not, when you try to build process, if library.so doesn't yet exist, then you'll get a strong complaint from the linker and your build will be aborted.

So, as a conclussion, the second Makefile is incorrect, and you should avoid it. State the dependencies as make(1) checks them.

A final note, depending on the implementation of Make, the order of the dependencies in the right part of a rule can be significant or not. Putting library.so to the left of process can be a bad idea if you run make on a multiprocessor and try to do a parallel build (see option -j of make(1)). In a parallel build, make(1) doesn't start the build of a dependent file until all its dependencies are satisfied... but it tries to start as many processes as possible to handle that possibility on multiprocessor machines. And the build process speeds up a lot in case you have a full set of cores, but can be dangerous if you don't observe the dependencies precisely.

Upvotes: 1

Related Questions