Skip to content

Commit 9a2a1e9

Browse files
fix(go): vendor bindings before native install
1 parent e6fc81f commit 9a2a1e9

4 files changed

Lines changed: 55 additions & 40 deletions

File tree

sdks/go/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ go run github.com/usemoss/moss/sdks/go/tools/install@latest
1515
The install tool downloads the static `libmoss` library for your platform from
1616
[Moss C SDK GitHub Releases](https://github.com/usemoss/moss/releases). You need
1717
CGO and a C compiler, but not a manual C SDK download or `LD_LIBRARY_PATH`.
18+
For external projects, the installer vendors the bindings before placing the
19+
library next to them, so the normal `go build` command finds it.
1820

19-
Alternatively, from a checkout of the bindings package:
21+
From a checkout of the bindings package, you can instead use:
2022

2123
```bash
2224
go generate github.com/usemoss/moss/sdks/go/bindings

sdks/go/bindings/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ go run github.com/usemoss/moss/sdks/go/tools/install@latest
1818

1919
The install tool downloads a prebuilt static library for your platform from
2020
[C SDK GitHub Releases](https://github.com/usemoss/moss/releases). No manual C
21-
SDK download, `LD_LIBRARY_PATH`, or `-tags libmoss` is required.
21+
SDK download, `LD_LIBRARY_PATH`, or `-tags libmoss` is required. For a
22+
downloaded module, it runs `go mod vendor` and installs the library beside the
23+
vendored bindings; subsequent `go build` commands use that copy automatically.
2224

2325
Requirements:
2426

@@ -32,7 +34,7 @@ bindings/
3234
include/libmoss.h # committed C header
3335
libmoss.go # CGO wrapper (requires CGO)
3436
prebuilt_<os>_<arch>.go # per-platform CGO linker flags
35-
generate.go # //go:generate install hook
37+
generate.go # //go:generate install hook for checkouts
3638
lib/
3739
linux-amd64/ # libmoss.a (gitignored, downloaded at build time)
3840
linux-arm64/
@@ -58,7 +60,7 @@ Or fetch all supported platforms:
5860
./sdks/go/scripts/fetch-static-libs.sh c-sdk-v0.9.0
5961
```
6062

61-
Or use `go generate` from this directory:
63+
Or use `go generate` from a checkout of this directory:
6264

6365
```bash
6466
cd sdks/go/bindings
@@ -79,7 +81,7 @@ source-only module tags (no binaries in git):
7981
- `sdks/go/sdk/v0.1.0`
8082
- `sdks/go/bindings/v0.1.0`
8183

82-
Consumers download native libraries at build time via `tools/install`.
84+
Consumers download native libraries during the explicit `tools/install` step.
8385

8486
## Build without CGO
8587

sdks/go/sdk/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ go run github.com/usemoss/moss/sdks/go/tools/install@latest
3232

3333
The install tool downloads the static `libmoss` library for your platform. See
3434
[`../bindings/README.md`](../bindings/README.md) for Windows toolchain notes.
35+
It vendors the SDK so CGO can link the downloaded library from a writable
36+
directory; commit `vendor/` if your project commits vendored dependencies.
3537

3638
Monorepo development uses the workspace in [`../go.work`](../go.work) and
3739
[`../scripts/link_dev_lib.sh`](../scripts/link_dev_lib.sh).

sdks/go/tools/install/main.go

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func main() {
6161
force := flag.Bool("force", false, "re-download even if the library is already installed")
6262
flag.Parse()
6363

64-
root, err := resolveBindingsDir(*bindingsDir)
64+
root, err := resolveWritableBindingsDir(*bindingsDir)
6565
if err != nil {
6666
fatal(err)
6767
}
@@ -81,24 +81,47 @@ func main() {
8181
}
8282

8383
for _, p := range targets {
84-
libDir, installHeader, err := resolveLibDir(root, version, p.id)
85-
if err != nil {
86-
fatal(err)
87-
}
88-
if err := installPlatform(root, libDir, installHeader, baseURL, version, p, checksums, *force); err != nil {
84+
libDir := filepath.Join(root, "lib", p.id)
85+
if err := installPlatform(root, libDir, true, baseURL, version, p, checksums, *force); err != nil {
8986
fatal(fmt.Errorf("%s: %w", p.id, err))
9087
}
91-
if libDir != filepath.Join(root, "lib", p.id) {
92-
printLinkerHint(p, libDir)
93-
}
9488
}
9589

9690
fmt.Printf("Installed Moss C SDK %s\n", *releaseTag)
91+
fmt.Printf("Native libraries installed under %s\n", root)
92+
}
93+
94+
// resolveWritableBindingsDir returns the bindings package CGO will compile.
95+
// Downloaded Go modules are read-only, so an external consumer is vendored
96+
// before installing the native library beside the bindings source.
97+
func resolveWritableBindingsDir(explicit string) (string, error) {
98+
root, err := resolveBindingsDir(explicit)
99+
if err != nil {
100+
return "", err
101+
}
102+
if isWritableDir(root) {
103+
return root, nil
104+
}
105+
if explicit != "" || strings.TrimSpace(os.Getenv("MOSS_BINDINGS_DIR")) != "" {
106+
return "", fmt.Errorf("bindings directory %s is not writable", root)
107+
}
108+
109+
goMod, err := goEnv("GOMOD")
110+
if err != nil || goMod == os.DevNull || goMod == "" {
111+
return "", fmt.Errorf("the downloaded bindings module is read-only; run this command from a Go module that imports the Moss SDK")
112+
}
113+
if out, err := exec.Command("go", "mod", "vendor").CombinedOutput(); err != nil {
114+
return "", fmt.Errorf("vendor Moss bindings for native installation: %w\n%s", err, strings.TrimSpace(string(out)))
115+
}
116+
117+
root, err = bindingsDirFromGoList("-mod=vendor")
118+
if err != nil {
119+
return "", fmt.Errorf("locate vendored Moss bindings after `go mod vendor`: %w", err)
120+
}
97121
if !isWritableDir(root) {
98-
fmt.Println("Build with -mod=vendor after `go mod vendor`, or export the CGO_LDFLAGS shown above.")
99-
} else {
100-
fmt.Printf("Native libraries installed under %s\n", root)
122+
return "", fmt.Errorf("vendored bindings directory %s is not writable", root)
101123
}
124+
return root, nil
102125
}
103126

104127
func resolveBindingsDir(explicit string) (string, error) {
@@ -118,8 +141,10 @@ func resolveBindingsDir(explicit string) (string, error) {
118141
return filepath.Abs(filepath.Join(filepath.Dir(file), "..", "..", "bindings"))
119142
}
120143

121-
func bindingsDirFromGoList() (string, error) {
122-
cmd := exec.Command("go", "list", "-f", "{{.Dir}}", bindingsModulePath)
144+
func bindingsDirFromGoList(args ...string) (string, error) {
145+
args = append([]string{"list"}, args...)
146+
args = append(args, "-f", "{{.Dir}}", bindingsModulePath)
147+
cmd := exec.Command("go", args...)
123148
out, err := cmd.Output()
124149
if err != nil {
125150
return "", err
@@ -131,19 +156,6 @@ func bindingsDirFromGoList() (string, error) {
131156
return filepath.Abs(dir)
132157
}
133158

134-
func resolveLibDir(bindingsRoot, version, platformID string) (string, bool, error) {
135-
inBindings := filepath.Join(bindingsRoot, "lib", platformID)
136-
if isWritableDir(bindingsRoot) {
137-
return inBindings, true, nil
138-
}
139-
140-
cacheRoot, err := os.UserCacheDir()
141-
if err != nil {
142-
return "", false, err
143-
}
144-
return filepath.Join(cacheRoot, "moss-go", version, platformID), false, nil
145-
}
146-
147159
func isWritableDir(dir string) bool {
148160
if dir == "" {
149161
return false
@@ -156,15 +168,12 @@ func isWritableDir(dir string) bool {
156168
return true
157169
}
158170

159-
func printLinkerHint(p platform, libDir string) {
160-
switch p.id {
161-
case "darwin-arm64":
162-
fmt.Printf("export CGO_LDFLAGS=\"-L%s -lmoss -lc++ -framework Security -framework SystemConfiguration\"\n", libDir)
163-
case "windows-amd64":
164-
fmt.Printf("set CGO_LDFLAGS=-L%s %s\n", libDir, p.libFile)
165-
default:
166-
fmt.Printf("export CGO_LDFLAGS=\"-L%s -lmoss -lstdc++ -ldl -lm -lpthread\"\n", libDir)
171+
func goEnv(name string) (string, error) {
172+
out, err := exec.Command("go", "env", name).Output()
173+
if err != nil {
174+
return "", err
167175
}
176+
return strings.TrimSpace(string(out)), nil
168177
}
169178

170179
func selectPlatforms(all bool) ([]platform, error) {

0 commit comments

Comments
 (0)