我正在做一个增长非常快的项目并且保持目标文件更新是不可取的。 wildcard命令之外的问题介于“我不想要递归的makefile”和“我不希望它手动列出”之间。这些对象应该进入一个单独的目录,该目录已经可以使用。 注意:我不是 用于制作文件,我知道基础知识,但除了......之外的一切......
所以我的问题: 如何递归扫描src文件夹并以智能方式执行此操作?
我已经使用多个SRC变量完成了它,但这很丑陋并且整个makefile与越来越多的目录混杂在一起。
我目前使用的是:
OS = Linux
VERSION = 0.0.1
CC = /usr/bin/gcc
CFLAGS = -Wall -g -D_REENTRANT -DVERSION=\"$(VERSION)\"
LDFLAGS = -lm `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0`
BUILDDIR = build
SOURCEDIR = src
HEADERDIR = src
SOURCES = $(wildcard $(SOURCEDIR)/*.c)
OBJECTS = $(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/%.o, $(SOURCES))
NAME = cinnamon
BINARY = cinnamon.bin
ECHO = echo
RM = rm -rf
MKDIR = mkdir
INSTALL = install
.PHONY: all clean setup
all: $(BINARY)
$(BINARY): $(BUILDDIR)/$(OBJECTS)
$(CC) $(CFLAGS) $(LDFLAGS) -I$(HEADERDIR) -I$(SOURCEDIR) $(OBJECTS) -o $(BINARY)
$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
$(CC) $(CFLAGS) $(LDFLAGS) -I$(HEADERDIR) -I$(SOURCEDIR) -c $< -o $@
setup:
$(MKDIR) -p $(BUILDDIR)
install:
$(INSTALL) -m 755 -o 0 -g 0 -d $(DESTDIR)/usr/local/bin/
$(INSTALL) -m 755 -o 0 -g 0 $(BINARY) $(DESTDIR)/usr/local/bin/$(BINARY)
$(INSTALL) -m 755 -o 0 -g 0 -d $(DESTDIR)/usr/local/$(NAME)/ui/
$(INSTALL) -m 644 -o 0 -g 0 ./ui/*.ui $(DESTDIR)/usr/local/$(NAME)/ui/
# $(INSTALL) -m 755 -o 0 -g 0 -d $(DESTDIR)/usr/local/$(NAME)/model/
# $(INSTALL) -m 644 -o 0 -g 0 ./model/*.model $(DESTDIR)/usr/local/$(NAME)/model/
clean:
$(RM) $(BINARY) $(OBJECTS)
distclean: clean
help:
@$(ECHO) "Targets:"
@$(ECHO) "all - buildcompile what is necessary"
@$(ECHO) "clean - cleanup old .o and .bin"
@$(ECHO) "install - not yet fully supported"
感谢答案#1,归结为如何解决这个问题:
$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
$(CC) $(CFLAGS) $(LDFLAGS) $(SOURCETREE) -c $< -o $@
特别是在BUILDDIR = build和SOURCEDIR的情况下,必须用来自SOURCES的单个.c文件替换它们的路径:/
答案 0 :(得分:73)
做你想做的最简单的选择可能只是使用shell转义并调用find
:
SOURCES := $(shell find $(SOURCEDIR) -name '*.c')
这会为您提供包含路径的源文件列表。请注意,使用立即赋值:=
而不是递归赋值=
非常重要:每次SOURCES
时,不都希望运行shell转义通过make检查(发生的事情比你在Makefiles中想象的要多得多)。我觉得有用的一般规则是总是使用立即赋值,除非我真的需要递归扩展(这很少见;看起来这个例子中的所有赋值都可以是立即的)。这意味着使用递归赋值也是一个有用的信号,需要仔细使用该变量。
回到你的问题。你接下来要做什么取决于你是否想在构建树中使用源代码树的镜像,或者构建目录是否应该包含所有源文件的目标文件的平面列表,或者是否需要单独的构建目录在树中的每个源头目录下。
假设您需要镜像构建树,您可以执行以下操作:
# Get list of object files, with paths
OBJECTS := $(addprefix $(BUILDDIR)/,$(SOURCES:%.c=%.o))
$(BINARY): $(OBJECTS)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJECTS) -o $(BINARY)
$(BUILDDIR)/%.o: %.c
$(CC) $(CFLAGS) $(LDFLAGS) -I$(HEADERDIR) -I$(dir $<) -c $< -o $@
这并没有完全考虑到作业的完整复杂性,因为它不能确保构建树中的目录确实存在(在Makefile语法中这会非常痛苦)。
我从$(BINARY)构建规则中删除了-I指令;链接对象时你真的需要它们吗?我没有留下它们的原因是你不再只有一个源目录了,从对象列表中获取源dirs列表是非常重要的(就像在Makefile语法中那样,它是可行的但是真烦人)。
答案 1 :(得分:43)
递归通配符可以完全在Make中完成,无需调用shell或find命令。仅使用Make进行搜索意味着此解决方案也适用于Windows,而不仅仅是* nix。
# Make does not offer a recursive wildcard function, so here's one:
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
# How to recursively find all files with the same name in a given folder
ALL_INDEX_HTMLS := $(call rwildcard,foo/,index.html)
# How to recursively find all files that match a pattern
ALL_HTMLS := $(call rwildcard,foo/,*.html)
文件夹名称中的尾部斜杠是必需的。这个 rwildcard 函数不像Make的内置通配符函数那样支持多个通配符,但添加该支持可以直接使用 foreach的更多用法强>
答案 2 :(得分:14)
我喜欢做以下事情。
SRCDIR = src
OBJDIR = obj
LIBDIR = lib
DOCDIR = doc
HDRDIR = include
CFLAGS = -g -Wall -O3
STRUCTURE := $(shell find $(SRCDIR) -type d)
CODEFILES := $(addsuffix /*,$(STRUCTURE))
CODEFILES := $(wildcard $(CODEFILES))
# Filter Only Specific Files
SRCFILES := $(filter %.c,$(CODEFILES))
HDRFILES := $(filter %.h,$(CODEFILES))
OBJFILES := $(subst $(SRCDIR),$(OBJDIR),$(SRCFILES:%.c=%.o))
# Filter Out Function main for Libraries
LIBDEPS := $(filter-out $(OBJDIR)/main.o,$(OBJFILES))
compile: $(OBJFILES)
$(OBJDIR)/%.o: $(addprefix $(SRCDIR)/,%.c %.h)
$(CC) -c $< -o $@ $(CFLAGS)
使用这种方法,你可以看到我只使用STRUCTURE变量来获取SRCDIR目录中的文件,但它也可以用于其他目的,比如一旦STRUCTURE存储就镜像OBJDIR中的SRCDIR只有内部子目录。在诸如以下的清洁操作之后,它非常有用。
clean:
-rm -r $(OBJDIR)/*
注意:编译规则只适用于每个* .c都有相应的* .h文件(具有相同的基本名称,我的意思)。
答案 3 :(得分:1)
这个问题的另一个好方法似乎是 - 实现一个非递归的makefile,如下所述:http://sites.e-advies.nl/nonrecursive-make.html。这种方法很好,因为它看起来相当可扩展 - 开发人员可以在源文件的目录中添加依赖信息,而不必担心整个makefile。