Thursday, March 5th, 2020 c code make makefile techniques • 267w
It is known that I don't like writing header files and forward declarations. The keyword is writing, I like what you can do with header files, however I would also like to have an option, some times, to just autogen the headers. Also it could be nice to have a header file even in a single file program just to avoid forward declarations.

Lets thing about it... ok you need a C parser because C can be crazy some times. You can have macros and also you can have all kinds of whitespace in a function declaration. So you either write a program witch handles everything or you write a one-line bash thingy to handle only your style of code.

Lets do the second one (obviously). I write functions like this:

int send(cost char* text) {

So we grep all the lines which start with a character and end with a open bracket and then we replace the bracket with a semicolon:

grep '^\w.* {$' | sed 's/ {/;/'

Ok, lets put it in the Makefile:

%.h: %.c
    cat $< | grep '^\w.* {$$' | sed 's/ {/;/' > $@

What about the structs? Done, already handled.
What about typedefs? Nope... next post?

And a bonus effect is that if you include the header of the source file in its self you no longer need forward declarations.

Monday, February 24th, 2020 build c code make makefile • 284w
And here we are:

} else {
    // TODO print the --help text

On ho... where to start... Lets start by making a simple plain text file and write all the things there. Now we have a help.txt.

The easiest solution would be to just read the help.txt file and print it at runtime, but then we need to package the extra help.txt file and also is "slow".

Could we somehow just pasted in? Yes but then we have to add quotes and handle special characters and half of our main.c file will be strings.

xxd. Oh! What is that? If you run:

xxd -i help.txt

We get this very readable c-string

unsigned char help[] = {
  0x63, 0x61, 0x74, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x75, 0x6e,
  0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
  0x3a, 0x20, 0x25, 0x63, 0x0a, 0x0a, 0x55, 0x73, 0x61, 0x67, 0x65, 0x3a,

Seems good. Does it matter that is so readable, not really its just a build intermediate file. Lets add it to the Makefile with all the other build intermediate files:

    xxd -i help > help.c

Now, how about a include in the middle of nowhere:

} else {
    #include "help.c"

EDIT: xxd does not add a null character at the end...

Thursday, February 20th, 2020 build c code make makefile • 438w
When I write some small C program I usually put everything in a single main.c file and to compile I just:

gcc -O3 main.c -o program_name_here

I will use the catimage program which reads an image and prints it in the terminal.

Now lets be civil and put the compile command into a Makefile:

    gcc -O3 -lm main.c -o catimage

I also like to run a recompile on file change loop:

echo main.c | entr make

Now, a dependency. To read the images I use the stb_image, which is an awesome lib so they only I need is a single "header" file stb_image.h and two # lines to include it.

Simpler solution: Download a copy of the stb_image.h and handle it as the rest of the source code. This is a very good solution, however lets have some fun.

To compile we need the file, so lets just download it:


catimage: stb_image.h
    gcc -O3 -lm main.c -o catimage

The stb_image.h takes some seconds to compile... the loop is too slow... I will not touch the library so lets wrap it in a image.c file and compile it once:

image.o: stb_image.h
    gcc -O3 -c image.c


Where the image.c:

// image.c

#include "stb_image.h"

unsigned char* read_image(FILE* file, ...) {
    return call_to_the_lib(...) 

Now we need a header image.h to use it in the main.c. No we don't, its just one function:

// main.c

// #include "image.c"
unsigned char* read_image(FILE*, ...);

Put all together:

catimage: image.o
    gcc -O3 -lm image.o main.c -o catimage

First build: download stb_image.h (slow), compile image.c (slow), compile and link main.c (instant)
Rest of the builds: compile and link main.c (instant)