如何让我的高度模块化项目易于最终用户编译?

Jas*_*ick 5 c modularity makefile compilation project

我正在研究一组相对较大的串行代码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();
}
Run Code Online (Sandbox Code Playgroud)

现在我在Initialize和PerformMoves中执行的步骤将根据我正在构建的模拟类型而有很大不同.速度是最高性能,因为我的代码是蒙特卡罗模拟,将执行数百万次移动,每次移动涉及可能数千次计算.因此,我想避免任何不必要的条件.

因此,我基本上想要不同的"即插即用"C模块,例如

InitializeSimType1.cpp InitializeSimType2.cpp InitializeSimType3.cpp

PerformMovesType1.cpp PerformMovesType2.cpp PerformMovesType3.cpp

....

每个优化用于某种类型的模拟程序,没有大条件的"绒毛"来处理各种情况.

目前我有两种不同类型的模拟,只需将它们放在两个不同的文件夹中,编译如下:

g ++ Main.cc Initialize.cpp PerformMoves.cpp CollectResults.cpp -o MC_Prog

我想转向某种条件编译方案,其中我有一些排序配置文件,我指定选项,它抓取正确的cpp文件并编译它们.

我认为makefile +配置文件是我最好的选择,但除了基本目标和非常线性的编译之外,我对复杂makefile的世界都很新手.

有什么好方法可以创建一个列表驱动的编译系统,这将允许具有非常基本的C知识的用户轻松地使用他们想要的模块构建目标.我的最终用户不会拥有大量的makefile知识(或者一般的编程知识),因此最终的复杂系统是不可能的.我希望他们基本上有一个透明的配置文件驱动系统,允许他们选择一个Initialize模块,一个PerformMoves模块和一个CollectResults模块.一旦他们用他们的选择修改了这个配置文件,我希望他们能够进行单命令编译.

换句话说,我希望能够创建一个指导用户的自述文件:

  1. 在此配置文件中输入这些条目(给出配置文件名,为每个配置文件条目添加选项)...
  2. 使用此命令编译(提供单个命令)

我知道这是一个相当抽象和复杂的问题,但我知道有一些Makefile/c专家可以为我提供一些很好的指导.

Ben*_*son 1

我可以想到几个类似的构建系统:

  1. 当您构建 GCC 时,它会使用辅助例程构建库(例如libgcc.a)。它希望确保只有必要的功能链接到您的程序中。由于链接器粒度的单位是单个,因此它会使用不同的选项多次.o构建一个文件,以单独打开每个功能。然后它用于将结果构建为您可以链接的结果以仅获取您需要的内容。.c-D_enable_xxxar.o.a
  2. X 窗口服务器有几个函数执行基本相同的操作,只是最内层循环中的数学不同。例如,它可能xorandor位在一起。它也不希望在其内部循环中使用条件,因此它使用不同的-D_math_xor选项一遍又一遍地构建相同的文件,以生成数十个.o文件,每个文件都有一个函数执行循环函数中包含的特定数学运算。它使用了所有的.o(因为该操作实际上是由X客户端选择的,而不是在编译时)所以.a技术并不重要。

听起来您可以使用这些的混合来生成一个库,其中.o每个库都使用您的特定选项进行预编译,然后一遍main.c又一遍地调用不同的函数并链接到您的库进行重建。

根据您下面的评论,这里有一些其他想法:

  1. 当您构建 binutils、GCC、glibc 等时,您会解压缩源代码分发版,但不会在源代码树内进行构建。相反,您的mkdir obj-mytarget; cd obj-mytarget; ../path/to/gcc/configure --options...; make 脚本configure会创建config.h特定Makefile于选项(当然还有系统功能)的选项。此设置的一大优点是该组选项的所有构建产品(从.o可执行文件)都是独立的,不会干扰使用其他选项的构建。
  2. 经典的 BSD 内核配置是通过编辑文件(通常给出全大写名称,例如GENERIC生成默认内核或LINT打开所有内容以进行静态分析)然后调用config CONFNAME. config解析简单的配置文件并创建以该文件命名的目录树。目录树包含一个Makefile和各种.h文件来控制构建。基本优点与上述方法相同。
  3. avr-libc(Atmel AVR 微控制器的 libc)通过自动为数十个微控制器(内存大小、外设可用性等存在差异)创建目标目录并在一次传递中构建所有变体,自动执行上述方法结果是产生了许多库,提供了能够针对任何 AVR 的交叉构建环境。您可以使用类似的想法来自动构建程序的每个排列。