Skip to content

Commit 2df0249

Browse files
authored
Merge pull request #43 from dicej/go-wrapper
add Go wrapper module
2 parents e0d2df7 + 142cd25 commit 2df0249

File tree

5 files changed

+181
-0
lines changed

5 files changed

+181
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
componentize-go
12
target
23
*.wasm
34

go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module github.com/bytecodealliance/componentize-go
2+
3+
go 1.25
4+
5+
require github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13
6+
7+
require github.com/gofrs/flock v0.13.0
8+
9+
require golang.org/x/sys v0.37.0 // indirect

go.sum

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13 h1:JtuelWqyixKApmXm3qghhZ7O96P6NKpyrlSIe8Rwnhw=
2+
github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13/go.mod h1:7kfpUbyCdGJ9fDRCp3fopPQi5+cKNHgTE4ZuNrO71Cw=
3+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
6+
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
7+
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
8+
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
9+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
10+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
11+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
12+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
13+
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
14+
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
15+
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
16+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
17+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package main
2+
3+
import (
4+
"archive/tar"
5+
"compress/gzip"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"log"
10+
"net/http"
11+
"os"
12+
"os/exec"
13+
"path/filepath"
14+
"runtime"
15+
"strings"
16+
17+
"github.com/apparentlymart/go-userdirs/userdirs"
18+
"github.com/gofrs/flock"
19+
)
20+
21+
// This is a simple wrapper program which downloads, caches, and runs the
22+
// appropriate `componentize-go` binary for the current platform.
23+
//
24+
// Although `componentize-go` is written in Rust, we can use this wrapper to
25+
// make it available using e.g. `go install` and/or `go tool`.
26+
func main() {
27+
// This is hard-coded to point to the latest canary release, which is
28+
// appropriate for the `main` branch, but should be changed to the tag
29+
// name for each tagged release.
30+
//
31+
// TODO: Can we automate updating this for each release?
32+
release := "canary"
33+
34+
directories := userdirs.ForApp(
35+
"componentize-go",
36+
"bytecodealliance",
37+
"com.github.bytecodealliance-componentize-go",
38+
)
39+
binDirectory := filepath.Join(directories.CacheDir, "bin")
40+
versionPath := filepath.Join(directories.CacheDir, "version.txt")
41+
lockFilePath := filepath.Join(directories.CacheDir, "lock")
42+
binaryPath := filepath.Join(binDirectory, "componentize-go")
43+
44+
maybeDownload(release, binDirectory, versionPath, lockFilePath, binaryPath)
45+
46+
run(binaryPath)
47+
}
48+
49+
// Download the specified version of `componentize-go` if we haven't already.
50+
func maybeDownload(release, binDirectory, versionPath, lockFilePath, binaryPath string) {
51+
if err := os.MkdirAll(binDirectory, 0755); err != nil {
52+
log.Fatalf("unable to create directory `%v`: %v", binDirectory, err)
53+
}
54+
55+
// Lock the lock file to prevent concurrent downloads.
56+
lockFile := flock.New(lockFilePath)
57+
if err := lockFile.Lock(); err != nil {
58+
log.Fatalf("unable to lock file `%v`: %v", lockFilePath, err)
59+
}
60+
defer lockFile.Unlock()
61+
62+
versionBytes, err := os.ReadFile(versionPath)
63+
var version string
64+
if err != nil {
65+
version = "<unknown>"
66+
} else {
67+
version = strings.TrimSpace(string(versionBytes))
68+
}
69+
70+
// If the binary doesn't already exist and/or the version doesn't match
71+
// the desired release, download it.
72+
if _, err := os.Stat(binaryPath); errors.Is(err, os.ErrNotExist) || version != release {
73+
base := fmt.Sprintf(
74+
"https://github.com/bytecodealliance/componentize-go/releases/download/%v",
75+
release,
76+
)
77+
78+
url := fmt.Sprintf("%v/componentize-go-%v-%v.tar.gz", base, runtime.GOOS, runtime.GOARCH)
79+
80+
fmt.Printf("Downloading `componentize-go` binary from %v and extracting to %v\n", url, binDirectory)
81+
82+
response, err := http.Get(url)
83+
if err != nil {
84+
log.Fatalf("unable to download URL `%v`: %v", url, err)
85+
}
86+
defer response.Body.Close()
87+
88+
if response.StatusCode < 200 || response.StatusCode > 299 {
89+
log.Fatalf("unexpected status for URL `%v`: %v", url, response.StatusCode)
90+
}
91+
92+
uncompressed, err := gzip.NewReader(response.Body)
93+
if err != nil {
94+
log.Fatalf("unable to decompress content of URL `%v`: %v", url, err)
95+
}
96+
defer uncompressed.Close()
97+
98+
untarred := tar.NewReader(uncompressed)
99+
for {
100+
header, err := untarred.Next()
101+
if err == io.EOF {
102+
break
103+
} else if err != nil {
104+
log.Fatalf("unable to untar content of URL `%v`: %v", url, err)
105+
}
106+
path := filepath.Join(binDirectory, header.Name)
107+
file, err := os.Create(path)
108+
if err != nil {
109+
log.Fatalf("unable to create file `%v`: %v", path, err)
110+
}
111+
if _, err := io.Copy(file, untarred); err != nil {
112+
log.Fatalf("unable to untar content of URL `%v`: %v", url, err)
113+
}
114+
file.Close()
115+
}
116+
117+
if err := os.Chmod(binaryPath, 0755); err != nil {
118+
log.Fatalf("unable to make file `%v` executable: %v", binaryPath, err)
119+
}
120+
}
121+
122+
// If we just downloaded a new version, remember which one so we don't
123+
// download it redundantly next time.
124+
if version != release {
125+
if err := os.WriteFile(versionPath, []byte(release), 0600); err != nil {
126+
log.Fatalf("unable to write version to `%v`: %v", versionPath, err)
127+
}
128+
}
129+
}
130+
131+
// Run the specified binary, forwarding all our arguments to it and piping its
132+
// stdout and stderr back to the user.
133+
func run(binaryPath string) {
134+
command := exec.Command(binaryPath, os.Args[1:]...)
135+
command.Stdout = os.Stdout
136+
command.Stderr = os.Stderr
137+
138+
if err := command.Start(); err != nil {
139+
log.Fatalf("unable to start `%v` command: %v", binaryPath, err)
140+
}
141+
142+
if err := command.Wait(); err != nil {
143+
if exiterr, ok := err.(*exec.ExitError); ok {
144+
code := exiterr.ExitCode()
145+
if code != 0 {
146+
os.Exit(code)
147+
}
148+
} else {
149+
log.Fatalf("trouble running `%v` command: %v", binaryPath, err)
150+
}
151+
}
152+
}

src/command.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ pub struct WitOpts {
5050
#[arg(long, short = 'w')]
5151
pub world: Vec<String>,
5252

53+
/// If `true`, skip scanning the current Go module's dependencies for
54+
/// `componentize-go.toml` files.
5355
#[arg(long)]
5456
pub ignore_toml_files: bool,
5557

0 commit comments

Comments
 (0)