2009-12-23 18 views
63

Soy un nuevo miembro de makefiles y quiero crear directorios usando makefile. Mi directorio de proyecto es asíCrear directorios usando make file

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

he intentado con varias opciones, pero no podía 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 [email protected] 

$(OBJS): $(OUT_O_DIR)/%.o: %.cpp 
    @$(CC) -c $< -o [email protected] 

title: $(DIR_TARGET) $(OBJS) 

Gracias de antemano. Por favor guíame si cometí algún error también.

Respuesta

59

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 tendría que ser ejecutado en el directorio de nivel superior - o la definición de $ {} OUT_DIR tendría que ser correcta con respecto a 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) por el 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, mostrando el comando 'mkdir' como la acción de '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).

+0

Gracias Jonathan. cuando probé recibí un error "make: *** No rule to make target 'output/debug', necesario para' directories '. Stop. " Pero no voy a preocuparme por eso ahora. se quedará con las reglas básicas. :). Gracias por guiarY estoy ejecutando "make" desde el directorio toplevel solamente. – Jabez

+0

Simplemente elimine los $ {OUT_DIR} detrás de los directorios :, entonces debería funcionar. –

+0

Funcionó. Gracias Doc. – Jabez

2

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, una forma de compilar en un directorio diferente del directorio de origen es VPATH; Prefiero pattern rules

+0

Gracias. Se apegará a las reglas básicas. – Jabez

102

En mi opinión, los directorios no deben considerarse objetivos de su archivo MAKE, ni en el sentido técnico ni en el sentido del 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 usted está apuntando un archivo normal o "patrón", sólo tiene que utilizar make 's interno variable de $(@D), que significa 'el directorio de la meta actual reside en'(cmp. [email protected] con el objetivo).Por ejemplo,

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

title: $(OBJS) 

Entonces, usted es efectivamente hacen lo mismo: crear directorios para todos $(OBJS), pero usted a ella en forma menos complicada.

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


Nota: Si vas a usarlo, podría ser útil introducir una variable de conveniencia y utilizar reglas de expansión make 's.

[email protected] -p $(@D) 

$(OUT_O_DIR)/%.o: %.cpp 
     $(dir_guard) 
     @$(CC) -c $< -o [email protected] 

$(OUT_O_DIR_DEBUG)/%.o: %.cpp 
     $(dir_guard) 
     @$(CC) -g -c $< -o [email protected] 

title: $(OBJS) 
+8

Si bien vincular el requisito del directorio a los archivos es una mejor opción en mi opinión, su solución también tiene el inconveniente significativo de que el archivo make llamará al proceso mkdir por cada archivo reconstruido, la mayoría de los cuales no será necesario el directorio nuevamente Cuando se adapta a sistemas de compilación no Linux como Windows, en realidad causa una salida de error no bloqueable ya que no hay -p equivalente al comando mkdir, y lo que es más importante, una gran cantidad de sobrecarga ya que la invocación del shell no es mínimamente invasiva. – mtalexan

4

acabo de llegar a una solución bastante razonable que permite definir los archivos para construir y tener directorios se crean 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. A continuación, utilice el siguiente código:

define depend_on_dir 
$(1): | $(dir $(1)) 

ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED 
$(dir $(1)): 
    mkdir -p [email protected] 

$(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1 
endif 
endef 

$(foreach file,$(ALL_TARGET_FILES),$(eval $(call depend_on_dir,$(file)))) 

Así es cómo 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 a 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.

+0

Crear una variable que contenga "el nombre de archivo de cada archivo que creará tu archivo MAKE" es una especie de requisito oneroso: me gusta definir mis objetivos de nivel superior, luego las cosas de las que dependen, y así sucesivamente. Tener una lista plana de todos los archivos de destino va en contra de la naturaleza jerárquica de la especificación de archivo MAKE, y puede no ser (fácilmente) posible cuando los archivos de destino dependen de los cálculos de tiempo de ejecución. –

3

Todas las soluciones, incluida la aceptada, tienen algunos problemas como se indica en sus respectivos comentarios. El accepted answer by @jonathan-leffler ya es bastante bueno, pero no tiene en cuenta que los requisitos previos no necesariamente deben construirse en orden (durante make -j por ejemplo). Sin embargo, simplemente mover el requisito previo 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) 
1

la independencia del sistema operativo es fundamental para mí, así 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 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 > [email protected] 

w/h/a/t/things.dat: | $(TARGET_DIRS) 
    echo whatever happens > [email protected] 

Por ejemplo, la ejecución del 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 
3

O, KISS.

DIRS=build build/bins 

... 

$(shell mkdir -p $(DIRS)) 

Esto creará todos los directorios después de analizar el Makefile.

+0

Me gusta este enfoque porque no tengo que ocupar cada objetivo con comandos para manejar los directorios. –

Cuestiones relacionadas