it-swarm.dev

Crear directorios utilizando make file.

Soy muy nuevo en makefiles y quiero crear directorios usando makefile. Mi directorio de proyectos es así.

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

Quiero poner todos los objetos y la salida en la carpeta de salida respectiva. Quiero crear una estructura de carpetas que sería así después de compilar.

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

Lo intenté con varias opciones, pero no lo logré. Por favor, ayúdame a hacer directorios usando make file. Estoy publicando mi Makefile para su consideración.

#---------------------------------------------------------------------
# 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)

Gracias por adelantado. Por favor guíame si he cometido algún error también.

84
Jabez

Esto lo haría - asumiendo un entorno similar a Unix.

MKDIR_P = mkdir -p

.PHONY: directories

all: directories program

directories: ${OUT_DIR}

${OUT_DIR}:
        ${MKDIR_P} ${OUT_DIR}

Esto debería ejecutarse en el directorio de nivel superior, o la definición de $ {OUT_DIR} debería ser correcta en relación con el lugar donde se ejecuta. Por supuesto, si sigue los edictos del documento " Recursive Make Considered Harmful " de Peter Miller, estará ejecutando make en el directorio de nivel superior de todos modos.

Estoy jugando con esto (RMCH) en este momento. Necesitaba un poco de adaptación al conjunto de software que estoy usando como campo de prueba. La suite tiene una docena de programas independientes creados con código fuente distribuidos en 15 directorios, algunos de ellos compartidos. Pero con un poco de cuidado, se puede hacer. OTOH, podría no ser apropiado para un novato.


Como se señaló en los comentarios, listar el comando 'mkdir' como la acción para 'directorios' es incorrecto. Como también se señaló en los comentarios, hay otras formas de corregir el error "no sé cómo hacer salida/depuración" que se produce. Una es eliminar la dependencia en la línea 'directorios'. Esto funciona porque 'mkdir -p' no genera errores si todos los directorios que se le pide crear ya existen. El otro es el mecanismo que se muestra, que solo intentará crear el directorio si no existe. La versión "modificada" es lo que tenía en mente la noche anterior, pero ambas técnicas funcionan (y ambas tienen problemas si existe una salida/depuración, pero es un archivo y no un directorio).

74
Jonathan Leffler

En mi opinión, los directorios no deben considerarse objetivos de su makefile, ya sea en el sentido técnico o de diseño. Debe crear archivos y si la creación de un archivo necesita un nuevo directorio, entonces silenciosamente cree el directorio dentro de la regla para el archivo relevante.

Si está apuntando a un archivo habitual o "con patrón", simplemente use la variable interna de make$(@D), que significa "el directorio donde reside el destino actual" (cmp. Con $@ para el objetivo). Por ejemplo,

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

title: $(OBJS)

Entonces, efectivamente estás haciendo lo mismo: crea directorios para todos $(OBJS), pero lo harás de una manera menos complicada.

La misma política (los archivos son destinos, los directorios nunca lo son) se usa en varias aplicaciones. Por ejemplo, git sistema de control de revisión no almacena directorios.


Nota: Si lo va a usar, podría ser útil introducir una variable conveniente y utilizar las reglas de expansión de make.

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)
122
P Shved

O bien, KISS.

DIRS=build build/bins

... 

$(Shell mkdir -p $(DIRS))

Esto creará todos los directorios después de que se analice el Makefile.

18
Nick Zalutskiy

Todas las soluciones, incluida la aceptada, tienen algunos problemas como se indica en sus respectivos comentarios. La respuesta aceptada por @ jonathan-leffler ya es bastante buena, pero no tiene en cuenta que los requisitos previos no necesariamente se deben construir en orden (durante make -j por ejemplo). Sin embargo, simplemente moviendo el requisito directories de all a program provoca reconstrucciones en cada ejecución AFAICT. La siguiente solución no tiene ese problema y AFAICS funciona según lo previsto.

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
stefanct

make dentro y fuera de sí mismo, maneja los destinos de directorio de la misma manera que los objetivos de archivo. Entonces, es fácil escribir reglas como esta:

outDir/someTarget: Makefile outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

El único problema con eso es que la marca de tiempo de los directorios depende de lo que se haga con los archivos que contiene. Para las reglas anteriores, esto lleva al siguiente resultado:

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

Esto definitivamente no es lo que quieres. Cada vez que toca el archivo, también toca el directorio. Y como el archivo depende del directorio, el archivo parece estar desactualizado, lo que obliga a reconstruirlo.

Sin embargo, puede romper fácilmente este bucle si le dice a make que ignore la marca de tiempo del directorio . Esto se hace declarando el directorio como un requisito previo de solo orden:

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

outDir:
    mkdir -p outDir

Esto correctamente produce:

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

TL; DR:

Escribe una regla para crear el directorio:

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

Y tener los objetivos para las cosas en el interior depende del orden del directorio solamente:

$(OUT_DIR)/someTarget: ... | $(OUT_DIR)
6
cmaster

Acabo de encontrar una solución bastante razonable que te permite definir los archivos para compilar y hacer que los directorios se creen automáticamente. Primero, defina una variable ALL_TARGET_FILES que contenga el nombre de archivo de cada archivo que se construirá su makefile. Luego usa el siguiente código:

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))))

Así es como funciona. Defino una función depend_on_dir que toma un nombre de archivo y genera una regla que hace que el archivo dependa del directorio que lo contiene y luego define una regla para crear ese directorio si es necesario. Luego uso foreach para call esta función en cada nombre de archivo y eval el resultado.

Tenga en cuenta que necesitará una versión de GNU make que admita eval, que creo que es la versión 3.81 y superior.

4
Ryan Thompson

La independencia del SO es fundamental para mí, por lo que mkdir -p no es una opción. Creé esta serie de funciones que usan eval para crear destinos de directorio con el requisito previo en el directorio principal. Esto tiene la ventaja de que make -j 2 funcionará sin problemas ya que las dependencias se determinan correctamente.

# 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/a.o obj/three/b.o) -> 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 > $@

Por ejemplo, ejecutando lo anterior se creará:

$ 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
SimplyKnownAsG

dado que eres un novato, diría que no intentes hacer esto todavía. Es definitivamente posible, pero complicará innecesariamente su Makefile. apégate a las formas simples hasta que te sientas más cómodo con make.

dicho esto, una forma de crear un directorio diferente del directorio de origen es VPATH ; prefiero reglas de patrón

3
just somebody

Consulte 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)

Cuando uso Ubuntu, también necesitaba agregar esto en la parte superior de mi Makefile:

Shell := /bin/bash # Use bash syntax
0
Rudiger Wolf