DanielB
DanielB

Reputation: 369

GNU Make - how to handle paths in object files

I'm currently writing a bigger makefile, which will handle a recursive structure of source files and it will put them in the same recursive structure in a destination directory. Basically you can assume this makefile:

DESTINATION_DIR = out
SOURCE_DIR = src
OBJECTS = out/abc.o out/subdir/test.o out/subdir/subdir/xyz.o

.PHONY: all
all: $(OBJECTS)

$(DESTINATION_DIR)/%.o: $(SOURCE_DIR)/%.c
    @echo "$@ --- $<"

As you can see I'm trying to replace $(DESTINATION_DIR) with $(SOURCE_DIR) in the dependency, but this doesn't work. How can I solve this? I even tried to use a replacement script this way:

%.o: $(shell myreplacementscript.py "%.c" "$(DESTINATION_DIR)" "$(SOURCE_DIR)")
    @echo "$@ --- $<"

but the percentage sign will be passed to my script without replacement. So how can I tell it that the output depends on the source file dynamically? I need to keep the sub directories in the OBJECTS variable, I can't flatten the structure. And the sub directories are dynamic, not predefined.

I've searched for others having the same problem but they either do have flat OBJECTS or a recursive make, which in my case isn't well suited because of interconnections between the directories according to dependencies.

Summary/Error Message:

It doesn't replace out/ in the OBJECTS with src/. So the error message is "Keine Regel vorhanden, um das Target »out/abc.o«, benötigt von »all«, zu erstellen. Schluss." which is the typical error "No rule to make target ...".

The full makefile can be seen here: Full Makefile

Whenever I leave the righthandside of the colon with %.html at the left side it works, but I need to have the source file as dependency to get a working incremental build (hopefully without creating dependency files).

Kind regards,

Daniel

Upvotes: 2

Views: 753

Answers (2)

Oleksiy Chernyavskyy
Oleksiy Chernyavskyy

Reputation: 71

The solution to use GNU make extension is not a good way. I'd recommend not to use any GNU Make specific feature. First of all it's not portable. Second, it's syntax is horrible. Use standard tools and technologies ever invented. Your solution is - Shell. You can wrap you Makefile into a shell script and do all calculations and string manipulations using common shell language. Take the following example:

src_files="file1.c file2.c file3.c"

cat > Makefile <<EOF
-
-
YOUR MAKEFILE STATIC STUFF GOES HERE
-

all: $(OBJECTS)

EOF

for file in $src_files; do
  src="src/$file"
  obj=`echo $file | sed 's/\.c$/\.o/'`
  sources="$sources $src"
  objects="$objects out/$obj"

  cat >>Makefile <<EOF
$obj: $src
    YOUR CODE HERE
EOF
done

cat >>Makefile <<EOF
SOURCES = $sources
OBJECTS = $objects

EOF

Name your script 'mkmf' and run ./mkmf. It will generate Makefile. You can parametrize it and make it very powerful and smart. No need to use scanty and dirty gnu make extension.

Upvotes: 2

DanielB
DanielB

Reputation: 369

With Michael Grünewald's answer and MadScientist's website, I've decided to go the VPATH way. But therefore I had to switch to the output directory first and remove the out/ prefixes of the OBJECTS (with a recursive call to the makefile). The above short example would be modified like this:

ROOTDIR = $(abspath ./)

DESTINATION_DIR = out
SOURCE_DIR = src

DESTINATION_DIRABS = $(abspath $(shell cd $(ROOTDIR); cd $(DESTINATION_DIR); pwd))
SOURCE_DIRABS = $(abspath $(shell cd $(ROOTDIR); cd $(SOURCE_DIR); pwd))

ifneq ($(abspath ./),$(DESTINATION_DIRABS))
# we are not in the destination directory, so we first need to change to it
OBJECTS     = 
else
# without out/ prefix!
OBJECTS     = abc.o subdir/test.o subdir/subdir/xyz.o
# set the VPATH for the source files
VPATH       = $(SOURCE_DIRABS)
endif

.PHONY: all
all: $(OBJECTS)
ifneq ($(abspath ./),$(DESTINATION_DIRABS))
    # call make inside the destination directory
    $(MAKE) -C $(DESTINATION_DIRABS) -f $(ROOTDIR)/Makefile ROOTDIR='$(ROOTDIR)'
endif

%.o: %.c
    @echo "$@ --- $<"

(untested in this small example, but works in my bigger Makefile)

So finally it is working now as expected. Thank you very much for your comments Michael Grünewald, MadScientist and Beta.

Update:

Finally I retried the $(DESTINATION_DIR)/%.o: $(SOURCE_DIR)/%.c approach and the fault was simply that the $(DESTINATION_DIR) already contained the slash in my full makefile.

Sorry for the circumstances and thanks again. For anyone reading this because of having a similar problem: check the slashes twice!

Upvotes: 0

Related Questions