【问题标题】:Makefile issue: smart way to scan directory tree for .c filesMakefile 问题:扫描目录树以查找 .c 文件的智能方法
【发布时间】:2010-09-23 00:16:59
【问题描述】:

我正在做一个发展很快的项目,保持目标文件的最新状态是不可能的。通配符命令之外的问题介于“我不想要递归生成文件”和“我不希望它手动列出”之间。这些对象应该进入一个单独的目录,该目录已经有效。 注意:我不是那个习惯于制作文件的,我知道基础知识,但除了...

所以我的问题: 如何递归扫描 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 文件(包括它们的路径)替换的情况下:/

【问题讨论】:

标签: tree makefile


【解决方案1】:

做你想做的最简单的选择可能是使用shell转义并调用find

SOURCES := $(shell find $(SOURCEDIR) -name '*.c')

这将为您提供带有路径的源文件列表。请注意,使用立即赋值 := 而不是递归赋值 = 在这里很重要:您确实希望每次 make 检查 SOURCES 时都运行 shell 转义(这种情况会发生比你在 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 指令;链接对象时真的需要它们吗?我没有留下它们的原因是您不再只有一个源目录,并且从对象列表中获取源目录列表并非易事(就像在 Makefile 语法中一样,它是可行的,但是真烦人)。

【讨论】:

  • 谢谢!是正确的提示。 VPATH = srcdri1:srcdir2:srcdir3:.. 需要额外的调整,所以像 $(BUILDDIR)/%.o : %.c 这样简单的条件会再次起作用,仅此而已!
  • find 命令的唯一问题是它在 windows 构建中搞砸了,因为它默认使用 windows find 命令。
  • 有没有办法在windows下以兼容MinGW64的方式做到这一点?
【解决方案2】:

递归通配符可以纯粹在 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 的内置 wildcard 函数那样支持多个通配符,但是通过更多地使用 foreach 来添加这种支持会很简单

【讨论】:

  • @Sharpie:这将扩展到 foo/,index.html foo/bar/,index.html 等,而不是 foo/index.html foo/bar/index.html
  • rwildcard=$(wildcard $(addsuffix $2, $1)) $(foreach d,$(wildcard $(addsuffix *, $1)),$(call rwildcard,$d/,$2))它支持 $1 中的多目录
  • 好的,谢谢。小改进:删除[...]$(wildcard $1$2)_$(foreach[...] 中的空格(此处为_)以避免结果开头出现多余的无用空格。
【解决方案3】:

我喜欢做以下事情。

为项目的每个目录创建变量

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 仅存储内部子目录。在清理操作后非常有用,例如:

clean:
    -rm -r $(OBJDIR)/*

注意:只有当每个 *.c 都有对应的 *.h 文件(我的意思是具有相同的基本名称)时,编译规则才能正常工作。

【讨论】:

    【解决方案4】:

    这个问题的另一个很好的解决方案似乎是 - 实现一个非递归的 makefile,例如这里描述的那个:http://sites.e-advies.nl/nonrecursive-make.html。这种方法很好,因为它看起来相当可扩展 - 开发人员可以在包含源文件的目录中添加依赖信息,而不必担心整个 makefile。

    【讨论】:

    • 发布的链接已损坏,答案可以使用方法摘要
    猜你喜欢
    • 1970-01-01
    • 2011-12-30
    • 2014-04-26
    • 2018-11-21
    • 2020-07-30
    • 2022-12-11
    • 1970-01-01
    • 1970-01-01
    • 2015-08-27
    相关资源
    最近更新 更多