Gentoo Forums
Gentoo Forums
Gentoo Forums
Quick Search: in
improving Makefile object handling
View unanswered posts
View posts from last 24 hours

 
Reply to topic    Gentoo Forums Forum Index Portage & Programming
View previous topic :: View next topic  
Author Message
pjp
Administrator
Administrator


Joined: 16 Apr 2002
Posts: 20407

PostPosted: Mon Sep 23, 2024 6:22 am    Post subject: improving Makefile object handling Reply with quote

First the general question with some details, then the specific question and the rest of the details.

What is a better way to compile with all of the object files given the current structure and Makefile's handling of foobarbaz and fib objects?

The releveant Makefile snippet (whole file at the end):
Code:
# EXE is the target 'main'.
# FBB_OBJ and FIB_OBJ generate their respective object files.
${EXE}: ${FBB_OBJ} ${FIB_OBJ} ${SRC}
        ${CC} ${CFLAGS} ${INC} -o ${EXE} ${SRC} ${FBB_OBJ} ${FIB_OBJ}
$(FBB_OBJ): obj/%.o: foobarbaz/%.c
        ${CC} ${CFLAGS} ${INC} -c $^ -o $@
$(FIB_OBJ): obj/%.o: fib/%.c
        ${CC} ${CFLAGS} ${INC} -c $^ -o $@

The specific question is in handling target EXE compilation when adding more *_OBJ dependencies. Yes I can add A_OBJ, ... ZZ_OBJ, but at some point that seems like the wrong solution.

The real project is for learning, so I don't know how many I'll have. The purpose of breaking them out into their own directories is for code organization and readability.

This currently works. Here is the output for clarity:
Code:
$ ./main
[foobarbaz]
Fibonacci:
  1  2  3  5  8 13 21 34 55 89


Given this layout:
Code:
.
├── fib                 Next number in fibonacci series, from 1.
│   └── fib.c           Static variables retain previous results.
├── foobarbaz           Combines multiple strings, returns a struct string.
│   ├── bar.c           Returns "bar".
│   ├── bar.h           Declares function bar() for foobarbaz().
│   ├── baz.c           Returns "baz".
│   ├── baz.h           Declares function baz() for foobarbaz().
│   └── foobarbaz.c     Combines foo, bar, baz, into a struct string.
├── inc                 "Public" api.
│   ├── fib.h           Declares function next_in_fib() for main().
│   └── foobarbaz.h     Declares function foobarbaz() for main().
├── main                Executable
├── Makefile
├── obj
│   ├── bar.o
│   ├── baz.o
│   ├── fib.o
│   └── foobarbaz.o
└── src
    └── main.c          Calls foobarbaz() and loops next_in_fib().
The makefile:
Code:
CC := /usr/lib/llvm/18/bin/clang
C_STANDARD := c11
CFLAGS := -Wall -Wextra -std=$(C_STANDARD) -pedantic
INC_PATHS := ./inc
INC := $(addprefix -I,${INC_PATHS})

EXE := main
SRC := src/${EXE}.c
FBB_SRC := $(wildcard foobarbaz/*.c)
FBB_OBJ := $(patsubst foobarbaz/%.c, obj/%.o, $(FBB_SRC))
FIB_SRC := $(wildcard fib/*.c)
FIB_OBJ := $(patsubst fib/%.c, obj/%.o, $(FIB_SRC))

${EXE}: ${FBB_OBJ} ${FIB_OBJ} ${SRC}
        ${CC} ${CFLAGS} ${INC} -o ${EXE} ${SRC} ${FBB_OBJ} ${FIB_OBJ}
$(FBB_OBJ): obj/%.o: foobarbaz/%.c
        ${CC} ${CFLAGS} ${INC} -c $^ -o $@
$(FIB_OBJ): obj/%.o: fib/%.c
        ${CC} ${CFLAGS} ${INC} -c $^ -o $@

.PHONY: clean showvar

clean:
ifneq (,$(wildcard obj/*.o ${EXE}))
        rm $(wildcard obj/*.o ${EXE})
endif


I've spent about a week to get this far, pretty much from close to zero knowledge of how to use a Makefile. Around half of that time was getting the Makefile to work with something in it's own directory separate from src/, so I'm a bit slow on thoughts about it right now.

My first thought is to simply find a way to combine all of the *_OBJ variables into one used for the compilation. Something like:
Code:
ALL_OBJ := ${A_OBJ} ... ${ZZ_OBJ}
But before I try that, I'm wondering if I'm going down a wrong path and should be doing something very different (Makefile, directory structure, something else).

Hopefully that makes sense and I didn't omit something important.

Thanks!
_________________
Quis separabit? Quo animo?
Back to top
View user's profile Send private message
eschwartz
Developer
Developer


Joined: 29 Oct 2023
Posts: 163

PostPosted: Mon Sep 23, 2024 2:05 pm    Post subject: Reply with quote

I would suggest using VPATH builds as described at:

https://make.mad-scientist.net/papers/multi-architecture-builds/

(and paying heed to https://make.mad-scientist.net/papers/how-not-to-use-vpath/ as well)
Back to top
View user's profile Send private message
Zucca
Moderator
Moderator


Joined: 14 Jun 2007
Posts: 3634
Location: Rasi, Finland

PostPosted: Mon Sep 23, 2024 8:10 pm    Post subject: Reply with quote

Very informative site. Thanks!
_________________
..: Zucca :..
Gentoo IRC channels reside on Libera.Chat.
--
Quote:
I am NaN! I am a man!
Back to top
View user's profile Send private message
eschwartz
Developer
Developer


Joined: 29 Oct 2023
Posts: 163

PostPosted: Mon Sep 23, 2024 8:13 pm    Post subject: Reply with quote

Yeah, it's by the author of GNU Make so as you can imagine it knows all the tricks. :D
Back to top
View user's profile Send private message
pjp
Administrator
Administrator


Joined: 16 Apr 2002
Posts: 20407

PostPosted: Tue Sep 24, 2024 5:29 am    Post subject: Reply with quote

Thanks. I'm skimming it now and will read it more closely tomorrow.
_________________
Quis separabit? Quo animo?
Back to top
View user's profile Send private message
Zucca
Moderator
Moderator


Joined: 14 Jun 2007
Posts: 3634
Location: Rasi, Finland

PostPosted: Tue Sep 24, 2024 6:45 am    Post subject: Reply with quote

eschwartz wrote:
Yeah, it's by the author of GNU Make
Rules of Makefiles - Rule #1 wrote:
Use GNU make.

Don’t hassle with writing portable makefiles, use a portable make instead!
... explains...

I tend to plan my little projects so that users don't need to install too much dependencies. That being said... I haven't tested or ran any other makes. So the question is: Is it fair to assume all Linuxes use gmake as /usr/bin/make? And how about BSDs?
_________________
..: Zucca :..
Gentoo IRC channels reside on Libera.Chat.
--
Quote:
I am NaN! I am a man!
Back to top
View user's profile Send private message
pjp
Administrator
Administrator


Joined: 16 Apr 2002
Posts: 20407

PostPosted: Tue Sep 24, 2024 3:44 pm    Post subject: Reply with quote

I chuckled at that one because it seems like MS saying "use MS".


Regarding vpath, that's a lot to absorb. It seems I may be doing some things "I shouldn't be doing" in the Makefile. And to add, I don't want to build object files in the same directory as the source files. I'm not sure why that's the recommended approach. When I compile on the command line, there are zero issues, so maybe it's a problem with make.
_________________
Quis separabit? Quo animo?
Back to top
View user's profile Send private message
Hu
Administrator
Administrator


Joined: 06 Mar 2007
Posts: 22440

PostPosted: Tue Sep 24, 2024 4:19 pm    Post subject: Reply with quote

In my opinion, the Makefile is easier to write, and the consequences easier to understand, if you use matched directory structure. As I read the Makefile earlier in this thread, all objects end up in obj/, regardless of their source directory. I suggest instead having:
Sources:
  • src/a.c
  • fib/b.c
  • foobarbaz/c.c
Outputs:
  • obj/src/a.o
  • obj/fib/b.o
  • obj/foobarbaz/c.o
This has two advantages. First, if you had src/a.c and fib/a.c, then the resulting a.o will not overwrite each other. As I read the existing Makefile, they would collide. Second, it becomes easier to wipe out all object files corresponding to a given source directory: rm -r obj/fib/.

As regards BSDs, my understanding is that /usr/bin/make is not necessarily GNU make on BSD systems, but that if /usr/bin/gmake exists, then that will be GNU make. I could be wrong. I have not looked in a long time.
Back to top
View user's profile Send private message
dmpogo
Advocate
Advocate


Joined: 02 Sep 2004
Posts: 3388
Location: Canada

PostPosted: Tue Sep 24, 2024 4:23 pm    Post subject: Reply with quote

Do you need EXE to be dependent on SRC ? I would write EXE line to just assembling objects and necessary libraries into executable. Though you will have to make src/main.o somewhere else
Back to top
View user's profile Send private message
eschwartz
Developer
Developer


Joined: 29 Oct 2023
Posts: 163

PostPosted: Tue Sep 24, 2024 4:35 pm    Post subject: Reply with quote

Zucca wrote:
eschwartz wrote:
Yeah, it's by the author of GNU Make
Rules of Makefiles - Rule #1 wrote:
Use GNU make.

Don’t hassle with writing portable makefiles, use a portable make instead!
... explains...

I tend to plan my little projects so that users don't need to install too much dependencies. That being said... I haven't tested or ran any other makes. So the question is: Is it fair to assume all Linuxes use gmake as /usr/bin/make? And how about BSDs?


POSIX make describes very little, although Issue 8 is adding some more badly-needed functionality that is commonly implemented by Make editions.

Linux in general always tends to use GNU Make as /usr/bin/make.

BSD uses "BSD make". You can install it as dev-build/bmake. It is regularly imported from NetBSD's "make" and the version of bmake reflects the date of the import from NetBSD. Then bmake is also exported into FreeBSD. I didn't look to see what OpenBSD does since no one should pay attention to OpenBSD...

GNU Make and bmake have wildly different featuresets outside of the common POSIX make stuff (including Issue 8 improvements). For example, $(shell ...) is specific to GNU, and bmake has != which GNU Make added support for as part of standardizing it for POSIX. GNU Make has ifeq/ifdef, bmake does not -- and instead has .if / .ifdef / .ifmake and a specialized operator grammar for that context. GNU make generalizes $(function ...) and provides many such functions such as patsubst, filter, strip.... bmake supports $(variable:M<pattern>) or $(variable:O) and other "modifiers".

If you want to write portable Makefiles, give up. If you want to write non-portable Makefiles, then either you force BSD users to install gmake (trivial via the package manager) or you force linux users to install bmake (trivial via the package manager).

Since your goal was that users don't need to install too much dependencies, consider this: the entire point of GNU autoconf is that users don't need to install ANY dependencies, since autoconf is a developer tool run by people named "Zucca" for creating portable configure scripts that generate portable Makefiles.

:)
Back to top
View user's profile Send private message
eschwartz
Developer
Developer


Joined: 29 Oct 2023
Posts: 163

PostPosted: Tue Sep 24, 2024 4:40 pm    Post subject: Reply with quote

pjp wrote:
I chuckled at that one because it seems like MS saying "use MS".


Regarding vpath, that's a lot to absorb. It seems I may be doing some things "I shouldn't be doing" in the Makefile. And to add, I don't want to build object files in the same directory as the source files. I'm not sure why that's the recommended approach. When I compile on the command line, there are zero issues, so maybe it's a problem with make.


Some might argue that one shouldn't be doing things in a Makefile at all, but rather do it in configure.ac and Makefile.am, but if your goal is to be doing things in a Makefile then I don't think what you're doing is intrinsically wrong overall.

It is *easy* to build object files in the same directory as the source files, but every configurable build system (autoconf + automake, meson, cmake, etc.) support building "out of source" for very good reason: it makes it easy to clean up or run parallel builds with different options if you produce object files in a different directory.

Implementing this in raw Make is fiddly. VPATH is a tool you can use for this. You can also just write out each object file and each source file as separate commands, but pattern rules (which you want to use, presumably) make this much more annoying to do reliably.
Back to top
View user's profile Send private message
pjp
Administrator
Administrator


Joined: 16 Apr 2002
Posts: 20407

PostPosted: Tue Sep 24, 2024 5:49 pm    Post subject: Reply with quote

Hu wrote:
As I read the existing Makefile, they would collide. Second, it becomes easier to wipe out all object files corresponding to a given source directory: rm -r obj/fib/.
Yes, they could collide. I only used this approach for simplicity while trying to get compiling to work. I originally considered using fib/obj/, but didn't like that. It didn't occur to me to use sub directories in obj. That was a simple change and now works (I haven't fixed "clean" yet).


dmpogo wrote:
Do you need EXE to be dependent on SRC ? I would write EXE line to just assembling objects and necessary libraries into executable. Though you will have to make src/main.o somewhere else
I honestly don't know :).

There is no main.o, only the executable main which is put into the project directory (mainly because I got tired of using ./bin/main). I think what depending on SRC achieves is recompiling when a .c file changes. And as I have it written, I don't know that it is sufficient. I think if I change a .h file, make doesn't recompile. At any rate, I believe that is why I put it there.
_________________
Quis separabit? Quo animo?
Back to top
View user's profile Send private message
pjp
Administrator
Administrator


Joined: 16 Apr 2002
Posts: 20407

PostPosted: Tue Sep 24, 2024 5:49 pm    Post subject: Reply with quote

eschwartz wrote:
Some might argue that one shouldn't be doing things in a Makefile at all, but rather do it in configure.ac and Makefile.am, but if your goal is to be doing things in a Makefile then I don't think what you're doing is intrinsically wrong overall.
Something in one of the vpath links made me think that. I'd have to go back and find it.

I'm not attached to using a Makefile, it was the first solution I knew about, and it seemed like understanding at least at a basic level was beneficial. Maybe now is a good time to learn configure.ac. Is that part of Autotools? I only ask because of some "bad press" Autotools received after the xz problem.

eschwartz wrote:
It is *easy* to build object files in the same directory as the source files, but every configurable build system (autoconf + automake, meson, cmake, etc.) support building "out of source" for very good reason: it makes it easy to clean up or run parallel builds with different options if you produce object files in a different directory.
I may have misunderstood the point being made in the vpath information. It's a bit confusing so far.

eschwartz wrote:
Implementing this in raw Make is fiddly. VPATH is a tool you can use for this. You can also just write out each object file and each source file as separate commands, but pattern rules (which you want to use, presumably) make this much more annoying to do reliably.
I'm not attached to using pattern rules, it just seemed better than having to hard code file names and paths and subsequently having to update them. Maybe that's where configure.ac helps? I'll look into it (and continue reading about vpath).

This works too :) (yes, I get that my small project makes it not impractical).
Code:
$ time ./build.sh

real    0m0.651s
user    0m0.094s
sys     0m0.045s

$ cat build.sh
#!/bin/sh

gcc -c fib/fib.c -o obj/fib/fib.o
gcc -c foobarbaz/bar.c -o obj/foobarbaz/bar.o
gcc -c foobarbaz/baz.c -o obj/foobarbaz/baz.o
gcc -c foobarbaz/foobarbaz.c -o obj/foobarbaz/foobarbaz.o
gcc src/main.c -o main obj/fib/fib.o obj/foobarbaz/*.o -I./inc

_________________
Quis separabit? Quo animo?
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    Gentoo Forums Forum Index Portage & Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum