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

dra*_*hnr 54 tree makefile

我正在做一个快速增长的项目,保持目标文件的日期是没有选择的.wildcard命令之外的问题介于"我不想要递归的makefile"和"我不希望它手动列出"之间.这些对象应该进入一个单独的目录,该目录已经可以使用.注:我不是用的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"
Run Code Online (Sandbox Code Playgroud)

感谢答案#1,归结为如何解决这个问题:

$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
    $(CC) $(CFLAGS) $(LDFLAGS) $(SOURCETREE) -c $< -o $@
Run Code Online (Sandbox Code Playgroud)

特别是在BUILDDIR = build和SOURCEDIR的情况下,必须用来自SOURCES的单个.c文件替换它们的路径:/

Ben*_*Ben 78

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

SOURCES := $(shell find $(SOURCEDIR) -name '*.c')
Run Code Online (Sandbox Code Playgroud)

这将为您提供包含路径的源文件列表.请注意,使用立即赋值:=而不是递归赋值=在这里很重要:您希望每次SOURCES被make检查时都运行shell转义(这比Makefile中的想法要多得多).我觉得有用的一般规则是始终使用立即赋值,除非我实际上需要递归扩展(这很少见;看起来这个例子中的所有赋值都可以是立即的).这意味着使用递归赋值也是一个有用的信号,需要仔细使用该变量.

回到你的问题.你接下来要做什么取决于你是否想在构建树中使用源代码树的镜像,或者构建目录是否应该包含所有源文件的目标文件的平面列表,或者是否需要单独的构建目录在树的每个源目录下.

假设您需要镜像构建树,您可以执行以下操作:

# 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 $@
Run Code Online (Sandbox Code Playgroud)

这并没有完全考虑到作业的完全复杂性,因为它不能确保构建树中的目录实际存在(在Makefile语法中这将是非常痛苦的).

我从$(BINARY)构建规则中删除了-I指令; 链接对象时你真的需要它们吗?我没有留下它们的原因是你不再只有一个源目录了,从对象列表中获取源dirs列表是非常重要的(就像在Makefile语法中那样,它是可行的但是真烦人).

  • find命令的唯一问题是在Windows构建中混乱,因为默认情况下使用Windows find命令。 (2认同)

Lig*_*ruk 48

递归通配符可以完全在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)
Run Code Online (Sandbox Code Playgroud)

文件夹名称中的尾部斜杠是必需的.这个rwildcard函数不像Make的内置通配符函数那样支持多个通配符,但是通过foreach的更多用法添加该支持将是直截了当的.

  • @Sharpie:那会扩展到`foo /,index.html foo/bar /,index.html`等,而不是`foo/index.html foo/bar/index.html` (2认同)
  • 好的,谢谢.几点改进:删除`[...] $(通配符$ 1 $ 2)_ $(foreach [...]`中的空格(这里是`_`),以避免在结果开始时出现额外的无用空格. (2认同)

小智 14

我喜欢做以下事情.

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

SRCDIR = src                                                           
OBJDIR = obj
LIBDIR = lib
DOCDIR = doc
HDRDIR = include

CFLAGS = -g -Wall -O3
Run Code Online (Sandbox Code Playgroud)

仅递归获取SRCDIR的内部结构

STRUCTURE := $(shell find $(SRCDIR) -type d)     
Run Code Online (Sandbox Code Playgroud)

获取STRUCTURE变量中的所有文件

CODEFILES := $(addsuffix /*,$(STRUCTURE))
CODEFILES := $(wildcard $(CODEFILES))            
Run Code Online (Sandbox Code Playgroud)

仅过滤掉特定文件

# 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))
Run Code Online (Sandbox Code Playgroud)

现在是创建规则的时候了

compile: $(OBJFILES)

$(OBJDIR)/%.o: $(addprefix $(SRCDIR)/,%.c %.h)
    $(CC) -c $< -o $@ $(CFLAGS) 
Run Code Online (Sandbox Code Playgroud)

使用这种方法,您可以看到我仅使用STRUCTURE变量来获取SRCDIR目录中的文件,但它也可以用于其他目的,例如,一旦STRUCTURE仅存储内部子目录,则在OBJDIR内镜像SRCDIR.目录.在清洁操作之后它非常有用,例如:

clean:
    -rm -r $(OBJDIR)/*
Run Code Online (Sandbox Code Playgroud)

注意:编译规则只适用于每个*.c都有相应的*.h文件(具有相同的基本名称,我的意思).