linux下用CMake + SWIG为GO语言写动态库
网上没有完整的例子,这里是本人参考各个代码repo总结的结果。遗憾的是,我没有实现Pure Go的库。调用该lib的GO程序需要和wrap.cxx,还有.h文件一起build产生可执行文件。本文采用go 1.15版本, SWIG 4.0.1 cmake 3.17.1
本文的代码参考https://github.com/zacg/simplelib. 不同的是,原例子并没有使用CMake,也没有产生lib文件,而是手动输入swig和go命令,将所有c++代码和go代码混在一起完成了go对c++的调用。这在实际工程应用中是很不方便的。我们希望C++写的部分编译成一个lib,GO调用时只需要调用这个lib就可以,而不需要连lib一起重新编译。因此我对这个例子做了改动,加入了CMake,并且将执行程序和动态库分开编译。
下面是我修改后的代码目录结构,本文使用默认GOPATH = ~/go:
xian@chaos:~/go/src/simplelib$ tree
.
├── CMakeLists.txt
├── include
│ └── simpleclass.h
├── lib.go
├── simplelib.i
└── src
└── simpleclass.cpp
2 directories, 5 files
此外还有个调用代码main.go,他可以在任何地方。
首先看一下c++/go代码
simpleclass.h
#ifndef SIMPLECLASS_H
#define SIMPLECLASS_H
#include <iostream>
#include <vector>
class SimpleClass
{
public:
SimpleClass(){};
std::string hello();
void helloString(std::vector<std::string> *results);
void helloBytes(std::vector<char> *results);
};
#endif
simpleclass.cpp
#include "simpleclass.h"
std::string SimpleClass::hello(){
return "world";
}
void SimpleClass::helloString(std::vector<std::string> *results){
results->push_back("world");
}
void SimpleClass::helloBytes(std::vector<char> *results){
results->push_back('w');
results->push_back('o');
results->push_back('r');
results->push_back('l');
results->push_back('d');
}
lib.go
package simplelib
/*
#cgo LDFLAGS: -L. -lExample
*/
import "C"
simplelib.i
%module simplelib
%{
#include "simpleclass.h"
%}
%include <typemaps.i>
%include "std_string.i"
%include "std_vector.i"
// This will create 2 wrapped types in Go called
// "StringVector" and "ByteVector" for their respective
// types.
namespace std {
%template(StringVector) vector<string>;
%template(ByteVector) vector<char>;
}
%include "simpleclass.h"
main.go
package main
import (
"fmt"
"simplelib/build/go/Example"
//这是swig 产生wrapper文件的目录
)
func main() {
simpleClass := simplelib.NewSimpleClass()
result := simpleClass.Hello()
fmt.Println(result)
}
CMakeLists.txt
分三个部分:第一部分与go无关,只需要.h .cpp生成动态库libExample.so。第二部分使用swig_add_library,根据.i文件以及.i文件中涉及到的.h文件生成wrap.cxx文件。第三部分则是复制一些文件,包括libExample.so到特定目录以解决go build时的路径问题。
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0 FATAL_ERROR)
find_package(SWIG REQUIRED)
include(UseSWIG)
find_program(GO_EXECUTABLE go REQUIRED)
if(POLICY CMP0078)
cmake_policy(SET CMP0078 NEW)
endif()
project(
Example
LANGUAGES CXX
)
#########################################################
# 第一步产生Example.so,这里只需要.cpp .h文件
#########################################################
add_library(Example SHARED "")
target_sources(Example
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/simpleclass.h>
$<INSTALL_INTERFACE:include/simpleclass.h>
PRIVATE
src/simpleclass.cpp
)
target_include_directories(Example
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_features(Example PUBLIC cxx_std_11)
add_library(${PROJECT_NAME}::Example ALIAS Example)
############################################################
## 第二步产生wrap.cxx文件,这里只需要*.i及其include的头文件
###########################################################
set(CMAKE_SWIG_FLAGS -cgo -intgosize 64)
set_property(SOURCE simplelib.i PROPERTY CPLUSPLUS ON) #没有这一条会生成wrap.c
set_property(SOURCE simplelib.i PROPERTY COMPILE_OPTIONS
-package simplelib)
swig_add_library(exampleForGo
LANGUAGE go
TYPE OBJECT
OUTPUT_DIR ${PROJECT_BINARY_DIR}/go/${PROJECT_NAME}
SOURCES simplelib.i)
target_include_directories(exampleForGo PRIVATE ${PROJECT_SOURCE_DIR}/include)
#simplelib.i中有include “*.h”, 这里为这些头文件提供路径
set_target_properties(exampleForGo PROPERTIES
SWIG_INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/include #SWIG也添加include路径
SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON
POSITION_INDEPENDENT_CODE ON)
target_link_libraries(exampleForGo PRIVATE ${PROJECT_NAME}::Example)
#############################################################################
# 第三步复制一些文件到wrapper所在目录,供go build使用
#############################################################################
file(COPY "${CMAKE_SOURCE_DIR}/include/simpleclass.h" DESTINATION "${PROJECT_BINARY_DIR}/go/${PROJECT_NAME}")
file(COPY "${CMAKE_SOURCE_DIR}/lib.go" DESTINATION "${PROJECT_BINARY_DIR}/go/${PROJECT_NAME}")
#############################################################################
# 复制动态链接库libExample.so到系统目录 /usr/local/lib
#############################################################################
install(TARGETS Example DESTINATION ${CMAKE_INSTALL_LIBDIR})
好了。接下来进入到CMakeLists.txt开始build
cmake -Bbuild
sudo cmake --build build --target install #这里需要sudo权限访问/usr/local/lib
这样就算是安装好库了。接下来看怎么调用它。进入main.go所在目录,
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib #把库目录加入到环境变量
go build main.go #build
./main #run!
world #<==you should read this
最后,我好像没找到go的打包程序,看来只能使用CPack了。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么构造函数需要尽可能的简单
· 探秘 MySQL 索引底层原理,解锁数据库优化的关键密码(下)
· 大模型 Token 究竟是啥:图解大模型Token
· 35岁程序员的中年求职记:四次碰壁后的深度反思
· 继承的思维:从思维模式到架构设计的深度解析
· 基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程
· 由 MCP 官方推出的 C# SDK,使 .NET 应用程序、服务和库能够快速实现与 MCP 客户端
· 电商平台中订单未支付过期如何实现自动关单?
· 上周热点回顾(3.31-4.6)
· 用 .NET NativeAOT 构建完全 distroless 的静态链接应用