├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.rs └── src ├── go └── main.go └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "buildmodes" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 6 | ] 7 | 8 | [[package]] 9 | name = "libc" 10 | version = "0.2.7" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "buildmodes" 3 | version = "0.1.0" 4 | authors = ["Josh Rickmar "] 5 | links = "buildmodes" 6 | build = "build.rs" 7 | 8 | [dependencies] 9 | libc = "0.2.7" 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | buildmodes 2 | ========== 3 | 4 | This repo demonstrates statically linking and calling Go code from 5 | a non-Go project. It requires Go 1.5 or higher, which will introduce 6 | additional [execution modes](https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit?pli=1) 7 | to allow programs written in other languages to statically or 8 | dynamically link to and call Go code using the system C ABI. 9 | Rust was chosen for this demonstration because of its excellent 10 | package manager Cargo and how simple it is to integrate seamlessly 11 | with other build systems, but linking to other languages should be 12 | relatively straightforward as well. 13 | 14 | Demo 15 | ---- 16 | 17 | To build and run the entire project, simply run: 18 | 19 | ```bash 20 | cargo run 21 | ``` 22 | 23 | After compiling the Go and Rust code and executing the binary, the 24 | number 20 is printed. This number is calculated and returned by Go as 25 | the multiplication of two numbers (4 and 5) passed by Rust. 26 | 27 | Explanation 28 | ----------- 29 | 30 | When Cargo builds the project, it compiles and executes `build.rs`, 31 | which calls the `go` tool to build a C archive from the Go source file 32 | `src/go/main.go` and writes it to a Cargo build directory. See the 33 | [Cargo documentation](http://doc.crates.io/build-script.html) for more 34 | details about build scripts. 35 | 36 | If integrating with non-Cargo projects, the archive can be built 37 | manually by running something like: 38 | 39 | ```bash 40 | go build -buildmode=c-archive -o libbuildmodes.a src/go/main.go 41 | ``` 42 | 43 | The `go build` call must be used to build a Go main package. All cgo 44 | exported functions (annotated with `//export FuncName` directives) are 45 | exported by the archive. For this example, there exists a single 46 | exported function: 47 | 48 | ```Go 49 | //export Multiply 50 | func Multiply(a, b int) int { 51 | return a * b 52 | } 53 | ``` 54 | 55 | When using any of the C buildmodes, `cgo` will generate a C `Multiply` 56 | function with the signature: 57 | 58 | ```C 59 | int Multiply(int, int); 60 | ``` 61 | 62 | This is the function that Rust calls. See the [cgo documentation](http://golang.org/cmd/cgo/) 63 | for more details about how `cgo` creates C interfaces for Go code. 64 | 65 | Since Rust does not understand C header files, external functions must 66 | be declared manually in an `extern` block. This is done in 67 | `src/main.rs` in the `go` module: 68 | 69 | ```Rust 70 | #[link(name = "buildmodes")] 71 | extern { 72 | fn Multiply(a: c_int, b: c_int) -> c_int; 73 | } 74 | ``` 75 | 76 | Since `rustc` may not make any assumptions about the safety of calling 77 | non-Rust code, this function is automatically marked unsafe to call. 78 | A safe Rust wrapper is created in the `go` module: 79 | 80 | ```Rust 81 | pub fn multiply(a: c_int, b: c_int) -> c_int { 82 | unsafe { Multiply(a, b) } 83 | } 84 | ``` 85 | 86 | It is then called by `main`, and the result is printed to `stdout`: 87 | 88 | ```Rust 89 | fn main() { 90 | println!("{}", go::multiply(4, 5)); 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::env; 3 | 4 | fn main() { 5 | let out_dir = env::var("OUT_DIR").unwrap(); 6 | 7 | Command::new("go").args(&["build", "-buildmode=c-archive", "-o"]) 8 | .arg(&format!("{}/libbuildmodes.a", out_dir)) 9 | .arg(&"src/go/main.go") 10 | .status().unwrap(); 11 | 12 | println!("cargo:rustc-link-search=native={}", out_dir); 13 | println!("cargo:rustc-link-lib=static=buildmodes"); 14 | } 15 | -------------------------------------------------------------------------------- /src/go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "C" 4 | 5 | //export Multiply 6 | func Multiply(a, b int) int { 7 | return a * b 8 | } 9 | 10 | func main() { 11 | } 12 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | 3 | mod go { 4 | use libc::c_int; 5 | 6 | #[link(name = "buildmodes")] 7 | extern { 8 | fn Multiply(a: c_int, b: c_int) -> c_int; 9 | } 10 | 11 | pub fn multiply(a: c_int, b: c_int) -> c_int { 12 | unsafe { Multiply(a, b) } 13 | } 14 | } 15 | 16 | fn main() { 17 | println!("{}", go::multiply(4, 5)); 18 | } 19 | --------------------------------------------------------------------------------