User:Solar/Makefile

From OSDev.wiki
Revision as of 12:33, 23 January 2012 by Solar (talk | contribs) (Some comments.)
Jump to navigation Jump to search

Note: This isn't finished, just a rough draft. I didn't test this exhaustively, and will probably not do so anytime soon. But what's there already might be helpful.




As an extension to Makefile, this should outline how one could build several binaries with different settings using a single Makefile.

The idea is to create a subdirectory for each module (executable or library). In each these module subdirectories, module-specific settings are stored in a file build.mk.

Each module subdirectory contains a directory includes, where all headers with "global" visibility are stored (i.e., headers visible to other modules). All other headers and source files are located in src, with object files being stored in obj by the compiler.

.
├── bar
│   ├── build.mk
│   ├── includes
│   │   └── global.h
│   ├── obj
│   └── src
│       ├── bar_local.h
│       └── bar_test.c
├── bin
├── foo
│   ├── build.mk
│   ├── includes
│   │   └── global.h
│   ├── obj
│   └── src
│       ├── foo_local.h
│       ├── foo_test.c
│       └── main.c
├── includes
├── lib
└── Makefile

The contents of the several build.mk files is similar to what is done in Makefile.am for Automake:

# CFLAGS for module 'foo'
CFLAGS_foo := -Wall

# Executables to build in module 'foo'
foo_PROGRAMS := foo
# Archives / Libs to build in module 'foo'
foo_ARCHIVES := foolib.a

# Sources for the executable 'foo' (without headers)
foo_SOURCES := main.c
# Sources for the archive / lib 'foolib.a' (without headers)
foolib.a_SOURCES := foo_test.c

The main Makefile looks like this:

# Modules in the project (you coul 'find' these, but stating
# them explicitly allows for subdirectories like 'tmp' or 'doc'
# without upsetting the build process.
MODULES := foo bar

# Global CFLAGS. Add to them if you must, but don't remove '-MMD -I includes'.
CFLAGS := -MMD -I includes
# Global ARFLAGS.
ARFLAGS := rc

.PHONY: clean mrproper

# Add whatever should be your default / global target.
all:
	@echo "Default target."

###################################################################
# What follows are several templates (think "functions"), which are
# later instantiated for each registered module.
###################################################################

# Including a module's build.mk
define MK_template
include $(1)/build.mk
endef

# Setting a module's build rules for object files
define RULES_template
$(1)/obj/%.o: $(1)/src/%.c
	$$(CC) $$(CFLAGS) $$(CFLAGS_$(1)) -c $$< -o $$@
endef

# Setting a module's build rules for executable targets.
# Also adds a module's dependency files to the global list.
define PROGRAM_template
DEPENDENCIES := $(DEPENDENCIES) $(patsubst %,$(2)/obj/%.d,$(basename $($(1)_SOURCES)))
bin/$(1): $(patsubst %,$(2)/obj/%.o,$(basename $($(1)_SOURCES))) $(foreach library,$($(1)_LIBRARIES),lib/$(library))
	$$(LD) $$(LDFLAGS) $$(LDFLAGS_$(2)) $$^ -o $$@
endef

# Setting a module's build rules for archive targets
define ARCHIVE_template
DEPENDENCIES := $(DEPENDENCIES) $(patsubst %,$(2)/obj/%.d,$(basename $($(1)_SOURCES)))
lib/$(1): $(patsubst %,$(2)/obj/%.o,$(basename $($(1)_SOURCES)))
	$$(AR) $$(ARFLAGS) $$@ $$?
endef

# Linking a module's global includes into the global include directory
# (where they will be available as <module>/filename.h)
define INCLUDE_template
ifeq ($(wildcard includes/$(1)),)
    $$(shell ln -s ../$(1)/includes includes/$(1))
endif
endef

$(foreach module,$(MODULES),$(eval include $(module)/build.mk))
$(foreach module,$(MODULES),$(eval $(call RULES_template,$(module))))
$(foreach module,$(MODULES),$(eval $(foreach binary,$($(module)_PROGRAMS),$(call PROGRAM_template,$(binary),$(module)))))
$(foreach module,$(MODULES),$(eval $(foreach library,$($(module)_ARCHIVES),$(call ARCHIVE_template,$(library),$(module)))))
$(foreach module,$(MODULES),$(eval $(call INCLUDE_template,$(module))))

# Include the dependency files (generated by GCC's -MMD option)
-include $(sort $(DEPENDENCIES))

clean:
	$(RM) $(foreach mod,$(MODULES),$(mod)/obj/*.o)

mrproper: clean
	$(RM) $(foreach mod,$(MODULES),$(mod)/obj/*.d) includes/* bin/* lib/*