97 lines
3.5 KiB
Markdown
97 lines
3.5 KiB
Markdown
|
# purego
|
||
|
[![Go Reference](https://pkg.go.dev/badge/github.com/ebitengine/purego?GOOS=darwin.svg)](https://pkg.go.dev/github.com/ebitengine/purego?GOOS=darwin)
|
||
|
|
||
|
A library for calling C functions from Go without Cgo.
|
||
|
|
||
|
> This is beta software so expect bugs and potentially API breaking changes
|
||
|
> but each release will be tagged to avoid breaking people's code.
|
||
|
> Bug reports are encouraged.
|
||
|
|
||
|
## Motivation
|
||
|
|
||
|
The [Ebitengine](https://github.com/hajimehoshi/ebiten) game engine was ported to use only Go on Windows. This enabled
|
||
|
cross-compiling to Windows from any other operating system simply by setting `GOOS=windows`. The purego project was
|
||
|
born to bring that same vision to the other platforms supported by Ebitengine.
|
||
|
|
||
|
## Benefits
|
||
|
|
||
|
- **Simple Cross-Compilation**: No C means you can build for other platforms easily without a C compiler.
|
||
|
- **Faster Compilation**: Efficiently cache your entirely Go builds.
|
||
|
- **Smaller Binaries**: Using Cgo generates a C wrapper function for each C function called. Purego doesn't!
|
||
|
- **Dynamic Linking**: Load symbols at runtime and use it as a plugin system.
|
||
|
- **Foreign Function Interface**: Call into other languages that are compiled into shared objects.
|
||
|
- **Cgo Fallback**: Works even with CGO_ENABLED=1 so incremental porting is possible.
|
||
|
This also means unsupported GOARCHs (freebsd/riscv64, linux/mips, etc.) will still work
|
||
|
except for float arguments and return values.
|
||
|
|
||
|
## Supported Platforms
|
||
|
|
||
|
- **FreeBSD**: amd64, arm64
|
||
|
- **Linux**: amd64, arm64
|
||
|
- **macOS / iOS**: amd64, arm64
|
||
|
- **Windows**: 386*, amd64, arm*, arm64
|
||
|
|
||
|
`*` These architectures only support SyscallN and NewCallback
|
||
|
|
||
|
## Example
|
||
|
|
||
|
This example only works on macOS and Linux. For a complete example look at [libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports Windows and FreeBSD.
|
||
|
|
||
|
```go
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"runtime"
|
||
|
|
||
|
"github.com/ebitengine/purego"
|
||
|
)
|
||
|
|
||
|
func getSystemLibrary() string {
|
||
|
switch runtime.GOOS {
|
||
|
case "darwin":
|
||
|
return "/usr/lib/libSystem.B.dylib"
|
||
|
case "linux":
|
||
|
return "libc.so.6"
|
||
|
default:
|
||
|
panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
var puts func(string)
|
||
|
purego.RegisterLibFunc(&puts, libc, "puts")
|
||
|
puts("Calling C from Go without Cgo!")
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Then to run: `CGO_ENABLED=0 go run main.go`
|
||
|
|
||
|
## Questions
|
||
|
|
||
|
If you have questions about how to incorporate purego in your project or want to discuss
|
||
|
how it works join the [Discord](https://discord.com/channels/842049801528016967/1123106378731487345)!
|
||
|
|
||
|
### External Code
|
||
|
|
||
|
Purego uses code that originates from the Go runtime. These files are under the BSD-3
|
||
|
License that can be found [in the Go Source](https://github.com/golang/go/blob/master/LICENSE).
|
||
|
This is a list of the copied files:
|
||
|
|
||
|
* `abi_*.h` from package `runtime/cgo`
|
||
|
* `zcallback_darwin_*.s` from package `runtime`
|
||
|
* `internal/fakecgo/abi_*.h` from package `runtime/cgo`
|
||
|
* `internal/fakecgo/asm_GOARCH.s` from package `runtime/cgo`
|
||
|
* `internal/fakecgo/callbacks.go` from package `runtime/cgo`
|
||
|
* `internal/fakecgo/go_GOOS_GOARCH.go` from package `runtime/cgo`
|
||
|
* `internal/fakecgo/iscgo.go` from package `runtime/cgo`
|
||
|
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
|
||
|
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
|
||
|
|
||
|
The files `abi_*.h` and `internal/fakecgo/abi_*.h` are the same because Bazel does not support cross-package use of
|
||
|
`#include` so we need each one once per package. (cf. [issue](https://github.com/bazelbuild/rules_go/issues/3636))
|