【问题标题】:How can I create a Makefile for C projects with SRC, OBJ, and BIN subdirectories?如何为具有 SRC、OBJ 和 BIN 子目录的 C 项目创建 Makefile?
【发布时间】:2011-10-23 16:46:28
【问题描述】:

几个月前,我想出了以下通用Makefile 用于学校作业:

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2010-11-05
#
# Changelog :
#   0.01 - first version
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc -std=c99 -c
# compiling flags here
CFLAGS   = -Wall -I.

LINKER   = gcc -o
# linking flags here
LFLAGS   = -Wall

SOURCES  := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS  := $(SOURCES:.c=*.o)
rm       = rm -f

$(TARGET): obj
    @$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

obj: $(SOURCES) $(INCLUDES)
    @$(CC) $(CFLAGS) $(SOURCES)
    @echo "Compilation complete!"

clean:
    @$(rm) $(TARGET) $(OBJECTS)
    @echo "Cleanup complete!"

这将基本上编译每个.c.h文件以生成.o文件和可执行文件projectname都在同一个文件夹中。

现在,我想稍微推动一下。 如何编写 Makefile 来编译具有以下目录结构的 C 项目?

 ./
 ./Makefile
 ./src/*.c;*.h
 ./obj/*.o
 ./bin/<executable>

换句话说,我想要一个 Makefile,它将 C 源代码从 ./src/ 编译成 ./obj/,然后链接所有内容以在 ./bin/ 中创建可执行文件。

我尝试阅读不同的 Makefile,但我根本无法使它们适用于上述项目结构;相反,该项目无法编译并出现各种错误。当然,我可以使用成熟的 IDE(Monodevelop、Anjuta 等),但老实说,我更喜欢使用 gEdit 和良好的终端。

有没有大师可以给我一个可行的解决方案,或者关于如何做到这一点的明确信息?谢谢!

** 更新 (v4) **

最终解决方案:

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2011-08-10
#
# Changelog :
#   2010-11-05 - first version
#   2011-08-10 - added structure : sources, objects, binaries
#                thanks to http://stackoverflow.com/users/128940/beta
#   2017-04-24 - changed order of linker params
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc
# compiling flags here
CFLAGS   = -std=c99 -Wall -I.

LINKER   = gcc
# linking flags here
LFLAGS   = -Wall -I. -lm

# change these to proper directories where each file should be
SRCDIR   = src
OBJDIR   = obj
BINDIR   = bin

SOURCES  := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm       = rm -f


$(BINDIR)/$(TARGET): $(OBJECTS)
    @$(LINKER) $(OBJECTS) $(LFLAGS) -o $@
    @echo "Linking complete!"

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    @$(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

.PHONY: clean
clean:
    @$(rm) $(OBJECTS)
    @echo "Cleanup complete!"

.PHONY: remove
remove: clean
    @$(rm) $(BINDIR)/$(TARGET)
    @echo "Executable removed!"

【问题讨论】:

  • 这里的具体问题是什么?
  • 我不确定我是否理解你想要做什么。
  • 更新了Makefile。我快接近了,但自动变量有问题,所以无论如何
  • 我刚刚找到了解决方案。如果有人想找到更好的东西,Makefile 仍然可以改进。
  • @YanickRochon 我并不是要批评你的英语技能。但是为了让 PHONY 目标有意义,你绝对不能写 BANANA ;) gnu.org/software/make/manual/html_node/Phony-Targets.html

标签: c makefile


【解决方案1】:

首先,您的$(OBJECTS) 规则有问题,因为:

  1. 有点不分青红皂白,让所有源成为每个对象的先决条件,
  2. 它经常使用错误的来源(正如你在file1.ofile2.o 发现的那样)
  3. 它尝试构建可执行文件而不是停止在对象上,并且
  4. 目标名称 (foo.o) 不是规则实际生成的名称 (obj/foo.o)。

我建议如下:

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

$(TARGET) 规则存在相同的问题,即目标名称实际上并未描述规则构建的内容。因此,如果您多次键入make,Make 将每次都重建目标,即使没有理由这样做。一个小改动解决了以下问题:

$(BINDIR)/$(TARGET): $(OBJECTS)
    $(LINKER) $@ $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

一切就绪后,您可能会考虑更复杂的依赖关系处理;如果您修改其中一个头文件,则此 makefile 将不知道必须重建哪些对象/可执行文件。但这可以再等一天。

编辑:
抱歉,我省略了上面$(OBJECTS) 规则的一部分;我已经纠正了。 (我希望我可以在代码示例中使用“strike”。)

【讨论】:

  • 根据您建议的更改,我得到:obj/file1.o: In function 'main': \n main.c:(.text+0x0): multiple definition of 'main' \n obj/main.o:main.c:(.text+0x0): first defined here
  • @Yanick Rochon:你有多个main 函数吗?也许file1.c 中的一个和main.c 中的一个?如果是这样,那么您将无法链接这些对象;一个可执行文件中只能有一个main
  • 不,我没有。我在问题中发布的最后一个版本一切正常。当我将我的 Makefile 更改为您的建议时(我确实理解您所说的好处),这就是我得到的。我刚刚粘贴了file1.c,但它为项目的每个文件提供了相同的信息。而main.c唯一的 一个具有主要功能的...并且main.c 导入file1.hfile2.hfile1.cfile2.c 之间没有关系),但我怀疑问题出在哪里。
  • @Yanick Rochon:我在粘贴$(OBJECTS) 规则的第一行时出错了;我已经编辑过了。用坏线我得到一个错误,但不是你得到的那个......
【解决方案2】:

您可以将 -I 标志添加到编译器标志 (CFLAGS) 以指示编译器应在何处查找源文件,并添加 -o 标志以指示应将二进制文件留在何处:

CFLAGS   = -Wall -I./src
TARGETPATH = ./bin

$(TARGET): obj
    @$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

为了将目标文件放入obj 目录,请在编译时使用-o 选项。另外,请查看$@$&lt; automatic variables

例如,考虑这个简单的 Makefile

CFLAGS= -g -Wall -O3                                                            
OBJDIR= ./obj

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)

%.o: %.c 
   $(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@

更新>

通过查看您的 makefile,我意识到您正在使用 -o 标志。好的。继续使用它,但添加一个目标目录变量来指示输出文件的写入位置。

【讨论】:

  • 你能说得更具体点吗?您的意思是将-l ... 添加到CFLAGS 并且...链接器已经有-o 参数(LINKER
  • 是的,CFLAGS,是的,继续使用 -o,只需添加 TARGETPATH 变量。
  • 谢谢您,我已经进行了修改,但似乎我仍然缺少一些东西(请参阅问题的更新)
  • 只是 make,Makefile 所在的位置
  • 你看不懂正在执行的命令吗?例如 gcc -c yadayada。很确定有一个变量不包含您的期望
【解决方案3】:

这些天我已经停止编写 makefile 了,如果你打算继续学习,否则你有 eclipse CDT 附带的好的 makefile 生成器。如果您想在构建树中获得一些可维护性/多项目支持,请查看以下内容 -

https://github.com/dmoulding/boilermake我觉得这个不错..!

【讨论】:

  • 基于意见。不回答OP的问题。假设 Eclipse 环境。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-16
  • 1970-01-01
  • 2021-05-19
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多