如何使我的高度模块化项目易于最终用户编译?
我正在开发一组相对较大的串行代码 C 代码库,然后我的合作者将在 CUDA 中对其进行并行化。
对于这个项目,我的代码基本上可以归结为
#include "Initialize.cpp"
#include "PerformMoves.cpp"
#include "CollectResults.cpp"
main()
{
//DECLARE General Vars
Initialize();
for (unsigned int step=0; step < maxStep; step++)
{
PerformMoves();
}
CollectResults();
}
现在,我在 Initialize 和 PerformMoves 中执行的步骤将非常不同,具体取决于我正在构建的模拟类型。速度是最高的性能,因为我的代码是蒙特卡罗模拟,将执行数百万次移动,每个移动可能涉及数千次计算。因此我想避免任何不必要的条件。
因此我本质上想要不同的“即插即用”C 模块,例如
InitializeSimType1.cpp 初始化SimType2.cpp 初始化SimType3.cpp
PerformMovesType1.cpp PerformMovesType2.cpp PerformMovesType3.cpp
...
每个都针对某种类型的模拟程序进行了优化,没有大条件的“绒毛”来处理各种情况。
目前我有两种不同类型的模拟,只需将它们放在两个不同的文件夹中并按如下方式编译:
g++ Main.cc 初始化.cpp PerformMoves.cpp CollectResults.cpp -o MC_Prog
我想转向某种条件编译方案,其中我有某种配置文件,我在其中指定选项,它会获取正确的 cpp 文件并编译它们。
我认为 makefile+config 文件是我最好的选择,但除了基本目标和非常线性的编译之外,我对复杂 makefile 的世界还很陌生。
有哪些好的方法可以创建列表驱动的编译系统,使具有非常基本的 C 知识的用户能够轻松地使用他们想要的模块构建目标。我的最终用户不会有大量的 makefile 知识(或一般的编程知识),因此他们端的复杂系统是不可能的。我希望他们基本上拥有一个透明的配置文件驱动系统,允许他们选择一个 Initialize 模块、一个 PerformMoves 模块和一个 CollectResults 模块。一旦他们用自己的选择修改了这个配置文件,我希望他们能够进行单命令编译。
换句话说,我希望能够创建一个指导用户的自述文件:
- 在此配置文件中输入这些条目 (给出配置文件名,为每个配置文件条目放置的选项)...
- 使用此命令进行编译(给出单个命令)
我知道这是一个相当抽象和复杂的问题,但我知道那里有一些 Makefile/c 专家谁可以给我一些好的指导。
I am working on a relatively large set of serial code C-code libraries, which will then be parallelized in CUDA by my collaborators.
For this project my code essentially boils down to
#include "Initialize.cpp"
#include "PerformMoves.cpp"
#include "CollectResults.cpp"
main()
{
//DECLARE General Vars
Initialize();
for (unsigned int step=0; step < maxStep; step++)
{
PerformMoves();
}
CollectResults();
}
Now the steps I perform inside Initialize and PerformMoves will be very different depending on what kind of simulation I'm building. Speed is of the utmost performance as my code is a Monte Carlo simulation that will be performing millions of moves, each of which involves potentially thousands of calculations. Thus I want to avoid any unnecessary conditionals.
Thus I essentially want different "plug and play" C modules, e.g.
InitializeSimType1.cpp
InitializeSimType2.cpp
InitializeSimType3.cppPerformMovesType1.cpp
PerformMovesType2.cpp
PerformMovesType3.cpp....
Each optimized for a certain type of simulation procedure, without the "fluff" of large conditionals to handle a variety of cases.
Currently I have two different types of simulations, and just have these in two different folders and compile as follows:
g++ Main.cc Initialize.cpp
PerformMoves.cpp CollectResults.cpp -o
MC_Prog
I would like to move to some sort of conditional compilation scheme, where I have some sort config file where I specify options and it grabs the correct cpp files and compiles them.
I would assume that a makefile+config files is my best bet, but beyond basic targets and very linear compilation I'm pretty novice to the world of complex makefiles.
What are some good ways that I could create a list-driven compilation system, that would allow users with very basic C knowledge to easily build a target with the modules they want. My end users won't have a great deal of makefile knowledge (or programming knowledge in general), so a complex system on their end is out of the question. I want them basically to have a transparent configuration-file driven system that will allow them to pick one Initialize module, one PerformMoves module, and one CollectResults module. Once they've modified this configuration file with their picks, I want them to be able to do a single-command compilation.
In other words I want to be able to create a README that directs users:
- Input these entries in this config file
(gives config file name, options to put for each config file entry)... - Compile using this command (gives single command)
I know this is a rather abstract and complex question, but I know there are some Makefile/c experts out there who could offer me some good guidance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我可以想到几个类似的构建系统:
libgcc.a
)。它希望确保只有必要的功能链接到您的程序中。由于链接器粒度的单位是单个.o
,因此它会使用不同的-D_enable_xxx
选项多次构建一个.c
文件,以打开每个文件单独发挥作用。然后,它使用ar
将生成的.o
构建为.a
,您可以链接该文件以仅获取您需要的内容。xor
或and
或or
位在一起。它也不希望在其内部循环中使用条件,因此它使用不同的 -D_math_xor 选项一遍又一遍地构建相同的文件,以生成数十个.o
,每个文件都有一个函数执行循环函数中包含的特定数学运算。它使用了所有的.o
(因为该操作实际上是由X客户端选择的,而不是在编译时),因此.a
技术并不重要。听起来您可以混合使用这些内容来生成
.o
库,其中每个库都使用您的特定选项进行预编译,然后仅重建main.c
。以及调用不同的函数并链接到您的 libaray。根据您下面的评论,这里有一些其他想法:
configure
脚本创建config.h
和一个特定于选项(当然还有系统功能)的Makefile
。此设置的一大优点是该组选项的所有构建产品(从.o
到可执行文件)都是独立的,不会干扰使用其他选项的构建。LINT
,它打开所有内容以进行静态分析)然后调用config CONFNAME
。config
解析简单的配置文件并创建一个以该文件命名的目录树。目录树包含一个Makefile
和各种.h
文件来控制构建。基本优点与上述方法相同。I can think of a couple of similar build systems:
libgcc.a
). It wants to ensure that only the necessary functions link into your program. Since the unit of linker granularity is a single.o
it builds one.c
file dozens of times with different-D_enable_xxx
options to turn on each function individually. Then it usesar
to build the resulting.o
into a.a
which you can link against to get only what you need.xor
orand
oror
bits together. It doesn't want a conditional in its inner loop either so it builds the same file over and over with different-D_math_xor
options to produce dozens of.o
each with one function that performs the specific math operation wrapped in the looping function. It uses all of the.o
(because the operation is actually selected by the X client, not at compile time) so the.a
technique is not important.It sounds like you could use a mix of those to produce a library of
.o
where each one is pre-compiled with your specific options and then rebuild justmain.c
over and over calling different functions and linking against your libaray.Based on your comments below, here are a few other ideas:
mkdir obj-mytarget; cd obj-mytarget; ../path/to/gcc/configure --options...; make
Theconfigure
script createsconfig.h
and aMakefile
specific to the options (and, of course, the system capabilities). One big advantage of this setup is that all of the build products (from.o
to executables) for that set of options are self-contained and do not interfere with builds using other options.GENERIC
which produces the default kernel orLINT
which turns on everything for static analysis purposes) and then invokingconfig CONFNAME
.config
parses the simple config file and creates a directory tree named after the file. The directory tree contains aMakefile
and various.h
files to control the build. Same basic advantages as the method above.