This commit is contained in:
184
packages/go/v5/cmd/download_ffi/main.go
generated
Normal file
184
packages/go/v5/cmd/download_ffi/main.go
generated
Normal file
@@ -0,0 +1,184 @@
|
||||
// Tool to download platform-specific FFI libraries from GitHub releases.
|
||||
// Invoked via `go generate` before compilation.
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
moduleVersion = "5.0.0-rc.3"
|
||||
repoURL = "https://github.com/kreuzberg-dev/kreuzberg"
|
||||
assetPrefix = "kreuzberg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
// Determine cache and library paths
|
||||
cacheBase, libPath, err := determinePaths()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if library already exists
|
||||
if _, err := os.Stat(libPath); err == nil {
|
||||
// Library already cached, nothing to do
|
||||
return nil
|
||||
}
|
||||
|
||||
// Download the FFI library tarball
|
||||
if err := downloadAndExtractLibrary(cacheBase); err != nil {
|
||||
return fmt.Errorf("failed to download FFI library: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func determinePaths() (string, string, error) {
|
||||
goos := runtime.GOOS
|
||||
goarch := runtime.GOARCH
|
||||
|
||||
// Map Go platform names to asset names
|
||||
osName := goos
|
||||
if goos == "darwin" {
|
||||
osName = "macos"
|
||||
}
|
||||
|
||||
libName := "kreuzberg_ffi"
|
||||
// Use the current working directory as the module root (where this script is run from)
|
||||
// and place libraries in .lib/{os}-{arch}/
|
||||
moduleRoot, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("cannot determine module root: %w", err)
|
||||
}
|
||||
|
||||
libDir := filepath.Join(moduleRoot, ".lib", fmt.Sprintf("%s-%s", osName, goarch))
|
||||
libPath := filepath.Join(libDir, libFilename(libName, goos))
|
||||
|
||||
return libDir, libPath, nil
|
||||
}
|
||||
|
||||
func libFilename(libName, goos string) string {
|
||||
switch goos {
|
||||
case "windows":
|
||||
return libName + ".dll"
|
||||
case "darwin":
|
||||
return "lib" + libName + ".dylib"
|
||||
default:
|
||||
return "lib" + libName + ".so"
|
||||
}
|
||||
}
|
||||
|
||||
func downloadAndExtractLibrary(cacheDir string) error {
|
||||
goos := runtime.GOOS
|
||||
goarch := runtime.GOARCH
|
||||
|
||||
osName := goos
|
||||
if goos == "darwin" {
|
||||
osName = "macos"
|
||||
}
|
||||
|
||||
// Map Go arch names to the alef platform names used in release asset filenames.
|
||||
// The local .lib/<os>-<goarch>/ directories use Go arch names (matching cgo LDFLAGS),
|
||||
// but alef's packager emits tarballs with its own arch names: x86_64, aarch64.
|
||||
archName := goarch
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
archName = "x86_64"
|
||||
case "arm64":
|
||||
// macOS arm64 stays "arm64" (alef go_java_platform special-cases it);
|
||||
// all other platforms use "aarch64".
|
||||
if goos != "darwin" {
|
||||
archName = "aarch64"
|
||||
}
|
||||
}
|
||||
|
||||
// Clean version for asset name
|
||||
version := strings.TrimPrefix(moduleVersion, "v")
|
||||
assetName := fmt.Sprintf("%s-go-v%s-%s-%s.tar.gz", assetPrefix, version, osName, archName)
|
||||
downloadURL := fmt.Sprintf("%s/releases/download/v%s/%s", repoURL, version, assetName)
|
||||
|
||||
// Create cache directory
|
||||
if err := os.MkdirAll(cacheDir, 0755); err != nil {
|
||||
return fmt.Errorf("mkdir cache: %w", err)
|
||||
}
|
||||
|
||||
// Download tarball
|
||||
resp, err := http.Get(downloadURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download %s: %w", downloadURL, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
// Extract tarball to cache directory
|
||||
if err := extractTarGz(resp.Body, cacheDir); err != nil {
|
||||
return fmt.Errorf("extract tarball: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractTarGz(src io.Reader, dstDir string) error {
|
||||
gzr, err := gzip.NewReader(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gzr.Close()
|
||||
|
||||
tr := tar.NewReader(gzr)
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Construct destination path, stripping any leading directory
|
||||
// from the tarball (e.g., "staging/lib..." -> "lib...")
|
||||
targetPath := filepath.Join(dstDir, filepath.Base(header.Name))
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeReg:
|
||||
f, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(f, tr); err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
|
||||
case tar.TypeDir:
|
||||
if err := os.MkdirAll(targetPath, os.FileMode(header.Mode)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user