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了。

posted @ 2020-07-03 15:25  qxred  阅读(1262)  评论(0编辑  收藏  举报