浏览器内Golang编译器使用指南
浏览器内Golang编译器使用指南 我最近了解到Go可以通过WebAssembly在浏览器中运行。这让我开始思考如何让Go编译器在浏览器中工作,以便编译可下载的二进制文件。
我在StackOverflow上提出了一个关于这个问题的问题,并得到了一个确认这是可能的答案,同时提供了一个需要完成事项的高层次概述。然而,在查看了Golang的GitHub仓库之后,我意识到这将有多么困难。我甚至不知道从哪里开始!
我想知道是否有人能给我一些关于如何进行此事的指导。我基本上是在考虑将Go编译器本身编译为WASM。
抱歉久等了——我目前正在休假。我该如何执行引导代码?对WASM不太熟悉。
更多关于浏览器内Golang编译器使用指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢提供的链接。我会马上查看。至于编译器是一个带有主入口点的二进制文件,难道不能通过重命名“main”然后在准备好时从JavaScript调用新名称来解决吗?还是我误解了什么?
TR-SLimey:
这让我想到让Go编译器在浏览器中工作,以便编译可下载的二进制文件。
这能让你下载源代码并在浏览器中编译它,但你为什么想这样做,而不是直接下载适合你平台的二进制文件呢?
我想尝试一下,看起来足够简单,以下是我的操作步骤:
- 克隆 https://github.com/golang/go
- 进入
src目录 - 执行
GOOS=js GOARCH=wasm ./bootstrap.bash
这个命令本应编译用于 WASM 的 Go 源代码,但我执行时遇到了一个错误。所以我认为你的挑战将是解决这个错误。
真的吗?在我的Linux系统上使用Brave浏览器,搭载Core i7-8750H处理器,大约只需要4-5秒。你用的是哪个浏览器?我记得作者提到过Edge和Safari非常慢。无论如何,对于我的项目来说,跨平台支持、安全性和易用性比速度稍微重要一些,所以我愿意做出这个妥协。更不用说,当网络连接非常慢但硬件性能良好时,这种方式实际上可能更快。
您是否使用过 play.golang.org?该网站允许您输入 Go 代码,构建并运行您的程序。它有一个下载功能,但只允许您下载编辑器中已有的源代码。我还没有查看 play.golang.org 的源代码,但我建议从这里开始,然后添加一个功能,提示用户选择 GOOS 和 GOARCH,然后下载结果。
我刚刚在我的酷睿 i9-9900 上试了一下,编译并运行默认的 “hello world” 程序大约需要 17 秒,即使运行了几次之后也是如此。相比之下,在 play.golang.org 上只需要 1 秒。
经过进一步研究,看来最终还是有人抢先一步了。这里是相关 Reddit 帖子的链接:https://www.reddit.com/r/golang/comments/c95ngi/i_compiled_go_for_wasm_and_made_a_playground_that/
虚拟主机套餐是许多主机提供商提供的一种服务——用户只能访问主机系统上的一个文件夹(他们的网站根目录),并且预装了网站托管环境,无法访问机器的其他部分。例如,这些方案通常比VPS便宜得多,但限制也大得多。这就是为什么在服务器端编译并不总是可行的。我也在考虑将我的应用程序打包成类似Electron的形式,这基本上也需要在客户端进行编译,因为不会有服务器。
请原谅我的无知,但我不明白什么是网络托管包,即使在谷歌搜索之后也不明白(我不是专业程序员,我的兴趣在于底层内容,因此我对编译问题感兴趣)。编译时选项的论点对我来说是合理的,但我仍然不明白为什么你希望在浏览器中客户端编译Go。对于编译时选项,为什么不通过网页提示用户选择他们想要的选项,然后在服务器端检查之前是否请求过该组合?如果是,发送预编译版本,否则编译并发送。我个人几乎一直使用移动设备,客户端编译对我来说并不是最好的体验。我只是很难理解这个场景。
好的,我已经成功将HTML内容转换为Markdown格式,并遵循了所有严格规则。以下是最终的Markdown文档:
好的,我已经成功修改了帖子中的项目,制作了一个功能完整的浏览器内Go编译器。目前它仅支持为amd64架构的Linux系统进行编译,并且和原帖中的项目一样,只支持运行时库。但我计划在未来加入对其他架构和库的支持。同时,我将Go编译器版本从原帖的1.12更新到了1.15.1,因此应该会有一些速度上的提升。这里是演示链接,供感兴趣的人查看:https://tr-slimey.github.io/IBGC/。该演示页面也包含了主仓库的链接。
我之前没有使用过WASM,所以在这方面可能帮不上忙。我查阅了MDN

加载和运行WebAssembly代码
要在JavaScript中使用WebAssembly,你首先需要在编译/实例化之前将模块加载到内存中。本文提供了可用于获取WebAssembly字节码的不同机制的参考,以及如何…
其中涉及实例化二进制文件,然后从中导出函数。在我们的案例中,它是一个带有主入口点的可执行二进制文件,而不是一个库,因此我在该页面上看到的方法与你在这里尝试实现的目标不太相关。
将Go编译器编译为WebAssembly并在浏览器中运行是一个复杂但可行的项目。以下是实现这一目标的关键步骤和示例代码:
1. 编译Go编译器为WASM
首先,你需要将Go编译器本身编译为WebAssembly格式:
// 在构建环境中设置目标为wasm
GOOS=js GOARCH=wasm go build -o compiler.wasm cmd/compile/*
2. 浏览器端执行环境
创建HTML页面加载WASM编译器并设置执行环境:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go Compiler in Browser</title>
<script src="wasm_exec.js"></script>
</head>
<body>
<textarea id="source" rows="20" cols="80">
package main
import "fmt"
func main() {
fmt.Println("Hello from browser Go!")
}
</textarea>
<button onclick="compile()">Compile</button>
<div id="output"></div>
<script>
const go = new Go();
async function compile() {
const source = document.getElementById('source').value;
// 加载WASM编译器
const response = await fetch('compiler.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, go.importObject);
await go.run(instance);
// 调用编译器功能
const result = window.compileGo(source);
document.getElementById('output').innerText = result;
}
// 暴露给WASM调用的函数
window.compileGo = function(source) {
// 这里实现编译逻辑
return "Compilation result";
};
</script>
</body>
</html>
3. Go编译器适配层
创建适配层,让Go编译器能在浏览器环境中工作:
// main.go - 编译器的浏览器适配版本
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"syscall/js"
)
func compile(this js.Value, args []js.Value) interface{} {
source := args[0].String()
// 创建临时文件
tmpfile, err := ioutil.TempFile("", "*.go")
if err != nil {
return fmt.Sprintf("Error creating temp file: %v", err)
}
defer os.Remove(tmpfile.Name())
// 写入源代码
if _, err := tmpfile.Write([]byte(source)); err != nil {
return fmt.Sprintf("Error writing source: %v", err)
}
tmpfile.Close()
// 调用Go编译器
var buf bytes.Buffer
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// 模拟命令行参数
os.Args = []string{"compile", tmpfile.Name()}
// 运行编译
go func() {
// 这里需要调用实际的编译器逻辑
w.Close()
}()
// 读取输出
output, _ := ioutil.ReadAll(r)
os.Stdout = oldStdout
return string(output)
}
func main() {
// 注册JavaScript可调用的函数
js.Global().Set("compileGo", js.FuncOf(compile))
// 保持程序运行
select {}
}
4. 文件系统模拟
由于浏览器没有真实的文件系统,需要实现虚拟文件系统:
// vfs.go - 虚拟文件系统实现
package main
import (
"syscall/js"
)
type BrowserFS struct {
files map[string][]byte
}
func NewBrowserFS() *BrowserFS {
return &BrowserFS{
files: make(map[string][]byte),
}
}
func (fs *BrowserFS) WriteFile(name string, data []byte) error {
fs.files[name] = data
return nil
}
func (fs *BrowserFS) ReadFile(name string) ([]byte, error) {
data, exists := fs.files[name]
if !exists {
return nil, fmt.Errorf("file not found: %s", name)
}
return data, nil
}
// 集成到编译器
func setupVirtualFS() {
fs := NewBrowserFS()
// 重定向文件操作到虚拟文件系统
}
5. 构建脚本
创建构建脚本自动化整个过程:
#!/bin/bash
# build.sh
# 1. 构建Go编译器为WASM
echo "Building Go compiler for WASM..."
GOOS=js GOARCH=wasm go build -o wasm/compiler.wasm ./cmd/compile
# 2. 复制WASM执行支持文件
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" wasm/
# 3. 构建用户界面
echo "Building interface..."
go build -o server main.go
echo "Build complete!"
6. 限制和注意事项
// 需要注意的浏览器环境限制:
// 1. 没有真正的文件系统
// 2. 网络访问受限(需要CORS)
// 3. 内存限制
// 4. 无法直接生成可执行文件(但可生成WASM)
// 可能的解决方案:
// - 使用IndexedDB模拟文件系统
// - 通过Web Workers处理编译任务
// - 使用Service Worker缓存编译器
这个实现展示了将Go编译器移植到浏览器的基本架构。实际实现需要处理更多细节,包括完整的编译器工具链、依赖管理和二进制输出生成。


