从 OSX 平台转换并使用 gcc 和 llvm 时构建失败
Build failure when transitioning from OSX platforms and using gcc and llvm
我继承了一个用 C 和 C++ 编写的相当大和复杂的软件包,它成功地构建在 Mac OSX Lion 上使用 gcc:
$> gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
我的任务是使用命令行工具附带的默认 Mac 编译器在 OSX Mavericks 上构建相同的包。
$> gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix
OSX Lion 构建完成,但 OSX Mavericks 构建失败并出现以下错误:
Undefined symbols for architecture x86_64:
"_MRIsetVoxVal", referenced from ...
我已将错误源追踪到具有以下代码块的头文件:
#ifdef __cplusplus
float MRIgetVoxVal( const MRI *mri, int c, int r, int s, int f);
int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
void MRIdbl2ptr(double v, void *pmric, int mritype);
double MRIptr2dbl(void *pmric, int mritype);
#else
inline float MRIgetVoxVal(const MRI *mri, int c, int r, int s, int f);
inline int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
inline void MRIdbl2ptr(double v, void *pmric, int mritype);
inline double MRIptr2dbl(void *pmric, int mritype);
#endif
如果我通过简单地删除 if else
和 inline
语句来修改上面的代码块,使其看起来如下所示,那么构建将在两个平台上完成:
float MRIgetVoxVal( const MRI *mri, int c, int r, int s, int f);
int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
void MRIdbl2ptr(double v, void *pmric, int mritype);
double MRIptr2dbl(void *pmric, int mritype);
因此,在 OSX Lion 上,似乎触发了 ifdef __cplusplus
语句,从而产生了所需的行为。在 Mavericks 上,else
语句被触发,最终导致错误。
请原谅这是一个非常基本的问题,但 C 和 C++ 不在我的专业领域之内。这里发生了什么? #ifdef __cplusplus
是什么意思,为什么一个版本的 gcc 会被它触发而另一个版本不会?
在编译器正确介入之前,preprocessor 执行许多任务。以哈希('#')开头的行是预处理程序指令,包括文件包含(#include <stdio.h>
)、宏变量定义(#define DEBUG 1
)、宏函数定义(#define LENGTH(array) (sizeof(array) / sizeof(0[array]))
)等任务和条件编译(#if ... #endif
),如您的代码片段所示。
宏允许在编译时通过用其值替换宏名称来重写源代码。它们是实现许多任务的早期方法,但大体上不尊重 C/C++ 语法,因此在许多方面被(例如)inline
说明符取代,模板,trait classes and nullptr
,更好地尊重语法和语义。
某些宏在 headers 中定义(请参阅 "limits.h",在 C++ 中由 numeric_limits
替换某些示例)。其他由预处理器定义; __cplusplus
是其中之一,它应该在编译器处理 C++ 文件时定义(由文件扩展名或可能的 command-line 参数确定,例如 GCC 的“-x”)。您可以使用“-dD”或“-dM”选项指示 gcc 列出宏(如 gcc man page and online docs), along with the '-E' option to stop after preprocessing. Nadeau Software Consulting posted a tip on how to list predefined macros 中针对各种编译器所述。根据它,clang 应该接受与 gcc 相同的参数,因此请尝试以下关于小牛队:
gcc -dM -E -x c++ /dev/null | less
__cplusplus
是 C++ 标准(C++11 中的第 16.8 1 节)所要求的。 Lion 和 Mavericks 构建的 gcc 之间的一大区别是后者使用 clang 前端,而前者使用 GCC。网络搜索 "clang __cplusplus" 表明 clang 应该支持它,因此如果未定义 __cplusplus
,则 clang 可能会将文件编译为 C。您可以尝试使用“-x c++”选项强制C++。您也可以尝试“-std=gnu++11”选项,但我怀疑它在这里不会有什么不同,因为它旨在支持 non-standard Gnu C++ extensions 而不是提供完整的 GCC 兼容性。
我继承了一个用 C 和 C++ 编写的相当大和复杂的软件包,它成功地构建在 Mac OSX Lion 上使用 gcc:
$> gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
我的任务是使用命令行工具附带的默认 Mac 编译器在 OSX Mavericks 上构建相同的包。
$> gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix
OSX Lion 构建完成,但 OSX Mavericks 构建失败并出现以下错误:
Undefined symbols for architecture x86_64:
"_MRIsetVoxVal", referenced from ...
我已将错误源追踪到具有以下代码块的头文件:
#ifdef __cplusplus
float MRIgetVoxVal( const MRI *mri, int c, int r, int s, int f);
int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
void MRIdbl2ptr(double v, void *pmric, int mritype);
double MRIptr2dbl(void *pmric, int mritype);
#else
inline float MRIgetVoxVal(const MRI *mri, int c, int r, int s, int f);
inline int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
inline void MRIdbl2ptr(double v, void *pmric, int mritype);
inline double MRIptr2dbl(void *pmric, int mritype);
#endif
如果我通过简单地删除 if else
和 inline
语句来修改上面的代码块,使其看起来如下所示,那么构建将在两个平台上完成:
float MRIgetVoxVal( const MRI *mri, int c, int r, int s, int f);
int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
void MRIdbl2ptr(double v, void *pmric, int mritype);
double MRIptr2dbl(void *pmric, int mritype);
因此,在 OSX Lion 上,似乎触发了 ifdef __cplusplus
语句,从而产生了所需的行为。在 Mavericks 上,else
语句被触发,最终导致错误。
请原谅这是一个非常基本的问题,但 C 和 C++ 不在我的专业领域之内。这里发生了什么? #ifdef __cplusplus
是什么意思,为什么一个版本的 gcc 会被它触发而另一个版本不会?
在编译器正确介入之前,preprocessor 执行许多任务。以哈希('#')开头的行是预处理程序指令,包括文件包含(#include <stdio.h>
)、宏变量定义(#define DEBUG 1
)、宏函数定义(#define LENGTH(array) (sizeof(array) / sizeof(0[array]))
)等任务和条件编译(#if ... #endif
),如您的代码片段所示。
宏允许在编译时通过用其值替换宏名称来重写源代码。它们是实现许多任务的早期方法,但大体上不尊重 C/C++ 语法,因此在许多方面被(例如)inline
说明符取代,模板,trait classes and nullptr
,更好地尊重语法和语义。
某些宏在 headers 中定义(请参阅 "limits.h",在 C++ 中由 numeric_limits
替换某些示例)。其他由预处理器定义; __cplusplus
是其中之一,它应该在编译器处理 C++ 文件时定义(由文件扩展名或可能的 command-line 参数确定,例如 GCC 的“-x”)。您可以使用“-dD”或“-dM”选项指示 gcc 列出宏(如 gcc man page and online docs), along with the '-E' option to stop after preprocessing. Nadeau Software Consulting posted a tip on how to list predefined macros 中针对各种编译器所述。根据它,clang 应该接受与 gcc 相同的参数,因此请尝试以下关于小牛队:
gcc -dM -E -x c++ /dev/null | less
__cplusplus
是 C++ 标准(C++11 中的第 16.8 1 节)所要求的。 Lion 和 Mavericks 构建的 gcc 之间的一大区别是后者使用 clang 前端,而前者使用 GCC。网络搜索 "clang __cplusplus" 表明 clang 应该支持它,因此如果未定义 __cplusplus
,则 clang 可能会将文件编译为 C。您可以尝试使用“-x c++”选项强制C++。您也可以尝试“-std=gnu++11”选项,但我怀疑它在这里不会有什么不同,因为它旨在支持 non-standard Gnu C++ extensions 而不是提供完整的 GCC 兼容性。