108

I'm trying to setup a Makefile that will search and copy some files (if-else condition) and I can't figure out what exactly is wrong with it? (thou I'm pretty sure it's because a combination of spaces/tabs written in the wrong place). Can I get some help with it, please?

Here's what I have currently:

obj-m = linuxmon.o

KDIR = /lib/modules/$(shell uname -r)/build
UNAME := $(shell uname -m)

all:

    $(info Checking if custom header is needed)
    ifeq ($(UNAME), x86_64)
        $(info Yes)
        F1_EXISTS=$(shell [ -e /usr/include/asm/unistd_32.h ] && echo 1 || echo 0 )
        ifeq ($(F1_EXISTS), 1)
            $(info Copying custom header)
            $(shell sed -e 's/__NR_/__NR32_/g' /usr/include/asm/unistd_32.h > unistd_32.h)
        else    
            F2_EXISTS=$(shell [[ -e /usr/include/asm-i386/unistd.h ]] && echo 1 || echo 0 )
            ifeq ($(F2_EXISTS), 1)
                $(info Copying custom header)
                $(shell sed -e 's/__NR_/__NR32_/g' /usr/include/asm-i386/unistd.h > unistd_32.h)
            else
                $(error asm/unistd_32.h and asm-386/unistd.h does not exist)
            endif
        endif
        $(info No)
    endif

    @make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean
    rm unistd_32.h

Anyways, that'll print "Yes", "Copying header" twice and then it will quit saying that sed can't read /usr/include/asm-i386/unistd.h (which of course it can't read as I'm on a x64 system). I could say that make just isn't understanding the if/else and instead is running everything line by line.

alexandernst
  • 14,352
  • 22
  • 97
  • 197

2 Answers2

208

You can simply use shell commands. If you want to suppress echoing the output, use the "@" sign. For example:

clean:
    @if [ "test" = "test" ]; then\
        echo "Hello world";\
    fi

Note that the closing ; and \ at each line are necessary

(This is because make interpret each line as a seperate command unless it ends with \)

Omer Dagan
  • 14,868
  • 16
  • 44
  • 60
  • 29
    Simple answer to actual question I've had. – jcubic Aug 24 '19 at 10:05
  • 1
    That will not work on Windows. The answer is to use Make `ifeq` or `ifneq`. – s.ouchene Sep 29 '20 at 13:02
  • 4
    @s.ouchene you shouldn't use make on windows anyway. Make is made for Unix and Linux. Use a cross platform build system such as cmake – cael ras Nov 24 '20 at 11:27
  • A longer example would be helpful, to show that the semicolon is needed at the end of *each line*, NOT just the last line. That wasn't clear to me. [Here is a longer example](https://stackoverflow.com/a/58602879/4561887) which makes that point obvious, and is where I learned it. – Gabriel Staples Jul 03 '21 at 20:48
  • @GabrielStaples It is mentioned in the answer. Your suggestion is somewhat off topic – Omer Dagan Jul 05 '21 at 10:31
  • 2
    @caelras Off-topic but clarification: Make was perhaps first developed in Unix world, but can be used in Windows as long as you are using a Windows-compatible make or an environment that can run it. There are several 'make' dialects. GNU Make, referenced here, is the most popular in the Unix-like OSes and often used with autotools. It can also run under Windows given some work, under Cygwin for instance. CMake is quite another beast; it's a different build manager. Popular in C++ world but not only under Windows, it can also be used in Unix-like OSes. – PRouleau Jul 07 '22 at 15:41
  • Make sure that no character after '\' appears at each line. Even whitespace! – Caglayan DOKME Apr 28 '23 at 05:48
97

There are several problems here, so I'll start with my usual high-level advice: Start small and simple, add complexity a little at a time, test at every step, and never add to code that doesn't work. (I really ought to have that hotkeyed.)

You're mixing Make syntax and shell syntax in a way that is just dizzying. You should never let it get this big without testing. Let's start from the outside and work inward.

UNAME := $(shell uname -m)

all:
    $(info Checking if custom header is needed)
    ifeq ($(UNAME), x86_64)
    ... do some things to build unistd_32.h
    endif

    @make -C $(KDIR) M=$(PWD) modules

So you want unistd_32.h built (maybe) before you invoke the second make, you can make it a prerequisite. And since you want that only in a certain case, you can put it in a conditional:

ifeq ($(UNAME), x86_64)
all: unistd_32.h
endif

all:
    @make -C $(KDIR) M=$(PWD) modules

unistd_32.h:
    ... do some things to build unistd_32.h

Now for building unistd_32.h:

F1_EXISTS=$(shell [ -e /usr/include/asm/unistd_32.h ] && echo 1 || echo 0 )
ifeq ($(F1_EXISTS), 1)
    $(info Copying custom header)
    $(shell sed -e 's/__NR_/__NR32_/g' /usr/include/asm/unistd_32.h > unistd_32.h)
else    
    F2_EXISTS=$(shell [[ -e /usr/include/asm-i386/unistd.h ]] && echo 1 || echo 0 )
    ifeq ($(F2_EXISTS), 1)
        $(info Copying custom header)
        $(shell sed -e 's/__NR_/__NR32_/g' /usr/include/asm-i386/unistd.h > unistd_32.h)
    else
        $(error asm/unistd_32.h and asm-386/unistd.h does not exist)
    endif
endif

You are trying to build unistd.h from unistd_32.h; the only trick is that unistd_32.h could be in either of two places. The simplest way to clean this up is to use a vpath directive:

vpath unistd.h /usr/include/asm /usr/include/asm-i386

unistd_32.h: unistd.h
    sed -e 's/__NR_/__NR32_/g' $< > $@
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
Beta
  • 96,650
  • 16
  • 149
  • 150
  • Thank you, but I'm missing 2 if's here. Will be that done automatically by make? – alexandernst Apr 12 '13 at 18:17
  • Yes. If neither header exists, Make will complain that it doesn't know how to build `unistd_32.h`. – Beta Apr 12 '13 at 18:18
  • 1
    Ok, that really makes it simpler. Just one more thing, can I print an error message instead of making ```make``` complain about not knowing how to build it? – alexandernst Apr 12 '13 at 18:20
  • Oh, I just tried that but it doesn't work. That's what I have: http://pastebin.com/cHP7ZL26 but it says that there's a missing separator on line 14 – alexandernst Apr 12 '13 at 18:24
  • @alexandernst: 1) Yes, you can get Make to print your error message, but do you care about the other effects (e.g. aborting the build, returning a non-zero value)? 2) each command in a Make rule must start with a TAB. – Beta Apr 12 '13 at 18:31
  • 1) Well, I'd like to abort the make it none of those files are available and UNAME is X86_64. 2) I knew it would be something like that, but now I'm getting another error: http://pastebin.com/QbAZBKy9 – alexandernst Apr 12 '13 at 18:37
  • @alexandernst: Sorry, my mistake. I forgot that in a case like this, those two rules must be *double-colon rules. I'll edit my answer, and think about the best way to handle the error condition... – Beta Apr 12 '13 at 18:48
  • Thank you for the clarification :) But I have bad news, now I'm getting yet another error: http://pastebin.com/8FDYk83q – alexandernst Apr 12 '13 at 18:51
  • No problem, don't worry, take you time :) – alexandernst Apr 12 '13 at 18:53
  • Did you find a way how to fix it? – alexandernst Apr 13 '13 at 16:31
  • @alexandernst: My last edit fixed it. I haven't done the error part yet. – Beta Apr 14 '13 at 23:56