Makefiles, How to Use Them

how to execute make file

You don't tend to execute the make file itself, rather you execute make, giving it the make file as an argument:

make -f pax.mk

If your make file is actually one of the standard names (like makefile or Makefile), you don't even need to specify it. It'll be picked up by default (if you have more than one of these standard names in your build directory, you better look up the make man page to see which takes precedence).

Makefile: use multiple makefiles

any way, I show you something and hope can help you:

all:
@$(MAKE) -C subfolder

or if you want to use *.mk as your sub makefile, you can write:

all:
@$(MAKE) -C busfolder -f somename.mk

I show you my example, my DIR looks like:

TOPDIR-- Makefile
|
|-- debug
| |-- debug.c
| |-- debug.h
| |-- debug.mk
| |-- instrument.c
| `-- uart_print.c
|-- driver
| |-- driver.c
| |-- driver_ddi.c
| |-- driver_ddi.h
| |-- driver.h
| `-- driver.mk
|-- include
| `-- common.h
|-- Makefile
|-- mw
| |-- manager.c
| `-- mw.mk
|-- root
| |-- main.c
| `-- root.mk

and my TOP makefile looks like:

MAKE_DIR = $(PWD)

ROOT_DIR := $(MAKE_DIR)/root
DRV_DIR := $(MAKE_DIR)/driver
INCLUDE_DIR := $(MAKE_DIR)/include
DEBUG_DIR := $(MAKE_DIR)/debug

INC_SRCH_PATH :=
INC_SRCH_PATH += -I$(ROOT_DIR)
INC_SRCH_PATH += -I$(DRV_DIR)
INC_SRCH_PATH += -I$(INCLUDE_DIR)
INC_SRCH_PATH += -I$(DEBUG_DIR)

LIB_SRCH_PATH :=
LIB_SRCH_PATH += -L$(MAKE_DIR)/libs


COLOR_ON = color
COLOR_OFF =
CC = $(COLOR_ON)gcc
#CC = $(COLOR_OFF)gcc
LD = ld

LINT = splint

LIBS := -ldriver -ldebug -lmw -lm -lpthread

CFLAGS :=
CFLAGS += $(INC_SRCH_PATH) $(LIB_SRCH_PATH)
CFLAGS += -Wall -O -ggdb -Wstrict-prototypes -Wno-pointer-sign -finstrument-functions -fdump-rtl-expand
CFLAGS += -DDEBUG -D_REENTRANT

LDFLAGS :=

export MAKE_DIR CC LD CFLAGS LDFLAGS LIBS LINT INC_SRCH_PATH

all:
@$(MAKE) -C debug -f debug.mk
@$(MAKE) -C driver -f driver.mk
@$(MAKE) -C mw -f mw.mk
@$(MAKE) -C root -f root.mk

.PHONY: clean
clean:
@$(MAKE) -C debug -f debug.mk clean
@$(MAKE) -C driver -f driver.mk clean
@$(MAKE) -C mw -f mw.mk clean
@$(MAKE) -C root -f root.mk clean

it will call sub DIR *.mk during the compile. The sub DIR makefile, I just write a simple example for you reference:

LIB = $(MAKE_DIR)/libs/yourmodulename.a
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))

$(LIB): $(OBJS)
@mkdir -p ../libs
@$(AR) cr $@ $^
@echo " Archive $(notdir $@)"

$(OBJS): $(SRCS)
@$(CC) $(CFLAGS) -c $^
@echo " CC $(OBJS)"

.PHONY: clean
clean:
@$(RM) -f $(LIB) $(OBJS)
@$(RM) -f *.expand
@echo " Remove Objects: $(OBJS)"

for the makefile, which generate the final target file(like a.out) is little bit different, because I use the sub makefile to generate LIB file, and I use a root.mk to generate final target:

PROG = ../prog/DEMO

SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))

$(PROG): $(SRCS)
@mkdir -p ../prog
@$(CC) $^ $(CFLAGS) -Wl,-Map=$(PROG).map $(LIBS) -o $@
@echo " Generate Program $(notdir $(PROG)) from $^"

.PHONY: clean
clean:
@$(RM) -f $(OBJS) $(PROG)
@$(RM) -f *.expand
@$(RM) -rf ../prog ../libs
@echo " Remove Objects: $(OBJS)"
@echo " Remove Libraries: $(notdir $(PROG))"

How to use makefiles in Visual Studio?

A UNIX guy probably told you that. :)

You can use makefiles in VS, but when you do it bypasses all the built-in functionality in MSVC's IDE. Makefiles are basically the reinterpret_cast of the builder. IMO the simplest thing is just to use Solutions.

What do the makefile symbols $@ and $ mean?

$@ is the name of the target being generated, and $< the first prerequisite (usually a source file). You can find a list of all these special variables in the GNU Make manual.

For example, consider the following declaration:

all: library.cpp main.cpp

In this case:

  • $@ evaluates to all
  • $< evaluates to library.cpp
  • $^ evaluates to library.cpp main.cpp

Any interesting uses of Makefiles to share?

Make's parallelism is particularly handy for shell scripting. Say you want to get the 'uptime' of a whole set of hosts (or basically perform any slow operation). You could do it in a loop:

cat hosts | while read host; do echo "$host: $(ssh $host uptime)"; done

This works, but is slow. You can parallelise this by spawning subshells:

cat hosts | while read host; do (echo "$host: $(ssh $host uptime)")&; done

But now you have no control over how many threads you spawn, and CTRL-C won't cleanly interrupt all threads.

Here is the Make solution: save this to a file (eg. showuptimes) and mark as executable:

#!/usr/bin/make -f

hosts:=$(shell cat)
all: ${hosts}

${hosts} %:
@echo "$@: `ssh $@ uptime`"

.PHONY: ${hosts} all

Now running cat hosts | ./showuptimes will print the uptimes one by one. cat hosts | ./showuptimes -j will run them all in parallel. The caller has direct control over the degree of parallelisation (-j), or can specify it indirectly by system load (-l).

How to write loop in a Makefile?

The following will do it if, as I assume by your use of ./a.out, you're on a UNIX-type platform.

for number in 1 2 3 4 ; do \
./a.out $$number ; \
done

Test as follows:

target:
for number in 1 2 3 4 ; do \
echo $$number ; \
done

produces:

1
2
3
4

For bigger ranges, use:

target:
number=1 ; while [[ $$number -le 10 ]] ; do \
echo $$number ; \
((number = number + 1)) ; \
done

This outputs 1 through 10 inclusive, just change the while terminating condition from 10 to 1000 for a much larger range as indicated in your comment.

Nested loops can be done thus:

target:
num1=1 ; while [[ $$num1 -le 4 ]] ; do \
num2=1 ; while [[ $$num2 -le 3 ]] ; do \
echo $$num1 $$num2 ; \
((num2 = num2 + 1)) ; \
done ; \
((num1 = num1 + 1)) ; \
done

producing:

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
4 1
4 2
4 3

Add .so and .a libraries to Makefile

Lets consider your /usr/local/lib/libYARP_OS.a.

What you can do is, have -L/usr/local/lib/ in your makefile as one of the variables. And then you can have -lYARP_OS appended to the LDLIBS.

-L is for path to the lib and -l is the lib name here libYARP_OS.a will be passed as -lYARP_OS.

On the command line you would do something like: gcc -o main main.c -L/usr/local/lib/ -lYARP_OS. This should give you an idea.

How to install and use make in Windows?

make is a GNU command so the only way you can get it on Windows is installing a Windows version like the one provided by GNUWin32. Anyway, there are several options for getting that:

  1. The most simple choice is using Chocolatey. First you need to install this package manager. Once installed you simlpy need to install make (you may need to run it in an elevated/admin command prompt) :

    choco install make
  2. Other recommended option is installing a Windows Subsystem for Linux (WSL/WSL2), so you'll have a Linux distribution of your choice embedded in Windows 10 where you'll be able to install make, gccand all the tools you need to build C programs.

  3. For older Windows versions (MS Windows 2000 / XP / 2003 / Vista / 2008 / 7 with msvcrt.dll) you can use GnuWin32.

An outdated alternative was MinGw, but the project seems to be abandoned so it's better to go for one of the previous choices.



Related Topics



Leave a reply



Submit