para - makefile 112 makeopts no existe el fichero o el directorio
Crear directorios usando make file (8)
Acabo de presentar una solución bastante razonable que te permite definir los archivos para construir 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 generará en su archivo MAKE. 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. depend_on_dir
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.
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
Intenté con varias opciones, pero no pude tener éxito. 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 cometí algún error también.
En mi opinión, los directorios no deben considerarse objetivos de su archivo MAKE, ya sea en términos técnicos o de diseño. Debe crear archivos y si la creación de un archivo necesita un nuevo directorio, cree silenciosamente el directorio dentro de la regla para el archivo relevante.
Si tiene como objetivo un archivo normal o "modelado", simplemente use la variable interna de make
$(@D)
, que significa "el directorio donde reside el objetivo actual" (cmp. Con $@
para el destino). Por ejemplo,
$(OUT_O_DIR)/%.o: %.cpp
@mkdir -p $(@D)
@$(CC) -c $< -o $@
title: $(OBJS)
Entonces, efectivamente haces lo mismo: creas directorios para todos $(OBJS)
, pero lo harás de una manera menos complicada.
La misma política (los archivos son objetivos, los directorios nunca lo son) se usa en diversas aplicaciones. Por ejemplo, el sistema de control de revisiones git
no almacena directorios.
Nota: Si va a usarlo, puede ser útil introducir una variable de conveniencia 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)
Esto lo haría, suponiendo un entorno tipo 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, entonces ejecutará 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 paquete de software que estoy usando como campo de pruebas. La suite tiene una docena de programas separados construidos con fuente distribuida 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, enumerar el comando ''mkdir'' como acción para ''directorios'' es incorrecto. Como también se señaló en los comentarios, existen otras maneras de corregir el error ''no sé cómo hacer la salida / depurar''. Una es eliminar la dependencia en la línea de ''directorios''. Esto funciona porque ''mkdir -p'' no genera errores si ya existen todos los directorios que se le pide que cree. El otro es el mecanismo que se muestra, que solo intentará crear el directorio si no existe. La versión ''enmendada'' es lo que tenía en mente anoche, pero ambas técnicas funcionan (y ambas tienen problemas si existe salida / eliminación de fallas, pero es un archivo en lugar de un directorio).
La independencia del sistema operativo es crítica 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 padre. Esto tiene el beneficio que make -j 2
que make -j 2
funcione 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, ejecutar lo anterior 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
O, KISS.
DIRS=build build/bins
...
$(shell mkdir -p $(DIRS))
Esto creará todos los directorios después de que se haya analizado Makefile.
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 deben construirse necesariamente en orden (durante make -j
por ejemplo). Sin embargo, simplemente mover el requisito previo de all
los directories
para 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)
dado que eres un novato, yo diría que no trates de hacer esto todavía. definitivamente es posible, pero complicará innecesariamente tu Makefile. mantén las formas simples hasta que te sientas más cómodo con make.
dicho esto, una forma de compilar en un directorio diferente al directorio de origen es VPATH ; prefiero reglas de patrones
make
in, y off itself, maneja los destinos del directorio de la misma manera que los objetivos del archivo. Por lo tanto, 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 hace a los archivos dentro. 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 dado que el archivo depende del directorio, el archivo, en consecuencia, parece estar desactualizado, lo que obliga a reconstruirlo.
Sin embargo, puede romper fácilmente este ciclo diciendo a make que ignore la marca de tiempo del directorio . Esto se hace declarando el directorio como un prerrequisito 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 cede correctamente:
$ 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 los objetivos para el contenido interno dependen de la orden de directorio solo:
$(OUT_DIR)/someTarget: ... | $(OUT_DIR)