Skip to content

Commit 326a6fb

Browse files
committed
Initial commit: bindgen Go bindings
0 parents  commit 326a6fb

239 files changed

Lines changed: 86586 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/go.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master, main]
6+
tags:
7+
- 'v*'
8+
pull_request:
9+
branches: [master, main]
10+
11+
jobs:
12+
test:
13+
runs-on: windows-latest
14+
timeout-minutes: 20
15+
16+
steps:
17+
- uses: actions/checkout@v6
18+
19+
- uses: actions/setup-go@v6
20+
with:
21+
go-version: '1.26.2'
22+
23+
- name: Test c2go package
24+
run: |
25+
go test ./c2go/ -v -timeout 60s
26+
27+
- name: Test cc package
28+
run: |
29+
go test ./cc/ -v -timeout 60s
30+
31+
- name: Verify build
32+
run: go build ./...
33+
34+
release:
35+
if: startsWith(github.ref, 'refs/tags/v')
36+
needs: test
37+
runs-on: ubuntu-latest
38+
39+
steps:
40+
- uses: actions/checkout@v6
41+
42+
- name: Create archive
43+
run: zip -r bindgen-${{ github.ref_name }}.zip . -x '.git/**'
44+
45+
- uses: softprops/action-gh-release@v3
46+
with:
47+
files: bindgen-${{ github.ref_name }}.zip
48+
env:
49+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
*.ps1
2+
*.log
3+
build/
4+
todo/
5+
clone/
6+
cachedir/
7+
project/
8+
vendor
9+
.idea/
10+
*.apk
11+
*.json
12+
todo/
13+
clone/
14+
build_debug/
15+
build/
16+
/init.cmd
17+
/cmds.cmd
18+
.codeartsdoer/

README.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# bindgen - C/C++ to Go Bindings Generator
2+
3+
**纯 Go 实现,无 CGO 依赖** - 将任意 C/C++ 项目自动生成为 Go 语言绑定。
4+
5+
## 愿景
6+
7+
让任何 C/C++ 库都能快速、无痛地获得 Go 语言绑定:
8+
9+
- 🚀 **无 CGO** - 纯 Go 实现,使用 `golang.org/x/sys/windows` 动态加载 DLL
10+
-**编译快** - 无 CGO 开销,编译速度极快
11+
- 🔄 **交叉编译** - 轻松支持跨平台编译
12+
- 📦 **DLL 嵌入** - 动态库嵌入到 Go 二进制,无需手动分发
13+
- 🛠️ **自动生成** - 从 C/C++ 头文件自动生成所有绑定代码
14+
15+
## 绑定的项目
16+
17+
| 项目 | 描述 | 仓库 |
18+
|------|------|------|
19+
| **GLFW** | 跨平台窗口和输入库 | [github.com/ddkwork/glfw](https://github.com/ddkwork/glfw) |
20+
| **Everything SDK** | Windows 文件搜索 | [github.com/ddkwork/everything](https://github.com/ddkwork/everything) |
21+
| **Intel XED** | x86 指令编码/解码 | [github.com/ddkwork/xed](https://github.com/ddkwork/xed) |
22+
| **Zydis** | x86/x64 反汇编器 | [github.com/ddkwork/zydis](https://github.com/ddkwork/zydis) |
23+
| **ImGui** | 即时模式 GUI | [github.com/ddkwork/imgui](https://github.com/ddkwork/imgui) |
24+
| **Qt6** | 跨平台 GUI 框架 | [github.com/ddkwork/qt6](https://github.com/ddkwork/qt6) |
25+
| **Keystone** | 多架构汇编引擎 | [github.com/ddkwork/keystone](https://github.com/ddkwork/keystone) |
26+
| **WinDivert** | Windows 网络包拦截 | [github.com/ddkwork/WinDivert](https://github.com/ddkwork/WinDivert) |
27+
| **ARImpRec** | PE 导入表重建 | [github.com/ddkwork/ARImpRec](https://github.com/ddkwork/ARImpRec) |
28+
29+
## 架构
30+
31+
```
32+
bindgen/
33+
├── c2go/ # C 到 Go 绑定生成核心
34+
│ ├── generate.go # 主生成逻辑
35+
│ ├── fake_headers.go # 虚拟 C 标准库头文件
36+
│ ├── msenv.go # MSVC 编译器环境
37+
│ └── types.go # 类型定义
38+
├── cc/ # C 预处理和解析器
39+
├── cpp2c/ # C++ 到 C 转换
40+
├── bindgen_test.go # 集成测试(测试所有项目)
41+
└── project/ # 绑定项目示例
42+
├── glfw/
43+
├── xed/
44+
├── zydis/
45+
└── ...
46+
```
47+
48+
## 使用方法
49+
50+
### 1. 创建配置文件
51+
52+
```go
53+
// generate_test.go
54+
package mylib
55+
56+
import (
57+
"testing"
58+
"github.com/ddkwork/bindgen/c2go"
59+
)
60+
61+
func TestGenerate(t *testing.T) {
62+
c2go.Generate(t, []c2go.BindgenConfig{{
63+
HeadersDir: "include", // C 头文件目录
64+
OutputDir: ".", // Go 输出目录
65+
PackageName: "mylib", // Go 包名
66+
HeaderOrder: []string{"mylib.h"}, // 处理顺序
67+
BindDll: true, // 生成 DLL 绑定
68+
DllName: "mylib.dll", // DLL 名称
69+
DllFuncFilter: func(name string) bool {
70+
return true // 过滤导出函数
71+
},
72+
}})
73+
}
74+
```
75+
76+
### 2. 运行生成
77+
78+
```bash
79+
go test -run TestGenerate -v
80+
```
81+
82+
### 3. 使用绑定
83+
84+
```go
85+
package main
86+
87+
import "github.com/ddkwork/mylib"
88+
89+
func main() {
90+
lib := &mylib.Mylib{}
91+
lib.Init()
92+
defer lib.Destroy()
93+
94+
// 使用生成的函数...
95+
}
96+
```
97+
98+
## 核心特性
99+
100+
### Fake Headers
101+
102+
内置虚拟 C 标准库头文件,无需依赖系统头文件:
103+
104+
- `stddef.h`, `stdint.h` - 标准类型(size_t, int32_t 等)
105+
- `stdlib.h`, `stdio.h`, `string.h` - 标准函数
106+
- `windows.h` - Windows API 类型(HWND, DWORD 等)
107+
108+
### 类型映射
109+
110+
自动将 C 类型映射为 Go 类型:
111+
112+
| C 类型 | Go 类型 |
113+
|--------|---------|
114+
| `int32_t` | `int32` |
115+
| `uint64_t` | `uint64` |
116+
| `size_t` | `uintptr` |
117+
| `void*` | `unsafe.Pointer` |
118+
| `char*` | `*int8` |
119+
| 函数指针 | `func(...) uintptr` |
120+
121+
### DLL 嵌入
122+
123+
自动嵌入 DLL 到 Go 二进制:
124+
125+
```go
126+
//go:embed mylib.dll
127+
var dllBytes []byte
128+
```
129+
130+
运行时解压到用户缓存目录,无需手动分发 DLL。
131+
132+
## 运行所有测试
133+
134+
```bash
135+
go test -v -run TestAllBindings
136+
```
137+
138+
自动测试所有绑定项目,成功的项目自动推送到 GitHub。
139+
140+
## 依赖
141+
142+
- Go 1.26+
143+
- `golang.org/x/sys/windows` - Windows API 调用
144+
- MSVC cl.exe (可选,用于预处理)
145+
146+
## 许可证
147+
148+
MIT License
149+
150+
## 贡献
151+
152+
欢迎提交 Issue 和 Pull Request!

bindgen_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package bindgen
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"strings"
9+
"testing"
10+
"time"
11+
)
12+
13+
type Project struct {
14+
Name string
15+
Path string
16+
TestFile string
17+
RemoteURL string
18+
SkipPush bool
19+
}
20+
21+
var projects = []Project{
22+
{Name: "glfw", Path: "project/glfw/clone", TestFile: "generate_test.go", RemoteURL: "https://github.com/ddkwork/glfw.git"},
23+
{Name: "everything", Path: "project/everything", TestFile: "generate_test.go", RemoteURL: "https://github.com/ddkwork/everything.git"},
24+
{Name: "ARImpRec", Path: "project/ARImpRec", TestFile: "generate_test.go", RemoteURL: "https://github.com/ddkwork/ARImpRec.git"},
25+
{Name: "keystone", Path: "project/keystone", TestFile: "generate_test.go", RemoteURL: "https://github.com/ddkwork/keystone.git"},
26+
{Name: "WinDivert", Path: "project/WinDivert", TestFile: "generate_test.go", RemoteURL: "https://github.com/ddkwork/WinDivert.git"},
27+
{Name: "xed", Path: "project/xed", TestFile: "generate_test.go", RemoteURL: "https://github.com/ddkwork/xed.git"},
28+
{Name: "zydis", Path: "project/zydis", TestFile: "generate_test.go", RemoteURL: "https://github.com/ddkwork/zydis.git"},
29+
{Name: "hyperdbgsdk", Path: "project/hyperdbgsdk", TestFile: "generate_test.go", RemoteURL: "", SkipPush: true},
30+
{Name: "imgui", Path: "project/imgui/gen", TestFile: "project_test.go", RemoteURL: "https://github.com/ddkwork/imgui.git"},
31+
{Name: "qt6", Path: "project/qt6/gen", TestFile: "project_test.go", RemoteURL: "https://github.com/ddkwork/qt6.git"},
32+
}
33+
34+
func TestAllBindings(t *testing.T) {
35+
rootDir, err := os.Getwd()
36+
if err != nil {
37+
t.Fatalf("Getwd: %v", err)
38+
}
39+
40+
results := make(map[string]bool)
41+
var failed []string
42+
43+
for _, p := range projects {
44+
t.Run(p.Name, func(t *testing.T) {
45+
projectDir := filepath.Join(rootDir, p.Path)
46+
47+
if _, err := os.Stat(projectDir); os.IsNotExist(err) {
48+
t.Skipf("Project directory not found: %s", projectDir)
49+
return
50+
}
51+
52+
start := time.Now()
53+
cmd := exec.Command("go", "test", "-v", "-run", "TestGenerate", "-timeout", "40m", ".")
54+
cmd.Dir = projectDir
55+
output, err := cmd.CombinedOutput()
56+
duration := time.Since(start)
57+
58+
if err != nil {
59+
results[p.Name] = false
60+
failed = append(failed, p.Name)
61+
t.Errorf("Test failed (%v):\n%s", duration, string(output))
62+
return
63+
}
64+
65+
results[p.Name] = true
66+
t.Logf("PASS (%v)", duration)
67+
68+
if !p.SkipPush && p.RemoteURL != "" {
69+
if err := pushProject(projectDir); err != nil {
70+
t.Logf("Push failed: %v", err)
71+
} else {
72+
t.Logf("Pushed to %s", p.RemoteURL)
73+
}
74+
}
75+
})
76+
}
77+
78+
t.Log("\n=== Summary ===")
79+
for _, p := range projects {
80+
status := "FAIL"
81+
if results[p.Name] {
82+
status = "PASS"
83+
}
84+
t.Logf("%s: %s", p.Name, status)
85+
}
86+
87+
if len(failed) > 0 {
88+
t.Errorf("Failed projects: %v", failed)
89+
}
90+
}
91+
92+
func pushProject(dir string) error {
93+
cmd := exec.Command("git", "status", "--porcelain")
94+
cmd.Dir = dir
95+
output, err := cmd.Output()
96+
if err != nil {
97+
return fmt.Errorf("git status: %v", err)
98+
}
99+
100+
if len(strings.TrimSpace(string(output))) == 0 {
101+
return nil
102+
}
103+
104+
commands := [][]string{
105+
{"git", "add", "-A"},
106+
{"git", "commit", "-m", "Update bindings\n\n🤖 Generated with CodeMate"},
107+
{"git", "push"},
108+
}
109+
110+
for _, args := range commands {
111+
cmd := exec.Command(args[0], args[1:]...)
112+
cmd.Dir = dir
113+
if out, err := cmd.CombinedOutput(); err != nil {
114+
return fmt.Errorf("%s: %v\n%s", strings.Join(args, " "), err, string(out))
115+
}
116+
}
117+
118+
return nil
119+
}

0 commit comments

Comments
 (0)