OSDev.org https://forum.osdev.org/ |
|
Writing a makefile for an OS https://forum.osdev.org/viewtopic.php?f=1&t=44339 |
Page 1 of 1 |
Author: | rpio [ Sun Jun 13, 2021 8:52 am ] |
Post subject: | Writing a makefile for an OS |
After struggling to easily modify my OS, I decided to rewrite all of the build system(makefiles). I read through a couple of makefile tutorials and I have no idea how to write a normal makefile for an OS(I use cmake for my other projects and also they are easier to build) or any other large project, actually I couldn't even understand how to write a makefile for any project that consists of more then one directory. What I came up with is very broken - either I have a root makefile which does "make -C dir" to start other makefiles, but in this case the variables from root makefile are not passed to other makefiles and I need to copy then over and over and over again even though they are 100% the same; the second thing I thought of is to include all of the make files from other directories, but then all of them would have project root as their working directory and so I would need to add /dir/dir before every file and every directory in every makefile. So how should I write makefiles for multi-directory projects? Any resources on how to use makefiles(except the offical doc which is 100% useless unless you need to find a specific feature or understand how some specific feature works) for larger projects? How do you do it in your OS? |
Author: | bzt [ Sun Jun 13, 2021 9:10 am ] |
Post subject: | Re: Writing a makefile for an OS |
ngx wrote: actually I couldn't even understand how to write a makefile for any project that consists of more then one directory. The same way.Code: (output): (input files) That simple. Input files can be in another directory as well, and output can be too.(commands to be executed to create output from input files) If you have submodules, eg. the directory has it's own Makefile, then do: Code: dir/submodule.a: This will enter the "dir" directory and call "make all" there. You should set the output to a file that whis submodule produces, so that if that file exists, the submodule's make will be skipped when you run "make all" in the project's directory.make -C dir all Here's a simple example in USBImager. Here for example "bzip2" is in a subdirectory and has it's own Makefile. So I have Code: bzip2/libbz2.a: When I run "make all" in the project's directory, and ./bzip2/libbz2.a doesn't exists, this will run Makefile in the "bzip2" directory too.@make -C bzip2 libbz2.a As for the variables, you could include a Makefile.env from both your project's Makefile and from the submodule's Makefile. For example: Makefile.env: Code: CFLAGS = -g Makefile: Code: include Makefile.env dir/out: make -C dir all dir/Makefile: Code: include ../Makefile.env ...whatever Cheers, bzt |
Author: | rpio [ Sun Jun 13, 2021 9:42 am ] |
Post subject: | Re: Writing a makefile for an OS |
bzt wrote: Code: (output): (input files) That simple. Input files can be in another directory as well, and output can be too.(commands to be executed to create output from input files) I know, I had that at the start, but that makes a giant monolothic makefile managing all of the dirs which is very large and not really extendable. bzt wrote: If you have submodules, eg. the directory has it's own Makefile, then do: Code: dir/submodule.a: This will enter the "dir" directory and call "make all" there. You should set the output to a file that whis submodule produces, so that if that file exists, the submodule's make will be skipped when you run "make all" in the project's directory.make -C dir all If it is normal calling of make file with make -C then it is my second idea, but the problem is - how do I pass variables to the submakefiles bzt wrote: As for the variables, you could include a Makefile.env from both your project's Makefile and from the submodule's Makefile. For example: Makefile.env: Code: CFLAGS = -g Makefile: Code: include Makefile.env dir/out: make -C dir all dir/Makefile: Code: include ../Makefile.env I also have thought of this, but if I will have deeper subdirectories then it will look like ../../../../… and if i change something it would not be obvious how many ../.. to remove |
Author: | neon [ Sun Jun 13, 2021 2:15 pm ] |
Post subject: | Re: Writing a makefile for an OS |
Hi, Quote: I also have thought of this, but if I will have deeper subdirectories then it will look like ../../../../… and if i change something it would not be obvious how many ../.. to remove What we do is use a system environment variable for this. E.g. from a component makefile:Code: TARGETNAME = nasm We don't use recursive make so won't comment on it.
TARGETTYPE = PROGRAM TARGETPATH = $(SDK)/bin INCLUDES = $(STDINC) SOURCES = main.c\ table.c\ ... !INCLUDE $(NBE_ENVDIR)/makefile.def |
Author: | rpio [ Sun Jun 13, 2021 2:28 pm ] |
Post subject: | Re: Writing a makefile for an OS |
neon wrote: Hi, Quote: I also have thought of this, but if I will have deeper subdirectories then it will look like ../../../../… and if i change something it would not be obvious how many ../.. to remove What we do is use a system environment variable for this. E.g. from a component makefile:Code: TARGETNAME = nasm We don't use recursive make so won't comment on it.TARGETTYPE = PROGRAM TARGETPATH = $(SDK)/bin INCLUDES = $(STDINC) SOURCES = main.c\ table.c\ ... !INCLUDE $(NBE_ENVDIR)/makefile.def Thanks, can you please exapand, I didn't really understand the solution, should we have a variable with a directory and append it before paths? |
Author: | neon [ Sun Jun 13, 2021 2:43 pm ] |
Post subject: | Re: Writing a makefile for an OS |
Hi, The basic idea is just to set it in an environment variable i.e. [Windows]. This would allow the path to be anywhere on the system and you can just use that variable in your makefiles to include and access environment makefiles i.e. !INCLUDE $(NBE_ENVDIR)/makefile.def |
Author: | rpio [ Sun Jun 13, 2021 2:46 pm ] |
Post subject: | Re: Writing a makefile for an OS |
neon wrote: Hi, The basic idea is just to set it in an environment variable i.e. [Windows]. This would allow the path to be anywhere on the system and you can just use that variable in your makefiles to include and access environment makefiles i.e. !INCLUDE $(NBE_ENVDIR)/makefile.def Oh, do you mean like set a variable in the shell environment (e.g. zsh/bash/cmd) and add it(e.g. $(env_var)/path) before every pathin the makefile? |
Author: | neon [ Sun Jun 13, 2021 2:54 pm ] |
Post subject: | Re: Writing a makefile for an OS |
Hi, If it makes sense, sure. I use a master makefile so only do it once per component makefile. I was just addressing a way to include a location without needing all those "../../" etc. |
Author: | rpio [ Sun Jun 13, 2021 3:07 pm ] |
Post subject: | Re: Writing a makefile for an OS |
neon wrote: Hi, If it makes sense, sure. I use a master makefile so only do it once per component makefile. I was just addressing a way to include a location without needing all those "../../" etc. Okay thanks, but you said that you are doing it only once per makefile, how? |
Author: | neon [ Sun Jun 13, 2021 8:38 pm ] |
Post subject: | Re: Writing a makefile for an OS |
Hi, We have a master set of makefiles ("environment") and per-project makefiles ("component"). What was posted above was an example of a "component" makefile. Each "component" makefile includes the master makefile which defines all the targets and inference rules. The following is a snippet from the master include file, $(NBE_ENVDIR)/makefile.def: Code: !if "$(TARGETTYPE)" == "LIBRARY" Per project component makefiles just need to include this file which defines all of the targets so we can call make on them.$(TARGET) : $(OBJECTS) $(LIB) $(LIBRARIAN_FLAGS) $(OBJECTS) !else $(TARGET) : $(OBJECTS) $(TARGETLIBS) $(LINK) $(LINKER_FLAGS) $(OBJECTS) $(TARGETLIBS) -out:$(TARGET) !endif clean : del "$(O:/=\)\*.obj" del "$(TARGET:/=\)" The last line of all component makefiles include this file once: they set up what is needed and let the environment do the rest. |
Author: | Korona [ Sun Jun 13, 2021 10:04 pm ] |
Post subject: | Re: Writing a makefile for an OS |
If you know CMake, why don't you just use CMake? |
Author: | bzt [ Tue Jun 15, 2021 12:51 pm ] |
Post subject: | Re: Writing a makefile for an OS |
ngx wrote: I also have thought of this, but if I will have deeper subdirectories then it will look like ../../../../… And what's the matter with that? You only have to write them once. Not using absolute paths has the great benefit that anybody can clone your repo anywhere on their disks and it will Just-Work (TM) without Anything-Crazy-Cmakeish-or-Sconsish-or-Automakeish-Ninjaish magic requiring many additional dependencies with incompatible versions in your toolchain. It just works as it should. Relative paths are good. Relative paths are useful.ngx wrote: and if i change something it would not be obvious how many ../.. to remove Yes, it is pretty obvious, you should be knowing your own directory structure, so should be no probs there. And how often do you reorganize your entire project structure? Chances are good that once you've settled with the structure you'll never ever change it again, max. you'll add some new directories later when you implement a new driver for example, in which case - I bet - you'll just copy an existing Makefile and update the target in it. No need to touch the paths and the include.Cheers, bzt |
Author: | thewrongchristian [ Wed Jun 16, 2021 1:49 pm ] |
Post subject: | Re: Writing a makefile for an OS |
ngx wrote: After struggling to easily modify my OS, I decided to rewrite all of the build system(makefiles). I read through a couple of makefile tutorials and I have no idea how to write a normal makefile for an OS(I use cmake for my other projects and also they are easier to build) or any other large project, actually I couldn't even understand how to write a makefile for any project that consists of more then one directory. What I came up with is very broken - either I have a root makefile which does "make -C dir" to start other makefiles, but in this case the variables from root makefile are not passed to other makefiles and I need to copy then over and over and over again even though they are 100% the same; the second thing I thought of is to include all of the make files from other directories, but then all of them would have project root as their working directory and so I would need to add /dir/dir before every file and every directory in every makefile. So how should I write makefiles for multi-directory projects? Any resources on how to use makefiles(except the offical doc which is 100% useless unless you need to find a specific feature or understand how some specific feature works) for larger projects? How do you do it in your OS? I wrestled with this for a while as well. At my previous job, we used imake to generate Makefiles using the C pre-processor macros, and to be fair, it was quite useful, as our product was cross platform (Linux, Solaris, HP-UX, AIX, Windows), and we could abstract a lot of platform specifics behind CPP macros, but it was a horror to understand the subtleties, especially as you had Makefile subtleties obfuscated with the CPP macros. It also didn't help that we used the platform make, rather than a single cross platform make (such as gmake.) I pondered cmake and even ant, but for my kernel project, I decided to use a single top level Makefile, and include per-subdirectory fragments. I did this after reading papers like "recursive make considered harmful" I define a TOP directory so I can address files relative to the project root, without relying on '..', snapshot here Basically, the structure is (Makefile): Code: all:: TOP=$(CURDIR) ARCH=i386 OBJS=$(SRCS_S:.S=.o) $(SRCS_C:.c=.o) SRCS_C := SRCS_S := SYS_H := \ include/sys/times.h \ include/sys/stat.h \ include/sys/time.h \ include/sys/types.h \ include/sys/errno.h \ include/sys/unistd.h subdir := build include $(subdir)/subdir.mk subdir := libk include $(subdir)/subdir.mk subdir := kernel include $(subdir)/subdir.mk subdir := posix include $(subdir)/subdir.mk subdir := fs include $(subdir)/subdir.mk subdir := drivers include $(subdir)/subdir.mk subdir := build include $(subdir)/tools.mk subdir := arch/$(ARCH) include $(subdir)/subdir.mk subdir := user include $(subdir)/subdir.mk subdir := initrd include $(subdir)/subdir.mk The each of the subdirectories listed has a subdir.mk fragment that defines what's going on in that directory (here's kernel/subdir.mk in full): Code: SRCS_KERNEL_C := $(subdir)/main.c $(subdir)/core.c $(subdir)/pci.c $(subdir)/printk.c $(subdir)/panic.c $(subdir)/thread.c $(subdir)/sync.c $(subdir)/check.c $(subdir)/vm.c $(subdir)/vfs.c $(subdir)/dev.c $(subdir)/timer.c $(subdir)/input.c $(subdir)/intr.c $(subdir)/device.c SRCS_C += $(SRCS_KERNEL_C) subdir.mk can include other subdir.mk fragments (fs/subdir.mk): Code: fsdir:=$(subdir) subdir:=$(fsdir)/tarfs include $(subdir)/subdir.mk subdir:=$(fsdir)/devfs include $(subdir)/subdir.mk subdir:=$(fsdir)/fatfs include $(subdir)/subdir.mk subdir:=$(fsdir)/procfs include $(subdir)/subdir.mk At the end of all the includes, SRCS_C and SRCS_S variables contain all the source files that need to be compiled/assembled, and the usual make rules kick in to do the compiling. The result is only a single set of rules have to be included, the only relative paths are subdirectory paths (no '..' parent directory walking), there are no recursive make invocations complicating passing information, and make is blindingly fast and totally capable of parallel builds (-j option). And I can easily re-arrange the directory structure, as a sub-directory subdir.mk will have the subdir variable pointing to the containing sub-directory, and TOP pointing to the root source directory. I'm not sure how portable the above is, it may need tweaking on different make type, but I just use gnu make (why write a portable Makefile when you can use a portable make). |
Page 1 of 1 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |