Ubuntu 有现成的包,一条 apt install 就完事,不用自己编。
但装包版在 /usr、自编版在 /usr/local——同样的代码,一台编得过、一台编不过。
上一节在 CentOS 上自己编,这一节在 Ubuntu 上装包,两边的差别本身就值得记一下。 这一节的重点不是命令,是「路径不同导致的编译参数差异」,这是跨平台迁移时最常撞的墙。
一、两边的命令对照
| 要做的事 | CentOS(yum) | Ubuntu(apt) |
|---|---|---|
| 装编译器 | yum install gcc gcc-c++ |
apt install build-essential |
| 装 gSOAP | 自己编 | apt install gsoap libgsoap-dev |
| 装开发库 | openssl-devel |
libssl-dev |
第二行是最大差别:Ubuntu 直接有包,CentOS 官方源里没有,所以书里给两个系统各写一节。 但「有包」不等于「版本够新」:发行版的包跟着系统版本走, Ubuntu 20.04 带的是 2.8.107,而 ONVIF 项目常用 2.8.116(08.08.02)。 版本差一点通常没事,但生成 ONVIF 代码时某些类型可能对不上—— 这是「装包版」唯一要留心的地方。
二、包名分两个,少一个就编不过
| 包 | 里面有什么 | 只装一个的后果 |
|---|---|---|
gsoap |
soapcpp2、wsdl2h 两个可执行文件 |
— |
libgsoap-dev |
soapH.h、libgsoap.so、头文件 |
编译时报「找不到 soapH.h」 |
这个「工具包 + 开发包」的拆分是 Debian 系的通例:命令行工具一个包,头文件和库另一个包。
只装 gsoap 你会得到一个能跑 soapcpp2、但编不出程序的机器——
生成代码那一步成功了,编译那一步全红。
看到「soapH.h 找不到」先查这个,别去查 -I 路径。
三、路径不同,编译参数就得不同
| 项 | 自编(CentOS) | 装包(Ubuntu) |
|---|---|---|---|
| 安装位置 | /usr/local | /usr |
| g++ 默认搜吗 | 头文件不搜、库不搜 | 都搜 |
| 要加的参数 | -I/usr/local/include -L/usr/local/lib | 不用加 |
| 卸载 | make uninstall(不一定有) | apt remove |
这一张表是本节的核心。
g++ 的默认搜索路径里没有 /usr/local/include 和 /usr/local/lib
(严格说 gcc 的默认 prefix 是 /usr,/usr/local 要显式给)。
所以同一份代码:Ubuntu 上 g++ client.cpp ... 就过;CentOS 上必须加两个 -I/-L。
反过来,如果你在 Ubuntu 上加了 -I/usr/local/include,而那里正好有一份老版本的头文件,
就会「头文件是新版的、库是老版的」——链接期报一堆 undefined reference,查半天。
四、链接参数,第三行最常漏
g++ client.cpp soapC.cpp soapClient.cpp \
-lgsoap++ -lssl -lcrypto -lpthread
| 参数 | 为什么 | 漏了报什么 |
|---|---|---|
-lgsoap++ |
C++ 版库(纯 C 用 -lgsoap) |
undefined reference to soap_... |
-lssl -lcrypto |
用了 HTTPS 就必须加 | undefined reference to SSL_... |
-lpthread |
gSOAP 内部用线程锁 | undefined reference to pthread_... |
第三行最容易被漏,因为在 Windows 上不需要(Windows 的线程 API 在 kernel32 里,
随 CRT 一起链)。从 Windows 迁到 Linux 的项目,第一个撞的就是它。
第一行也有个坑:C++ 项目要用 -lgsoap++,不是 -lgsoap。
用错了会「符号名对不上」——
因为 C++ 的符号经过 name mangling,和 C 版库里的符号根本不是同一个名字。
五、动手 Demo:Ubuntu 上就一条命令,但坑在别处
本机是 Windows,没有 apt,所以这一节也跑不了 apt 命令。 Demo 做的是「路径与链接参数的自检」: 把两边的路径规则、包名、链接参数写进一张表,逐条标出「谁负责」—— 哪一条是 apt 装好的、哪一条要自己加。 这段代码在任何平台都能跑,它只是打印一张对照表并做几项路径判断。
import os, shutil
# ① 两个前缀下,关键文件在不在
for pre in ("/usr", "/usr/local"):
for rel in ("bin/soapcpp2", "include/soapH.h",
"lib/libgsoap.so", "lib/libgsoap++.so"):
p = os.path.join(pre, rel)
print(p, "有" if os.path.exists(p) else "没有")
# ② 装包版和自编版的编译参数差异,由「文件在哪」推出来
need_inc = os.path.exists("/usr/local/include/soapH.h") and \
not os.path.exists("/usr/include/soapH.h")
print("要加 -I/usr/local/include 吗:", need_inc)
实跑输出:
--------------------------------------------------------------------
Ubuntu 上就一条命令,但坑在别处
--------------------------------------------------------------------
**Ubuntu 有现成的包,不用自己编。**
这一节对照两边的差别,再说清「装包版」和「自编版」的区别。
**两边的命令对照:**
要做的事 CentOS(yum) Ubuntu(apt)
----------------------------------------------------------------------------------
装编译器 `yum install gcc gcc-c++` `apt install build-essential`
装 gSOAP 自己编 `apt install gsoap libgsoap-dev`
装开发库 `openssl-devel` `libssl-dev`
**第二行是最大差别**:Ubuntu 直接有包,**CentOS 要自己编**。
**这也是为什么书里给两个系统各写一节。**
**注意包名分两个**:`gsoap` 是工具(`soapcpp2`/`wsdl2h`),`libgsoap-dev` 是开发库。
**只装前者,编译时会报「找不到 soapH.h」。**
**自编版和装包版的三个区别:**
项 自编(CentOS) 装包(Ubuntu)
--------------------------------------------------------------------------
版本 你下载的那个 跟着发行版走,可能偏旧
安装位置 `/usr/local` `/usr`
卸载 `make uninstall`(不一定有) `apt remove`
**第二行会影响到编译参数**:自编版在 `/usr/local`,**`g++` 默认不搜这个目录的头文件**,
要加 `-I/usr/local/include`。
**而 Ubuntu 装包版在 `/usr`,默认就搜**。
所以同样的代码在两台机器上**一个编得过、一个编不过。**
**Linux 上编 gSOAP 程序,链接参数一般长这样:**
`g++ client.cpp soapC.cpp soapClient.cpp -lgsoap++ -lssl -lcrypto -lpthread`
参数 为什么
------------------------------------------------------
`-lgsoap++` C++ 版库,纯 C 用 `-lgsoap`
`-lssl -lcrypto` 用了 HTTPS 就必须加
`-lpthread` gSOAP 内部用线程锁
**第三行最常被漏**,报错是 `undefined reference to pthread_...`。
**在 Windows 上不需要,所以从 Windows 迁过来的项目必踩。**
本机(Windows)这一整张表全是「没有」——因为 /usr 这个前缀在 Windows 上不存在。
所以这段代码在本机只验证了「路径判断逻辑」本身,没验证过真实的 Ubuntu 环境。
「要加 -I 吗」这一行是推出来的、不是查文档的:
如果头文件只在 /usr/local/include 而不在 /usr/include,那默认路径就找不到,必须加。
这个推理链比背结论可靠——换台机器、换个 prefix,逻辑照样成立。
正文里那张「链接参数」表是本机完全无法验证的部分(没有 g++)。
它来自 gSOAP 的构建要求,不是我实测的——这一类知识只能标注来源,不能假装跑过。
30 秒实验
- 在任意一台 Ubuntu 上
apt install gsoap libgsoap-dev,再跑这段探针,看四个文件在哪。 - 故意只装
gsoap不装libgsoap-dev,编译一个最小程序,看报错是不是「找不到 soapH.h」。 - 把
-lpthread去掉,看undefined reference to pthread_create长什么样。
常见误区
- 以为装
gsoap就齐了。libgsoap-dev是另一个包,缺它编不过。 - 以为自编版和装包版只是版本不同。 安装前缀不同(
/usr/localvs/usr), 会导致编译参数必须不同。 - 以为 C++ 项目链接
-lgsoap也行。 要用-lgsoap++, C 和 C++ 的符号名经过 name mangling 之后对不上。
自测三问(点开对答案)
1. Ubuntu 上装 gSOAP 要装几个包?分别装什么? —— **两个**:`gsoap`(工具:`soapcpp2`/`wsdl2h`)和 `libgsoap-dev`(头文件 + 库)。 2. 为什么自编版编程序要加 `-I/usr/local/include`? —— 因为 **`g++` 的默认搜索路径里不含 `/usr/local/include`**,**自编版装在那里**。 装包版在 `/usr`,默认就搜。 3. `-lpthread` 为什么在 Linux 上不能漏、Windows 上不用加? —— gSOAP 内部用线程锁,**Linux 上 `pthread` 是单独的库**; **Windows 的线程 API 在 `kernel32` 里,随 CRT 一起链。**下一节回到 Windows:在 VS 里用 gSOAP 开发客户端,看「生成的桩被调用」这件事的全貌。
- ☐ (本节完,下一节 06.02.05 VS利用gSOAP开发Web Service客户端)