0

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.

Ali You
  • 13
  • 3
  • This appears to be the old method of generating dependency files, using a sed command that is effective but cryptic -- like many sed commands. Newer versions of gcc offer a much cleaner approach. Do you still want the explanation, or just the new approach? – Beta May 21 '19 at 15:46
  • (...On second look, it's more up-to-date than I thought, but poorly implemented.) – Beta May 21 '19 at 16:28
  • HI Beta , to be honest I am not sure which version is better so if you think the newer version is a better option, then yes please do let me know how I should Implement it. At the end of the day I want my code to compile and all my .o files to be generated and linked. The .elf file is what I actually need to load in to my hardware. Also if you have any source material that can help me understand these better please do link them. Appreciate it a lot ! – Ali You May 21 '19 at 21:04

1 Answers1

1

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:

  • $@, which represents the name of the target of the rule
  • $(@D), which represents the directory of the target name (i.e. the target's dirname)
  • $<, which represents the first prerequisite

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Hi John thanks for the great explanation. That was much more complicated than I thought. Is there perhaps an easier alternative to generating these dependencies? is it not possible to write a single rule that can perform the following tasks? Get the source files `SRCFILES` compile them with `sparc-elf-gcc` using the required flags e.g. `-Wall` etc. link them to their respective libraries as it is creating the `.o` files? (instead of creating an temporary file with all these dependencies) finally produce the required executable and link-able (ELF) format? Thanks again! – Ali You May 22 '19 at 08:44
  • There is also 1 more thing that I don't understand. When I remove this `objdir` folder which contains all the `.o` and `.d` files from my project and run `make clean` before it actually runs the clean command `- rm -f lwip.elf`that removes the `.elf` file it will create a new `objdir` that only contains the `.d` files I am assuming this is the dependency files which are created for each and every source code I have. Then running `make` will result in compiling the `.c` codes and producing the `.o` files. Is this correct? why is it generating dependencies when I run `make clean` ? – Ali You May 22 '19 at 08:57
  • @AliYou, the `gcc` command used is a standard way to automatically generate dependency information. You do have the alternative of managing header dependencies manually, or, technically, not managing them at all. But I think what I would probably do is stick with generating the `.d` files, but drop all the mess of munging them after they are generated. In conjunction with that, I would change their prerequisite in the `make` rule from the corresponding `.c` file to the `.o` file. This is not exactly correct semantically, but pragmatically, it will work as needed. – John Bollinger May 22 '19 at 12:05