aboutsummaryrefslogtreecommitdiff
path: root/_Makefile
blob: d22a79882beb54f640ce34b04f203104fceb02aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# This is the makefile goal ... TODO integrate the rust project into this...


# -----------------------------------------------------------------------------
# 
#                              M A K E F I L E
# 
# for building OOStuBS, the teaching OS for "Operating-System Construction"
# -----------------------------------------------------------------------------
#
# By entering 'make', the system's source codes get compiled and linked.
# 'make bootdisk' writes the system image to an USB stick that allows to boot
# the system on a real machine.
# 'make clean' cleans up the directory.
# The targets 'qemu', 'qemu-smp' and 'qemu-gdb' start the QEMU emulator in
# single-core mode, multi-core mode (2 CPUs), respectively in single-core mode
# waiting for a debugger to attach before starting the system.
# The targets 'gdb', 'ddd' and 'cgdb' each run one of these three debuggers
# that automatically connect to the GDB stub of an already started 'qemu-gdb'
# (see above).
#
# -----------------------------------------------------------------------------

# -----------------------------------------------------------------------------
# source-code files

STARTUP_SOURCE = ./startup.asm
CC_SOURCES = $(shell find . -name "*.cc")
C_SOURCES = $(shell find . -name "*.c")
ASM_SOURCES = $(shell find ./machine -name "*.asm")

# build settings
#
#    ASMOBJFORMAT: assembler output format (must match the format of the used
#                  C++ compiler to make linking possible)
#    OBJDIR:       directory for storing object files
#    DEPDIR:       directory for storing dependency files
#    DRIVE_HD:     HDD/USB device the system image gets written to
#                  with 'make bootdisk'
#    DELETE:       file-deletion command
#    ASM:          assembler command
#    CC/CXX:       C/C++ compiler command
#    CFLAGS:       C-compiler flags
#    CXXFLAGS:     C++-compiler flags
#    QEMU:         architecture-specific name of the QEMU command
#    MKRESCUE:     name of the GRUB mkrescue program

VERBOSE = @
ASMOBJFORMAT = elf64
OBJDIR = ./build
DEPDIR = ./dep
# ATTENTION: using the wrong device name here will destroy data on the specified
# device! Do not run with root privileges!
DRIVE_HD = /dev/sdz
DELETE = rm
ASM = nasm
QEMU ?= qemu-system-x86_64
CC ?= gcc
CXX ?= g++
CFLAGS := $(CFLAGS) -m64 -mno-red-zone -Wall -Wno-write-strings -fno-stack-protector -nostdlib -mno-sse -I. -g #-DDEBUG
CXXFLAGS := $(CFLAGS) -Wno-non-virtual-dtor -fno-threadsafe-statics -fno-use-cxa-atexit -fno-rtti -fno-exceptions -std=c++11

# enforce i386-pc grub variant also on EFI systems
ifneq ($(wildcard /usr/lib/grub/i386-pc/.),)
        MKRESCUE_OPTION ?= /usr/lib/grub/i386-pc
else ifneq ($(wildcard /usr/share/grub2/i386-pc/.),)
        MKRESCUE_OPTION ?= /usr/share/grub2/i386-pc
else
        MKRESCUE_OPTION ?=
endif

ifneq ($(shell which grub-mkrescue 2> /dev/null),)
MKRESCUE = grub-mkrescue $(MKRESCUE_OPTION)
endif
ifneq ($(shell which grub2-mkrescue 2> /dev/null),)
MKRESCUE = grub2-mkrescue $(MKRESCUE_OPTION)
endif

# -------------------------------------------------------------------------
# names of sub-directories with source-code files

VPATH = $(sort $(dir $(STARTUP_SOURCE) $(CC_SOURCES) $(C_SOURCES) $(ASM_SOURCES)))

# -------------------------------------------------------------------------
# lists of object files that are generated during compilation

FIRST_OBJECT = $(addprefix $(OBJDIR)/,$(patsubst %.asm,_%.o, $(notdir $(STARTUP_SOURCE))))
C_OBJECTS = $(notdir $(C_SOURCES:.c=.o))
CC_OBJECTS = $(notdir $(CC_SOURCES:.cc=.o))

DEP_FILES = $(patsubst %.o,$(DEPDIR)/%.d,$(C_OBJECTS))
DEP_FILES += $(patsubst %.o,$(DEPDIR)/%.d,$(CC_OBJECTS))

ASM_OBJECTS = $(patsubst %.asm,_%.o, $(notdir $(ASM_SOURCES)))
OBJPRE = $(addprefix $(OBJDIR)/,$(ASM_OBJECTS) $(C_OBJECTS) $(CC_OBJECTS))

# --------------------------------------------------------------------------
# Default target: the image file for USB sticks and QEMU



all: $(OBJDIR)/bootdisk.iso

# --------------------------------------------------------------------------
# rules for generating dependency files


$(DEPDIR)/%.d : %.c
	@echo "DEP		$@"
	@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
	$(VERBOSE) $(CC) $(CFLAGS) -MM -MT $(OBJDIR)/$*.o -MF $@ $<

$(DEPDIR)/%.d : %.cc
	@echo "DEP		$@"
	@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
	$(VERBOSE) $(CXX) $(CXXFLAGS) -MM -MT $(OBJDIR)/$*.o -MF $@ $<

# --------------------------------------------------------------------------
# rules for generating object files

$(OBJDIR)/%.o : %.c
	@echo "CC		$@"
	@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
	$(VERBOSE) $(CC) -c $(CFLAGS) -o $@ $<

$(OBJDIR)/%.o : %.cc
	@echo "CXX		$@"
	@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
	$(VERBOSE) $(CXX) -c $(CXXFLAGS) -o $@ $<

$(OBJDIR)/_%.o : %.asm
	@echo "ASM		$@"
	@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
	$(VERBOSE) $(ASM) -f $(ASMOBJFORMAT) -o $@ $<

# --------------------------------------------------------------------------
# linked system

$(OBJDIR)/system: $(FIRST_OBJECT) $(OBJPRE)
	@echo "LD		$@"
	@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
	$(VERBOSE) $(CXX) $(CXXFLAGS) -static -z max-page-size=0x1000 -e startup -T sections -o $(OBJDIR)/system $(FIRST_OBJECT) $(OBJPRE)

# --------------------------------------------------------------------------
# 'bootdisk.iso' consists of the system and a boot loader (GRUB) with boot
# block and setup code. The system gets started via Multiboot, so we don't
# have to care about switching to protected mode or how to deal with the BIOS.

$(OBJDIR)/bootdisk.iso : $(OBJDIR)/system
	@echo "BUILD		$@"
	@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
	$(VERBOSE) cp $< isofiles/boot/system
	$(VERBOSE) $(MKRESCUE) -o $@ isofiles

# --------------------------------------------------------------------------
# 'clean' deletes the generated system, the object files, and the dependency
# files.

clean:	
	@echo "RM		$(OBJDIR)"
	$(VERBOSE) rm -rf $(OBJDIR)
	@echo "RM		$(DEPDIR)"
	$(VERBOSE) rm -rf $(DEPDIR)

# 'list' shows the OBJS for debuging linker

list:
	@echo "C_OBJECTS $(C_OBJECTS)"
	@echo "CC_OBJECTS $(CC_OBJECTS)"
	@echo "OBJDIR $(OBJDIR)"
	@echo "OBJPRE $(OBJPRE)"
	@echo "FIRST_OBJECT $(FIRST_OBJECT)"


# --------------------------------------------------------------------------
# 'bootdisk' depends on bootdisk.iso, which in turn depends on the system. The
# ISO image gets written to the specified drive, which can be a HDD/SSD or an
# USB stick.
# ATTENTION: using the wrong device name here will destroy data on the specified
# device! Do not run with root privileges!

bootdisk: $(OBJDIR)/bootdisk.iso
	@echo "CP		$<"
	$(VERBOSE) cp $< $(DRIVE_HD)

# --------------------------------------------------------------------------
# 'qemu' runs the QEMU emulator with the system

qemu: $(OBJDIR)/bootdisk.iso
	$(QEMU) -drive file=build/bootdisk.iso,format=raw -k en-us

# --------------------------------------------------------------------------
# 'qemu-smp' runs QEMU in SMP (symmetric multi-processing) mode with 2 CPUs

qemu-smp: $(OBJDIR)/bootdisk.iso
	$(QEMU) -drive file=build/bootdisk.iso,format=raw -k en-us -smp 2

# --------------------------------------------------------------------------
# 'qemu-gdb' runs QEMU with an activated GDB stub, which waits for a debugger
# (GDB, DDD, CGDB, etc.) to connect before starting the system.

qemu-gdb: $(OBJDIR)/bootdisk.iso
	$(VERBOSE) echo "target remote localhost:$(shell echo $$(( $$(id -u) % (65536 - 1024) + 1024 )))" > /tmp/gdbcommands.$(shell id -u)
	$(QEMU) -drive file=build/bootdisk.iso,format=raw -k en-us -S -gdb tcp::$(shell echo $$(( $$(id -u) % (65536 - 1024) + 1024 )))

# --------------------------------------------------------------------------
# 'gdb' starts the GDB debugger and makes it connect to an already started
# 'qemu-gdb'.

gdb:
	gdb -x /tmp/gdbcommands.$(shell id -u) $(OBJDIR)/system

# --------------------------------------------------------------------------
# 'ddd': same as 'gdb' but uses the 'ddd' debugger

ddd:
	ddd --gdb -x /tmp/gdbcommands.$(shell id -u) $(OBJDIR)/system

# --------------------------------------------------------------------------
# 'cgdb': same as 'gdb' but uses the 'cgdb' debugger

cgdb:
	cgdb -x /tmp/gdbcommands.$(shell id -u) $(OBJDIR)/system

# --------------------------------------------------------------------------
# include dependency files

ifneq ($(MAKECMDGOALS),clean)
-include $(DEP_FILES)
endif

.PHONY: clean bootdisk gdb ddd qemu qemu-smp qemu-gdb qemu-ddd qemu-cgdb