Search code examples
cmakefilecompilation

How do I avoid recompiling compiled files in a make file?


My C files:

  • NumClass.h
  • advancedClassificationLoop.c
  • advancedClassificationRecursion.c
  • basicClassification.c
  • main.c

This is my makefile:


LOOP=advancedClassificationLoop.o
REC=advancedClassificationRecursion.o
BASIC=basicClassification.o
FLAG=-Wall -g
CC=gcc

# Compiling Source Files:

main.o: main.c NumClass.h
    $(CC) $(FLAG) -fPIC -c main.c -o main.o

advancedClassificationLoop.o: advancedClassificationLoop.c NumClass.h
    $(CC) $(FLAG) -fPIC -c advancedClassificationLoop.c -o $(LOOP)

advancedClassificationRecursion.o: advancedClassificationRecursion.c NumClass.h
    $(CC) $(FLAG) -fPIC -c advancedClassificationRecursion.c -o $(REC)

basicClassification.o: basicClassification.c NumClass.h
    $(CC) $(FLAG) -fPIC -c basicClassification.c -o $(BASIC)

# loops: creates the static library libclasslops.a
loops: $(LOOP) $(BASIC)
    ar rcs libclassloops.a $(LOOP) $(BASIC)

# recursives: creates the static library libclassrec.a
recursives: $(REC) $(BASIC) 
    ar rcs libclassrec.a $(REC) $(BASIC)

# recursived: creates the dynamic library libclassrec.so
recursived: $(REC) $(BASIC) 
    gcc $(FLAG) -shared -o libclassrec.so $(REC) $(BASIC) 

# loopd: creates the dynamic library libclassloops.so
loopd: $(LOOP) $(BASIC)
    gcc $(FLAG) -shared -o libclassloops.so $(LOOP) $(BASIC)

# mains: creates a main program linked to libclassrec.a
mains: main.o libclassrec.a 
    gcc $(FLAG) -o mains main.o -L. -lclassrec

# maindloop: creates a main program linked to libclassloops.so
maindloop: main.o libclassloops.so
    gcc -o maindloop main.o -L. -lclassloops

#maindrec: creates a main program linked to libclassrec.so
maindrec: main.o libclassrec.so 
    gcc -o maindrec main.o -L. -lclassrec

# all: compiles all libraries and programs (do no re compile)
all: $(BASIC) $(LOOP) $(REC) loops recursives recursived loopd mains maindloop maindrec

# clean: deletes all the compiled files except of .h, .c, .txt and the makefile itself
clean:
    rm -f libclassloops.a libclassrec.a libclassrec.so libclassloops.so mains maindloop maindrec *.o *.txt~



According to an auto-test:

compiling with make all
running:  make all
running tests
running:  make loops > compilation_out.txt
unneeded recompilation happand for target  loops
running:  make maindrec > compilation_out.txt
running:  ./mains < ../inputs/input1.txt > tmp_out.txt
command fained
 failed p.returncode= 127

How do I avoid recompiling in target loops as mentioned above? Generally is the makefile syntax and logic is correct?

According to an auto-test:

compiling with make all
running:  make all
running tests
running:  make loops > compilation_out.txt
unneeded recompilation happand for target  loops
running:  make maindrec > compilation_out.txt
running:  ./mains < ../inputs/input1.txt > tmp_out.txt
command fained
 failed p.returncode= 127

How do I avoid recompiling in target loops as mentioned above? Generally is the makefile syntax and logic is correct?


Solution

  • How do I avoid recompiling compiled files in a make file?

    Use make correctly. make's is not a scripting language. Makefile rules are not functions.

    In particular, any rule intended to build a persistent artifact should have that artifact as its target. This, together with an appropriate prerequisite list, is what allows make to determine whether a target is already up to date, vs whether bringing it up to date would require running the rule's recipe. Your makefile fails on that in several places, which is why you get unneeded work done, including some unneeded compilations. Here's a good consistency check: if a rule builds a persistent artifact, then its recipe should use special variable $@ to express that artifact's name. That variable expands to the name of the rule's target.

    Other recommendations:

    • the first target of the first rule in the Makefile is the default target. Usually, in a makefile that provides a "build everything" target (conventionally named all, though that has no inherent significance to make), that should be the default target.

    • Rely on built-in rules where they are suitable, and minimize unneeded repetition. Using standard names for variable conveying compilation and linking flags helps here.

    • Rules should express only direct prerequisites. This makes the makefile easier to maintain, and possibly more flexible.

    • Use variables effectively to avoid repetition and make your makefile easier to maintain

    • Where possible, use automatic variable $^ and / or $< in recipes to avoid repeating the names of prerequisites

    • Don't build libraries needlessly. If you don't need to provide a library for some other, separate build to use later, then you probably don't need a library at all. Just link object files directly. It's unclear to me whether that applies to your case, but I suspect it does, for at least some of the executable you are building.

    • if you do build libraries, you can link directly against the built results by their full names. You don't need to mess with -L and -l link options, and it's often to your advantage not to do in such cases.

    • targets of rules that do not generate persistent artifacts generally should be declared .PHONY

    Here's a first pass at rewriting your original makefile along these lines:

    CFLAGS = -fPIC -Wall -g
    CC = gcc
    
    EXECUTABLES = mains maindloop maindrec
    
    BASIC = basicClassification
    LOOP = advancedClassificationLoop
    REC = advancedClassificationRecursion
    
    OBJS = $(BASIC).o $(LOOP).o $(REC).o
    
    all: $(EXECUTABLES)
    
    mains: main.o libclassrec.a 
        $(CC) $(CFLAGS) -o $@ $^
    
    # maindloop: a main program linked to libclassloops.so
    maindloop: main.o libclassloops.so
        $(CC) -o $@ $^
    
    #maindrec: a main program linked to libclassrec.so
    maindrec: main.o libclassrec.so 
        $(CC) -o $@ $^
    
    # All object files to be built depend on NumClass.h
    # They will otherwise be built according to the built-in rule
    $(OBJS): NumClass.h
    
    libclassrec.a: $(REC).o $(BASIC).o 
        $(AR) rcs $@ $^
    
    libclassrec.so: $(REC).o $(BASIC).o
        $(CC) $(CFLAGS) -shared -o $@ $^
    
    libclassloops.a: $(LOOP).o $(BASIC).o
        $(AR) rcs $@ $^
    
    libclassloops.so: $(LOOP).o $(BASIC).o
        $(CC) $(CFLAGS) -shared -o $@ $^
    
    # clean: delete all generated files
    clean:
        rm -f $(EXECUTABLES) $(OBJS) libclassloops.a libclassrec.a libclassrec.so libclassloops.so
    
    .PHONY: all clean