Creating Directories Using the Make File

I am very new to makefiles and I want to create directories using a makefile. My project directory is like this

+--Project +--output +--source +Testfile.cpp +Makefile 

I want to put all the objects and output them to the corresponding output folder. I want to create a folder structure that is the same as after compilation.

 +--Project +--output +--debug (or release) +--objs +Testfile.o +Testfile (my executable file) +--source +Testfile.cpp +Makefile 

I tried several options, but could not. Please help me make directories using the make file. I submit my Makefile for your consideration.

 #--------------------------------------------------------------------- # Input dirs, names, files #--------------------------------------------------------------------- OUTPUT_ROOT := output/ TITLE_NAME := TestProj ifdef DEBUG TITLE_NAME += _DEBUG else ifdef RELEASE TITLE_NAME += _RELEASE endif endif # Include all the source files here with the directory tree SOURCES := \ source/TestFile.cpp \ #--------------------------------------------------------------------- # configs #--------------------------------------------------------------------- ifdef DEBUG OUT_DIR := $(OUTPUT_ROOT)debug CC_FLAGS := -c -Wall else ifdef RELEASE OUT_DIR := $(OUTPUT_ROOT)release CC_FLAGS := -c -Wall else $(error no build type defined) endif endif # Put objects in the output directory. OUT_O_DIR := $(OUT_DIR)/objs #--------------------------------------------------------------------- # settings #--------------------------------------------------------------------- OBJS = $(SOURCES:.cpp=.o) DIRS = $(subst /,/,$(sort $(dir $(OBJS)))) DIR_TARGET = $(OUT_DIR) OUTPUT_TARGET = $(OUT_DIR)/$(TITLE_NAME) CC_FLAGS += LCF_FLAGS := LD_FLAGS := #--------------------------------------------------------------------- # executables #--------------------------------------------------------------------- MD := mkdir RM := rm CC := g++ #--------------------------------------------------------------------- # rules #--------------------------------------------------------------------- .PHONY: all clean title all: title clean: $(RM) -rf $(OUT_DIR) $(DIR_TARGET): $(MD) -p $(DIRS) .cpp.o: @$(CC) -c $< -o $@ $(OBJS): $(OUT_O_DIR)/%.o: %.cpp @$(CC) -c $< -o $@ title: $(DIR_TARGET) $(OBJS) 

Thanks in advance. Please guide me if I made a mistake.

+82
object directory makefile
Dec 23 '09 at 5:58
source share
9 answers

That would do it - assuming a Unix-like environment.

 MKDIR_P = mkdir -p .PHONY: directories all: directories program directories: ${OUT_DIR} ${OUT_DIR}: ${MKDIR_P} ${OUT_DIR} 

This must be done in the top-level directory β€” or the definition of $ {OUT_DIR} must be correct relative to where it started. Of course, if you follow the instructions of Peter Miller's article, " Recursive creation deemed harmful, " you will still run make in the top-level directory.

I am playing with this (RMCH) at the moment. It took a little adaptation to the software that I use as a testing ground. The package includes a dozen separate programs created using source code distributed across 15 directories, some of which are shared. But with a little caution, this can be done. OTOH, this may not be suitable for a beginner.




As noted in the comments, specifying the 'mkdir' command as an action for 'directoryies' is incorrect. As also noted in the comments, there are other ways to correct the received error β€œI don’t know how to make a conclusion / debugging”. One of them is to remove the dependency on the 'directoryies' line. This works because "mkdir -p" does not generate errors if all the directories it is invited to create already exist. Another mechanism is shown that will try to create a directory only if it does not exist. The bug fix version is what I had in mind last night - but both methods work (and both have problems if output / debugging exists, but it is a file, not a directory).

+71
Dec 23 '09 at 6:35
source share

In my opinion, directories should not be construed as the goals of your makefile, either technically or in a design sense. You must create files, and if a new directory is required to create a file, then calmly create a directory in the rule for the corresponding file.

If you are targeting a regular or "template" file, just use the internal variable make $(@D) , which means "the directory where the current target is located" (cmp. C $@ for the target). For example,

 $(OUT_O_DIR)/%.o: %.cpp @mkdir -p $(@D) @$(CC) -c $< -o $@ title: $(OBJS) 

Then you actually do the same: create directories for all $(OBJS) , but you will do it in a less complicated way.

The same policy (files are targets, directories are never used) is used in different applications. For example, git version control does not store directories.




Note: If you intend to use it, it may be useful to introduce variable convenience and use make extension rules.

 dir_guard=@mkdir -p $(@D) $(OUT_O_DIR)/%.o: %.cpp $(dir_guard) @$(CC) -c $< -o $@ $(OUT_O_DIR_DEBUG)/%.o: %.cpp $(dir_guard) @$(CC) -g -c $< -o $@ title: $(OBJS) 
+119
Dec 23 '09 at 6:58
source share

Or, KISS.

 DIRS=build build/bins ... $(shell mkdir -p $(DIRS)) 

This will create all directories after parsing the Makefile.

+18
Jul 12 '17 at 5:15
source share

All decisions, including those adopted, have some problems, as indicated in their respective comments. @ jonathan-leffler's accepted answer is already quite good, but does not suggest that the premises do not have to be built in order (for example, in make -j ), however, just moving the directories background from all to program causes rebuilds for each AFAICT run. The next solution does not have this problem, and AFAICS works as intended.

 MKDIR_P := mkdir -p OUT_DIR := build .PHONY: directories all clean all: $(OUT_DIR)/program directories: $(OUT_DIR) $(OUT_DIR): ${MKDIR_P} $(OUT_DIR) $(OUT_DIR)/program: | directories touch $(OUT_DIR)/program clean: rm -rf $(OUT_DIR) 
+6
Dec 14 '16 at 11:56
source share

make in and off handles directory targets in the same way as file targets. Thus, it is easy to write such rules:

 outDir/someTarget: Makefile outDir touch outDir/someTarget outDir: mkdir -p outDir 

The only problem is that the timestamp of directories depends on what is done with the files inside. For the above rules, this leads to the following result:

 $ make mkdir -p outDir touch outDir/someTarget $ make touch outDir/someTarget $ make touch outDir/someTarget $ make touch outDir/someTarget 

This is definitely not what you want. Whenever you touch a file, you also touch the directory. And since the file depends on the directory, the file is therefore outdated and has to be rebuilt.

However, you can easily break this loop by telling make to ignore the directory timestamp . This is done by declaring the catalog as pre-order for order only:

 # The pipe symbol tells make that the following prerequisites are order-only # | # v outDir/someTarget: Makefile | outDir touch outDir/someTarget outDir: mkdir -p outDir 

This correctly gives:

 $ make mkdir -p outDir touch outDir/someTarget $ make make: 'outDir/someTarget' is up to date. 



TL; DR:

Write a rule to create a directory:

 $(OUT_DIR): mkdir -p $(OUT_DIR) 

And the goals for the content inside depend only on the directory order:

 $(OUT_DIR)/someTarget: ... | $(OUT_DIR) 
+5
Jun 28 '18 at 12:28
source share

I just came up with a pretty reasonable solution that allows you to define files for assembly and create directories automatically. First, define the ALL_TARGET_FILES variable, which contains the file name for each file that your make file will create. Then use the following code:

 define depend_on_dir $(1): | $(dir $(1)) ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED $(dir $(1)): mkdir -p $$@ $(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1 endif endef $(foreach file,$(ALL_TARGET_FILES),$(eval $(call depend_on_dir,$(file)))) 

Here's how it works. I define a function depend_on_dir that takes a file name and generates a rule that makes the file depend on the directory that contains it, and then defines a rule to create this directory if necessary. Then I use foreach to call this function for each file name and eval result.

Note that you will need a version of GNU make that supports eval , which I think is version 3.81 and higher.

+4
Oct 21 2018-10-21
source share

given that you are a beginner, I would say do not try to do this. this is definitely possible, but it will uselessly complicate your Makefile. follow simple methods until you feel comfortable doing it.

who said, one way to create a directory other than the source directory is VPATH ; I prefer pattern rules

+3
Dec 23 '09 at 6:31
source share

OS independence is important to me, so mkdir -p not an option. I created this series of functions that use eval to create directory targets with a precondition in the parent directory. This has the advantage that make -j 2 will work without problems, since dependencies are defined correctly.

 # convenience function for getting parent directory, will eventually return ./ # $(call get_parent_dir,somewhere/on/earth/) -> somewhere/on/ get_parent_dir=$(dir $(patsubst %/,%,$1)) # function to create directory targets. # All directories have order-only-prerequisites on their parent directories # https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html#Prerequisite-Types TARGET_DIRS:= define make_dirs_recursively TARGET_DIRS+=$1 $1: | $(if $(subst ./,,$(call get_parent_dir,$1)),$(call get_parent_dir,$1)) mkdir $1 endef # function to recursively get all directories # $(call get_all_dirs,things/and/places/) -> things/ things/and/ things/and/places/ # $(call get_all_dirs,things/and/places) -> things/ things/and/ get_all_dirs=$(if $(subst ./,,$(dir $1)),$(call get_all_dirs,$(call get_parent_dir,$1)) $1) # function to turn all targets into directories # $(call get_all_target_dirs,obj/ao obj/three/bo) -> obj/ obj/three/ get_all_target_dirs=$(sort $(foreach target,$1,$(call get_all_dirs,$(dir $(target))))) # create target dirs create_dirs=$(foreach dirname,$(call get_all_target_dirs,$1),$(eval $(call make_dirs_recursively,$(dirname)))) TARGETS := w/h/a/t/e/v/e/r/things.dat w/h/a/t/things.dat all: $(TARGETS) # this must be placed after your .DEFAULT_GOAL, or you can manually state what it is # https://www.gnu.org/software/make/manual/html_node/Special-Variables.html $(call create_dirs,$(TARGETS)) # $(TARGET_DIRS) needs to be an order-only-prerequisite w/h/a/t/e/v/e/r/things.dat: w/h/a/t/things.dat | $(TARGET_DIRS) echo whatever happens > $@ w/h/a/t/things.dat: | $(TARGET_DIRS) echo whatever happens > $@ 

For example, when starting above, create:

 $ make mkdir w/ mkdir w/h/ mkdir w/h/a/ mkdir w/h/a/t/ mkdir w/h/a/t/e/ mkdir w/h/a/t/e/v/ mkdir w/h/a/t/e/v/e/ mkdir w/h/a/t/e/v/e/r/ echo whatever happens > w/h/a/t/things.dat echo whatever happens > w/h/a/t/e/v/e/r/things.dat 
+3
Dec 14 '16 at 21:30
source share

See https://www.oreilly.com/library/view/managing-projects-with/0596006101/ch12.html.

 REQUIRED_DIRS = ... _MKDIRS := $(shell for d in $(REQUIRED_DIRS); \ do \ [[ -d $$d ]] || mkdir -p $$d; \ done) $(objects) : $(sources) 

Since I use Ubuntu, I also needed to add this at the top of my Makefile:

 SHELL := /bin/bash # Use bash syntax 
0
Apr 22 '19 at 18:05
source share



All Articles