1
votes

I've been using target-specific variables for a while to pass object-specific flags. For example, if I need to link an executable with a certain library, I might do:

bin/foo: LDFLAGS += -lbar

And the bin/% rule will pick up -lbar. This works great for system libraries, but there's a problem when the libraries I'm linking against are also part of the project. In this case I'll have a dependency like

bin/foo: lib/libbar.so

and the target-specific rule I applied to bin/foo is also passed when building lib/libbar.so! This doesn't work since libbar.so can't link to itself. As the manual says,

There is one more special feature of target-specific variables: when you define a target-specific variable that variable value is also in effect for all prerequisites of this target, and all their prerequisites, etc. (unless those prerequisites override that variable with their own target-specific variable value).

What can I do to add that flag only for bin/foo but not its prerequisites? The quote above suggests something like

lib/libbar.so: LDFLAGS :=

but that's not ideal since my real project has other LDFLAGS set globally that I want to keep.

Here's a reproducer:

Makefile

bin/%:
  @mkdir -p $(@D)
  gcc $(LDFLAGS) $(filter %.o,$^) -o $@

lib/%:
  @mkdir -p $(@D)
  gcc $(LDFLAGS) -shared $(filter %.o,$^) -o $@

obj/%.o: src/%.c
  @mkdir -p $(@D)
  gcc $(CFLAGS) -c $< -o $@

bin/foo: obj/foo.o lib/libbar.so
bin/foo: LDFLAGS := -Wl,-rpath='$$ORIGIN/../lib' -Llib -lbar

lib/libbar.so: obj/bar.o

clean:
  rm -rf bin lib obj

.PHONY: clean

src/foo.c

int bar(void);

int main() {
  return bar();
}

src/bar.c

int bar() {
  return 42;
}

This is broken:

$ make bin/foo
gcc  -c src/foo.c -o obj/foo.o
gcc  -c src/bar.c -o obj/bar.o
gcc -Wl,-rpath='$ORIGIN/../lib' -Llib -lbar -shared obj/bar.o -o lib/libbar.so
/bin/ld: lib/libbar.so: file not recognized: file truncated
collect2: error: ld returned 1 exit status
make: *** [Makefile:7: lib/libbar.so] Error 1

But if I build the lib first it works:

$ make lib/libbar.so
gcc  -c src/bar.c -o obj/bar.o
gcc  -shared obj/bar.o -o lib/libbar.so
$ make bin/foo
gcc  -c src/foo.c -o obj/foo.o
gcc -Wl,-rpath='$ORIGIN/../lib' -Llib -lbar obj/foo.o -o bin/foo
$ ./bin/foo; echo $?
42
2

2 Answers

2
votes

You could use constructed variable names instead of target-specific variables. It's a different syntax but allows for specific target settings.

Something like:

LDFLAGS += $($@_FLAGS)

bin/foo_FLAGS = -lbar

More details can be found here (and related articles)

The short answer is there's no target-specific-without-inheritance feature available. You'll have to use something different if you don't want inheritance.

1
votes

The problem I think is the heritage of the (old fashioned, uhh sorry) compilation and linking method in Unix. Or maybe its me who is unaware of some context. I find it hard to understand why library names are mangled in the linker call to hide the fact that in reality we are linking against an interface, not an actual file - although we need that very file to pull out a measly description of said interface. Oh well. Whatever the true reason, make is unhappy with such hidden and twisted depenency information and so you are running into problems because, for make, the information you give is neither fish nor flesh. Maybe a function-like variable like

LIBSWITCHES = $(patsubst lib%,-l%,$(basename $(notdir $(filter %.so,$^))))

may help, as it tries to reconstruct the relations of dependencies to linked libraries. It takes all shared-objects from the dependency list and creates an -l+library name switch from them. As your library doesn't have itself as dependency, it doesn't show up in this list and therefore shouldn't confuse the linker. Just take out all the -l switches which name libraries that you are actually rebuilding from the $(LDFLAGS):

bin/%:
    @mkdir -p $(@D)
    gcc $(LDFLAGS) $(LIBSWITCHES) $(filter %.o,$^) -o $@

lib/%:
    @mkdir -p $(@D)
    gcc $(LDFLAGS) $(LIBSWITCHES) -shared $(filter %.o,$^) -o $@

bin/foo: LDFLAGS := -Wl,-rpath='$$ORIGIN/../lib' -Llib