summaryrefslogtreecommitdiff
path: root/mrpt-libapps.spec
blob: c043c5ea433ecaab027eab4a816966796d7c0082 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
%define ros_distro humble
%define RosPkgName mrpt-libapps
%define src_version 2.15.20
%define mrpt_version 2.15.21
# mrpt 的 git submodule 内容(GitHub archive tarball 不含),按需手工补:
%define nanoflann_version 1.5.5
%define simpleini_version 4.22

Name:           ros-%{ros_distro}-%{RosPkgName}
Version:        2.15.20
Release:        4%{?dist}
Summary:        Mobile Robot Programming Toolkit (MRPT) libraries (apps C++ libraries)
License:        BSD
URL:            https://github.com/MRPT/mrpt_ros.git
Source0:        %{RosPkgName}-%{src_version}.tar.gz
# mrpt upstream source, pre-fetched to avoid ExternalProject git clone at build time
Source1:        https://github.com/MRPT/mrpt/archive/refs/tags/%{mrpt_version}.tar.gz#/mrpt-%{mrpt_version}.tar.gz
# mrpt 的 git submodule(COPR chroot 无外网,构建期无法下载/克隆,须随 SRPM 携带):
#   nanoflann    —— mrpt 强制依赖(script_nanoflann.cmake: FATAL_ERROR if not found),
#                   openEuler 官方源无系统包,只能喂 3rdparty/nanoflann 让内层自建
#   simpleini    —— mrpt-config 编译 CConfigFile.cpp 需要 SimpleIni.h
# 本包装工程内层只建 mrpt-apps + mrpt-graphslam,both 都不碰 rplidar_sdk / xspublic /
# libfreenect / libfyaml(包装工程已把 ROBOPEAK_LIDAR / rplidar_sdk / xsens 全写死 OFF),
# 故无需像同链 libhwdrivers 那样再预取 rplidar_sdk,只补空目录过上游存在性检查即可。
Source2:        https://github.com/jlblancoc/nanoflann/archive/refs/tags/v%{nanoflann_version}.tar.gz#/nanoflann-%{nanoflann_version}.tar.gz
Source3:        https://github.com/brofield/simpleini/archive/refs/tags/v%{simpleini_version}.tar.gz#/simpleini-%{simpleini_version}.tar.gz

%global debug_package %{nil}
%global __os_install_post %(echo '%{__os_install_post}' | sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g')
%global __provides_exclude_from ^/opt/ros/%{ros_distro}/.*$
%global __requires_exclude_from ^/opt/ros/%{ros_distro}/.*$

# 本包内层只构建 mrpt-apps 与 mrpt-graphslam。按上游依赖声明:
#   libs/apps/CMakeLists.txt 的 define_mrpt_lib:
#       mrpt-apps → mrpt-hwdrivers + mrpt-gui + mrpt-slam + mrpt-tclap +
#                   mrpt-topography (+ mrpt-graphslam,本包内层 BUILD_mrpt-graphslam=ON)
#   libs/graphslam/CMakeLists.txt 的 define_mrpt_lib:
#       mrpt-graphslam → mrpt-gui + mrpt-slam
# 再往下的既有依赖链(取自同链各兄弟包的 libs/<lib>/CMakeLists.txt):
#   mrpt-hwdrivers → mrpt-comms + mrpt-maps + mrpt-gui + mrpt-vision
#   mrpt-gui       → mrpt-opengl;mrpt-slam → mrpt-vision + mrpt-maps
#   mrpt-vision    → mrpt-obs;mrpt-obs → mrpt-opengl + mrpt-tfest
#   mrpt-maps      → mrpt-obs + mrpt-graphs;mrpt-graphs → mrpt-opengl
#   mrpt-opengl    → mrpt-poses + mrpt-img;mrpt-img → mrpt-io / -math / -config ...
# 这些兄弟库在本包内层均为 BUILD_mrpt-*=OFF(包装工程写死),靠
# DeclareMRPTLib.cmake 的 find_package(mrpt-XXX) 分支复用已安装的兄弟库,
# 故 BuildRequires 须列全提供方:
#   mrpt-topography 由 ros-humble-mrpt-libobs 提供(该包内层
#   -DBUILD_mrpt-topography=ON,见其包装工程),**模板最初漏了它**;
#   mrpt-tclap      ← ros-humble-mrpt-libtclap
#   mrpt-hwdrivers / mrpt-comms                ← ros-humble-mrpt-libhwdrivers
#   mrpt-gui                                   ← ros-humble-mrpt-libgui
#   mrpt-maps / mrpt-graphs                    ← ros-humble-mrpt-libmaps
#   mrpt-obs / mrpt-topography                 ← ros-humble-mrpt-libobs
#   mrpt-slam / mrpt-vision                    ← ros-humble-mrpt-libslam
#   mrpt-opengl / mrpt-img                     ← ros-humble-mrpt-libopengl
#   mrpt-tfest / mrpt-poses / mrpt-bayes       ← ros-humble-mrpt-libposes
#   mrpt-math                                  ← ros-humble-mrpt-libmath
#   mrpt-core/-io/-config/-random/-rtti/-serialization/-system/-expr/
#   -containers/-typemeta 等                   ← ros-humble-mrpt-libbase
BuildRequires:  ros-%{ros_distro}-mrpt-libbase
BuildRequires:  ros-%{ros_distro}-mrpt-libmath
BuildRequires:  ros-%{ros_distro}-mrpt-libposes
BuildRequires:  ros-%{ros_distro}-mrpt-libopengl
BuildRequires:  ros-%{ros_distro}-mrpt-libobs
BuildRequires:  ros-%{ros_distro}-mrpt-libmaps
BuildRequires:  ros-%{ros_distro}-mrpt-libgui
BuildRequires:  ros-%{ros_distro}-mrpt-libslam
BuildRequires:  ros-%{ros_distro}-mrpt-libhwdrivers
BuildRequires:  ros-%{ros_distro}-mrpt-libtclap
BuildRequires:  assimp-devel
BuildRequires:  eigen3-devel
BuildRequires:  ffmpeg-devel
# openEuler 的 freeglut 只有运行库,头文件/so 链接在 -devel;找不到系统 GLUT 时
# mrpt 会去编内置 freeglut(3rdparty/freeglut 为空目录),因
# -Werror=implicit-function-declaration 失败
BuildRequires:  freeglut-devel
BuildRequires:  git
BuildRequires:  gtest-devel
BuildRequires:  libXrandr-devel
# 上游 package.xml 写的是 Debian 名 libxxf86vm(apt 的 libxxf86vm-dev),
# RPM 名是 libXxf86vm-devel(dnf repoquery --whatprovides 已实证)
BuildRequires:  libXxf86vm-devel
BuildRequires:  libglvnd-devel
# 上游 package.xml 写的是 Debian 名 libjpeg,RPM 名 libjpeg-devel
# (由 openEuler 的 libjpeg-turbo-devel 提供)
BuildRequires:  libjpeg-devel
BuildRequires:  libpcap-devel
BuildRequires:  libudev-devel
# 本包装了 libusbx-devel → script_kinect.cmake 的 MRPT_WITH_KINECT_DEFAULT
# 变 ON,而 3rdparty/libfreenect 是空 submodule(openEuler 无 libfreenect-devel),
# 故内层必须显式注入 -DMRPT_WITH_KINECT=OFF(见 %prep)。xsens 那一路由包装工程
# 自带的 -DMRPT_BUILD_xsens=OFF 关掉,无需额外处理。
BuildRequires:  libusbx-devel
BuildRequires:  libzstd-devel
BuildRequires:  mesa-libGL-devel
BuildRequires:  mesa-libGLU-devel
# 上游 package.xml 的 libopencv-dev 经 pkg.remap 映射为 opencv;openEuler 的 opencv
# 包**不是**纯运行库,它同时提供头文件(/usr/include/opencv4/opencv2/*)与
# /usr/lib64/cmake/OpenCV/OpenCVConfig.cmake(dnf repoquery -l 实测),故保留以使
# mrpt/config.h 的 MRPT_HAS_OPENCV 与同链兄弟包一致。libs/apps 与 libs/graphslam 的
# 全部源码只在 *_unittest.cpp 里引用该宏(已 grep 实证,5 处全在 unittest),
# 本 spec BUILD_TESTING=OFF 且 DeclareMRPTLib 会滤掉 *_unittest.cpp,故不影响产物。
BuildRequires:  opencv
BuildRequires:  pkgconfig
BuildRequires:  pybind11-devel
BuildRequires:  suitesparse-devel
BuildRequires:  tbb-devel
BuildRequires:  tinyxml2-devel
BuildRequires:  zlib-devel
BuildRequires:  cmake
BuildRequires:  python3-pip

Requires:       ros-%{ros_distro}-ros-workspace
Requires:       ros-%{ros_distro}-mrpt-libbase
Requires:       ros-%{ros_distro}-mrpt-libmath
Requires:       ros-%{ros_distro}-mrpt-libposes
Requires:       ros-%{ros_distro}-mrpt-libopengl
Requires:       ros-%{ros_distro}-mrpt-libobs
Requires:       ros-%{ros_distro}-mrpt-libmaps
Requires:       ros-%{ros_distro}-mrpt-libgui
Requires:       ros-%{ros_distro}-mrpt-libslam
Requires:       ros-%{ros_distro}-mrpt-libhwdrivers
Requires:       ros-%{ros_distro}-mrpt-libtclap
# 装出的公共头直接暴露 Eigen:mrpt/graphslam/levmarq_impl.h include <Eigen/Dense>,
# apps 的公共头亦经 mrpt::poses/mrpt::maps 间接暴露 Eigen 类型
Requires:       eigen3-devel

Provides:       %{name}-devel = %{version}-%{release}
Provides:       %{name}-doc = %{version}-%{release}
Provides:       %{name}-runtime = %{version}-%{release}

%description
Mobile Robot Programming Toolkit (MRPT) libraries (apps C++ libraries).
  This package contains: mrpt-apps lib, mrpt-graphslam

%prep
%autosetup -n %{RosPkgName}-%{src_version}
# --- 复用预置 mrpt 源码,避免 ExternalProject 在构建期 git clone -------------
# mrpt_libapps/CMakeLists.txt 用 ExternalProject_Add(mrpt ...) 拉取上游
# mrpt,本地复用路径写成 ${CMAKE_BINARY_DIR}/../mrpt_libbase/mrpt-build/...,那是
# ROS 官方 buildfarm 的目录布局(build 目录 = <ws>/build/mrpt_libapps,故其
# .. 即 <ws>/build)。本 spec 的 CMAKE_BINARY_DIR = <top>/mrpt_libapps/.obj,
# 同一表达式会多出一层 mrpt_libapps(<top>/mrpt_libapps/mrpt_libbase/
# mrpt-build),EXISTS 检查必然失败而退回 git clone;COPR chroot 无外网,clone
# 100% 失败。统一改写成相对 CMakeLists.txt 自身目录的 ../mrpt-build,与下面预置的
# <top>/mrpt-build 对齐(EXISTS 检查 / SOURCE_DIR 两处)。
# 注:本仓库此处文案与 mrpt_libbase/libobs/libopengl/libmaps/libgui/libnav/
# libslam/libhwdrivers 等兄弟包**逐字相同**(同一个 mrpt_libbase 前缀 + 裸
# AND NOT 守卫,已在本包源码副本上实跑三处 sed 并 diff 核对命中)。
sed -i 's|\${CMAKE_BINARY_DIR}/\.\./mrpt_libbase/mrpt-build|${CMAKE_CURRENT_LIST_DIR}/../mrpt-build|g' \
    mrpt_libapps/CMakeLists.txt
# 去掉 HOME==/home/buildfarm 守卫:该条件只对 buildfarm 屏蔽本地复用,去掉后
# 复用分支在任何构建环境都成立。
sed -i 's/ AND NOT "\$ENV{HOME}" STREQUAL "\/home\/buildfarm"//' \
    mrpt_libapps/CMakeLists.txt
# 给内层 mrpt 追加开关(外层 cmake 命令行传的 MRPT_WITH_xxx 落在 ROS 包装工程上,
# 而包装工程只把它们原样忽略——真正生效的是写死在 ExternalProject_Add 里的
# CMAKE_ARGS,必须在此注入):
#   MRPT_HAS_LIBFYAML=OFF  openEuler 无 libfyaml,内置分支要 3rdparty/libfyaml
#                          (空 submodule)且会 file(READ .tarball-version) 报错
#   MRPT_WITH_KINECT=OFF   本包 BuildRequires 了 libusbx-devel,libusb 存在时
#                          script_kinect.cmake 默认开启 Kinect,会去编译空的
#                          3rdparty/libfreenect(openEuler 无 libfreenect-devel)
#   MRPT_HAS_OPENNI2=OFF   无 libopenni2;script_openni2.cmake 在开关为 ON 却找不到
#                          时是 FATAL_ERROR(不是静默关闭),必须显式关
#   MRPT_BUILD_OCTOMAP=OFF 顶层 CMakeLists 无条件 include script_octomap.cmake,
#                          找不到系统 octomap 时会退回 ExternalProject 去 GitHub
#                          下 devel.zip(chroot 无外网)。本包内层 BUILD_mrpt-maps=OFF,
#                          且 libs/apps + libs/graphslam 源码不含 octomap 引用;
#                          唯一相关处是 mrpt/maps/COctoMap.h(graphslam 的
#                          CGraphSlamEngine.h 无条件 include 它),该公共头**不含**
#                          octomap 头(pimpl + owner-指针,octomap 头只出现在
#                          libs/maps/src/*.cpp 里、由 libmaps 包编译),故无需引入
#                          octomap-devel(同链 libgui/libnav/libhwdrivers 路线)
#   MRPT_WITH_NANOGUI=OFF  必须关这个而不是包装工程自带的 MRPT_BUILD_nanogui=OFF:
#                          script_nanogui.cmake 里 MRPT_WITH_NANOGUI 为假才早退,
#                          nanogui 依赖 GLFW(全源无 provider)
#   DISABLE_WXWIDGETS=ON   wx 栈(wxGTK3-devel/wxBase3-devel)在 openEuler 存在但
#                          本包不引入:全链已构建的兄弟包(libbase/libmath/libposes/
#                          libtclap/libopengl/libmaps/libobs/libgui/libslam/libnav/
#                          libhwdrivers)都是无 wx 配置,特性一致优先;apps/graphslam
#                          侧 wx 只出现在 KFSLAMApp.cpp / ICP_SLAM_App.cpp /
#                          MonteCarloLocalization_App.cpp 的 #if MRPT_HAS_WXWIDGETS
#                          段内(含相反的 #if !MRPT_HAS_WXWIDGETS 分支,两条路都自洽)
#   CMAKE_PREFIX_PATH      内层 mrpt 对 BUILD_mrpt-*=OFF 的兄弟库走
#                          DeclareMRPTLib.cmake 的 find_package(mrpt-XXX) 分支,
#                          必须能搜到 mrpt-hwdrivers/-gui/-slam/-tclap/-topography/
#                          -maps/-obs/-opengl/-poses/-math/-base 装到
#                          /opt/ros/<distro> 的 mrpt-*-config.cmake;缺了它会打出
#                          "*Warning* ==> Disabling compilation of lib mrpt-apps for
#                          missing dependencies" 而静默产出空包。
sed -i 's|^\(\s*-DDISABLE_JSONCPP=ON\)$|\1\n    -DMRPT_HAS_LIBFYAML=OFF\n    -DMRPT_WITH_KINECT=OFF\n    -DMRPT_HAS_OPENNI2=OFF\n    -DMRPT_BUILD_OCTOMAP=OFF\n    -DMRPT_WITH_NANOGUI=OFF\n    -DDISABLE_WXWIDGETS=ON\n    -DCMAKE_PREFIX_PATH=/opt/ros/%{ros_distro}|' \
    mrpt_libapps/CMakeLists.txt
# 闸门:确认上面三处 sed 真的改到了 CMakeLists.txt(模式不匹配时 sed 是静默空
# 操作,要到构建期才以 git clone 超时 / 空包暴露)
if ! grep -q 'CMAKE_CURRENT_LIST_DIR}/../mrpt-build' mrpt_libapps/CMakeLists.txt; then
    echo "ERROR: CMakeLists.txt 本地复用路径改写失败(sed 模式不匹配)" >&2
    exit 1
fi
if ! grep -q 'MRPT_WITH_KINECT=OFF' mrpt_libapps/CMakeLists.txt; then
    echo "ERROR: 内层 CMAKE_ARGS 开关注入失败(sed 锚点 -DDISABLE_JSONCPP=ON 不匹配)" >&2
    exit 1
fi
if ! grep -q 'CMAKE_PREFIX_PATH=/opt/ros/%{ros_distro}' mrpt_libapps/CMakeLists.txt; then
    echo "ERROR: 内层 CMAKE_ARGS 的 CMAKE_PREFIX_PATH 注入失败(sed 锚点不匹配)" >&2
    exit 1
fi
if grep -q 'buildfarm' mrpt_libapps/CMakeLists.txt; then
    echo "ERROR: buildfarm HOME 守卫未移除(sed 模式不匹配)" >&2
    exit 1
fi
# 预置 ExternalProject 的 SOURCE_DIR:<top>/mrpt-build/src/mrpt
mkdir -p mrpt-build/src
tar -xzf %{SOURCE1} -C mrpt-build/src
mv mrpt-build/src/mrpt-%{mrpt_version} mrpt-build/src/mrpt
# GitHub archive tarball 不含 git submodule 内容,而上游 mrpt 顶层 CMakeLists.txt
# 有无条件存在性检查(3rdparty/nanogui/include、3rdparty/rplidar_sdk/sdk/src),
# 缺失即 FATAL_ERROR "git submodules missing!"。本包内层已用
# -DMRPT_WITH_NANOGUI=OFF 关闭 nanogui、包装工程把 ROBOPEAK_LIDAR/rplidar_sdk
# 写死 OFF,两个目录只需存在即可(无需真实源码,故不像 libhwdrivers 那样预取
# rplidar_sdk)。
mkdir -p mrpt-build/src/mrpt/3rdparty/nanogui/include \
         mrpt-build/src/mrpt/3rdparty/rplidar_sdk/sdk/src
# 填充 mrpt 的两个必需 submodule(见 Source2/Source3 说明)。
rm -rf mrpt-build/src/mrpt/3rdparty/nanoflann mrpt-build/src/mrpt/3rdparty/simpleini
mkdir -p mrpt-build/src/mrpt/3rdparty/nanoflann mrpt-build/src/mrpt/3rdparty/simpleini
tar -xzf %{SOURCE2} -C mrpt-build/src/mrpt/3rdparty/nanoflann --strip-components=1
tar -xzf %{SOURCE3} -C mrpt-build/src/mrpt/3rdparty/simpleini --strip-components=1
# 闸门:nanoflann 必须带上 CMakeLists.txt(script_nanoflann.cmake 会
# execute_process 直接 cmake 这个目录,缺了即 FATAL_ERROR);
# simpleini 必须带上 SimpleIni.h(script_simpleini.cmake 只用 find_path,
# 找不到就静默指向源码树内该目录,直到编译 CConfigFile.cpp 才报错)。
if [ ! -f mrpt-build/src/mrpt/3rdparty/nanoflann/CMakeLists.txt ]; then
    echo "ERROR: 3rdparty/nanoflann/CMakeLists.txt 缺失 —— Source2 解包失败,内层将 FATAL_ERROR" >&2
    exit 1
fi
if [ ! -f mrpt-build/src/mrpt/3rdparty/simpleini/SimpleIni.h ]; then
    echo "ERROR: 3rdparty/simpleini/SimpleIni.h 缺失 —— Source3 解包失败,构建 CConfigFile.cpp 时会失败" >&2
    exit 1
fi
# 覆盖包装工程从 package.xml 读到的版本号,与预取的上游源码一致
echo 'set(MRPT_VERSION_TO_DOWNLOAD "%{mrpt_version}")' > override_mrpt_version.cmake

%build
[ -f /opt/ros/%{ros_distro}/setup.sh ] && source /opt/ros/%{ros_distro}/setup.sh || true
export PYTHONPATH=/opt/ros/%{ros_distro}/lib/python%{python3_version}/site-packages${PYTHONPATH:+:$PYTHONPATH}
export PKG_CONFIG_PATH=/opt/ros/%{ros_distro}/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}
# 闸门:兄弟库的 cmake config 必须可见。内层对 BUILD_mrpt-*=OFF 的库走
# find_package(mrpt-XXX),找不到只会 Warning + 静默禁用(产出空包);这里把
# 最常见的"漏列 BuildRequires"提前变成可归因失败。
# ★ 判据一:这里的名字必须是**上游 mrpt 库名**,不是 ROS 包装包名。
#   ros-humble-mrpt-libbase 装出的是 mrpt-core/-config/-io/-system/-rtti/... ——
#   上游没有 "mrpt-base" 这个库(MRPT 1.x 才叫 base)。build 112607 踩在这:
#   `ls .../cmake/mrpt-base/mrpt-base-config.cmake` 对已装好的 libbase 恒为假。
#   实测定名规则(逐包 rpm -qpl 兄弟包产物):
#     mrpt-core/-config/-io/-system/-rtti/... ← ros-humble-mrpt-libbase
#     mrpt-math ← libmath;mrpt-poses/-tfest/-bayes ← libposes
#     mrpt-opengl/-img ← libopengl;mrpt-obs/-topography ← libobs
#     mrpt-maps/-graphs ← libmaps;mrpt-gui ← libgui;mrpt-slam/-vision ← libslam
#     mrpt-hwdrivers/-comms ← libhwdrivers;mrpt-tclap ← libtclap
#   包名 ≠ 库名是这条链的常态,新增闸门条目必须用 RPM 产物实证后再写。
# ★ 判据二:逐路径用 test -f 判定,**绝不能写成 `ls A B`**。ls 只要有一个操作数
#   不存在就返回 2(即便另一个存在),而各库的安装位置并不统一:实测产物里
#   libbase/libmath/libposes/libopengl/libobs/libmaps/libgui/libslam/libhwdrivers
#   装在 lib64/cmake/,而 **mrpt-tclap 在 share/cmake/**(mrpt-tclap 无编译产物,
#   走了 GNUInstallDirs 的另一支)。build 112609 就是踩在这:十个兄弟包全部已
#   installed(COPR installed_pkgs.log 实测),可 `ls lib64/... lib/...` 因第二个
#   操作数恒不存在而恒返回 2,闸门在 53 秒 100% 误报 —— 闸门本身必须先能证真。
CONFIG_DIRS="lib64/cmake lib/cmake share/cmake"
_config_missing=""
for _m in core math poses opengl obs topography maps gui slam hwdrivers tclap; do
    _hit=""
    for _d in $CONFIG_DIRS; do
        if [ -f "/opt/ros/%{ros_distro}/$_d/mrpt-$_m/mrpt-$_m-config.cmake" ]; then
            _hit="/opt/ros/%{ros_distro}/$_d/mrpt-$_m/mrpt-$_m-config.cmake"
            break
        fi
    done
    if [ -z "$_hit" ]; then
        _config_missing="$_config_missing mrpt-$_m"
    fi
done
if [ -n "$_config_missing" ]; then
    echo "ERROR: 未找到上游 mrpt 库的 cmake config:$_config_missing —— 对应 ros-%{ros_distro}-mrpt-lib* 未装进 buildroot(BuildRequires 漏列 / 包名≠库名)" >&2
    echo "---- buildroot 内实际的 mrpt cmake config(诊断用)----" >&2
    find /opt/ros/%{ros_distro} -maxdepth 5 -name 'mrpt-*-config.cmake' 2>/dev/null | sort | head -40 >&2
    exit 1
fi
echo "兄弟库 cmake config 全部就位(已按 lib64/lib/share 三种布局探测)"
cd mrpt_libapps
mkdir -p .obj && cd .obj
%cmake3 \
    -DCMAKE_INSTALL_PREFIX="/opt/ros/%{ros_distro}" \
    -DAMENT_PREFIX_PATH="/opt/ros/%{ros_distro}" \
    -DCMAKE_PREFIX_PATH="/opt/ros/%{ros_distro}" \
    -UINCLUDE_INSTALL_DIR -ULIB_INSTALL_DIR -USYSCONF_INSTALL_DIR -USHARE_INSTALL_PREFIX -ULIB_SUFFIX \
    -DSETUPTOOLS_DEB_LAYOUT=OFF \
    -DBUILD_TESTING=OFF -DTESTING=OFF -DCMAKE_SUPPRESS_REGENERATION=ON \
    ..
# 闸门:cmake 生成 mrpt-gitclone.cmake 即表示未复用准备段预置的 mrpt 源码,
# 构建期将 git clone(COPR chroot 无外网,必然失败)。这里提前失败并给出明确
# 原因,避免又白烧一轮 COPR 构建。
if [ -e mrpt-build/tmp/mrpt-gitclone.cmake ]; then
    echo "ERROR: cmake 准备 git clone mrpt —— %prep 预置源码未被复用,请检查预置路径" >&2
    exit 1
fi
# 注:内层 ExternalProject 的 configure 是下面的 make 触发的,此刻
# mrpt-build/src/mrpt-build/CMakeCache.txt 与 EP_octomap-prefix 都还不存在,
# 针对它们的闸门一律放在 make 之后(见下方"构建后闸门"),放在这里会 100% 误报。
# --- 并行度与内存 -----------------------------------------------------------
# mrpt-apps / graphslam 是链上模板最重的库之一(KFSLAM/RBPF 的
# CSerializable、CPose3D 等定长矩阵模板),单位 TU 峰值内存可达 GB 级;COPR
# builder 仅 4 核 / 6.9GiB / 无 swap,多路 cc1plus 叠峰会被 OOM killer SIGKILL
# (同链 mrpt-libmath 在 build 112579 实测:c++ fatal error: Killed signal
# terminated program cc1plus;限并行+降优化级别已在 build 112584 起验证有效)。
#   (1) 降单进程内存:CMake 用环境变量 CXXFLAGS/CFLAGS 初始化 CMAKE_<LANG>_FLAGS,
#       故在 make 之前覆盖为 -O1 -g0:本包 debug_package 已置空,-g 只烧内存
#       不出产物;并保留下游 hardening 项。
#   (2) 限并行:按"每个 cc1plus 预留 2.5GiB"从 MemTotal 反推 job 数,取
#       min(nproc, 内存上限)、至少 1 路。
#   ★ 本段注释里禁止出现百分号前缀的宏指令字样:rpm 的 scriptlet 解析器会把
#     注释行里的这类文本当宏定义提取,定义之后同一行的残留文本会被丢出来当
#     命令执行(同链 mrpt-libmath 的 build 112584 实踩:line 105: command not
#     found → Bad exit status,而编译本身已 100% 通过)。
export CFLAGS="-O1 -g0 -fstack-protector-strong -Wp,-D_FORTIFY_SOURCE=2 -fasynchronous-unwind-tables"
export CXXFLAGS="-O1 -g0 -fstack-protector-strong -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fasynchronous-unwind-tables"
JOBS="$(nproc 2>/dev/null || echo 1)"
MEM_KB="$(awk '/^MemTotal:/{print $2; exit}' /proc/meminfo 2>/dev/null)"
case "$MEM_KB" in
    ''|*[!0-9]*) MEM_KB=0 ;;
esac
if [ "$MEM_KB" -gt 0 ]; then
    MEM_JOBS=$(( MEM_KB / 2621440 ))
    if [ "$MEM_JOBS" -lt 1 ]; then MEM_JOBS=1; fi
    if [ "$JOBS" -gt "$MEM_JOBS" ]; then JOBS="$MEM_JOBS"; fi
fi
echo "mrpt-apps/graphslam 构建并行度: -j${JOBS}(nproc=$(nproc 2>/dev/null || echo '?'), MemTotal=${MEM_KB}KiB)"
make -j"$JOBS"
# --- 构建后闸门(内层 configure/build 已完成,内层产物与缓存此时才存在)------
# 闸门:MRPT_BUILD_OCTOMAP=OFF 未生效时应退回 ExternalProject 下载内置 octomap
# (chroot 无外网)。该 EP 的 prefix 目录在内层 configure 阶段即被创建,据此判定。
if [ -e mrpt-build/src/mrpt-build/EP_octomap-prefix ]; then
    echo "ERROR: 内层仍创建了 EP_octomap(MRPT_BUILD_OCTOMAP=OFF 未生效)—— 将去 GitHub 下内置 octomap(无外网必挂)" >&2
    exit 1
fi
# 闸门:确认真的产出了 libmrpt-apps.so 与 libmrpt-graphslam.so。内层若没搜到
# mrpt-hwdrivers/-gui/-slam/-tclap/-topography(CMAKE_PREFIX_PATH 失效 / 兄弟库
# 缺失),DeclareMRPTLib.cmake 只会打一行 Warning 就静默禁用该库,构建"成功"
# 但装出的是空包——这里把它变成构建期硬失败。
for _lib in apps graphslam; do
    _so="$(find mrpt-build/src/mrpt-build -name "libmrpt-${_lib}.so*" -print -quit 2>/dev/null)"
    if [ -z "$_so" ]; then
        echo "ERROR: 未产出 libmrpt-${_lib}.so —— 内层 mrpt 很可能因 find_package(mrpt-hwdrivers/mrpt-gui/mrpt-slam/mrpt-tclap/mrpt-topography) 未命中而静默禁用了 mrpt-${_lib}(空包)" >&2
        exit 1
    fi
    echo "已产出: $_so"
done

%install
[ -f /opt/ros/%{ros_distro}/setup.sh ] && source /opt/ros/%{ros_distro}/setup.sh || true
export PYTHONPATH=/opt/ros/%{ros_distro}/lib/python%{python3_version}/site-packages${PYTHONPATH:+:$PYTHONPATH}
export PKG_CONFIG_PATH=/opt/ros/%{ros_distro}/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}
cd mrpt_libapps
cd .obj
make install DESTDIR=%{buildroot}

%files
/opt/ros/%{ros_distro}

%changelog
* Tue Sep 22 2026 ai-importer <ai-importer@openeuler.org> - 2.15.20-2
- 依赖可用性预检无 provider 修复(触发面 prepare_failed/kind=deps,缺失清单
  libfreenect-devel / libglfw3-dev / libopenni2-dev / libxxf86vm / wx-common /
  wxwidgets,与同链 mrpt-libopengl / -libobs / -libgui / -libnav / -libslam /
  -libhwdrivers 完全同源,处置对齐已构建成功的这些兄弟包):
  * Debian 名修正:libxxf86vm → libXxf86vm-devel,libjpeg → libjpeg-devel,
    freeglut → freeglut-devel。
  * libfreenect-devel / libopenni2-dev / libglfw3-dev / wx-common / wxwidgets
    从 BuildRequires 移除,改为关掉内层 mrpt 对应特性:-DMRPT_WITH_KINECT=OFF /
    -DMRPT_HAS_OPENNI2=OFF / -DMRPT_WITH_NANOGUI=OFF / -DDISABLE_WXWIDGETS=ON。
    核实过 libs/apps 与 libs/graphslam 对这些特性的引用全部有宏守护
    (MRPT_HAS_WXWIDGETS 见 KFSLAMApp.cpp / ICP_SLAM_App.cpp /
    MonteCarloLocalization_App.cpp,含 #if !MRPT_HAS_WXWIDGETS 反向分支),
    且这些条目在 mrpt_libapps/package.xml 里本就自注为 "not actually required
    by this ROS package"。
  * 补齐 BuildRequires/Requires:ros-humble-mrpt-libobs(提供 mrpt-topography,
    模板漏列)、-libbase / -libmath / -libposes / -libopengl / -libslam /
    -libmaps / -libgui / -libhwdrivers / -libtclap,覆盖
    libs/apps → (hwdrivers + gui + slam + tclap + topography) 与
    libs/graphslam → (gui + slam) 的完整 find_package 闭包。
  * 补上离线构建所需的 Source1-3(预取 mrpt-2.15.21 源码 + nanoflann/simpleini
    submodule)、%prep 的复用路径改写与内层开关注入、以及 git clone /
    EP_octomap / 产物三类闸门,避免 chroot 内走外部网络或静默产出空包。
  * 不再预取 rplidar_sdk(包装工程已 -DMRPT_WITH_ROBEPEAK_LIDAR=OFF
    -DMRPT_BUILD_rplidar_sdk=OFF,本包内层不建该静态库)。

* Tue Sep 22 2026 ai-importer <ai-importer@openeuler.org> - 2.15.20-3
- %build 兄弟库 cmake config 闸门误报修复(触发面 build_failed,build 112607
  在 %build 第 1 条闸门即失败:`ls .../cmake/mrpt-base/mrpt-base-config.cmake`
  找不到,报 "ERROR: 未找到 mrpt-base 的 cmake config"):
  * 根因是本闸门自己写错了名字 —— 循环里的标识符必须是**上游 mrpt 库名**而非
    ROS 包装包名:ros-humble-mrpt-libbase 装出的是 mrpt-core / -config / -io /
    -system / -rtti / -serialization / -containers / -expr / -random /
    -typemeta 这十个 config,**上游没有 mrpt-base 这个库**(MRPT 1.x 时代才叫
    base)。故对已正确装进 buildroot 的 libbase,该判据恒为假,构建在 54 秒时
    被自己的闸门打死,根本没走到 cmake。
  * 实证:`rpm -qpl ros-humble-mrpt-libbase-2.15.20-3.aarch64.rpm | grep cmake`
    只有 lib64/cmake/mrpt-core/... 等,无 mrpt-base;同理逐包核对过
    libobs→mrpt-obs/mrpt-topography、libtclap→mrpt-tclap、
    libhwdrivers→mrpt-hwdrivers/mrpt-comms、libslam→mrpt-slam/mrpt-vision、
    libmaps→mrpt-maps/mrpt-graphs,其余标识符(math/poses/opengl/gui/slam/
    maps/obs/topography/tclap/hwdrivers)均正确。
  * 改动:闸门列表 base → core,并在注释里写明"包名 ≠ 库名"这条判据,避免
    后续再按 ROS 包名写库名。
  * BuildRequires/Requires 本身无缺,未做增删;verify_spec_requires.py
    仍报 "OK: 36 个依赖…全部有 provider"。

* Tue Sep 22 2026 ai-importer <ai-importer@openeuler.org> - 2.15.20-4
- %build 兄弟库 cmake config 闸门的**判据形式**缺陷修复(触发面 build_failed,
  build 112609 在 %build 首条闸门 53 秒即失败,报文为"未找到 mrpt-core 的
  cmake config"——core 恰好排在列表第一位):
  * 反证:该次构建 COPR 的 installed_pkgs.log 显示十个兄弟包**全部已
    installed**(libbase-2.15.20-3 / libmath-3 / libposes-2 / libopengl-2 /
    libobs-2 / libgui-2 / libmaps-3 / libslam-2 / libhwdrivers-2 / libtclap-1),
    且逐包 rpm -qpl 兄弟包产物确认 config 文件确实存在——缺的不是包,是闸门。
  * 根因一(判据形式):判据写成 `ls <lib64 路径> <lib 路径>`。ls 只要有一个
    操作数不存在就返回 2,而 /opt/ros/humble/lib/cmake/mrpt-*/ 根本不存在
    (逐包 rpm -qpl 实测:十个兄弟包无一在 lib/ 下装 mrpt-*-config.cmake),
    故 `if ! ls A B` 对任何 $_m 都恒为真 → 闸门 100% 误报,与包本身无关。
  * 根因二(安装布局):安装位置不止 lib64/cmake 一处 —— **mrpt-tclap 装在
    share/cmake/mrpt-tclap/**(mrpt-tclap 无编译产物,GNUInstallDirs 走了
    share 分支)。即便把 ls 改对,tclap 这一项仍会误报。
  * 改动:改为对 lib64/cmake、lib/cmake、share/cmake 三种布局逐路径 test -f
    探测,全部未命中才判失败;失败时额外 dump buildroot 内实际存在的
    mrpt-*-config.cmake,便于下一轮直接归因。
  * 闸门列表(core math poses opengl obs topography maps gui slam hwdrivers
    tclap)本轮逐条用兄弟包 RPM 产物做了**正例校验**:除 tclap 的 share/
    布局外全部命中 lib64/cmake/<名字>/<名字>-config.cmake,无幻觉条目。
  * BuildRequires/Requires 无缺,未做增删。