2

I'm trying to run the makefile, shown in this repo, which is a simple Wayland client. But when I run make, it seems that the output of $(WAYLAND) is blank, and it fails to compile due to not being able to find the necessary wayland-client.h header file. cc (GCC) version 5.3.1 on Fedora 23 in bash.

Below are some details. My question is, what mistake am I making with my environment that is preventing this makefile from working for me as intended?

Contents of makefile:

WAYLAND=`pkg-config wayland-client --cflags --libs`
CFLAGS?=-std=c11 -Wall -Werror -O3 -fvisibility=hidden

hello_wayland: hello_wayland.o helpers.o helpers.h images.bin
    $(CC) -o hello_wayland *.o $(WAYLAND) -lrt

images.bin: images/convert.py images/window.png images/fish.png
    images/convert.py
    cat window.bin fish.bin > images.bin

clean:
    $(RM) *.o fish.bin window.bin hello_wayland

Output of make:

cc -std=c11 -Wall -Werror -O3 -fvisibility=hidden -c -o hello_wayland.o hello_wayland.c hello_wayland.c:6:28: fatal error: wayland-client.h: No such file or directory

Notice how the options to cc above seem to be missing the output of $(WAYLAND) specified in makefile. If I manually execute cc like this:

cc -std=c11 -Wall -Werror -O3 -fvisibility=hidden -c -o hello_wayland.o hello_wayland.c \
-I/home/me/install/include -L/home/me/install/lib -lwayland-client

The compilation succeeds.

Output of pkg-config wayland-client --cflags --libs:

-I/home/me/install/include -L/home/me/install/lib -lwayland-client

Contents of ~/.bash_profile:

source ~/.profile
source ~/.bashrc

Relevant contents of ~/.bashrc:

export WLD=$HOME/install
export LD_LIBRARY_PATH=$WLD/lib
export PKG_CONFIG_PATH=$WLD/lib/pkgconfig/:$WLD/share/pkgconfig/
export PATH=$WLD/bin:$PATH
export ACLOCAL_PATH=$WLD/share/aclocal
export ACLOCAL="aclocal -I $ACLOCAL_PATH"

Thanks for pointing out something that should probably be totally obvious to me.

1
  • regarding the line setting CFLAGS, this part: CFLAGS?=-std=c11 should be: CFLAGS := -std=c11 Mar 20, 2016 at 21:02

1 Answer 1

2

the posted makefile is missing some details

Note: it is (almost) always best to separate the compile step from the link step

The following makefile corrects those details

Note: <tab> means to use the tab key in the actual makefile

RM             := /usr/bin/rm

LFLAGS         += `pkg-config wayland-client --libs`

CFLAGS         := -std=c11 -Wall -Werror -O3 -fvisibility=hidden
CFLAGS         += `pkg-config wayland-client --cflags`

OBJS           := hello_wayland.o helpers.o
HDRS           := helpers.h

TARGET         := hello_wayland

.PHONY: all
all: $(TARGET)

$(TARGET): $(OBJS) images.bin
<tab>$(CC) -o hello_wayland *.o $(LFLAGS) -lrt

%.o:%.c $(HDRS)
<tab>$(CC) $(CFLAGS)-c $< -o $@ -I.

images.bin: images/convert.py images/window.png images/fish.png
<tab>images/convert.py
<tab>cat window.bin fish.bin > images.bin

.PHONY: clean
clean:
<tab>$(RM) *.o fish.bin window.bin hello_wayland

You ask what is wrong with the original makefile:

Here are some observations:

Macros in a make file are of two types:

  • those evaluated only once using :=
  • those evaluated every time they are referenced =

In the rule

WAYLAND =`pkg-config wayland-client --cflags --libs`

WAYLAND will be re-evaluated every time referenced

CFLAGS ?= -std=c11 -Wall -Werror -O3 -fvisibility=hidden

Invalid format for macro. ?= should be :=

In a Makefile, typical usage is make all but this makefile is missing that target (which should be the first target so make by itself, will execute the first target) targets for a link step should not be the actual file name but rather a macro name where that macro contains the target name

This usage of a macro also makes it easy to set the resulting executable file name at the invocation of make while either way will work, for flexability it is best to create a macro.

This is expecially true when writing a recursive makefile or multiple different executables

hello_wayland: hello_wayland.o helpers.o helpers.h images.bin

This is a link step, with hidden compiles, using the default recipe, which knows nothing about any specific header file so the hidden compiles will not be done if the header file changes and a header file helpers.h should never be in a link step.

    $(CC) -o hello_wayland *.o $(WAYLAND) -lrt

The *.o is a glob operation, which should not be used in makefiles instead use the make facilties:

SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)

The 'target' is properly referenced by $@ and there can be no keypunch errors if the $@ is used.

images.bin: images/convert.py images/window.png images/fish.png
    images/convert.py
    cat window.bin fish.bin > images.bin

When a target is not the name of a produced file, especially in older versions of make, then the target needs to be preceeded by a .PHONY:

.PHONY: <list of target names that produce no actual output file>

clean:
    $(RM) *.o fish.bin window.bin hello_wayland

Using a glob operation (for instance *.o) in a makefile is a bad idea. Instead use the appropriate macro like the $(OBJS) macro listed above

So, why did the original makefile not work? a combination of things that accumulate into a make failure. Here are the more obvious reasons:

1) the incorrect setting of the CFLAGS macro
2) the combining of the link operation with the compile operations
3) the missing .PHONY: statement
4) the use of the `glob` statements
3
  • Thanks, this makefile works without issue. But I'm still curious about why the original fails to utilize the output of pkg-config via the original WAYLAND variable.
    – ybakos
    Mar 21, 2016 at 13:18
  • Whoah, awesome edit, thanks for taking the time to explain!
    – ybakos
    Mar 21, 2016 at 19:36
  • You also might want to put a - before the rm command, like -$(RM) *.o fish.bin window.bin hello_wayland. In that way, make clean will not complain if the files are not there and rm fails.
    – Anders
    Feb 24, 2022 at 5:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .