如何使用子目录中的源制作C和C ++的makefile [英] How to make a makefile for C and C++, with sources in subdirectories

查看:63
本文介绍了如何使用子目录中的源制作C和C ++的makefile的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个当前在OS X上的Xcode中构建的项目.我正在尝试构造一个makefile以使其可以在其他Un * x系统上构建.我是编写makefile的新手,所以我一直在网上整理各种示例中的makefile,也许并不奇怪,我无法获得结果.我在SO上查看了其他类似的问题,并从中收集了我所能提供的信息,但是我的处境似乎有所不同,因为

I have a project that presently builds in Xcode on OS X. I'm trying to construct a makefile to allow it to build on other Un*x systems. I'm a novice at writing makefiles, so I have been cobbling together a makefile from various examples on the web, and perhaps unsurprisingly, I can't get the result to work. I've looked at other similar questions on SO and gleaned what I can from them, but my situation seems to be a bit different because

  1. 我有大量的源文件,因此明确指定所有源和对象是不切实际的,我需要使用通配符
  2. 我的某些源文件位于子目录中,并且相同的文件名可用于不同的子目录中,因此需要将对象按其源文件放置在文件层次结构中,以免发生冲突
  3. 我正在构建C和C ++,并在最后将它们链接在一起
  4. 我希望能够使用目录中源文件的不同子集来构建两个不同的目标,分别为"slim"和"eidos".

因此,除非另一个问题确实解决了所有这些要求,否则请勿将其标记为重复项.谢谢.

So please don't mark this as a dup unless the other question really addresses all of these requirements; thanks.

我也想避免使用makefile生成工具,部分是因为它增加了我目前尚不了解的另一种复杂程度,部分是因为我对处理标头依赖性不感兴趣,这似乎有点过头了.等等(请参阅下面的讨论),部分是因为我需要最大的可移植性(我不想依赖香草make以外的特定工具).

I would also like to avoid the use of makefile-generation tools, partly because it adds another level of complexity that I don't presently understand, and partly because it seems like overkill since I'm not interested in handling header dependencies and such (see discussion below), and partly because I need maximum portability (I don't want to depend on particular tools being present beyond vanilla make).

我从make -n遇到的错误很简单,所以我的错误可能非常愚蠢:

The error I'm getting from make -n is a simple one, so my mistake is probably very dumb:

make: *** No rule to make target `gsl/complex/*.c.o', needed by `slim'.  Stop.

但是在我看来,*.c.o目标应该由我的%.c.o规则处理,所以我感到困惑.我的makefile文件:

But it seems to me that *.c.o targets ought to be handled by my %.c.o rule, so I'm puzzled. My 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.所有开发工作均在OS X上以Xcode完成.因此,它不必是最小/高效的,它只需要可靠地工作即可生产出最终产品.不过,我不确定我是否正确使用了FORCE.

I'm not trying to establish per-file header dependencies or anything smart like that. Instead, I'm just trying to use FORCE to force a full rebuild each time one of the top-level targets (slim, eidos, all) is built. This is because I don't expect people to use this makefile during development; all development is done on OS X in Xcode. So it doesn't need to be minimal/efficient, it just needs to work reliably to produce a final product. I'm not sure that I'm using FORCE correctly, though.

我还有两个附带问题.

I also have a couple of side questions.

  1. 我从网络上的示例makefile中获取了两个编译命令的$<结尾,但是我不知道它们的作用.那里发生了什么事?它看起来像一个重定向,但不是我熟悉的重定向命令,当然Google对于搜索这样的符号性内容毫无用处.
  2. patsubst业务有点棘手,我不知道如何判断它是否正常工作.我尝试使用make -np打印出make的内部信息,但似乎显示的变量具有其原始定义,而不具有其已解析的定义,因此我无法确定make的最终值实际上是什么用于我的变量.我该如何调试? patsubstmake的标准部分吗?有没有更好的方法可以做我想做的事?
  1. I took the $< endings for the two compilation commands from example makefiles on the web, but I don't know what they do. What's going on there? It looks like a redirection, but it's not a redirection command I'm familiar with, and of course Google is useless for searching for symbolic things like this.
  2. The patsubst business is a bit tricky, and I don't know how to tell whether it's working or not. I tried make -np to print out make's internal info, but it seems to show variables with their original definitions, not with their resolved definitions, so I can't tell what the final value is that make will actually use for my variables. How can I debug this? And is patsubst a standard part of make? Is there a better way to do what I'm trying to do there?

谢谢.对不起,这个忙碌的多部分问题很抱歉.顺便说一句,我真正想做的就是将以前存在的makefile转换为单独生成源文件,生成.o目标文件,然后最后链接.先前存在的makefile通过一次对g ++的调用来构建所有内容,这由于过多的内存使用和构建时间而给某些用户带来了问题.这是旧的Makefile:

Thanks. Sorry for the hectic multi-part question. By the way, all I'm really trying to do is convert the previously existing makefile to build source files individually, producing .o object files, and then link at the end. The previously existing makefile built everything in a single call to g++, which is causing problems for some users because of excessive memory usage and build time. Here's the old 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:

除了上述提到的速度慢和占用大量内存的问题之外,效果还不错.

That works fine, apart from the aforementioned issue of being slow and using a ton of memory.

推荐答案

make的基本概念实际上非常简单:对于要构建的每个文件,请指定其先决条件以及如何进行构建.根据这些先决条件构造目标文件. Make可以弄清楚如何将多个构建步骤链接在一起,以从实际存在的资源中创建您要求的最终目标,如果有的话,可以这样做.

The basic concept of make is actually quite simple: for each file you want to be able to build, you specify what its prerequisites are, and how to construct the target file from those prerequisites. Make can figure out how to chain together multiple build steps to create the ultimate target you request from the resources actually present, if in fact there as any way to do so.

尽管GNU make确实功能强大且已被广泛移植,但我通常建议避免使用特定于make版本的扩展.仅仅因为您可以为几乎任何计算机获取 GNU make都不会缓解混乱,而这并不是给定计算机上的默认make,如果确实需要,它也不会避免混乱在能够生成软件之前,请获取并安装GNU make.而且您的特定项目具有足够简单的结构(尽管有许多源文件),因此您不需要GNU扩展.

Although GNU make is indeed powerful, and has been widely ported, I generally recommend avoiding extensions specific to that version of make. Just because you can get GNU make for just about any machine does not alleviate confusion when that is not the default make on a given machine, nor does it stave off consternation if one actually has to obtain and install GNU make before being able to build your software. And your particular project has a simple enough structure (number of source files notwithstanding) that you don't need GNU extensions.

关于您的资格:

我有大量的源文件,因此明确指定所有源和对象是不切实际的,我需要使用通配符

I have a large number of source files, so specifying all sources and objects explicitly is not practical, I need to use wildcards

在注释中,您澄清了大量源文件"的意思是超过100个",我猜这意味着大"在旁观者的眼中.您所描述的内容肯定不小,但仍然相当适中.我已经为包含数百个源文件的软件套件构建了基于make的构建系统.

In comments you clarified that "a large number of source files" means "more than 100", which I guess means that "large" is in the eye of the beholder. What you're describing is certainly not small, but it's nevertheless fairly modest. I've built make-based build systems for software suites with many hundreds of source files.

我的某些源文件位于子目录中,并且相同的文件名可用于不同的子目录中,因此需要将对象按其源文件放置在文件层次结构中,以免发生冲突

Some of my sources are in subdirectories, and the same filename can be used in different subdirectories so objects need to be placed by their source files within the file hierarchy to avoid collisions

这根本不是问题.使自己并不真正了解目录.它主要与字符串一起使用,并让它执行的shell命令适当地解释它们.包含相对或什至绝对路径的目标名称和先决条件名称没有特殊问题.

This is not a problem at all. Make itself doesn't really know about directories; it works mainly with strings, and lets the shell commands it executes interpret those as appropriate. There is no special problem with target and prerequiste names that contain relative or even absolute paths.

我正在构建C和C ++,并在最后将它们链接在一起

I'm building both C and C++ and linking it together at the end

同样,这不是大问题,或者至少不是特定于make的问题.这里的问题是,如何将C链接到C ++可能取决于基础工具链(编译器和链接器).

Again, not a big problem, or at least not one specific to make. The problem here is that how you link C to C++ may be dependent on the underlying toolchain (compilers and linker).

我希望能够使用目录中源文件的不同子集构建两个不同的目标,分别为"slim"和"eidos"

I want to be able to build two different targets, named "slim" and "eidos", using different subsets of the source files in the directory

完全没有问题.这很常见.

No problem at all. This is very common.

您也要注明

我只是在每次构建一个顶级目标(slim,eidos,all)时都试图使用FORCE强制进行完全重建.这是因为我不希望人们在开发过程中使用此makefile.所有开发工作均在OS X上以Xcode完成.因此,它不必是最小/高效的,它只需要可靠地工作即可生产出最终产品.不过,我不确定我是否正确使用了FORCE.

I'm just trying to use FORCE to force a full rebuild each time one of the top-level targets (slim, eidos, all) is built. This is because I don't expect people to use this makefile during development; all development is done on OS X in Xcode. So it doesn't need to be minimal/efficient, it just needs to work reliably to produce a final product. I'm not sure that I'm using FORCE correctly, though.

对不起,但这没有任何意义.如果人们在开发过程中不使用makefile,则预期的用例是尚未构建任何内容,因此不需要强制.另一方面,通过强迫获得了什么呢?不多.强制执行的唯一原因是项目或make文件中的某些内容使make无法正确评估需要重建的目标.无论如何,如果您提供适当的clean目标,那么只要他们想要的话,用户都可以轻松获得干净的构建.投资于您的用户.它会得到回报.

I'm sorry, but that just doesn't make sense. If people aren't using the makefile during development, then the expected use case is that nothing has yet been built, so not forcing is needed. On the other hand, what is gained by forcing? Not much. The only reason to force is that something about your project or make file makes it impossible for make to correctly evaluate which targets need to be rebuilt. In any event, if you provide a proper clean target then the user can easily get a clean build whenever that's what they want. Invest power in your users. It pays off.

我拿了$< Web上示例文件的两个编译命令的结尾,但是我不知道它们的作用.那里发生了什么事?

I took the $< endings for the two compilation commands from example makefiles on the web, but I don't know what they do. What's going on there?

Make提供了一些自动变量;其中之一被命名为<.在构建规则中,通过语法$<访问的其值将扩展为当前规则的第一个前提条件的名称.

Make provides a few automatic variables; one of them is named <. In a build rule, its value, accessed via the syntax $<, expands to the name of the first prerequisite of the current rule.

patsubst业务有点棘手,我不知道如何判断它是否有效.

The patsubst business is a bit tricky, and I don't know how to tell whether it's working or not.

您不需要它,但您可以通过在其中一个生成规则中的生成规则中包含一个命令来测试它是否正常工作.

You don't need it, but you can test whether it's working by including a command in one of your build rules that echos its result.

以下是makefile可能适合您的模板:

Here's a template for a makefile that would probably work for you:

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测试构建项目,就很容易发现遗漏.

Note about wildcards: I recommend not using them, and instead specifying explicitly which sources need to be included in the build. Yes, that becomes something you have to maintain, but it also allows you to have source files in your tree that don't contribute to the build. These could be backup copies, working copies, etc.. And as long as you test building the project via make before you release it, it should be easy to catch omissions.

这篇关于如何使用子目录中的源制作C和C ++的makefile的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆