2026a

# 使用 julia 代码创建 C 共享库


此示例说明了如何使用 julia 函数创建 C 共享库。

注意

Syslab AppBundler 暂不支持在 Syslab Online 中使用。

# 1. 创建独立文件夹

准备一个独立的文件夹,其中包含用户依赖的 jl 文件以及一些其他的资源文件。

在此示例中,创建一个 example 的空文件夹。

# 2. 创建 Julia 函数

在 example 文件夹中创建 main.jl 文件,并在其中创建函数。

代码如下:

using TyMath
function add(a1, a2)
    return a1 + a2
end

function multiply_string(a1, a2)
    return a1 * a2
end

function eig_matrix(A)
    V,D,W = eig(A)
    return V,D,W
end

测试 julia 函数,执行 main.jl 文件,在 julia 终端输入:

add(3,4)

输出为:

7

在 julia 终端输入:

add(1.2+1.5im,3.4+2.8im)

输出为:

4.6 + 4.3im

在 julia 终端输入:

multiply_string("hello ","world!")

输出为:

"hello world!"

在 julia 终端输入:

V,D,W = eig_matrix([1 7 3; 2 9 12; 5 22 7])

输出为:

V =
3×3 Matrix{ComplexF64}:
 -0.260977+0.0im   -0.973445+0.0im   0.189104+0.0im
 -0.587027+0.0im    0.228061+0.0im  -0.581573+0.0im
 -0.766349+0.0im  -0.0198078+0.0im    0.79121+0.0im

D = 
3×3 Matrix{ComplexF64}:
 25.5548+0.0im        0.0+0.0im      0.0+0.0im
     0.0+0.0im  -0.578934+0.0im      0.0+0.0im
     0.0+0.0im        0.0+0.0im  -7.9759+0.0im

W =
3×3 Matrix{ComplexF64}:
 -0.179108+0.0im  -0.958733+0.0im  -0.188133+0.0im
 -0.812659+0.0im  0.0648539+0.0im   -0.74771+0.0im
 -0.554531+0.0im   0.276814+0.0im   0.636817+0.0im

# 3. 创建 C 共享库

在 example 文件中创建 build.jl 文件,用于构建 C 共享库。

使用以下模板代码,填写对应信息后构建 C 共享库。

using TyAppBundler

# 动态库名称
library_name = "libexample"

# 打包文件夹
src_dir = @__DIR__

# 目标文件夹
dest_dir = joinpath(dirname(src_dir), library_name)

# 主文件
user_file = joinpath(src_dir, "main.jl")

# 依赖函数库
deps = ["TyMath"]

# 是否打包 runtime
has_runtime = false

# 是否创建日志文件(若创建日志文件,则输出不会在终端打印)
create_log_file = false

# 是否需要加密代码
encrypt = true

# 是否覆盖目标文件夹(如果文件夹已存在)
force = false

# 需要打包的函数,结构为(c 函数名称,julia 函数名称,julia 输入类型,julia 输出类型)
# 对于同一个 julia 函数,可以根据不同的输入输出,生成不同的 c 函数
func1 = TyAppBundler.CFuncInfo("add_int64_t", "add", (Int64, Int64), (Int64,))
func2 = TyAppBundler.CFuncInfo("add_double_complex", "add", (ComplexF64, ComplexF64), (ComplexF64,))
func3 = TyAppBundler.CFuncInfo("multiply_string", "multiply_string", (String, String), (String,))
func4 = TyAppBundler.CFuncInfo("eig_matrix","eig_matrix", (Array,), (Array,Array,Array))
func_list = [func1, func2, func3, func4]

# 开始构建
TyAppBundler.bundle_clibrary(
    src_dir,
    dest_dir,
    user_file,
    deps,
    func_list;
    library_name=library_name,
    has_runtime=has_runtime,
    create_log_file=create_log_file,
    encrypt=encrypt,
    force=force,
)

TyAppBundler.CFuncInfo 数组,每一个元素包含(c 函数名,julia 函数名,julia 输入类型,julia 输出类型),代表一个需要打包的函数,支持的 julia 输入输出类型及对应的 C 类型表格如下:

C 类型 标准 Julia 别名 Julia 基本类型
int8_t / Int8
uint8_t Cuchar UInt8
int16_t Cshort Int16
uint16_t Cushort UInt16
int32_t Cint Int32
uint32_t Cuint UInt32
int64_t Clonglong Int64
uint64_t Culonglong UInt64
float Cfloat Float32
double Cdouble Float64
complex_float ComplexF32 Complex{Float32}
complex_double ComplexF64 Complex{Float64}
char * / String
jlArray * / Array

运行 build.jl 文件进行打包,打包完成后会在目标文件夹下获得以下文件:

# 4. 在 C 应用程序中实现 C 共享库

打包 C 共享库后,您可以从 C 应用程序中调用它。使用 C 应用程序代码调用共享库中包含的函数。

  1. 在上述目标文件夹中创建 main.c 文件,内容如下:

    #include <stdio.h>
    #include "libexample.h"
    #include <string.h>
    
    void jlarray_print_info(const jlArray* arr) {
        if (!arr) {
            printf("NULL array\n");
            return;
        }
    
        int i,j;
        int row,col;
    
        row = arr->size[0];
        col = arr->size[1];
    
        // 打印二维数组
        if(arr->complexflag){
            for(i = 0; i < row; i++){
                for(j = 0; j < col; j++){
                    complex_double v = ((complex_double*)arr->data)[j * row + i];
                    printf("%6f + %6fi\t", v.real, v.imag);
                }
                printf("\n");
            }
        }else{
            for(i = 0; i < row; i++){
                for(j = 0; j < col; j++){
                    printf("%6f\t",((double *)arr->data)[j * row + i]);
                }
                printf("\n");
            }
        }
        printf("\n");
    }
    
    int main() {
        if (!jlInitializeApplication()){
            fprintf(stderr, "%s\n", "init julia failed!");
            return -1;
        }
    
        if (!libexampleInitialize()){
            fprintf(stderr, "%s\n", "init library failed!");
            return -1;
        }
    
        // add_int64_t
        int64_t add_input1 = 3;
        int64_t add_input2 = 4;
        int64_t add_output1;
        int retcode = add_int64_t(&add_output1, add_input1, add_input2);
        if(!retcode){
            printf("add_int64_t failed\n");
            return -1;
        }
        printf("add_output1 = %lld\n\n", add_output1);
    
        // add_double_complex
        complex_double add_complex_input1 = { 1.2 , 1.5 };
        complex_double add_complex_input2 = { 3.4 , 2.8 };
        complex_double add_complex_output1;
        retcode = add_double_complex(&add_complex_output1, add_complex_input1, add_complex_input2);
        if(!retcode){
            printf("add_double_complex failed\n");
            return -1;
        }
        printf("add_complex_output1 = %4.2f + %4.2fi\n\n", add_complex_output1.real, add_complex_output1.imag);
    
        // multiply_string
        char str_input1[] = "hello ";
        char str_input2[] = "world!";
        char* str_output1;
        retcode = multiply_string(&str_output1, str_input1, str_input2);
        if(!retcode){
            printf("multiply_string failed\n");
            return -1;
        }
        printf("str_output1 = %s\n\n",str_output1);
        jlDestroyString(str_output1);
    
        //eig_matrix
        int sizes_arr[] = {3,3};
        double data[] = {1,2,5,7,9,22,3,12,7}; // 矩阵采用列主
        jlArray* arr_input1;
        arr_input1 = jlCreateArray(2,sizes_arr,jlFloat64,jlReal);
        memcpy(arr_input1->data, data, 9*sizeof(double));
        jlArray* arr_output1 = NULL;
        jlArray* arr_output2 = NULL;
        jlArray* arr_output3 = NULL;
        retcode = eig_matrix(&arr_output1, &arr_output2, &arr_output3, arr_input1);
        if(!retcode){
            printf("eig_matrix failed\n");
            return -1;
        }
        printf("arr_output1 =\n");
        jlarray_print_info(arr_output1);
        printf("arr_output2 =\n");
        jlarray_print_info(arr_output2);
        printf("arr_output3 =\n");
        jlarray_print_info(arr_output3);
    
        jlDestroyArray(arr_output1);
        jlDestroyArray(arr_output2);
        jlDestroyArray(arr_output3);
    
        libexampleTerminate();
    
        jlTerminateApplication();
        return 0;
    }
    
  2. 启动系统命令提示符,导航至目标文件夹,使用 gcc 编译并链接应用程序(此步骤需要安装 gcc)。

    Windows:

    gcc main.c -o main.exe -I. -L. -lexample
    

    Linux:

    gcc main.c -o main -I. -L. -lexample -Wl,-rpath,.
    

提示

若要使用 Visual Studio 编译并链接应用程序,需要添加 libexample.lib 依赖项,使用 libexample.h 头文件和 main.c 源文件生成 exe 可执行文件,最后将 exe 可执行文件拷贝至 libexample.dll 同级目录后可正常运行。

  1. 要在 Linux 系统上编译或运行该应用程序(脱离 Syslab ),需要将 Runtime 添加到 LD_LIBRARY_PATH 库路径中。

    • 如果打包时设置 has_runtime 为 false,则需要安装 Syslab,在 LD_LIBRARY_PATH 中添加 <JULIA_HOME>/lib 和 <JULIA_HOME>/lib/julia 路径。<JULIA_HOME> 的值为 Syslab 中 julia 的路径,以 Syslab 2025b 为例,<JULIA_HOME> 的值为 <Syslab安装目录>/Tools/julia-1.9.3。执行命令如下:

      export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}\
      <JULIA_HOME>/lib:\
      <JULIA_HOME>/lib/julia:"
      
    • 如果打包时设置 has_runtime 为 true,则无需安装 Syslab,在 LD_LIBRARY_PATH 中添加 <APP_DIR>/julia/lib 和 <APP_DIR>/julia/lib/julia 路径,其中<APP_DIR>为打包文件夹的路径。执行命令如下:

      export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}\
      <APP_DIR>/julia/lib:\
      <APP_DIR>/julia/lib/julia:"
      
  2. 从系统命令提示符运行该应用程序。

    首次执行会进行预编译,需要等待一段时间。

    Windows:

    .\main.exe
    

    Linux:

    ./main
    

    运行结果:

# 5. 部署 c 共享库

打包好的文件夹即为最终部署产物,将该文件夹拷贝到用户指定机器即可使用,无需安装。

该示例需要依赖 Syslab 运行,若用户不想依赖 Syslab 就可运行部署应用程序,则需要在步骤 3 创建 C 共享库中,将 has_runtime 设置为 true。

# 6. 局限性

不支持一个 C/C++ 应用程序同时调用多个 syslab 打包的动态库。

注意

  • 初次运行移动文件夹后运行应用程序均需要预编译,此过程会花费一定时间,此时无需操作,等待编译结束即可;

  • 如果打包依赖了多个 jl 文件或依赖了外部资源文件,则需要将这些文件全部放在打包文件夹中,并在 jl 代码中使用相对路径进行引用;

  • 执行打包脚本需要在全局环境下进行,如果用户代码依赖了本地开发的函数库,需要将该函数库安装在全局环境中,并在打包脚本中将该函数库添加至依赖函数库中;

  • 若用户选择打包 Runtime,且打包产物中存在 miniforge3 文件夹,则打包的文件夹不能放在中文路径下,这会导致 TyPlot 无法使用。