简介:IFCPlusPlus的Fork版本资源,面向需要在C++环境中使用CMake构建系统并以Clang编译器编译IFC相关项目的开发者。IFCPlusPlus是处理建筑信息模型(BIM)数据结构的常见开源库,这个分支在2014年6月1日仓库重置后补交了“第二波”改动,重点调整CMake构建流程,使其在Clang环境下能更顺畅地完成编译与链接。压缩包采用zip格式,大小约7.94MB,主体为源码工程与CMake构建配置。项目构建依赖Carve、Boost等外部库,Carve虽已包含在资源内,但更推荐自行编译;可选查看器还需额外依赖,整体保留了较高定制空间。构建配置以OS X 10.9环境示例,演示了通过ccmake设置CMAKE_BUILD_TYPE、CMAKE_OSX_ARCHITECTURES及CMAKE_OSX_SYSROOT等关键参数,对macOS下搭建工具链有直接参考价值。资源已有159人学习下载,适合从事IFC数据解析、BIM工具链开发,或需要掌握Clang与CMake配合使用的C++开发者。 做 BIM 开发绕不开 IFCPlusPlus 这个名字。它是极少数能把 buildingSMART 的 IFC 数据模型和 OpenCASCADE 几何内核直接绑在 C++ 层面的开源库,早期很多碰撞检查、构件统计、模型轻量化工具都在它上面改过。但这个库让不少人头疼的是编译门槛:老版本默认只带 Visual Studio 工程,换个环境就得手动理 include 路径和链接库。标题里这个 IFCPlusPlusArchiv1 fork,最打动我的地方就是它把构建系统改成了 CMake,并且以 Clang 为目标编译器重新整理了一遍代码。这篇文章我会从这次 fork 的背景、迁移思路、CMake/Clang 适配细节、完整实操到排错记录,一步不落全讲清楚,适合准备引入 IFC 解析能力、又不想在构建环境上耗太久的团队参考。
1. 项目背景:IFCPlusPlus 与这一支 Fork 的来龙去脉
1.1 IFCPlusPlus 在 BIM 开发中的位置
IFC(Industry Foundation Classes)是 buildingSMART International 维护的开放数据标准,也是目前 BIM 项目在不同软件之间交换模型的“通用语言”。一个 IFC 文件本质上是按照 ISO 10303-21(Part 21)格式组织的实体实例集合,里面既有 IfcWall、IfcDoor 这样的建筑构件,也有 IfcCartesianPoint、IfcPolyline 这样的几何信息,还有 IfcProject、IfcSite 这种组织层级。工程软件要读懂这些数据,需要做两层功夫:一层是解析 Part 21 的语法,另一层是把语义实体映射到本地几何内核。
IFCPlusPlus 的价值就在于它把两层都做了。项目底层直接持有 OpenCASCADE(OCCT),把 IFC 里的实体一边翻译成 OCCT 的 TopoDS_Shape,一边保留 IfcXxx 对象树。这样开发者拿到的不是一个孤立的配置文件,而是一套可以继续做布尔运算、碰撞检测、网格生成的几何数据。这一点到今天依然是很多商业 BIM 引擎仍在采用的架构,也解释了为什么这个库即便停更多年,还有人愿意在它上面做二次开发维护。
1.2 2014 年“神秘重置”与第二波提交
看到标题里“神秘重置”这个词,我特意去翻了提交记录。2014 年 6 月 1 日这个时间点,原仓库的提交历史发生了一次整体重置,很多早期 fork 和镜像都有断档。社区里说法并不统一:有人认为是维护者误把某个孤儿分支强推到了主分支,也有人认为是作者为了清理历史中的敏感信息刻意做的 reset。无论原因为何,直接后果很明确——所有引用旧 commit hash 的脚本、补丁、文档链接全部失效,基于旧快照的分支也很难直接 rebase 上去。
这时候 fork 的意义就出现了。标题里的“第二波”是分支作者自己的标注,意思是重置之后再次提交的“第二波”代码。我理解这个词有两层含义:第一,它确认这批提交是在重置事件之后基于新基线产生的;第二,它暗示作者打算让这份代码承接旧时代的基础能力,但用更现代的工程方式继续往前走。从实际效果看,最显眼的“更现代”就是 CMake 构建系统替换原有 Visual Studio 工程,以及面向 Clang 的编译适配。
2. 为什么“转 CMake + Clang”是这次改造的关键
2.1 老构建系统到底卡在哪
如果你只用 Windows + Visual Studio,老项目其实没那么多问题——作者早期就是在 VS 环境下开发的,原始的 .sln/.vcxproj 打开就能编。但一旦把同样的代码放到 Linux 服务器或者 macOS 的 CI 机器上,麻烦就来了:所有 include 路径、预定义宏、静态库顺序,都靠开发者手工猜,没有一份可复现的构建描述。更麻烦的是 OCCT 的依赖路径在不同平台完全不一致,Windows 下是C:\OpenCASCADE\...,Linux 下可能是/usr/include/opencascade,macOS 下可能来自 Homebrew 的/opt/homebrew/opt/opencascade。按平台各维护一套工程文件不现实,老项目也根本没人维护那么多套,所以很多团队只能在 Windows 上完成所有编译,部署到 Linux 时再重新踩一遍坑。
2.2 CMake 的迁移价值
CMake 是事实标准,选它来做这次迁移,成本低、覆盖广。一套 CMakeLists.txt 可以同时生成 Makefile、Ninja、Visual Studio 工程甚至 Xcode 工程,开发者习惯哪套就用哪套,CI 里则直接cmake -S . -B build && cmake --build build一跑到底。对于 IFCPlusPlus 这种依赖外部几何库的项目,CMake 的 find_package 机制能省去大量手写路径的功夫;通过传递CMAKE_PREFIX_PATH,可以很方便地切换不同版本的 OCCT,这对需要对比 OCCT 6.x 和 7.x 行为差异的团队来说特别实用。
另外一点常被忽略:CMake 迁移不需要动源文件组织结构。老项目的目录不能大改,否则历史 diff 就没法看了。CMake 允许你把现有目录原封不动保留下来,只写一份构建描述就能把模块串起来,这种“低侵入”特性对老项目尤其关键。
2.3 Clang 编译的意义
选择 Clang 作为目标编译器不只是流行。对于这种从老项目迁移来的代码,Clang 的报错信息要比 GCC 更可读,模板实例化错误会直接指出实例化的源头,而不是甩出一长串无法定位的堆栈。对习惯了老 VS 编译器的开发者来说,Clang 还有两个现实价值:一是它在 C++11/14 标准支持上更严格,能逼着旧代码暴露潜在的未定义行为;二是它的-Weverything级别警告对清理死代码、隐性类型转换很有用。在 CMake 流程里,指定 Clang 并不需要改任何构建脚本,只需要在配置阶段把CMAKE_CXX_COMPILER指向 clang++ 即可,这恰恰是 CMake + Clang 组合在移植老项目时比“单独改 Makefile”更省心的原因。
3. 构建迁移的核心细节解析
3.1 CMakeLists.txt 关键配置
一个可以跑通的最小 CMakeLists.txt 大致长这样:
cmake_minimum_required(VERSION 3.10) project(IFCPlusPlus CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(OpenCASCADE REQUIRED) file(GLOB_RECURSE IFC_PARSE_SOURCES CONFIGURE_DEPENDS "src/ifcparse/*.cpp" "src/ifcgeom/*.cpp" "src/ifcgeom_schema_agnostic/*.cpp" ) add_library(IFCPlusPlus STATIC ${IFC_PARSE_SOURCES}) target_include_directories(IFCPlusPlus PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src> $<INSTALL_INTERFACE:include> ) target_link_libraries(IFCPlusPlus PUBLIC ${OpenCASCADE_LIBRARIES})这里有几个配置值得展开说。cmake_minimum_required(VERSION 3.10)不要设太低。老项目为了“兼容”可能会把版本压到 2.8,但会失去很多 target 级 API,比如target_include_directories和target_link_libraries的冒号语法。find_package(OpenCASCADE REQUIRED)的前提是 CMake 能找到 OCCT 安装目录,找不到时用-DOpenCASCADE_DIR=...指过去。C++ 标准方面,IFCPlusPlus 代码是 C++11 时代的产物,设成 11 最稳;C++14 一般也能编,但没必要冒险。
构建时强烈建议打开-DCMAKE_EXPORT_COMPILE_COMMANDS=ON,它会在 build 目录生成 compile_commands.json,后续给 clang-tidy、ccls、clangd 做索引和静态分析都非常方便。对老代码做现代化改造时,这个文件等于给你提供了一张完整的编译单元地图,排查遗漏头文件比肉眼逐个目录翻要快得多。
3.2 从 Visual Studio 时代迁移到 Clang 的适配点
搬到 Clang 后,最常碰到三类问题。
第一类是隐式转换。VS 的旧编译器在/W3下对int到size_t的收缩只给 warning,而 Clang 在-Wshorten-64-to-32下会报 warning 甚至 error。比如从f->fct().size()返回的size_t直接赋给int,换编译器以后就得处理。修法很直接:显式static_cast<int>,或者把循环变量类型改成size_t,后者更合理。
第二类是 CRT 不安全函数。VS 的_CRT_SECURE_NO_WARNINGS宏在 Clang 下不存在,很多旧的strcpy、sprintf代码会报告 deprecation。替换成std::string或strncpy即可。这类修改虽然琐碎,但也是顺手清理代码的好机会。
第三类是模板特化遗漏。Clang 对 C++11 标准库的std::hash特化要求更严格,为自定义类型写namespace std { template<> struct hash<X> {...}; }时如果漏了const限定,Clang 会直接拒绝,而 GCC 和旧 VS 只给 warning。这些适配点在小项目里可能只需改十来个文件,但对 IFCPlusPlus 这种层级较多的项目,动手前最好先开-Wall -Wextra -Wpedantic跑一遍,把 warning 列表当清单逐项处理。
4. 实操:从拉取代码到跑通示例
4.1 环境准备:三个依赖
动手之前先把环境理顺,至少需要三个东西:Clang、CMake、OpenCASCADE。版本上,Clang 建议 10 以上,CMake 建议 3.16 以上,OCCT 建议 6.9 以上。如果你的目标只是跑示例而不是改几何内核,直接装发行版提供的 OCCT 开发包就够。
Ubuntu(20.04/22.04)下:
sudo apt update sudo apt install -y cmake clang libocct-data-exchange-dev libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-devmacOS 下用 Homebrew:
brew install cmake llvm opencascadeWindows 下我一般用 LLVM 自带的 clang-cl 驱动,或者 MSYS2/Mingw-w64 环境里的 clang。需要注意的是,如果 OCCT 是用 MSVC 编译的,那编译器侧最好也走 clang-cl,否则 object 文件的 ABI 很容易不一致,链接阶段会非常头疼。
4.2 获取代码
因为是 fork,注意 clone 时把子模块一起拉下来:
git clone https://github.com/<your-fork>/IFCPlusPlusArchiv1.git cd IFCPlusPlusArchiv1 git submodule update --init --recursive如果原仓库有子模块需要同步,--recursive不能省。这个 fork 的“第二波”提交对应重置之后的基线,clone 完成后可以直接 checkout 对应的分支或 tag。另外建议把原仓库加为 upstream,方便后续对照差异:
git remote add upstream https://github.com/<original-repo>.git git fetch upstream4.3 配置、编译与验证
配置阶段把编译器指到 clang++:
mkdir -p build && cd build cmake .. \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH=/path/to/opencascade \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON如果find_package(OpenCASCADE)成功,CMake 会打印出 OCCT 版本和库路径。编译:
cmake --build . -j$(nproc)编译结束后,可以用仓库自带的测试 IFC 文件做一次最小验证,或者自己导出一个简单的墙/板 IFC 文件。验证程序能成功解析并输出实体数量,就说明整条链路已经通了。一个最简验证片段是这样:
#include "ifcparse/IfcFile.h" #include <cstdio> int main(int argc, char* argv[]) { if (argc < 2) return 1; IfcParse::IfcFile f; if (!f.Init(argv[1])) { std::fprintf(stderr, "failed to open %s\n", argv[1]); return 2; } std::printf("entities: %zu\n", f.fct().size()); return 0; }这里用到的类名和头文件在不同分支里略有差异,以你拉到的代码里的实际接口为准。这个片段的目的只是确认库已经能编能链,不是完整的业务逻辑示例。
5. 常见问题与排查技巧实录
5.1 find_package 找不到 OpenCASCADE
现象很直接:CMake 报Could not find a package configuration file provided by "OpenCASCADE"。这通常是因为 OCCT 的 CMake 配置文件没有出现在默认搜索路径里。用源码编译安装 OCCT 时,配置文件一般会生成在opencascade/lib/cmake/opencascade下,所以解决办法就是把这个目录通过-DOpenCASCADE_DIR=...或-DCMAKE_PREFIX_PATH=...显式告诉 CMake。有时候系统装了多个版本 OCCT,find_package找到旧版,这时候先看打印出的版本号,再决定是卸载旧版还是指定路径。
5.2 链接错误 undefined reference
编译通过、链接失败,报undefined reference to TKernel这一类的符号。最常见原因是 OCCT 7.x 的库名是 TKernel、TKG2d、TKG3d 等,老代码里可能还引用了旧版 TKFillet 之类的库名;或者手动拼接静态库时顺序不对,导致符号解析失败。正确做法是把find_package得到的OpenCASCADE_LIBRARIES原样传给target_link_libraries,不要自己写一串静态库路径。如果必须手动写,注意静态库的依赖是反序的:底层库要放在依赖它的库后面。
5.3 Clang 版本与 warning-as-error
老代码在 Clang 下带警告编译是常态,比如#pragma pack这类 MSVC pragma 在 Clang 下不识别,开了-Werror会直接失败。处理思路是改代码而不是压制警告:用#ifdef _MSC_VER把 MSVC 特有的 pragma 包起来,或者用__attribute__((packed))替代。对于纯第三方头文件产生的警告,可以在 CMake 里用target_compile_options对特定文件关闭对应 warning,但不要全局关。实测下来,先把 warning 清单跑出来再逐项分类处理,比边编边改要高效得多。
5.4 Windows 下 clang-cl 的几个坑
Windows 上如果走 clang-cl 驱动,至少有三个坑要提前留意。第一,clang-cl 默认会调用 MSVC 的 link.exe,所以系统里必须有 Visual Studio 的链接环境;如果不想依赖 MSVC 链接器,可以在 CMake 里指定 lld-link,但要确认 OCCT 库和 object 文件的 COFF 格式能对上。第二,OCCT 如果本身是用 MinGW 编译的,和 clang-cl 编译出的目标文件 ABI 不兼容,混用基本无解,只能统一工具链。第三,老代码里写死的"C:\\OpenCASCADE\\..."硬编码路径虽然在 clang-cl 下能用,但强烈建议换成 CMake 变量或环境变量,否则换机器就得改源码。
| 问题 | 现象 | 排查方向 | 解决手段 |
|---|---|---|---|
| find_package 失败 | OpenCASCADE 找不到 | OCCT 安装目录不在搜索路径 | 指定 OpenCASCADE_DIR / CMAKE_PREFIX_PATH |
| 链接失败 | undefined reference TKernel | OCCT 库名或顺序不匹配 | 使用 OpenCASCADE_LIBRARIES 原样传递 |
| warning-as-error | 警告变错误 | 代码中有 MSVC 特有 pragma | 代码包宏或关 target 特定 warning |
| clang-cl 混链 | 链接器报 ABI 错误 | OCCT 编译工具链不一致 | 统一 MSVC 工具链或改为 MinGW 环境 |
我个人在把老项目切到 CMake + Clang 时的一个体会是:不要想着一次把所有 warning 清零,先让代码能重复构建,再逐步开告警级别。这个 fork 的做法其实非常适合做参考——它没有去重写 IFCPlusPlus 的功能逻辑,只是把构建这件事重新理顺了,却让整个项目从“只能在某台 Windows 机器上编”变成了“在任何有 clang/cmake/occt 的环境里都能复现”。如果你也在维护一个历史包袱比较重的 C++ 项目,我建议你至少先做这一步迁移,收益远超预期。
本文还有配套的精品资源,点击获取