6

To compile two files i have created a makefile where i use to mention the object name or i can use the pattern rule using patsubst.

# ----------------------------------------------------------------------------
# Makefile for building tapp
#
# Copyright 2010 FriendlyARM (http://www.arm9.net/)
#

ifndef DESTDIR
DESTDIR            ?= /opt/FriendlyARM/tiny6410/linux/rootfs_qtopia_qt4
endif

#CFLAGS              = -c -Wall -O2  # wall is for warning show and 02 is optiminisation level 2
CFLAGS              = -c -O2  # wall is for warning show and 02 is optiminisation level 2
#CC                    = arm-linux-gcc   # compiler name
CC                    = gcc   # compiler name
LD                    = ld

INSTALL             = install         # 

TARGET              = led_player_project

#OBJ = led-player_backup.o led-player.o
OBJ := $(patsubst %.c,%.o,$(wildcard *.c */*.c))

#OBJ = $(shell find . -name '*.c')

all: $(TARGET) 
#all: $(OBJ)

led_player_project : $(OBJ)
    $(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
#       $(LD) $(LDFLAGS) -o $@ $< $(LIBS)

%.o : %.c
    $(CC) $(CFLAGS) $< -o $@

#$< -o $@


install: $(TARGET)
    $(INSTALL) $^ $(DESTDIR)/usr/bin

clean :
    rm -rf *.o $(TARGET) $(OBJ)


# ----------------------------------------------------------------------------

.PHONY: $(PHONY) install clean

# End of file
# vim: syntax=make

#EOF

Now if my project contains folder contains subfolders & they contains further files. Then can i write pattern rule to compile every file & create an common executable?

1> Do i will have to create makefile in every-subfolder so that i can invoke that makefile from main makefile, like integrating static driver to linux kernel each driver have respective makefile ?
2> Or common makefile for full project ?
3> can i use patsubst to compile every file without mentioning there name.
4> How can i combine every *.o to create on executable called main.

enter image description here

Edit :---
@Jan Hudec I have modified my makefile as per your comment (i have posted it above). Now i am just trying with two folders inside my main folder. I am getting following error Folder structure :--

main Folder  ----> one Folder 
             ----> two Folder 

Folder Main contains :--

main.c 
main.h
Makefile

Folder one contains :--

one.c
one.h

Folder two contains :--

two.c
two.h

main.c content :--

#include <stdio.h>
#include <stdlib.h>

#include "main.h"

int main()
{
  char *p;

  printf("\n\n main \n");

  one();
  two();

  return 0;
}

main.h content :---

#include "one/one.h"
#include "two/two.h"

one.c content :---

    #include <stdio.h>
    #include <stdlib.h>

#include "one.h"

    void one()
    {

      printf("\n one \n");

    }

one.h content :---

void one();

two.c content :---

#include <stdio.h>
#include <stdlib.h>

#include "two.h"

void two()
{

  printf("\n two \n");

}

two.h content :---

void two();

Error i got at make time :----

ignite@ignite:~/testing/main$ make
gcc    -c -O2   main.c -o main.o
gcc    -c -O2   one/one.c -o one/one.o
gcc    -c -O2   two/two.c -o two/two.o
ld  -o led_player_project main.o one/one.o two/two.o 
ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080
main.o: In function `main':
main.c:(.text.startup+0x11): undefined reference to `puts'
one/one.o: In function `one':
one.c:(.text+0xb): undefined reference to `puts'
two/two.o: In function `two':
two.c:(.text+0xb): undefined reference to `puts'
make: *** [led_player_project] Error 1
ignite@ignite:~/testing/main$ 
Katoch
  • 2,709
  • 9
  • 51
  • 84

2 Answers2

10

Ad 1 and 2: The filenames can safely include directories and % matches / as necessary. So you can easily have:

$(wildcard subdir/*.c) $(wildcard anotherdir/*.c)

or even

$(wildcard */*.c)

... or as suggested by keltar in comment

$(shell find . -name '*.c')

which is recursive.

Ad 3: You are doing it.

Ad 4: Create a target with $(OBJ) as dependencies and use the automatic variable just as you do for compilation:

main : $(OBJ)
        $(LD) $(LDFLAGS) -o $@ $< $(LIBS)
Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • 2
    or `$(shell find . -name '*.c')` – keltar Oct 23 '13 at 10:52
  • Thanks for replying --- OBJ := $(patsubst %.c,%.o,$(wildcard */*.c)) -- so if i change OBJ as suggested by you then it will search from current directories & the sub-directories ..? .. Also *.o for every file will be created in their respective folders or in main folder ? – Katoch Oct 23 '13 at 15:13
  • One more question --- Is my clean target recipt is correct in this case ? – Katoch Oct 23 '13 at 15:14
  • @Katoch: `$(wildcard */*.c)` will only search in immediate subdirectories. `*` never matches `/`. If you want search across any depth, you need the `$(shell find . -name '*.c')`. – Jan Hudec Oct 24 '13 at 06:46
  • @Katoch: Of course the pattern in clean needs to match the one used to generate list of sources. It is possible to just do `rm -f $(OBJ)` (it's never `-r`, the arguments are always files, not directories), but if you delete a source, the object will not get cleaned afterwards. So there is some advantage to having matching pattern. For `$(shell find . -name '*.c')` the matching command is `find . -name '*.o' | xargs rm` (you need `-f` in the former case, because you are passing names that don't exist; you don't need it in the find case where you don't). – Jan Hudec Oct 24 '13 at 06:49
  • @JanHudec ... I am getting error with the changes in the makefile recommended by you. Please have a look at the post above i have edited it. – Katoch Oct 24 '13 at 08:14
  • @Katoch: It has nothing to do with changes we talked about so far. Your compilation command is simply missing the `-c` flag. – Jan Hudec Oct 24 '13 at 08:20
  • CFLAGS = -c -Wall -O2....ok as we are compiling different object files & then combining them together so we need -c option ... as per this link ... http://www.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html .. Also i am missing to define $(LD) ad ld .. right ? – Katoch Oct 24 '13 at 14:10
  • @JanHudec I have changed the $(LD) receipt as i was getting some error .. also changed the clean target receipt .... but still getting getting some link time error ... please suggest what i have missed to resolve this error.... ? – Katoch Oct 24 '13 at 14:56
  • @Katoch: That's not much make-related any more, but rather C compilation issue. It'd recommend you to write a new question listing the exact errors and all the compilation commands exactly as they get printed by make when you do a complete build. – Jan Hudec Oct 25 '13 at 06:25
2

Perhaps another solution too. I have a source directory in my project dir which contains subdirectories. And I dont want have a Makefile in every subdirectories or something else. And I want to build everything only with one makefile in rootdir of project: So for my static library in c++ i did this makefile. Perhaps it could be a solution for you too. But I didnt test it well with paralell builds via "make -j4" or so.

BUILDCXX=g++-10
CHECKCXX=clang++-12

CXXFLAGS=-std=c++17 -Wall -Werror -Wextra -g -pg -O0 -I. -DDEBUG
CXXFLREL=-std=c++17 -Wall -Werror -Wextra -O3 -s -I. -DNDEBUG
CXXFLAGSLIB=$(CXXFLAGS)
CXXFLAGSTST=$(CXXFLAGS) -DRLOG_COMPONENT="clbc"

LDFLAGS=
LDFLAGSLIB=$(LDFLAGS)
LDFLAGSTST=$(LDFLAGS) -L./target/lib -lUnitTest++ -lclbc

OUTDIR=target
OUTDIRLIB=$(OUTDIR)/lib
OUTDIRTST=$(OUTDIR)/bin
OUTDIROBJ=$(OUTDIR)/obj
OUTFILELIB=libclbc.a
OUTFILETST=runtests

SRCDIR=source
SRCDIRLIB=$(SRCDIR)/lib
SRCDIRTST=$(SRCDIR)/test
SRCDIRSLIBR := $(shell find $(SRCDIRLIB) -maxdepth 3 -type d)
SRCFILESLIB := $(foreach dir,$(SRCDIRSLIBR),$(wildcard $(dir)/*.cpp))
OBJFILESLIB := $(addprefix $(OUTDIROBJ)/,$(notdir $(patsubst %.cpp,%.o,$(SRCFILESLIB))))
SRCDIRSTSTR := $(shell find $(SRCDIRTST) -maxdepth 3 -type d)
SRCFILESTST := $(foreach dir,$(SRCDIRSTSTR),$(wildcard $(dir)/*.cpp))
OBJFILESTST := $(addprefix $(OUTDIROBJ)/,$(notdir $(patsubst %.cpp,%.o,$(SRCFILESTST))))


.PHONY: all


all: clean lib


check-syntax:
    $(CHECKCXX) $(CXXFLAGS) -s -o /dev/null -S $(CHK_SOURCES)


clean:
    @rm -rf $(OUTDIR)


lib:$(OBJFILESLIB)
    @mkdir -p $(OUTDIRLIB)
    @echo " TargetLib :" $(OUTDIRLIB)/$(OUTFILELIB) 
    @ ar rcs $(OUTDIRLIB)/$(OUTFILELIB) $^


test:$(OBJFILESTST)
    @mkdir -p $(OUTDIRTST)
    @echo "TargetTest :" $(OUTDIRTST)/$(OUTFILETST)
    @ $(BUILDCXX) $(OBJFILESTST) -o $(OUTDIRTST)/$(OUTFILETST) $(LDFLAGSTST)


release: CXXFLAGSLIB=$(CXXFLREL)
release:$(OBJFILESLIB)   
    @mkdir -p $(OUTDIRLIB)
    @echo "RTargetLib :" $(OUTDIRLIB)/$(OUTFILELIB) 
    @ ar rcs $(OUTDIRLIB)/$(OUTFILELIB) $^


define set_real_src_file
    $(eval REAL_SRC_FILE=$(strip $(1)))
endef

define set_nothing
endef

define get_real_src_file
    $(if $(strip $(findstring $(strip $(1)),$(strip $(2)))),$(call set_real_src_file,$(2)),$(call set_nothing))
endef

define get_source_file
    @echo ObjectFile : $(1)
    $(eval REAL_SRC_SEARCH=$(notdir $(patsubst %.o,%.cpp,$(1))))
    $(eval REAL_SRC_FILE=)
    $(foreach word,$(2), $(call get_real_src_file, $(REAL_SRC_SEARCH),$(word)))
endef


$(OBJFILESLIB): $(SRCFILESLIB)
    @mkdir -p $(OUTDIROBJ)
    $(call get_source_file,$@,$^,$<)
    @ $(BUILDCXX) $(CXXFLAGSLIB) -c $(REAL_SRC_FILE) -o $@


$(OBJFILESTST): $(SRCFILESTST)
    @mkdir -p $(OUTDIROBJ)
    $(call get_source_file,$@,$^,$<)
    @ $(BUILDCXX) $(CXXFLAGSTST) -c $(REAL_SRC_FILE) -o $@

But I guess it runs only with GNUMake and no other implementations of make.