makefilesparc

What does sed 's expression do in a Makefile


I have Makefile from an older project which I have to modify for a new system. It has the following global definitions for its source and object files:

SRCFILES = $(wildcard *.c) $(wildcard tasks/*.c) 
SRCDIR = .
OBJDIR = objdir
OBJFILES = $(SRCFILES:%.c=${OBJDIR}/%.o)

The Makefile was a mess and I had to modify it heavily. I manged to compile the code and generate the .o files from the .c source code. The expected .elf file is also compiled correctly.

the problem is that I can do not understand what the code is doing in the following lines I searched so many websites but no luck. any ideas?

 sed 's,^\(.*\)\.o[ :]*,$(@D)/\1.o $(@D)/\1.d : ,g' < $@.$$$$ > $@; \
 rm -f $@.$$$$

The makefile is as follows

lwip.elf: build_echo $(OBJFILES)
 @set -e; echo Linking the object files...; \
 sparc-elf-gcc $(OBJFILES) -o $@;
 @echo Successfully linked the files
 @echo lwip.elf file ready to load...

build_echo: httpserver_raw/fsdata.c
 @echo Building the source files first...
 @echo The Modified or New source code to be build are :

clean:
 - rm -f lwip.elf 

$(OBJDIR)/%.o: $(SRCDIR)/%.c
 @set -e; echo '    $<'; \
 sparc-elf-gcc-4.4.2 -Wall -O2 -g -msoft-float -mcpu=v8 -c -o $@ $(CFLAGS) -I. -I lwip/include -I lwip/include/ipv4 -I lwip/leon3 $<

$(OBJDIR)/%.d: $(SRCDIR)/%.c
 @set -e; rm -f $@; mkdir -p $(@D); \
 sparc-elf-gcc -MM -I. -I lwip/include -I lwip/include/ipv4 -I lwip/leon3 $< > $@.$$$$; \
 sed 's,^\(.*\)\.o[ :]*,$(@D)/\1.o $(@D)/\1.d : ,g' < $@.$$$$ > $@; \
 rm -f $@.$$$$

-include $(patsubst %.c,$(OBJDIR)/%.d,$(SRCFILES))

Note: The sparc-elf-gcc is a special cross-compiler used for the sparcV8 architecture.


Solution

  • There's a lot going on in there:

    sed 's,^\(.*\)\.o[ :]*,$(@D)/\1.o $(@D)/\1.d : ,g' < $@.$$$$ > $@; \
     rm -f $@.$$$$
    

    Let's break it down.

    1. There's the sed command itself: sed 's,^\(.*\)\.o[ :]*,$(@D)/\1.o $(@D)/\1.d : ,g',
    2. whose input is redirected (<) from a file designated by @.$$$$, and
    3. whose output is redirected (>) to a file designated by $@, then
    4. there's a second command, rm -f $@.$$$$.

    Furthermore, that's in the context of a larger list of commands, which set it up with

    @set -e; rm -f $@; mkdir -p $(@D); \
     sparc-elf-gcc -MM -I. -I lwip/include -I lwip/include/ipv4 -I lwip/leon3 $< > $@.$$$$;
    

    In interpreting that, you have to first recognize several make implicit variables:

    Next, you need to recognize that the dollar sign has special meaning to make, so if you want a literal $ you need to double it. The $$$$ simply repeat this, so that what gets passed to the shell is just two dollar signs ($$). The shell, in turn, replaces that with its own process ID -- a relatively common (but nevertheless insecure) idiom for generating unique file names.

    So, the part of the command that you omitted from your callout ensures that the target's destination directory exists, then runs a (cross-?) gcc with flags that cause it to emit information about header dependencies. These are captured in a file, something like objdir/src.d.12345, then in the part you called out, that file is redirected into sed, with the output going to the final target, something like objdir/src.d, after which the intermediate file is removed.

    The sed command itself is performing a substitution (s) on all lines, replacing every occurrence (g) of the pattern ^\(.*\)\.o[ :]* with a replacement that I'll come back to in a moment. First the pattern. The ^ anchors the pattern to the beginning of a line. The \(\) are metacharacters delimiting a capturing group, and the .* inside matches any number of any characters. Following that must be a literal decimal point, \., an o, and zero or more occurrences or the space and colon characters ([ :]*). For example, that would match this, up to but not including the header name:

    objdir/tasks/t1.o : my_header.h
    

    The replacement uses the aforementioned $(@D) -- which is expanded by make, not the shell or sed -- and \1, which sed expands to the text matched by the first capturing group. Thus, that would transform the above example line to this:

    objdir/tasks/t1.o objdir/tasks/t1.d : my_header.h
    

    In short, then, all that to say that it munges the computed dependency list to make the dependency file that is the target of the rule list itself as depending on all the same files that the corresponding object file does.