如何为带有SRC,OBJ和BIN子目录的C项目创建Makefile?


94

几个月前,我想出了以下通用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和出色的ol'终端。

是否有一位上师可以给我一个可行的解决方案,或者提供有关如何实现的明确信息?谢谢!

** 更新(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!"

这里的具体问题是什么?
奥利弗·查尔斯沃思

我不确定我知道你想做什么。
汤姆(Tom)”,

更新了Makefile。我越来越接近,但我与自动变量的麻烦,如此看来无论如何
Yanick罗森

我刚刚找到了解决方案。如果有人希望找到更好的东西,Makefile仍然可以改进。
Yanick Rochon的

2
@YanickRochon我不是要批评您的英语水平。但是要使PHONY目标有意义,您绝对不能编写BANANA;)gnu.org/software/make/manual/html_node/Phony-Targets.html
joni

Answers:


34

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

  1. 这是不加区别的,使所有资源成为每个对象的先决条件,
  2. 它通常使用错误的来源(如您与file1.o和发现的一样file2.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

@Yanick Rochon:你有多种main功能吗?也许一进file1.c一出main.c?如果是这样,那么您将无法链接这些对象;main可执行文件中只能有一个。
Beta

不,我没有。我在问题中发布的最新版本一切正常。当我将Makefile更改为您的建议(并且我确实理解您所说的内容的好处)时,这就是我所得到的。我只是粘贴了,file1.c但是它给项目的每个文件都提供了相同的信息。并且main.c唯一一个具有主要功能的人... and main.cimport file1.hfile2.hfile1.cand 之间没有关系file2.c),但我怀疑问题是否出在那儿。
亚尼克·罗雄

@Yanick Rochon:我在粘贴$(OBJECTS)规则的第一行时犯了一个错误;我已经编辑了 在错误的行中,我得到了一个错误,但是却没有得到一个...
Beta

6

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

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

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

为了将目标文件放入obj目录,请-o在编译时使用该选项。另外,查看$@$< 自动变量

例如,考虑这个简单的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)的论点
Yanick Rochon

是的,CFLAGS,是的,继续使用-o,只需添加TARGETPATH变量。
汤姆(Tom),

谢谢,我已经进行了修改,但似乎仍然缺少某些内容(请参阅问题的更新)
Yanick Rochon

只是makeMakefile所在的位置
Yanick Rochon

您无法阅读正在执行的命令吗?例如gcc -c yadayada。可以肯定,有一个变量不包含您的期望
汤姆(Tom)

-1

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

https://github.com/dmoulding/boilermake我觉得这个很好。


3
基于意见。不回答OP的问题。假设Eclipse环境。
纳撒尼尔·约翰逊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.