Makefile问题:扫描.c文件的目录树的智能方法

时间:2010-09-23 00:16:59

标签: tree makefile

我正在做一个增长非常快的项目并且保持目标文件更新是不可取的。 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文件替换它们的路径:/

4 个答案:

答案 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

递归获取SRCDIR的内部结构

STRUCTURE := $(shell find $(SRCDIR) -type d)     

获取STRUCTURE变量

中的所有文件
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。