我有一个目前在OS X上构建Xcode的项目。我正在尝试构建一个makefile,以允许它在其他Un * x系统上构建。我是编写makefile的新手,所以我一直在用网络上的各种例子拼凑一个makefile,也许不出所料,我无法得到结果。我已经在SO上查看了其他类似的问题,并从中获取了我能得到的信息,但我的情况似乎有点不同,因为
所以请不要将此标记为重复,除非另一个问题真正满足所有这些要求;谢谢。
我还想避免使用makefile生成工具,部分原因是它增加了我目前无法理解的另一层次的复杂性,部分是因为它似乎有点矫枉过正,因为我对此并不感兴趣处理标头依赖关系等(见下面的讨论),部分是因为我需要最大程度的可移植性(我不想依赖特定工具,而不是香草make
)。
我从make -n
得到的错误很简单,所以我的错误可能非常愚蠢:
make: *** No rule to make target `gsl/complex/*.c.o', needed by `slim'. Stop.
但在我看来*.c.o
目标应该由我的%.c.o
规则处理,所以我感到困惑。我的makefile:
SHELL = /bin/sh
CC = gcc
CXX = g++
INCLUDES = -iquote./eidos -iquote./gsl -iquote./gsl/rng -iquote./gsl/randist -iquote./gsl/sys -iquote./gsl/specfunc -iquote./gsl/complex
CCFLAGS = -O3 -v $(INCLUDES)
CXXFLAGS = -O3 -v $(INCLUDES) -std=c++11
OUTPUTDIR = ./bin/
MKDIR = mkdir -p $(OUTPUTDIR)
CSOURCES = ./gsl/*/*.c
SLIM_CXXSOURCES = ./core/*.cpp ./eidos/*.cpp
EIDOS_CXXSOURCES = ./eidostool/*.cpp ./eidos/*.cpp
COBJECTS = $(patsubst %.c, %.c.o, $(CSOURCES))
SLIM_CXXOBJECTS = $(patsubst %.cpp, %.cpp.o, $(SLIM_CXXSOURCES))
EIDOS_CXXOBJECTS = $(patsubst %.cpp, %.cpp.o, $(EIDOS_CXXSOURCES))
all: slim eidos FORCE
slim: $(COBJECTS) $(SLIM_CXXOBJECTS) FORCE
$(MKDIR)
$(CXX) $(COBJECTS) $(SLIM_CXXOBJECTS) -o ./bin/slim
eidos: $(COBJECTS) $(EIDOS_CXXOBJECTS) FORCE
$(MKDIR)
$(CXX) $(COBJECTS) $(EIDOS_CXXOBJECTS) -o ./bin/eidos
%.cpp.o : %.cpp
$(CXX) -c $(CXXFLAGS) -o $@ $<
%.c.o : %.c
$(CC) -c $(CCFLAGS) -o $@ $<
clean: FORCE
$(RM) -rf $(OUTPUTDIR)
$(RM) ./*.o
# Would use .PHONY, but we don't want to depend on GNU make.
# See http://www.gnu.org/software/make/manual/make.html#Phony-Targets
# and http://www.gnu.org/software/make/manual/make.html#Force-Targets
FORCE:
我没有尝试建立每个文件头依赖项或类似的任何智能。相反,我只是在每次构建一个顶级目标(slim,eidos,all)时尝试使用FORCE强制完全重建。这是因为我不希望人们在开发过程中使用这个makefile;所有开发都在Xcode的OS X上完成。因此,它不需要极小/高效,只需要可靠地工作以生产最终产品。我不确定我是否正确使用FORCE。
我也有几个问题。
$<
结尾,但我不知道他们做了什么。那里发生了什么?它看起来像是一个重定向,但它并不是我熟悉的重定向命令,当然Google对于搜索这样的符号事物毫无用处。 patsubst
业务有点棘手,而且我不知道如何判断它是否正常工作。我尝试make -np
打印出make
的内部信息,但它似乎显示了原始定义的变量,而不是其已解析的定义,因此我无法说明最终值是make
实际用于我的变量。我该怎么调试呢? patsubst
是make
的标准部分吗?是否有更好的方法来做我想做的事情?感谢。很抱歉忙碌的多部分问题。顺便说一句,我真正想做的就是将以前存在的makefile转换为单独构建源文件,生成.o目标文件,然后在最后进行链接。以前存在的makefile在一次调用g ++时构建了所有内容,由于内存使用和构建时间过长,导致某些用户出现问题。这是旧的Makefile:
SHELL = /bin/sh
CC = g++
CFLAGS = -O3 -Wno-deprecated-register
INCLUDES = -iquote./eidos -iquote./gsl -iquote./gsl/rng -iquote./gsl/randist -iquote./gsl/sys -iquote./gsl/specfunc -iquote./gsl/complex
ALL_CFLAGS = $(CFLAGS) $(INCLUDES) -std=c++11
all: slim eidos FORCE
slim: FORCE
mkdir -p ./bin
$(CC) $(ALL_CFLAGS) ./core/*.cpp ./eidos/*.cpp ./gsl/*/*.c -o ./bin/slim
eidos: FORCE
mkdir -p ./bin
$(CC) $(ALL_CFLAGS) ./eidostool/*.cpp ./eidos/*.cpp ./gsl/*/*.c -o ./bin/eidos
clean: FORCE
rm -f ./bin/slim
rm -f ./bin/eidos
# Would use .PHONY, but we don't want to depend on GNU make.
# See http://www.gnu.org/software/make/manual/make.html#Phony-Targets
# and http://www.gnu.org/software/make/manual/make.html#Force-Targets
FORCE:
除了前面提到的缓慢和使用大量内存的问题之外,这种方法还可以。
答案 0 :(得分:1)
make
的基本概念实际上非常简单:对于您希望能够构建的每个文件,您可以指定其先决条件,以及如何根据这些先决条件构建目标文件。 Make可以弄清楚如何将多个构建步骤链接在一起,以便从实际存在的资源中创建最终目标,如果事实上有任何方法可以这样做。
虽然GNU make
确实功能强大且已被广泛移植,但我通常建议避免使用特定于make
版本的扩展。仅仅因为你可以获取 GNU make
几乎任何机器都不会减轻混淆,当它不是给定机器上的默认make
时,也不会避免惊慌失措实际上,在构建软件之前,必须先获取并安装GNU
make。并且您的特定项目具有足够简单的结构(尽管有源文件的数量),您不需要GNU扩展。
关于你的资格:
我有大量的源文件,因此明确指定所有源和对象是不切实际的,我需要使用通配符
在评论中你澄清说“大量的源文件”意味着“超过100”,我猜这意味着“大”是在旁观者的眼中。你所描述的当然不小,但它仍然相当温和。我为具有数百个源文件的软件套件构建了基于make的构建系统。
我的一些源代码位于子目录中,并且可以在不同的子目录中使用相同的文件名,因此需要将对象放置在文件层次结构中以避免冲突
这根本不是问题。让自己不知道目录;它主要用于字符串,并让它执行的shell命令适当地解释它们。 target和prerequiste名称包含相对或甚至绝对路径没有特殊问题。
我正在构建C和C ++并在最后将它们链接在一起
同样,这不是一个大问题,或者至少不是make
特有的问题。这里的问题是你如何将C链接到C ++可能依赖于底层的工具链(编译器和链接器)。
我希望能够使用目录中不同的源文件子集构建两个名为“slim”和“eidos”的不同目标
完全没问题。这很常见。
你也注意到了
我只是在每次构建一个顶级目标(slim,eidos,all)时尝试使用FORCE强制完全重建。这是因为我不希望人们在开发过程中使用这个makefile;所有开发都在Xcode的OS X上完成。因此,它不需要极小/高效,只需要可靠地工作以生产最终产品。我不确定我是否正确使用FORCE。
对不起,但这没有意义。如果人们在开发期间没有使用makefile,那么预期的用例是什么都没有构建,所以不需要强制。另一方面,通过强制获得了什么?不多。强制使用的唯一理由是,有关项目或make文件的内容使make
无法正确评估需要重建的目标。无论如何,如果你提供了一个合适的clean
目标,那么只要这是他们想要的,用户就可以轻松地获得一个干净的构建。为用户提供投资力量。它得到了回报。
我拿了$&lt;来自web上的示例makefile的两个编译命令的结尾,但我不知道他们做了什么。那里发生了什么?
Make提供一些自动变量;其中一个名为<
。在构建规则中,通过语法$<
访问的值将扩展为当前规则的第一个先决条件的名称。
patsubst业务有点棘手,我不知道如何判断它是否正常工作。
您不需要它,但您可以通过在echo
其结果的构建规则中包含命令来测试它是否正常工作。
这是makefile
的模板,可能适合您:
CC = gcc
CXX = g++
INCLUDES = -Ieidos -Igsl -Igsl/rng -Igsl/randist -Igsl/sys -Igsl/specfunc -Igsl/complex
CFLAGS = -O3 -Wno-deprecated-register
CXXFLAGS = $(CFLAGS) -std=c++11
CSOURCES = \
gsl/subdir/something.c \
gsl/subdir/something_else.c \
gsl/dir2/important.c
COMMON_CXXSOURCES = \
eidos/file1.cpp \
eidos/file2.cpp
SLIM_CXXSOURCES = \
core/slim1.cpp \
core/slim2.cpp \
core/slim3.cpp
EIDOS_CXXSOURCES = \
eidostool/et.cpp \
eidostool/et42.cpp \
eidostool/phone_home.cpp
COMMON_OBJS = $(CSOURCES:.c=.o) $(COMMON_CXXSOURCES:.cpp=.o)
SLIM_OBJS = $(SLIM_CXXSOURCES:.cpp=.o)
EIDOS_OBJS = $(EIDOS_CXXSOURCES:.cpp=.o)
all: slim eidos
# NOTE: commands in build rules must start with a tab character, which
# will not be conveyed via the web representation of what follows.
# How to link object files together to build slim, using the C++ compiler as
# the linker driver
slim: $(SLIM_OBJS) $(COMMON_OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
# How to link object files together to build eidos, using the C++ compiler as
# the linker driver
eidos: $(EIDOS_OBJS) $(COMMON_OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
# remove all built files
clean:
rm -f slim eidos $(COMMON_OBJS) $(SLIM_OBJS) $(EIDOS_OBJS)
# You might not actually need anything below, because `make` has built-in
# rules for building object files from source files of various kinds, based
# on their extensions.
# How to build an object file (x.o) from a corresponding C source file (x.c)
.c.o:
$(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $<
# How to build an object file from a corresponding C++ source file (x.cpp)
.cpp.o:
$(CXX) $(INCLUDES) $(CXXFLAGS) -c -o $@ $<
关于通配符的注意事项:我建议不要使用它们,而是明确指定需要在构建中包含哪些源。是的,这成为您必须维护的东西,但它也允许您在树中拥有对构建没有贡献的源文件。这些可以是备份副本,工作副本等。只要您在发布之前通过make
测试构建项目,就应该很容易发现遗漏。
答案 1 :(得分:1)
不完全确定这对你有用,你的设置中可能会有一些惊喜并不是很明显。
但是,这应该让您了解如何构建Makefile
,使用GNU Make
选择不同的源/对象集。
CC = gcc
CXX = g++
INCLUDES = -iquote./eidos -iquote./gsl -iquote./gsl/rng -iquote./gsl/randist -iquote./gsl/sys -iquote./gsl/specfunc -iquote./gsl/complex
CCFLAGS = -O3
CXXFLAGS = -O3 -std=c++11
CPPFLAGS = $(INCLUDES)
OUTPUTDIR = ./bin/
MKDIR = mkdir -p $(OUTPUTDIR)
# shell find command is good for multiple level
# directory searching
CSOURCES := $(shell find gsl -name "*.c")
# wildcard selects all matching files
SLIM_SOURCES = \
$(CSOURCES) \
$(wildcard core/*.cpp) \
$(wildcard eidos/*.cpp)
# filter selects one type of file for substitution
# I use it here to prevent source files that don't match the
# pattern from being included
SLIM_OBJECTS := \
$(patsubst %.c,%.c.o,$(filter %.c,$(SLIM_SOURCES))) \
$(patsubst %.cpp,%.cpp.o,$(filter %.cpp,$(SLIM_SOURCES)))
EIDOS_SOURCES = \
$(CSOURCES) \
$(wildcard eidostool/*.cpp) \
$(wildcard eidos/*.cpp)
EIDOS_OBJECTS := \
$(patsubst %.c,%.c.o,$(filter %.c,$(EIDOS_SOURCES))) \
$(patsubst %.cpp,%.cpp.o,$(filter %.cpp,$(EIDOS_SOURCES)))
all: $(OUTPUTDIR)/slim $(OUTPUTDIR)/eidos
# I often find a show target useful for complex makefiles
# to figure out if you are setting the variables correctly
show:
@echo "SLIM_SOURCES: $(SLIM_SOURCES)"
@echo "SLIM_OBJECTS: $(SLIM_OBJECTS)"
@echo "EIDOS_SOURCES: $(EIDOS_SOURCES)"
@echo "EIDOS_OBJECTS: $(EIDOS_OBJECTS)"
$(OUTPUTDIR)/slim: $(SLIM_OBJECTS)
$(CXX) -o $@ $^ $(LDFLAGS)
$(OUTPUTDIR)/eidos: $(EIDOS_OBJECTS)
$(CXX) -o $@ $^ $(LDFLAGS)
%.cpp.o : %.cpp
$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o $@ $<
%.c.o : %.c
$(CC) -c $(CCFLAGS) $(CPPFLAGS) -o $@ $<
clean:
$(RM) $(OUTPUTDIR)/slim $(OUTPUTDIR)/eidos $(SLIM_OBJECTS) $(EIDOS_OBJECTS)