Ubuntu和Win 10下用CMAKE + SWIG 為C#寫動態庫
本文采用這個專案的教學程式碼:
https://github.com/Mizux/dotnet-native
作者自稱是cmake的開發人員,不知道真假,不過這個專案程式碼組織看起來挺專業的,就它了。這裡主要研究如何用cmake + swig + dotnet + gcc/vc 將程式碼部署到ubuntu和winows下面,程式碼本身功能不是重點。
1 Make It Work
1.1 Ubuntu
可以參考https://github.com/Mizux/dotnet-native/blob/master/ci/docker/ubuntu/Dockerfile,這裡略有修改,下載了各個軟體的最新版本
sudo apt-get update sudo apt install g++ g++ --version #檢查一下版本,顯示7.5.0, 這裡我們想用最新版本g++-9 sudo apt install software-properties-common #參考https://linuxize.com/post/how-to-install-gcc-compiler-on-ubuntu-18-04/#installing-gcc-on-ubuntu sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt install g++-9 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9 g++ --version #檢查一下版本,本例子中已經更新為9.3.0 wget "https://cmake.org/files/v3.17/cmake-3.17.2-Linux-x86_64.sh" #sudo apt install cmake只能下載到3.10版本,本例子需要在3.14或以上版本 chmod a+x cmake-3.17.2-Linux-x86_64.sh sudo ./cmake-3.17.2-Linux-x86_64.sh --prefix=/usr/local/ --skip-license rm cmake-3.17.2-Linux-x86_64.sh cmake --version #檢查一下版本,3.17.2。使用terminal的需要重開一個視窗,否則會顯示找不到cmake wget ftp://ftp.pcre.org/pub/pcre/pcre-8.00.tar.gz #下載pcre,安裝swig 4.0版本需要, sudo apt-get install -y swig只能拿到3.0.12版本 tar -xzf pcre-8.00.tar.gz cd pcre-8.00/ ./configure --prefix=/usr/local/pcre #安裝到/usr/local/pcre/bin/pcre-config make sudo make install #pcre安裝完成 PATH=/usr/local/pcre/bin:$PATH #讓pcre-config可以直接被call cd .. http://prdownloads.sourceforge.net/swig/swig-4.0.2.tar.gz tar -xzf swig-4.0.2.tar.gz cd swig-4.0.2 #參考http://www.swig.org/Doc4.0/SWIGDocumentation.html#Preface_unix_installation ./configure make sudo make install export LD_LIBRARY_PATH=/usr/local/pcre/lib:$LD_LIBRARY_PATH #把libpcre.so.0加入系統庫目錄中 swig -version #檢查一下版本,本例子中使用的是4.0.2版本 cd .. sudo apt-get update -qq #安裝.NET sudo apt-get install -yq wget apt-transport-https wget -q https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb sudo apt-get update -qq sudo DEBIAN_FRONTEND=noninteractive apt-get install -yq dotnet-sdk-3.1 sudo apt-get clean dotnet --version #檢查版本,本例子中是3.1.301 git clone https://github.com/Mizux/dotnet-native #下載專案程式碼 cd dotnet-native cmake -S. -Bbuild cmake --build build --target all -v sudo cmake --build build --target install -v cmake --build build --target test -v #should read 100% tests passed,0 tests failed out of 3 cd .. mkdir sample cd sample cp ../dotnet-native/build/dotnet/packages/*.nupkg ./ cp ../dotnet-native/ci/samples/* ./ #sample目錄下有Mizux.Foo.1.0.0.nupkg Mizux.Foo.runtime.linux-x64.1.0.0.nupkg Mizux.Sample.csproj Sample.cs dotnet build dotnet run #[1] Enter FooApp #[2] Enter Foo::hello(int) #[3] Enter fooHello(int) #[3] Exit fooHello(int) #[2] Exit Foo::hello(int) #[1] Exit FooApp #成功!!
1.2 Windows
安裝CMake
去https://cmake.org/download/ 下載並安裝cmake-3.16.8-win64-x64.msi
安裝完應該能看到
安裝visual studio 2019
現在都免費了.去https://visualstudio.microsoft.com/vs/下載prefessional版本
本專案需要c++和c#,所以要安裝兩個workload
下載github教學程式碼
可以下載zip,並解壓
構建專案
開啟CMake,點選右上角Browse Source,選擇上面解壓後dotnet-native-master目錄
browse build裡面的路徑=Browse Source裡面的路徑 + /build,完成後點選左下configure
這時會讓你選擇generator,這裡選擇Visual Studio 16 2019,然後點選Finish
這個過程需要十幾秒,然後會在下框中顯示Configuration done
然後點選Generate按鈕,下框中顯示Generating done。
點選Open Project,這時會彈出Visual Studio 2019,點選Build Solution,開始編譯專案
編譯完成後,File->Close Solution關閉專案。 此時build/dotnet/packages下有了nupkg檔案,這就是build產生的包檔案。接下來用例子說們怎麼用這個包檔案
測試樣例
重新開啟一個專案:Ctrl+Shift+O 開啟 dotnet-native-master\ci\samples\Mizux.Sample.csproj
設定nupkg,首先解除安裝Mizux.Sample.csproj中的Mizux.Foo包,因為這個包預設是從nuget.org上下載的,具體可以開啟Mizux.Sample.csproj,看裡面的設定。
Tools->NuGet Package Manager-> Manage NuGet Packages for Solution,在installed面板左邊選擇Mizux.Foo,在右邊選擇Mizux.Sample,點選Uninstall
接著需要載入本地編譯好的Mizux.Foo包。Tools->Options->NuGet Package Manager->Packages Sources,在右邊點綠色的+按鈕新增一個包,name填寫Mizux.Foo,Source裡面選擇
dotnet-native-master\dotnet-native-master\build\dotnet\packages,即含有nupkg和snupkg的資料夾。然後點選OK,這樣就設定好路徑了。
接下來新增nupkg檔案,右鍵Mizux.Sample,選擇Manager NuGet Packages
在Browse面板衝,在右上角的下拉框中選擇Mizux.Foo (預設是nuget.org),然後在左邊選擇Mizux.Foo,最後點選右邊的install。
現在已經把Mizux.Foo.1.0.0.nupkg安裝到專案中了。最後點選綠色的執行按鈕,執行結果如下:
2 程式碼分析
先看看程式碼組織:
[email protected]:~/code/dotnet-native$ tree
.
├── AUTHORS
├── ci
│?? ├── doc
│?? │?? ├── deps.dot
│?? │?? ├── deps.svg
│?? │?? └── generate_image.sh
│?? ├── docker
│?? │?? ├── alpine
│?? │?? │?? └── Dockerfile
│?? │?? ├── archlinux
│?? │?? │?? └── Dockerfile
│?? │?? ├── centos
│?? │?? │?? └── Dockerfile
│?? │?? ├── debian
│?? │?? │?? └── Dockerfile
│?? │?? ├── fedora
│?? │?? │?? └── Dockerfile
│?? │?? ├── opensuse
│?? │?? │?? └── Dockerfile
│?? │?? └── ubuntu
│?? │?? └── Dockerfile
│?? ├── Makefile
│?? ├── README.md
│?? └── samples
│?? ├── Mizux.Sample.csproj
│?? └── Sample.cs
├── cmake
│?? ├── CMakeLists.txt.swig
│?? ├── cpp.cmake
│?? ├── dotnet.cmake
│?? ├── FooConfig.cmake.in
│?? └── swig.cmake
├── CMakeLists.txt
├── doc
│?? ├── full_pipeline.dot
│?? ├── full_pipeline.png
│?? ├── full_pipeline.svg
│?? ├── generate_image.sh
│?? ├── legend.dot
│?? ├── legend.png
│?? ├── legend.svg
│?? ├── local_pipeline.dot
│?? ├── local_pipeline.png
│?? └── local_pipeline.svg
├── dotnet
│?? ├── base.i
│?? ├── Directory.Build.props
│?? ├── FooApp.cs
│?? ├── FooTests.cs
│?? ├── logo.png
│?? ├── logo.svg
│?? ├── Mizux.FooApp.csproj.in
│?? ├── Mizux.Foo.csproj.in
│?? ├── Mizux.Foo.runtime.csproj.in
│?? └── Mizux.FooTests.csproj.in
├── Foo
│?? ├── CMakeLists.txt
│?? ├── dotnet
│?? │?? ├── CMakeLists.txt
│?? │?? └── foo.i
│?? ├── include
│?? │?? └── foo
│?? │?? └── Foo.hpp
│?? ├── src
│?? │?? └── Foo.cpp
│?? └── test
│?? ├── CMakeLists.txt
│?? └── src
│?? ├── Foo_UT.cpp
│?? └── main.cpp
├── LICENSE
└── README.md
21 directories,51 files
先看一下程式碼,知道這個專案做了什麼
2.1 Foo/src/Foo.cpp
#include "foo/Foo.hpp"
#include <iostream>
namespace foo {
void fooHello(int level) {
std::cout << "[" << level << "] Enter fooHello(int)" << std::endl;
std::cout << "[" << level << "] Exit fooHello(int)" << std::endl;
}
void fooHello(int64_t level) {
std::cout << "[" << level << "] Enter fooHello(int64_t)" << std::endl;
std::cout << "[" << level << "] Exit fooHello(int64_t)" << std::endl;
}
void Foo::hello(int level) {
std::cout << "[" << level << "] Enter Foo::hello(int)" << std::endl;
foo::fooHello(level + 1);
std::cout << "[" << level << "] Exit Foo::hello(int)" << std::endl;
}
void Foo::hello(int64_t level) {
std::cout << "[" << level << "] Enter Foo::hello(int64_t)" << std::endl;
foo::fooHello(level + 1);
std::cout << "[" << level << "] Exit Foo::hello(int64_t)" << std::endl;
}
std::string Foo::operator()() const {
return std::string{"\"Foo\":{\"int\":"} + std::to_string(_intValue) +
",\"int64\":" + std::to_string(_int64Value) + "}";
}
int Foo::getInt() const {
return _intValue;
}
void Foo::setInt(int input) {
_intValue = input;
}
int64_t Foo::getInt64() const {
return _int64Value;
}
void Foo::setInt64(int64_t input) {
_int64Value = input;
}
} // namespace foo
就是一些set和print,根據輸入是int64還是int呼叫不同的函式
2.2 Foo/include/foo/Foo.hpp
#pragma once
#include <cstdint>
#include <string>
namespace foo {
void fooHello(int level);
void fooHello(int64_t level);
class Foo {
public:
static void hello(int level);
static void hello(int64_t level);
int getInt() const;
void setInt(int input);
int64_t getInt64() const;
void setInt64(int64_t input);
std::string operator()() const;
private:
int _intValue = 0;
int64_t _int64Value = 0;
};
} // namespace foo
2.3 Foo/dotnet/foo.i
其中/* */是本文追加註釋%module csFoo
/* c#沒有普通函式,所有函式必須是類的成員函式,如何呼叫c++的普通函式呢,可以用csFoo.函式名來呼叫,比如csFoo.fooHello(12) */
%include "std_string.i"
/* 提供std::string介面 */
%include "base.i"
// Add necessary symbols to generated header
%{
#include <foo/Foo.hpp>
%}
/* 在翻譯產生的 /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/fooCSHARP_wrap.cxx 檔案中加入#include <foo/Foo.hpp> */
/* swig只負責翻譯,對於翻譯好的c++程式碼中的函式由來,需要人工指定其標頭檔案 */
%ignore ""; /* 忽略標頭檔案中所有的宣告 參考 http://www.swig.org/Doc3.0/SWIGDocumentation.html#SWIG_chosen_unignore */
%define %unignore %rename("%s") %enddef
/* %rename(A) B;
%include "header.h"
將 header.h中的B函式重新命名為A供c#呼叫。這是為了防止c#和c++函式重名。
%rename("%s") "" 相當於 對於任意的A %rename(A) A
%define %unignore %rename("%s") %enddef 是一個巨集,
%rename("A") 展開為 %unignore %rename("A")
比如 %rename("FooHello") fooHello(int); 展開為
%unignore %rename("FooHello") fooHello(int); 即不忽略fooHello(int)函式,並將其重新命名為FooHello
*/
%unignore foo;
namespace foo {
%rename("FooHello") fooHello(int);
%rename("FooHello") fooHello(int64_t);
%unignore Foo;
%rename("Hello") Foo::hello(int);
%rename("Hello") Foo::hello(int64_t);
%rename("GetInt") Foo::getInt() const;
%rename("SetInt") Foo::setInt(int);
%rename("GetInt64") Foo::getInt64() const;
%rename("SetInt64") Foo::setInt64(int64_t);
%rename ("ToString") Foo::operator();
/* c#中 A.ToString()呼叫的是A()
} // namespace foo
// Process symbols in header
%include "foo/Foo.hpp"
%unignore ""; // unignore all
2.4 dotnet/base.i
%include "stdint.i"
%include "typemaps.i"
#if defined(SWIGCSHARP)
#if defined(SWIGWORDSIZE64)
// By default SWIG map C++ long int (i.e. int64_t) to C# int
// But we want to map it to C# long so we reuse the typemap for C++ long long.
// ref: https://github.com/swig/swig/blob/master/Lib/csharp/typemaps.i
// ref: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types
%define PRIMITIVE_TYPEMAP(NEW_TYPE,TYPE)
%clear NEW_TYPE;
%clear NEW_TYPE *;
%clear NEW_TYPE &;
%clear const NEW_TYPE &;
%apply TYPE { NEW_TYPE };
%apply TYPE * { NEW_TYPE * };
%apply TYPE & { NEW_TYPE & };
%apply const TYPE & { const NEW_TYPE & };
%enddef // PRIMITIVE_TYPEMAP
PRIMITIVE_TYPEMAP(long int,long long);
PRIMITIVE_TYPEMAP(unsigned long int,unsigned long long);
#undef PRIMITIVE_TYPEMAP
#endif // defined(SWIGWORDSIZE64)
#endif // defined(SWIGCSHARP)
映射了一些型別,這一段程式碼應該對所有c#專案有效,無需修改,就不深究了。
2.5 dotnet/FooApp.cs
using System;
using Mizux.Foo;
namespace FooApp {
class Program {
static void Main(string[] args) {
int level = 1;
Console.WriteLine($"[{level}] Enter FooApp");
Foo.Hello(level+1);
Console.WriteLine($"[{level}] Exit FooApp");
}
}
}
根據前面的分析,該程式碼應該輸出
[1] Enter FooApp
[2] Enter Foo::hello(int)
[3] Enter fooHello(int)
[3] Exit fooHello(int)
[2] Exit Foo::hello(int)
[1] Exit FooApp
3 專案組織程式碼*.csproj程式碼分析
4 CMake構建、安裝程式碼分析
4.1 CMakeLists.txt
現在開始理解cmake的流程了。從根目錄的CMakeLists.txt看起。###後面跟的是本文的註釋
# This file is just an orchestration
cmake_minimum_required(VERSION 3.14) ### cmake版本至少3.14
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") ###相當於 g++ -Icmake
###${CMAKE_CURRENT_SOURCE_DIR}指的是含有當前CMakeLists.txt的資料夾,即根目錄
option(CMAKE_EXPORT_COMPILE_COMMANDS "Export compile command" TRUE)
###設定了一個cmake build的引數,預設值為TRUE,具體作用應該在後面作為條件build的根據
project(Foo VERSION 1.0 LANGUAGES CXX)
###設定${PROJECT_NAME} = Foo,${PROJECT_VERSION} = 1.0 語言是C++
message(STATUS "project: ${PROJECT_NAME}") ###列印 project: Foo
message(STATUS "version: ${PROJECT_VERSION}") ### version: 1.0
# Force default build type to Release
if(NOT CMAKE_BUILD_TYPE) ###cmake build時候可以用-DCMAKE_BUILD_TYPE=Release/Debug/...
### 設定build type是release還是debug還是其他,
set(CMAKE_BUILD_TYPE "Release" CACHE STRING
###預設是Release,
###CACHE=global,表明這個type對其他cmake,CMakeLists檔案有效
"Choose the type of build,options are: Debug,Release (default),RelWithDebInfo and MinSizeRel."
FORCE) ###如果CMAKE_BUILD_TYPE被其他呼叫該專案的專案設定過,這裡則強制重新設定。
endif(NOT CMAKE_BUILD_TYPE)
# Layout build dir like install dir
include(GNUInstallDirs) ### 定義了一些安裝所需要的路徑變數,比如CMAKE_INSTALL_BINDIR用於安裝執行程式的相對路徑。
###(安裝實際上就是複製build好的檔案到指定的目錄,以便系統呼叫)
if(UNIX)
option(BUILD_SHARED_LIBS "Build shared libraries(.so or .dyld)." ON) ###build 動態連線庫,預設是TRUE
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
### 啟用RPATH。當CMake build好可執行檔案a.exe以及它所依賴的動態庫b.so後,安裝時會將他們複製到指定的目錄中去,
### 根據系統不同,安裝的絕對路徑也不同,比如python3.6/...和python3.7/... 這樣安裝好的a.exe不可能通過絕對路徑尋找b.so,因此RPATH
### 就用來提供尋找b.so的相對路徑
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
### CMAKE_LIBRARY_OUTPUT_DIRECTORY = build/lib 存放build好的*.so或*.dll等動態庫檔案
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
### CMAKE_ARCHIVE_OUTPUT_DIRECTORY = build/lib 存放build好的*.a或*.lib等靜態庫檔案
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
### CMAKE_RUNTIME_OUTPUT_DIRECTORY = build/bin 存放build好的可執行檔案
# for multi-config build system (e.g. xcode)
foreach(OUTPUTCONFIG IN LISTS CMAKE_CONFIGURATION_TYPES)
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_BINDIR})
endforeach()
else()
# Currently Only support static build for windows
set(BUILD_SHARED_LIBS OFF) ###如果是windows,不產生動態庫
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
# for multi-config builds (e.g. msvc)
foreach(OUTPUTCONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) ###such as Debug,Release,RelWithDebInfo etc.
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) ###Debug => DEBUG
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_BINDIR})
###CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG = build/DEBUG/bin
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_BINDIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_BINDIR})
endforeach()
endif()
include(CTest) ###先跳過單元測試
if(BUILD_TESTING)
include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG master
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(Catch2)
endif()
include(cpp) ### include "cmake/cpp.cmake"
if(WIN32)
include(swig) ###include "cmake/swig.cmake"
endif()
include(dotnet) ###include "cmake/dotnet.cmake"
看來根目錄的CMakeLists.txt主要設定了一些公共變數,至於具體執行什麼還要看cmake下面的*.cmake檔案
4.2 cmake/cpp.cmake
# Check primitive types
include(CMakePushCheckState) ###這幾行不知道要幹嘛
cmake_push_check_state(RESET)
set(CMAKE_EXTRA_INCLUDE_FILES "cstdint")
include(CheckTypeSize)
check_type_size("long" SIZEOF_LONG LANGUAGE CXX)
message(STATUS "Found long size: ${SIZEOF_LONG}")
check_type_size("long long" SIZEOF_LONG_LONG LANGUAGE CXX)
message(STATUS "Found long long size: ${SIZEOF_LONG_LONG}")
check_type_size("int64_t" SIZEOF_INT64_T LANGUAGE CXX)
message(STATUS "Found int64_t size: ${SIZEOF_INT64_T}")
check_type_size("unsigned long" SIZEOF_ULONG LANGUAGE CXX)
message(STATUS "Found unsigned long size: ${SIZEOF_ULONG}")
check_type_size("unsigned long long" SIZEOF_ULONG_LONG LANGUAGE CXX)
message(STATUS "Found unsigned long long size: ${SIZEOF_ULONG_LONG}")
check_type_size("uint64_t" SIZEOF_UINT64_T LANGUAGE CXX)
message(STATUS "Found uint64_t size: ${SIZEOF_UINT64_T}")
check_type_size("int *" SIZEOF_INT_P LANGUAGE CXX)
message(STATUS "Found int * size: ${SIZEOF_INT_P}")
cmake_pop_check_state()
add_subdirectory(Foo) ### include ../Foo/CMakeList.txt
# Install
include(GNUInstallDirs) ###和之前一樣,取出一些預定義的安裝路徑變數
install(EXPORT ${PROJECT_NAME}Targets
###安裝FooTargets中的Devel部分(命令最後COMPONENT=Devel)
###FooTargets在別的地方定義,build完之後,會產生一個FooTargets.cmake檔案
###該檔案用來讓別的專案呼叫FooTargets
###用install(EXPORT 將FooTargets中的Devel部分安裝到指定目錄DESTINATION
NAMESPACE ${PROJECT_NAME}::
###在別的專案引用FooTargets的時候加上Foo::字首
###即target_link_libraries(xxx Foo::FooTargets)
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
###${CMAKE_INSTALL_PREFIX}/lib/cmake/Foo
###linux 上 ${CMAKE_INSTALL_PREFIX}預設為/usr/local
###windows 上預設為 C:\Program Files
###注意預設的路徑需要root賬號才有寫許可權,
###可以通過cmake -DCMAKE_INSTALL_PREFIX=$HOME/Software/recipe-01 修改
COMPONENT Devel)
include(CMakePackageConfigHelpers) ###產生配置檔案,這個檔案用來檢查一些檔案是否存在
configure_package_config_file(cmake/${PROJECT_NAME}Config.cmake.in
### 配置cmake/FooConfig.cmake.in (將檔案中的變數替換)並複製到
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" ###build/FooConfig.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
###再安裝到lib/cmake/Foo下面
NO_SET_AND_CHECK_MACRO ###FooConfig.cmake中不含有set_and_check()
NO_CHECK_REQUIRED_COMPONENTS_MACRO) ###不含有 check_required_components()
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
COMPATIBILITY SameMajorVersion) ###設定版本為主版本號
###CMake的版本格式為 Release: <major>.<minor>.<patch>[-rc<n>]
### Develop: <major>.<minor>.<date>[-<id>]
install(
FILES ###複製檔案
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
COMPONENT Devel)
看一下cmake/FooConfig.cmake.in檔案,也就是一個include
include("${CMAKE_CURRENT_LIST_DIR}/FooTargets.cmake")
### CMAKE_CURRENT_LIST_DIR為當前檔案所在的目錄
4.3 Foo/CMakeLists.txt
add_library(Foo "") ###新增一個庫
target_sources(Foo ###新增程式碼
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/foo/Foo.hpp>
$<INSTALL_INTERFACE:include/foo/Foo.hpp>
###此處帶有尖括號的部分稱為生成器表示式,這兩行的意思是:
###如果當前是build階段,則包含${CMAKE_CURRENT_SOURCE_DIR}/include/foo/Foo.hpp
###如果當前是install階段,則包含include/foo/Foo.hpp
###這是因為在build階段,我們用的是原始的程式碼檔案,CMAKE_CURRENT_SOURCE_DIR = /home/xian/code/dotnet-native/Foo
###在install階段,原始檔會被安裝到某一個地方,所以需要用安裝地的路徑
PRIVATE
src/Foo.cpp
)
### PUBLIC: 目標中public的部分對其他專案可見,因為其他專案呼叫該庫的時候是需要知道其標頭檔案的,所以這裡為public
### 如果有很多標頭檔案,其中只有一個頭檔案是對外可見的,則將該檔案放入到INSTALL_INTERFACE中
### 如果標頭檔案如果都放在一個目錄中(可以有子目錄),那麼只需要設定該目錄(不需要例舉子目錄)即可包含所有標頭檔案。
### PRIVATE: 對其他專案不可見。常常是cpp檔案。這部分需要顯式包含所有檔案
target_include_directories(Foo ###包含標頭檔案目錄,相當於g++ -IFoo
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_features(Foo PUBLIC cxx_std_11)
###std=c++11
###PUBLIC ???
set_target_properties(Foo PROPERTIES
VERSION ${PROJECT_VERSION}
PUBLIC_HEADER $<TARGET_PROPERTY:Foo,INTERFACE_SOURCES>
### PUBLIC_HEADER = Foo.INTERFACE_SOURCES
### 可以用 file(GENERATE OUTPUT aaa.txt CONTENT $<TARGET_PROPERTY:Foo,INTERFACE_SOURCES>)
### 打印出值
### 這個值和BUILD_INTERFACE相同:/home/xian/code/dotnet-native/Foo/include/foo/Foo.hpp
###$<TARGET_PROPERTY:tgt,prop> = tgt.prop
)
add_library(${PROJECT_NAME}::Foo ALIAS Foo) ### 將Foo::Foo重新命名為Foo,Foo::為之前設定的NAMESPACE
if(BUILD_TESTING)
add_subdirectory(test)
endif()
# Install
install(TARGETS Foo ###此處定義install 目標
EXPORT ${PROJECT_NAME}Targets ###將Foo目標輸出為FooTargets目標 (FooTargets.cmake)
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/foo ###標頭檔案安裝到 include/foo
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ###靜態庫安裝到lib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ###動態庫安裝到lib
#RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
4.4 cmake/dotnet.cmake
# Will need swig
set(CMAKE_SWIG_FLAGS) ###設定一些swig的變數,比如set(CMAKE_SWIG_FLAGS -DPL_DOUBLE_INTERFACE -DSWIG_PYTHON)
###這裡什麼都沒有設定,只是引入,後面會設定
find_package(SWIG REQUIRED) ###尋找SWIG
include(UseSWIG) ###include UseSWIG.cmake 參考https://github.com/Kitware/CMake/blob/master/Modules/UseSWIG.cmake
if(UNIX AND NOT APPLE)
list(APPEND CMAKE_SWIG_FLAGS "-DSWIGWORDSIZE64")
endif()
# Find dotnet
find_program(DOTNET_EXECUTABLE dotnet) ###尋找dotnet的執行檔案,需要安裝dotnet
if(NOT DOTNET_EXECUTABLE)
message(FATAL_ERROR "Check for dotnet Program: not found")
else()
message(STATUS "Found dotnet Program: ${DOTNET_EXECUTABLE}")
endif()
### ${DOTNET_EXECUTABLE} = dotnet
# Create the native library
###native是最初始的包裝c++的庫,可供c#呼叫
add_library(mizux-foo-native SHARED "") ###新增動態庫mizux-foo-native
set_target_properties(mizux-foo-native PROPERTIES
PREFIX "" ###如果PREFIX = xxx則生成xxxmizux-foo-native.so
POSITION_INDEPENDENT_CODE ON) ###程式碼位置獨立
###多個程式利用相同的動態庫時,動態庫只加載一遍,從而減小記憶體
###這個共享的動態庫只有一個程式碼地址,與具體呼叫程式無關,所以是獨立的
###具體參考 How to write shared libraries https://akkadia.org/drepper/dsohowto.pdf
# note: macOS is APPLE and also UNIX !
if(APPLE)
set_target_properties(mizux-foo-native PROPERTIES INSTALL_RPATH "@loader_path")
# Xcode fails to build if library doesn‘t contains at least one source file.
if(XCODE)
file(GENERATE
OUTPUT ${PROJECT_BINARY_DIR}/mizux-foo-native/version.cpp
CONTENT "namespace {char* version = \"${PROJECT_VERSION}\";}")
target_sources(mizux-foo-native PRIVATE ${PROJECT_BINARY_DIR}/mizux-foo-native/version.cpp)
endif()
elseif(UNIX)
set_target_properties(mizux-foo-native PROPERTIES INSTALL_RPATH "$ORIGIN")
###設定INSTALL_RPATH = $ORIGIN,$ORIGIN是指mizux-foo-native安裝的目錄,這句話說明安裝後,
###mizux-foo-native.so 呼叫同一目錄下的 libFoo.so
###[email protected]:~/code/dotnet-native/build/lib$ ls
###libFoo.so libFoo.so.1.0 mizux-foo-native.so
endif()
set(DOTNET Mizux.Foo) ###${DOTNET} = Mizux.Foo
foreach(SUBPROJECT IN ITEMS Foo) ###foreach(SUBPROJECT IN ITEMS A B C ...)
add_subdirectory(${SUBPROJECT}/dotnet) ###include Foo/dotnet/CMakeLists.txt
target_link_libraries(mizux-foo-native PRIVATE dotnet_${SUBPROJECT})
###mizux-foo-native依賴於dotnet_Foo
###這裡的PRIVATE是指呼叫mizux-foo-native的專案不需要知道dotnet_Foo的實現和介面。
###mizux-foo-native對dotnet_Foo的所有功能進行重新封裝。
###具體參考 https://cmake.org/pipermail/cmake/2016-May/063400.html
endforeach()
file(COPY dotnet/logo.png DESTINATION dotnet) ###複製檔案
file(COPY dotnet/Directory.Build.props DESTINATION dotnet)
###Directory.Build.props是.net的檔案,為一組專案統一配置版本,作者等等
###接下來是一組lib或者可執行程式。這裡選取Mizux.Foo.runtime.<RID>,Mizux.Foo和Mizux.FooApp分析
###############################
## Mizux.Foo.runtime.<RID> ##
###############################
### runtime 是一個可以獨立執行的程式,包含了所有依賴,是平臺dependent的
set(DOTNET_PACKAGES_DIR ${PROJECT_BINARY_DIR}/dotnet/packages)
###DOTNET_PACKAGES_DIR = build/dotnet/packages , 所有build好的nupkg,snupkg包都放在這裡
if(APPLE)
set(RUNTIME_IDENTIFIER osx-x64)
elseif(UNIX)
set(RUNTIME_IDENTIFIER linux-x64)
elseif(WIN32)
set(RUNTIME_IDENTIFIER win-x64)
else()
message(FATAL_ERROR "Unsupported system !")
endif()
set(DOTNET_NATIVE ${DOTNET}.runtime.${RUNTIME_IDENTIFIER})
###DOTNET_NATIVE = Mizux.Foo.runtime.linux-x64 (ubuntu) 或者 Mizux.Foo.runtime.win-x64 (windows)
# pom*.xml.in contains:
# CMake variable(s) (@[email protected]) that configure_file() can manage and
# generator expression ($<TARGET_FILE:...>) that file(GENERATE) can manage.
configure_file(
${PROJECT_SOURCE_DIR}/dotnet/${DOTNET}.runtime.csproj.in
${PROJECT_BINARY_DIR}/dotnet/${DOTNET}.runtime.csproj.in
@ONLY) ###替換其中@[email protected]的變數
### dotnet/Mizux.Foo.runtime.csproj.in 複製到 build/dotnet/Mizux.Foo.runtime/csproj.in 並替換其中@[email protected]的變數
file(GENERATE
OUTPUT ${PROJECT_BINARY_DIR}/dotnet/$<CONFIG>/${DOTNET}.runtime.csproj.in
INPUT ${PROJECT_BINARY_DIR}/dotnet/${DOTNET}.runtime.csproj.in)
###根據build/dotnet/Mizux.Foo.runtime.csproj.in 產生檔案 build/dotnet/Release/Mizux.Foo.runtime.csproj.in
###其中$<xxx>的部分會被替換
add_custom_command(
OUTPUT dotnet/${DOTNET_NATIVE}/${DOTNET_NATIVE}.csproj
###build/dotnet/Mizux.Foo.runtime.linux-x64/Mizux.Foo.runtime.linux-x64.csproj
COMMAND ${CMAKE_COMMAND} -E make_directory ${DOTNET_NATIVE}
COMMAND ${CMAKE_COMMAND} -E copy ./$<CONFIG>/${DOTNET}.runtime.csproj.in ${DOTNET_NATIVE}/${DOTNET_NATIVE}.csproj
###build/dotnet/Release/Mizux.Foo.runtime.csproj.in 複製到
### build/dotnet/Mizux.Foo.runtime.linux-x64/Mizux.Foo.runtime.linux-x64.csproj
WORKING_DIRECTORY dotnet) ###在build/dotnet目錄下執行
add_custom_target(dotnet_native_package
DEPENDS
mizux-foo-native
dotnet/${DOTNET_NATIVE}/${DOTNET_NATIVE}.csproj
### dotnet/Mizux.Foo.runtime.linux-x64/Mizux.Foo.runtime.linux-x64.csproj
COMMAND ${CMAKE_COMMAND} -E make_directory packages ### mkdir build/dotnet/packages
COMMAND ${DOTNET_EXECUTABLE} build -c Release ${DOTNET_NATIVE}/${DOTNET_NATIVE}.csproj
### Builds a project and all of its dependencies.
COMMAND ${DOTNET_EXECUTABLE} pack -c Release ${DOTNET_NATIVE}/${DOTNET_NATIVE}.csproj
### Packs the code into a NuGet package.
WORKING_DIRECTORY dotnet)
#################
## Mizux.Foo ##
#################
configure_file(
${PROJECT_SOURCE_DIR}/dotnet/${DOTNET}.csproj.in ###dotnet/Mizux.Foo.csproj.in
${PROJECT_BINARY_DIR}/dotnet/${DOTNET}.csproj.in ###build/dotnet/Mizux.Foo.csproj.in
@ONLY)
###dotnet/Mizux.Foo.csproj.in 複製到 build/dotnet/Mizux.Foo.csproj.in,並替換其中@[email protected]的變數
add_custom_command(
OUTPUT dotnet/${DOTNET}/${DOTNET}.csproj
COMMAND ${CMAKE_COMMAND} -E make_directory ${DOTNET}
COMMAND ${CMAKE_COMMAND} -E copy ./${DOTNET}.csproj.in ${DOTNET}/${DOTNET}.csproj
WORKING_DIRECTORY dotnet)
###在build/dotnet下,建立一個目錄Mizux.Foo,並將Mizux.Foo.csproj.in複製到Mizux.Foo/Mizux.Foo.csproj
###OUTPUT 用於檢查複製完成的檔案
add_custom_target(dotnet_package ALL ###ALL : 該目標每次都要build
DEPENDS
dotnet_native_package
dotnet/${DOTNET}/${DOTNET}.csproj
COMMAND ${DOTNET_EXECUTABLE} build -c Release ${DOTNET}/${DOTNET}.csproj
COMMAND ${DOTNET_EXECUTABLE} pack -c Release ${DOTNET}/${DOTNET}.csproj
WORKING_DIRECTORY dotnet)
######################
## Mizux.FooTests ##
######################
add_custom_command(
OUTPUT dotnet/${DOTNET}Tests/FooTests.cs
COMMAND ${CMAKE_COMMAND} -E make_directory ${DOTNET}Tests
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/dotnet/FooTests.cs ${DOTNET}Tests/FooTests.cs
WORKING_DIRECTORY dotnet)
configure_file(
${PROJECT_SOURCE_DIR}/dotnet/${DOTNET}Tests.csproj.in
${PROJECT_BINARY_DIR}/dotnet/${DOTNET}Tests.csproj.in
@ONLY)
add_custom_command(
OUTPUT dotnet/${DOTNET}Tests/${DOTNET}Tests.csproj
COMMAND ${CMAKE_COMMAND} -E make_directory ${DOTNET}Tests
COMMAND ${CMAKE_COMMAND} -E copy ./${DOTNET}Tests.csproj.in ${DOTNET}Tests/${DOTNET}Tests.csproj
WORKING_DIRECTORY dotnet)
add_custom_target(FooTests ALL
DEPENDS
dotnet_package
dotnet/${DOTNET}Tests/FooTests.cs
dotnet/${DOTNET}Tests/${DOTNET}Tests.csproj
COMMAND ${DOTNET_EXECUTABLE} build -c Release ${DOTNET}Tests/${DOTNET}Tests.csproj
WORKING_DIRECTORY dotnet)
if(BUILD_TESTING)
add_test(NAME FooTestsUT
COMMAND ${DOTNET_EXECUTABLE} test -c Release ${DOTNET}Tests/${DOTNET}Tests.csproj
WORKING_DIRECTORY dotnet)
endif()
####################
## Mizux.FooApp ##
####################
add_custom_command(
OUTPUT dotnet/${DOTNET}App/FooApp.cs
COMMAND ${CMAKE_COMMAND} -E make_directory ${DOTNET}App
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/dotnet/FooApp.cs ${DOTNET}App/FooApp.cs
WORKING_DIRECTORY dotnet)
###複製dotnet/FooApp.cs到 build/Mizux.FooApp/FooApp.cs
###PROJECT_SOURCE_DIR = /home/xian/code/dotnet-native
configure_file(
${PROJECT_SOURCE_DIR}/dotnet/${DOTNET}App.csproj.in
${PROJECT_BINARY_DIR}/dotnet/${DOTNET}App.csproj.in
@ONLY)
###替換dotnet/Mizux.FooApp.csproj.in中的@[email protected]部分,並儲存到build/dotnet/Mizux.FooApp.csproj.in
add_custom_command(
OUTPUT dotnet/${DOTNET}App/${DOTNET}App.csproj
COMMAND ${CMAKE_COMMAND} -E make_directory ${DOTNET}App
COMMAND ${CMAKE_COMMAND} -E copy ./${DOTNET}App.csproj.in ${DOTNET}App/${DOTNET}App.csproj
WORKING_DIRECTORY dotnet)
###複製build/dotnet/FooApp.csproj.in到 build/dotnet/Mizux.FooApp/Mizux.FooApp.csproj
add_custom_target(FooApp ALL
DEPENDS
dotnet_package
dotnet/${DOTNET}App/FooApp.cs
dotnet/${DOTNET}App/${DOTNET}App.csproj
COMMAND ${DOTNET_EXECUTABLE} build -c Release ${DOTNET}App/${DOTNET}App.csproj
###這裡沒有打包
WORKING_DIRECTORY dotnet)
if(BUILD_TESTING)
add_test(NAME FooAppUT
COMMAND ${DOTNET_EXECUTABLE} run -c Release --project ${DOTNET}App/${DOTNET}App.csproj
WORKING_DIRECTORY dotnet)
endif()
4.5 Foo/dotnet/CMakeLists.txt
set_property(SOURCE foo.i PROPERTY CPLUSPLUS ON)
###Call SWIG in c++ mode 參考 https://cmake.org/cmake/help/v3.14/module/UseSWIG.html
set_property(SOURCE foo.i PROPERTY COMPILE_OPTIONS
### 編譯foo.i時,用COMPILE_OPTIONS中的編譯選項
-namespace ${DOTNET}
-dllimport mizux-foo-native) ###foo.i檔案中匯入mizux-foo-native
swig_add_library(dotnet_Foo
TYPE OBJECT
LANGUAGE csharp
OUTPUT_DIR ${PROJECT_BINARY_DIR}/dotnet/${PROJECT_NAME}/Foo
SOURCES foo.i)
###swig_add_library的作用就是將.i檔案翻譯成.cxx檔案。
###翻譯後的檔案為 /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/fooCSHARP_wrap.cxx
###上面 -namespace ${DOTNET}的作用就是翻譯foo.i中的函式名時加入namespace指定的字首。
###比如如果-namespace ABCD.E.F.G 則FooHello(int);被翻譯為CSharp_ABCDfEfFfG_FooHello__SWIG_0___(int jarg1)
###並且相對應的C#檔案 /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/csFoo.cs
### /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/csFoo.cs 也會有namspace:
### namespace ABCD.E.F.G {
### }
###swig_add_library實際上是執行了一行命令,輸入.i檔案,輸出4個檔案,這個命令可以在
###build/Foo/dotnet/CMakeFiles/dotnet_Foo_swig_compilation.dir/build.make
###中找到,
###/home/xian/.local/lib/python3.6/site-packages/cmake/data/bin/cmake -E env SWIG_LIB=/usr/local/share/swig/4.0.1 /usr/local/bin/swig -csharp -DSWIGWORDSIZE64 -I/home/xian/code/dotnet-native/dotnet -I/home/xian/code/dotnet-native/Foo/include -namespace Mizux.Foo -dllimport mizux-foo-native -outdir /home/xian/code/dotnet-native/build/dotnet/Foo/Foo -c++ -o /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/fooCSHARP_wrap.cxx /home/xian/code/dotnet-native/Foo/dotnet/foo.i
###生成的4個檔案是
### [email protected]:~/code/dotnet-native$ ls /home/xian/code/dotnet-native/build/dotnet/Foo/Foo
### csFoo.cs csFooPINVOKE.cs Foo.cs fooCSHARP_wrap.cxx
### 現在理解以下-dllimport mizux-foo-native做了什麼,如果上面命令去掉-dllimport mizux-foo-native,則csFooPINVOKE.cs會少幾行,其中一行是
### [global::System.Runtime.InteropServices.DllImport("mizux-foo-native",EntryPoint="CSharp_MizuxfFoo_FooHello__SWIG_0___")]
### public static extern void FooHello__SWIG_0(int jarg1); #這一行不少,少上面一行
### 也就是匯入mizux-foo-native中的CSharp_MizuxfFoo_FooHello__SWIG_0___函式,如果沒有-dllimport mizux-foo-native 那麼foo.i中的函式就沒有實現
### 讓我不解的是,mizux-foo-native依賴於dotnet_Foo,而dotnet_Foo依賴於foo.i,foo.i又依賴mizux-foo-native,這不是一個迴圈嗎?
set_target_properties(dotnet_Foo PROPERTIES
SWIG_INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/dotnet
###include dotnet/*
SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON
###???
POSITION_INDEPENDENT_CODE ON)
target_link_libraries(dotnet_Foo PRIVATE ${PROJECT_NAME}::Foo)
set_property(SOURCE foo.i PROPERTY CPLUSPLUS ON)
###Call SWIG in c++ mode 參考 https://cmake.org/cmake/help/v3.14/module/UseSWIG.html
set_property(SOURCE foo.i PROPERTY COMPILE_OPTIONS
### 編譯foo.i時,用COMPILE_OPTIONS中的編譯選項
-namespace ${DOTNET}
-dllimport mizux-foo-native) ###foo.i檔案中匯入mizux-foo-native
swig_add_library(dotnet_Foo
TYPE OBJECT
LANGUAGE csharp
OUTPUT_DIR ${PROJECT_BINARY_DIR}/dotnet/${PROJECT_NAME}/Foo
SOURCES foo.i)
###swig_add_library的作用就是將.i檔案翻譯成.cxx檔案。
###翻譯後的檔案為 /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/fooCSHARP_wrap.cxx
###上面 -namespace ${DOTNET}的作用就是翻譯foo.i中的函式名時加入namespace指定的字首。
###比如如果-namespace ABCD.E.F.G 則FooHello(int);被翻譯為CSharp_ABCDfEfFfG_FooHello__SWIG_0___(int jarg1)
###並且相對應的C#檔案 /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/csFoo.cs
### /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/csFoo.cs 也會有namspace:
### namespace ABCD.E.F.G {
### }
###swig_add_library實際上是執行了一行命令,輸入.i檔案,輸出4個檔案,這個命令可以在
###build/Foo/dotnet/CMakeFiles/dotnet_Foo_swig_compilation.dir/build.make
###中找到,
###/home/xian/.local/lib/python3.6/site-packages/cmake/data/bin/cmake -E env SWIG_LIB=/usr/local/share/swig/4.0.1 /usr/local/bin/swig -csharp -DSWIGWORDSIZE64 -I/home/xian/code/dotnet-native/dotnet -I/home/xian/code/dotnet-native/Foo/include -namespace Mizux.Foo -dllimport mizux-foo-native -outdir /home/xian/code/dotnet-native/build/dotnet/Foo/Foo -c++ -o /home/xian/code/dotnet-native/build/dotnet/Foo/Foo/fooCSHARP_wrap.cxx /home/xian/code/dotnet-native/Foo/dotnet/foo.i
###生成的4個檔案是
### [email protected]:~/code/dotnet-native$ ls /home/xian/code/dotnet-native/build/dotnet/Foo/Foo
### csFoo.cs csFooPINVOKE.cs Foo.cs fooCSHARP_wrap.cxx
### 現在理解以下-dllimport mizux-foo-native做了什麼,如果上面命令去掉-dllimport mizux-foo-native,則csFooPINVOKE.cs會少幾行,其中一行是
### [global::System.Runtime.InteropServices.DllImport("mizux-foo-native",EntryPoint="CSharp_MizuxfFoo_FooHello__SWIG_0___")]
### public static extern void FooHello__SWIG_0(int jarg1); #這一行不少,少上面一行
### 也就是匯入mizux-foo-native中的CSharp_MizuxfFoo_FooHello__SWIG_0___函式,如果沒有-dllimport mizux-foo-native 那麼foo.i中的函式就沒有實現
### 讓我不解的是,mizux-foo-native依賴於dotnet_Foo,而dotnet_Foo依賴於foo.i,foo.i又依賴mizux-foo-native,這不是一個迴圈嗎?
set_target_properties(dotnet_Foo PROPERTIES
SWIG_INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/dotnet
###include dotnet/*
SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON
###???
POSITION_INDEPENDENT_CODE ON)
target_link_libraries(dotnet_Foo PRIVATE ${PROJECT_NAME}::Foo)
總結一下依賴關係。
Foo/CMakeLists.txt + Foo.cpp --> Foo.so
Foo/dotnet/CMakeLists.txt + foo.i + Foo.so --> dotnet_Foo.dir (這裡需要swig)
dotnet_Foo.dir --> mizux-foo-native.so
mizux-foo-native.so --> dotnet_native_package (Mizux.Foo.runtime.
dotnet_native_package --> dotnet_package (Mizux.Foo)
dotnet_package + FooApp.cs --> FooApp