Search code examples
makefileavravr-gcc

How to tell Make to search for header files


If I wanted to compile a simple AVR C program, I would simply go:

avr-gcc -mmcu=atmega2560 -Os avr.c -o avr.o

And the code would compile with no errors. However, as soon as I put the above command in a Makefile, I get a compilation error saying that DDRC, PC2, and PORTC are undeclared (see the error chunk at the bottom). Since these constants are defined in avr/io.h I tried to put an -I parameter to point to the header files, but had no success. Here is my Makefile:

CC=avr-gcc
INCLUDE=-I/usr/avr/include
CCFLAGS+=$(INCLUDE)

build: avr.o
  $(CC) -mmcu=atmega2560 -Os avr.c -o avr.o

I found a similar problem here, but I was not lucky applying the solution. I believe the problem is in avr-gcc not being able to find the required libraries, and I thought -I directive would sort that out. But it seems that I am doing some silly mistake I cannot find. What would be the right way of telling a Makefile where header files are?

The error message that happens only when invoked make build:

avr.c: In function 'main':
avr.c:17:5: error: 'DDRC' undeclared (first use in this function)
     DDRC |= 1<<PC2;  /* PC2 will now be the output pin */
     ^~~~
avr.c:17:5: note: each undeclared identifier is reported only once for each function it appears in
avr.c:17:16: error: 'PC2' undeclared (first use in this function)
     DDRC |= 1<<PC2;  /* PC2 will now be the output pin */
                ^~~
avr.c:19:9: error: 'PORTC' undeclared (first use in this function)
         PORTC &= ~(1<<PC2);/* PC2 LOW */
         ^~~~~
make: *** [<builtin>: avr.o] Error 1
*** Failure: Exit code 2 ***

avr.c I am trying to compile using make:

#define F_CPU 100000000
#include <avr/io.h>
#include <util/delay.h>

void sleep(uint8_t millisec)
{
    while (millisec) {
        _delay_ms(1);/* 1 ms delay */
        millisec--;
    }
}

int main()
{
    DDRC |= 1<<PC2;  /* PC2 will now be the output pin */
    while (1) {
        PORTC &= ~(1<<PC2);/* PC2 LOW */
        sleep(100);/* 100 ms delay */
        PORTC |=(1<<PC2); /* PC2 HIGH */
        sleep(100);/* 100 ms delay */
    }
    return 0;
} 

Solution

  • This line in your makefile is the cause of your problems:

    build: avr.o
    

    What does this mean? It tells make that in order to create a target named build, it should look for a target named avr.o, either already existing or, if it doesn't exist, then make should build it (if it can figure out how to do so).

    So before make even tries to run the recipe for this rule, it will FIRST try to create a file avr.o.

    Well, you haven't told it how to create such a file, but luckily (or unluckily for you) make has a set of built-in rules that it can use to create some types of files that it knows about. One of those built-in rules says that if make wants to build a file *.o (for some string matching *) and it can find a file *.c (where * is the same as in *.o), then it can create the *.o with this recipe:

    $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
    

    How does this expand? Well, you've set CC to avr-cc so it will use that compiler. You haven't set CFLAGS, CXXFLAGS, or TARGET_ARCH so it will use the default value for those variables, which is nothing. So, it will run this compile line:

    avr-cc -c -o avr.o avr.c
    

    If you look up at the top of your list of error messages, make will print the compile line it runs and you'll see it's NOT the compile line you wanted it to run.

    So, you have lots of ways to fix this problem.

    One option is to keep your variables and fix your rule:

    CC=avr-gcc
    INCLUDE=-I/usr/avr/include
    CCFLAGS+=$(INCLUDE)
    
    build: avr.c
            $(CC) $(CCFLAGS) -mmcu=atmega2560 -Os avr.c -o avr.o
    

    Here, we list the build prerequisite as avr.c not avr.o, because that's what your recipe expects (it builds a .o from a .c, so the .c is the prerequisite). However, this is really a very bad makefile; it's a "useless use of make" because every time you type make it will rebuild the avr.o file.

    A better way is to fix your makefile to use the standard predefined variable settings instead of your own variables, and let make build the object file with its built-in rules:

    CC = avr-gcc
    CPPFLAGS = -I/usr/avr/include
    CFLAGS = -mmcu=atmega2560 -Os
    
    build: avr.o
    

    That's all you have to do (you say "build a simple AVR C program" but your makefile only compiles an object file, it doesn't invoke the linker, so I don't know exactly what you want to do).

    If you really want to keep your own rules and not use the built-in rules, then at least write it correctly so that the files built by the recipe are the targets in the makefile rule:

    CC=avr-gcc
    INCLUDE=-I/usr/avr/include
    CCFLAGS+=$(INCLUDE)
    
    build: avr.o
    
    avr.o: avr.c
            $(CC) $(CCFLAGS) -mmcu=atmega2560 -Os avr.c -o avr.o