目录: 点击这里

这一章介绍怎么构建 Musys 库, 以及如何使用 Musys 库写一个简单的 demo.

编译器、语言服务器与依赖问题

Musys-IR 是使用 Vala 编程语言 编写的, 依赖 Vala 编译器, 依赖 GLib 标准库与 Gee 数据结构库.

如果要语义高亮、智能提示等, 可以选择 vala-language-server 语言服务器,配合相关的扩展使用.

  • 在 Ubuntu (24.04+) 操作系统上, 需要使用 apt 安装依赖: valac meson libglib2.0-dev libgee-0.8-dev. vala-language-server 需要根据 GitHub 给出的 OBS repo 链接自行安装.
  • 在 Fedora 操作系统上, 需要 dnf 安装的依赖是: vala libgee-devel glib2-devel meson vala-language-server. 没有需要额外安装的依赖.
  • 在 Windows 操作系统上请使用 MSYS2 兼容环境 (清华镜像在此).

目前只有少数几个文本编辑器内置了对 Vala 编程语言的支持. 其中 KDE Kate 的支持相对完整, 只要装好 Vala 语言服务器就能用. VS Code 用户需要额外安装下面这两个扩展:

下载并构建 Musys-IR

Musys-IR 是共享库而不是可执行文件, 这里仅仅用于验证 Musys-IR 能否正常构建, 不能运行, 不建议安装. 若需要使用 Musys IR 写一个 demo, 建议跳转到下一节.

这里给出下载-构建的 Shell 命令。最后一行是测试安装使用的, 一般不执行:

1
2
3
4
5
6
7
8
9
# 目前正常开发的分支是 adaptive-traced-cfg
git clone -b adaptive-traced-cfg https://github.com/medihbt/musys-ir.git
cd musys-ir
# 使用 meson 初始化项目
meson setup build && cd build
# 编译
meson compile
# 安装——如果你想试试看怎么安装的话
meson install

上述命令执行顺利的话, meson compile 以后你就能在 build/src 目录下看到 libmusys-ir.so MusysIR-0.0.1.gir musys-ir.h musys-ir.vapi 这几个文件.

基于 Musys-IR 编写一个简单的 Demo

上面的构建仅仅是测试而已,接下来所有的教程、用例等等都要使用以下构建方式: Meson Subprojects + Git submodules.

项目初始化

首先, 在你喜欢的目录下创建一个 musys-demo 用作项目开发目录, 然后初始化 Meson:

1
2
3
mkdir musys-demo && cd musys-demo
meson init -l vala --type executable --version 0.1
meson setup build

然后初始化 Git. .gitignore 文件参考如下:

1
2
3
4
5
6
7
8
9
10
11
build/*
builddir/*
.vscode/*
.DS_Store/*
.cache/*
*.o
*.yy.c
*.yy.h
*.tab.c
*.tab.h
.clangd

Git & 子项目初始化命令如下:

1
2
3
4
5
6
git init -b master
git add .gitignore; git commmit

# 初始化子项目——也就是克隆我的 musys-ir
mkdir -p subprojects
git submodule add -b adaptive-traced-cfg https://github.com/medihbt/musys-ir subprojects/musys-ir

接下来, 修改 meson.build 文件如下. 标有 ‘+’ 的是要补充的内容, 标有 ‘-‘ 的是要删除的内容.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+musys_ir_subproj = subproject('musys-ir')
+musys_ir_dep = musys_ir_subproj.get_variable('musys_ir_dep')

dependencies = [
dependency('glib-2.0'),
dependency('gobject-2.0'),
+ dependency('gee-0.8'),
+ musys_ir_dep,
]

+sources = files([
+ 'musys_demo.vala',
+])

exe = executable('musys-demo',
- 'musys_demo.vala',
+ sources,
+ dependencies : dependencies,
install : true)

修改以后再做一次 commit:

1
2
git add *
git commit

完成.

怎么更新 Musys-IR?

Musys-IR 尚处于开发阶段, 其代码、仓库、分支等可能会频繁更新. 请使用 git submodule update 适时更新子项目.

第一个 Musys 前端

先从最简单的开始——用 Musys-IR 生成一个空的 Main 函数罢. 修改 musys_demo.vala 源文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** @file musys_demo.vala */
using Musys;
using Musys.IR;
using Musys.IRUtil;

void main()
{
/* 新建一个模块——“模块”相当于源文件. */
var module = new Module("musys-demo-main");
TypeContext tctx = module.type_ctx;

/* 函数的签名是 `int main(void)`, 因此要准备一个 int 类型和一个空参数数组 */
IntType i32ty = tctx.get_int_type(32);
FunctionType main_type = tctx.get_func_type(i32ty, null);

/* 定义一个函数 'main' */
var main_func = new Function.as_impl(main_type, "main");
/* 把 main 函数添加到模块 module 中 */
module.global_def["main"] = main_func;

/* 给模块里的所有内容标上号, 然后打印出来 */
number_module(module);
var writer = new IRUtil.Writer(module) {
llvm_compatible = true
};
writer.write_file(stdout);
}

现在试着编译运行一下:

1
2
3
cd build
meson compile
./musys-demo

你应该能看到输出:

1
2
3
4
5
6
define dso_local i32 @main () {
0:
ret i32 0
}

;module musys-demo-main

成功! 接下来所有的实践内容都要在这个项目下进行了.

如果我就想用 C 语言怎么办?

我试着写一段等价的 C 程序罢. Musys 的 C API 是 Vala 自动生成的, 现在还不稳定, 这里图一乐就好.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <musys-ir.h>

int main(void)
{
/* 新建一个模块——“模块”相当于源文件. */
MusysIRModule* module = musys_ir_module_new("musys-demo-main",
sizeof(size_t),
MusysPlatformEndianHost());
MusysTypeContext* tctx = musys_ir_module_get_type_ctx(module);

/* 函数的签名是 `int main(void)`, 因此要准备一个 int 类型和一个空参数数组 */
MusysType* i32ty = (MusysType*)musys_typecontext_get_int_type(tctx, 32);
MusysFunctionType* main_type = musys_typecontext_get_func_type(
tctx, i32ty, NULL, 0);

/* 定义一个函数 'main' */
MusysIRFunction* main_func = musys_ir_function_new_as_impl(main_type, "main");

/* 把 main 函数添加到模块 module 中 */
GeeTreeMap* global_def = musys_ir_module_get_global_def(module);
gee_abstract_map_set((GeeAbstractMap*)global_def, "main", main_func);

/* 给模块里的所有内容标上号, 然后打印出来 */
musys_ir_util_number_module(module);
MusysIRUtilWriter* writer = musys_ir_util_writer_new(module);
musys_ir_util_writer_set_llvm_compatible(writer, true);
musys_ir_util_writer_write_file(writer, stdout);

musys_ir_util_writer_unref(writer);
g_object_unref(main_func);
g_object_unref(module);
return 0;
}