13.7. 源代码
13.7.1. 代码风格
我们有意在Open MPI代码库中不设置过多的编码规范。
13.7.1.1. 所有语言
- 4个空格制表符。不多不少。 
- 绝对不要使用实际的制表符;始终使用空格。无论是emacs还是vim都有隐藏功能,可以在你按下 
13.7.1.2. C / C++
- 在进行常量相等或不相等比较时,始终将常量放在左侧。这是一种防御性编程:如果测试中存在拼写错误并遗漏了 - !或- =,将会产生编译器错误。例如:- /* 正确做法 */ if (NULL == foo) { ... } /* 因为如果出现拼写错误(比如用=代替==),这将产生编译错误而非难以察觉的bug */ if (NULL = foo) { ... } 
- 更防御性的编程:始终使用大括号 - { }包裹代码块,即使只有一行代码。例如:- /* 这样做 */ if (whatever) { return OMPI_SUCCESS; } /* 不要这样做 */ if (whatever) return OMPI_SUCCESS; 
- 从Open MPI 1.7版本开始,Open MPI需要一个符合C99标准的编译器。 - 现在允许(并推荐)使用C++风格的注释。 
- 允许(且推荐)使用C99风格的混合声明。 
 
- 始终将 - _config.h - ompi、- oshmem或- opal——这取决于你正在编写的层级。只有极少数情况下不需要这样做(例如某些特殊的Windows场景)。但在99.9999%的情况下,这个文件应该被首先包含,以便在必要时影响系统级的#include文件。
- 文件名和符号必须遵循前缀规则(参见[邮件讨论](http://www.open-mpi.org/community/lists/devel/2009/07/6389.php)): - 文件名必须以 - _ 
- 公共符号在组件中必须使用前缀 - _ - _ - mca、- ompi、- oshmem或- opal之一。请注意mca过去是最常用的前缀,但现在相比其他- 非公开符号必须声明为 - static或采取其他方式使其不出现在全局作用域中。
 
- 始终 #define宏,即使是逻辑值。 - GNU的方式是当宏为"真"时使用 - #define定义,当宏为"假"时使用- #undef取消定义。
- 在Open MPI中,我们始终使用 - #define将逻辑宏定义为0或1——我们从不使用- #undef取消定义。
- 这样做是出于防御性编程的考虑:如果你只是检查预处理宏是否定义(通过 - #ifdef FOO或- #if defined(FOO)),当宏名称拼写错误时,编译时不会收到任何警告。然而,如果你使用逻辑测试- #if FOO但宏未定义(例如由于拼写错误),你将收到编译器警告或错误。- 设计原理 - 当拼写错误的宏名称隐藏在数千行代码中时,可能极难发现;我们将尽可能利用预处理器/编译器提供的所有帮助! 
 - /* GNU Way - you will get no warning from the compiler if you misspell "FOO"; the test will simply be false */ #ifdef FOO ... #else ... #endif /* Open MPI Way - you will get a warning from the compiler if you misspell "FOO"; the result of the test is a different value than whether you spelled the macro name right or not */ #if FOO ... #else ... #endif 
13.7.1.3. Fortran
我们没有针对Fortran的具体编码风格指南。请阅读源代码树中现有的Fortran代码,并尝试使用类似的风格。
13.7.1.4. Shell脚本编写
请阅读源代码树中的一些现有shell代码,并尝试使用类似的风格。
- 始终将评估的shell变量用引号括起来,以确保正确处理多标记值。 - # This is bad if test $foo = bar; then # This is good if test "$foo" = "bar"; then - 唯一的例外是,当将一个shell变量的值赋给另一个shell变量时,右侧不需要使用引号: - # 这样做无害但多余 foo="$bar" # 这样写就足够了,即使$bar包含多个标记 foo=$bar 
 
- 不要在 - test命令中使用- ==运算符——这是GNU的扩展功能,在BSD系统上可能导致可移植性问题。应该使用单个- =运算符。- # 错误用法 if test "$foo" == "bar"; then # 正确用法 if test "$foo" = "bar"; then 
13.7.1.5. m4
我们没有针对m4语言(用于创建configure脚本)的具体编码风格指南。请阅读源代码树中现有的m4代码,并尝试使用类似的风格。
13.7.2. 树状布局
源代码树中有几个值得注意的顶级目录:
- 主要子项目: - oshmem: 顶层的OpenSHMEM代码库
- ompi: Open MPI代码库
- opal: OPAL代码库
 
- config: 支持顶层- configure脚本的M4脚本- mpi.h
- etc: 一些杂项文本文件
- docs: Open MPI文档的源代码
- examples: 简单的MPI/OpenSHMEM示例程序
- 3rd-party: 包含所需核心库的副本(通过Git克隆中的Git子模块或二进制压缩包提供)。- 注意 - 虽然可能被认为不太常见,但我们为第三方项目提供了二进制压缩包(而非Git子模块),这些项目包括: - Open MPI正常运行所需,以及 
- 并非所有操作系统发行版都默认包含,以及 
- 很少更新。 
 
三个主要源代码目录(oshmem、ompi和opal)各自至少会生成一个顶级库,分别命名为liboshmem、libmpi和libopen-pal。这些库可以构建为静态库或共享库。某些目录树下还会生成可执行文件。
libopen-pal顶层库在内部构建时分为两部分:
- libopen-pal_coreOPAL内部的核心组件,包含mpicc/mpirun等工具链接所需的基础源码和MCA框架。该"核心"库不会被安装。- 包含以下MCA框架: - backtrace,- dl,- installdirs,- threads,- timer
- 包含 - opal/class目录下的所有源代码以及- opal/util目录下的大部分内容
- 包含 - opal/runtime目录中后缀为- _core的文件
 
- libopen-pal包含"核心"以及所有其他OPAL项目源代码。该库会被安装。
每个子项目源代码目录下都有类似(但不完全相同)的目录结构:
- class: 本项目特有的类(使用OPAL类系统),类似C++的"类"
- include: 本项目专用的顶层包含文件
- mca: 本项目特定的MCA框架和组件
- runtime: 项目在运行时的启动和关闭
- tools: 本项目专用的可执行文件
- util: 随机实用代码
每个子项目中还有其他顶级目录,这些目录与该项目特定的逻辑和代码相关。例如,MPI API的实现可以在ompi/mpi/LANGUAGE下找到,其中LANGUAGE可以是c、fortran或java。
mca 目录树的布局有严格定义,其格式如下:
PROJECT/mca/FRAMEWORK/COMPONENT
明确说明:在mca目录树下,不允许存在不符合此模板的目录(下文将解释的base目录除外)。因此,只有框架和组件代码可以存放在mca目录树中。
也就是说,框架和组件名称必须是有效的目录名(以及C变量;稍后会详细说明)。例如,TCP BTL组件位于opal/mca/btl/tcp/。
名称base是保留关键字;不能存在名为base的框架或组件。名为base的目录专用于MCA和框架的实现。以下是几个示例(基于v5.0.x源码树):
# Main implementation of the MCA
opal/mca/base
# Implementation of the btl framework
opal/mca/btl/base
# Implementation of the sysv framework
oshmem/mcs/sshmem/sysv
# Implementation of the pml framework
ompi/mca/pml/base
在这些指定的目录下,框架和/或组件可以拥有任意的目录结构。
13.7.3. 符号可见性
*_DECLSPEC宏提供了一种方法来标注符号,以指示在编译动态共享对象文件(例如libmpi.so)时它们的预期可见性。这些宏是基于每个项目定义的:
- Open MPI: - OMPI_DECLSPEC
- Open PAL: - OPAL_DECLSPEC
- OpenSHMEM: - OSHMEM_DECLSPEC
这些宏展开为适当的编译器和平台标志,用于标记符号是否应在目标项目的库命名空间中显式公开。*_DECLSPEC属性用于声明符号在该库/DSO范围之外可见。例如,OMPI_DECLSPEC用于控制哪些符号在libmpi.so范围内可见。
注意
这完全与动态库编译相关,不适用于静态编译。
注意
这些宏最初是在Open MPI支持Windows时引入的(大约在Open MPI v1.0.0版本),其设计灵感来源于Windows的__declspec。虽然Open MPI已不再支持Windows平台,但这些符号可见性宏仍然保留了下来。