更改旧的 makefile 系统以利用并行编译

发布于 2024-10-15 02:15:16 字数 1200 浏览 1 评论 0原文

我们使用Microsoft NMAKE编译大量的原生C++和一些Intel Fortran文件。通常,makefile 包含如下行(对于每个文件):

$(LINKPATH)\olemisc.obj : ole2\olemisc.cpp $(OLEMISC_DEP)
$(CCDEBUG) ole2\olemisc.cpp
$(GDEPS) ole2\olemisc.cpp

OLEMISC_DEP =\
e:\ole2\ifaceole.hpp\
e:\ole2\cpptypes.hpp\
等等。

它工作正常,但一次编译一个文件。我们希望利用多核处理器并一次编译多个文件。请提供一些有关实现这一目标的最佳方法的建议,我将不胜感激。这是我到目前为止所拥有的。

一: GNU make 允许您使用 --jobs=2 选项执行并行作业,这与 GCC 配合得很好(遗憾的是我们不能使用 GCC)。但微软的NMAKE似乎不支持这样的选项。这两个名称程序的兼容性如何?如果我们确实开始使用 GNU MAKE,您可以同时运行两个 cl.exe 进程吗?我希望他们抱怨 PDB(调试)文件被锁定,或者较新的 cl.exe 命令行参数之一是否可以解决这个问题?

二: cl.exe 有一个 /MP(使用多个进程构建)标志,如果通过命令行一起传递,它可以让您同时编译多个文件,例如:

cl /MP7 a。 cpp b.cpp c.cpp d.cpp e.cpp

但使用它需要更改 makefile。我们的 make 文件是由我们自己的程序从其他文件生成的,因此我可以轻松更改我们放入 makefile 中的内容。但是,如何在 makefile 中将不同 cpp 文件的依赖项组合在一起,以便通过一次 cl.exe 调用将它们编译在一起?每个 .obj 都是一个不同的目标,有一组命令来实现它?

或者我是否将 makefile 更改为不调用 cl.exe,而是调用我们编写的其他一些小可执行文件,然后将一系列 .cpp 文件收集在一起并通过 shell 传递给 cl.exe 传递多个参数?这会起作用并且似乎可行,但也似乎过于复杂,我看不到其他人这样做。

我错过了一些明显的东西吗?一定有更简单的方法来实现这一点吗?

我们不使用 Visual Studio 或解决方案文件进行编译,因为文件列表很广泛,我们的 makefile 中有一些特殊项目,理论上不希望与 MS C++ 等过度依赖。

We use Microsoft NMAKE to compile a large number of native C++ and some Intel Fortran files. Typically the makefiles contains lines such as this (for each file):

$(LINKPATH)\olemisc.obj : ole2\olemisc.cpp $(OLEMISC_DEP)
$(CCDEBUG) ole2\olemisc.cpp
$(GDEPS) ole2\olemisc.cpp

OLEMISC_DEP =\
e:\ole2\ifaceole.hpp\
e:\ole2\cpptypes.hpp\
etc.

It works fine, but compiles one file at a time. We would like to take advantage of multi core processors and compile more than one file at a time. I would appreciate some advice about the best way to make that happen, please. Here is what I have so far.

One: GNU make lets you execute parallel jobs using the --jobs=2 option for example and that works fine with GCC (we cant use GCC sadly). But Microsoft's NMAKE does not seem to support such an option. How compatible would the two name programs be, and if we did start using GNU MAKE, can you run two cl.exe processes at the same time? I would expect them to complain about the PDB (debug) file being locked, or does one of the newer cl.exe command line arguments get you around that?

Two: cl.exe has a /MP (build with multiple processes) flag, which lets you compile multiple files at the same time if passed together via the command line, for example:

cl /MP7 a.cpp b.cpp c.cpp d.cpp e.cpp

But using this would require changes to the makefile. Our make files are generated by a our own program from other files, so I can easily change what we put in the makefiles. But how do you combine the dependencies from different cpp files together in the makefile so they get compiled together via one cl.exe call? Each .obj is a different target with a set of commands to make it?

Or do I change the makefile to not call cl.exe, but rather some other little executable that we write, which then collects a series of .cpp files together and shells out to cl.exe passing multiple arguments? That would work and seems doable, but also seems overly complicated and I cant see anyone else doing that.

Am I missing something obvious? There must be a simpler way of accomplishing this?

We do not use Visual Studio or a solution file to do the compiles, because the list of files is extensive, we have a few special items in our makefiles, and theoretically do not want to be overly tied to MS C++ etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

枉心 2024-10-22 02:15:16

我强烈推荐 Windows 上的 GNU make。我倾向于使用 cygwin make,因为它创建的环境非常可以移植到类 Unix 平台(首先是 Mac 和 Linux)。使用 Microsoft 工具链进行并行编译,具有 100% 准确的依赖关系和 CPU 使用率,效果非常好。不过,您还有其他要求。

就您的nmake问题而言,请查找手册中的批处理模式推理规则。基本上,nmake 能够调用 C 编译器一次,一次性传递一整套 C 文件。因此,您可以使用编译器的 /MP... 类型开关。

编译器内置并行编译?呸!我说,非常破碎。无论如何,这是一个框架:

OBJECTS = a.obj b.obj c.obj
f.exe: $(OBJECTS)
    link $** -o $@

$(OBJECTS): $(@R).c

# "The only syntactical difference from the standard inference rule
# is that the batch-mode inference rule is terminated with a double colon (::)."
.c.obj::
    cl -c /MP4 
lt;

编辑

如果每个 .obj 都有自己的依赖项(可能!),那么您只需将它们添加为单独的依赖项行(即,它们不附加任何 shell 命令)。

a.obj: b.h c.h ../include/e.hpp
b.obj: b.h ../include/e.hpp
    ∶

通常这样的样板是由另一个工具生成的,并!INCLUDE添加到主makefile中。如果你很聪明,那么你可以在编译时免费生成这些依赖项。 (如果你走到这一步,那么nmake开始在接缝处吱吱作响,你也许应该更改为GNU make。)

I thoroughly recommend GNU make on windows. I tend to use cygwin make as the environment it creates tends to be very portable to Unix-like platforms (Mac and Linux for a start). Compiling using the Microsoft toolchain, in parallel and with 100% accurate dependencies and CPU usage works very well. You have other requirements though.

As far as your nmake question goes, look up batch-mode inference rules in the manual. Basically, nmake is able to call the C compiler once, passing it a whole load of C files in one go. Thus you can use the compiler's /MP... type switches.

Parallel compiling built into the compiler? Pah! Horribly broken I say. Here is a skeleton anyway:

OBJECTS = a.obj b.obj c.obj
f.exe: $(OBJECTS)
    link $** -o $@

$(OBJECTS): $(@R).c

# "The only syntactical difference from the standard inference rule
# is that the batch-mode inference rule is terminated with a double colon (::)."
.c.obj::
    cl -c /MP4 
lt;

EDIT

If each .obj has its own dependencies (likely!), then you simply add these as separate dependency lines (i.e., they don't have any shell commands attached).

a.obj: b.h c.h ../include/e.hpp
b.obj: b.h ../include/e.hpp
    ∶

Often such boiler plate is generated by another tool and !INCLUDEd into the main makefile. If you are clever, then you can generate these dependencies for free as you compile. (If you go this far, then nmake starts to creak at the seams and you should maybe change to GNU make.)

哑剧 2024-10-22 02:15:16

好吧,今天早上我花了一些时间来解决这个问题,多亏了 bobbogo,我才让它开始工作。以下是其他正在考虑这一点的人的具体细节:

一次编译一个文件的旧式 makefile 有大量这样的内容:

$(LINKPATH)\PS_zlib.obj : zlib\PS_zlib.cpp $(PS_ZLIB_DEP)
        $(CC) zlib\PS_zlib.cpp

$(LINKPATH)\ioapi.obj : zlib\minizip\ioapi.c $(IOAPI_DEP)
        $(CC) zlib\minizip\ioapi.c

$(LINKPATH)\iowin32.obj : zlib\minizip\iowin32.c $(IOWIN32_DEP)
        $(CC) zlib\minizip\iowin32.c

请注意,每个文件一次编译一个。因此,现在您想使用精美的 Visual Studio 2010 /MP 开关“/MP[n] 使用最多 'n' 个进程进行编译”来同时编译多个文件。如何?您的 makefile 需要使用 nmake 中的批处理推理规则,如下所示:

$(LINKPATH)\PS_zlib.obj : zlib\PS_zlib.cpp $(PS_ZLIB_DEP)

$(LINKPATH)\ioapi.obj : zlib\minizip\ioapi.c $(IOAPI_DEP)

$(LINKPATH)\iowin32.obj : zlib\minizip\iowin32.c $(IOWIN32_DEP)

#Batch inference rule for extension "cpp" and path "zlib":
{zlib}.cpp{$(LINKPATH)}.obj::
        $(CC) $(CCMP) 
lt;

#Batch inference rule for extension "c" and path "zlib\minizip":
{zlib\minizip}.c{$(LINKPATH)}.obj::
        $(CC) $(CCMP) 
lt;

在这种情况下,在其他地方,我们

CCMP = /MP4

注意到 nmake 推理批处理规则不支持路径中的通配符或空格。我在某处找到了一些不错的 nmake 文档,其中指出您需要为每个扩展名和源文件位置创建单独的规则,如果文件位于不同的位置,则不能有一条规则。此外,使用#import 的文件无法使用/MP 进行编译。

我们有一个生成 makefile 的工具,因此它现在还可以生成批量推理规则。

但它有效!编译一个大型 dll 的时间从 12 分钟缩短到 7 分钟!呜呼!

Ok, I spent some time this morning working on this, and thanks to bobbogo, I got it to work. Here are the exact details for anyone else who is considering this:

Old style makefile which compiles one file at a time has tons of this:

$(LINKPATH)\PS_zlib.obj : zlib\PS_zlib.cpp $(PS_ZLIB_DEP)
        $(CC) zlib\PS_zlib.cpp

$(LINKPATH)\ioapi.obj : zlib\minizip\ioapi.c $(IOAPI_DEP)
        $(CC) zlib\minizip\ioapi.c

$(LINKPATH)\iowin32.obj : zlib\minizip\iowin32.c $(IOWIN32_DEP)
        $(CC) zlib\minizip\iowin32.c

Note that each file is compiled one at a time. So now you want to use the fancy Visual Studio 2010 /MP switch "/MP[n] use up to 'n' processes for compilation" to compile multiple files at the same time. How? Your makefile needs to make use of batch inference rules in nmake, as follows:

$(LINKPATH)\PS_zlib.obj : zlib\PS_zlib.cpp $(PS_ZLIB_DEP)

$(LINKPATH)\ioapi.obj : zlib\minizip\ioapi.c $(IOAPI_DEP)

$(LINKPATH)\iowin32.obj : zlib\minizip\iowin32.c $(IOWIN32_DEP)

#Batch inference rule for extension "cpp" and path "zlib":
{zlib}.cpp{$(LINKPATH)}.obj::
        $(CC) $(CCMP) 
lt;

#Batch inference rule for extension "c" and path "zlib\minizip":
{zlib\minizip}.c{$(LINKPATH)}.obj::
        $(CC) $(CCMP) 
lt;

In this case, elsewhere, we have

CCMP = /MP4

Note that nmake inference batch rules do not support wildcards or spaces in the paths. I found some decent nmake documentation somewhere that states that you need to create a separate rule for every extension and source file location, you can not have one rule if the files are in the different locations. Also, files that use #import can not be compiled with /MP.

We have a tool that generates our makefiles, so it now also also generates the batch inference rules.

But it works! The time to compile one large dll went from 12 minutes down to 7 minutes! Woohoo!

流绪微梦 2024-10-22 02:15:16

这里需要记住的另一个考虑因素是:您基本上必须为每个路径和扩展名定义一个批处理规则。但是,如果两个不同的源目录中有两个同名的文件,并且这两个目录都有批处理推理规则,则批处理规则可能不会选择您想要的文件。

基本上,make 系统知道它需要创建某个 obj 文件,并且一旦它找到允许它执行此操作的推理规则,它就会使用它。

解决方法是不要有重复的命名文件,如果无法避免,请不要对这些文件使用推理或批处理规则。

One further consideration to keep in mind here is this: You basically have to define one batch rule for each path and extension. But if you have two files with the same name in two different source directories with a batch inference rule for both of those directories, the batch rule might not pick the one you want.

Basically the make system knows it needs to make a certain obj file, and as soon as it finds an inference rule that lets it do that, it will use it.

The work around is to not have duplicate named files, and if that cant be avoided, dont use inference or batch rules for those files.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文