mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 18:18:03 +00:00
embeds and extracts platform-specific libraries at runtime using Go's embed package
This commit is contained in:
parent
322c2859e6
commit
2476d2c6c2
1 changed files with 133 additions and 0 deletions
133
bindings/go/embedded.go
Normal file
133
bindings/go/embedded.go
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Go bindings for the Limbo database.
|
||||
//
|
||||
// This file implements library embedding and extraction at runtime, a pattern
|
||||
// also used in several other Go projects that need to distribute native binaries:
|
||||
//
|
||||
// - github.com/kluctl/go-embed-python: Embeds a full Python distribution in Go
|
||||
// binaries, extracting to temporary directories at runtime. The approach used here
|
||||
// was directly inspired by its embed_util implementation.
|
||||
//
|
||||
// - github.com/kluctl/go-jinja2: Uses the same pattern to embed Jinja2 and related
|
||||
// Python libraries, allowing Go applications to use Jinja2 templates without
|
||||
// external dependencies.
|
||||
//
|
||||
// This approach has several advantages:
|
||||
// - Allows distribution of a single, self-contained binary
|
||||
// - Eliminates the need for users to set LD_LIBRARY_PATH or other environment variables
|
||||
// - Works cross-platform with the same codebase
|
||||
// - Preserves backward compatibility with existing methods
|
||||
// - Extracts libraries only once per execution via sync.Once
|
||||
//
|
||||
// The embedded library is extracted to a user-specific temporary directory and
|
||||
// loaded dynamically. If extraction fails, the code falls back to the traditional
|
||||
// method of searching system paths.
|
||||
package limbo
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:embed libs/*
|
||||
var embeddedLibs embed.FS
|
||||
|
||||
var (
|
||||
extractOnce sync.Once
|
||||
extractedPath string
|
||||
extractErr error
|
||||
)
|
||||
|
||||
// extractEmbeddedLibrary extracts the library for the current platform
|
||||
// to a temporary directory and returns the path to the extracted library
|
||||
func extractEmbeddedLibrary() (string, error) {
|
||||
extractOnce.Do(func() {
|
||||
// Determine platform-specific details
|
||||
var libName string
|
||||
var platformDir string
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
libName = "lib_limbo_go.dylib"
|
||||
case "linux":
|
||||
libName = "lib_limbo_go.so"
|
||||
case "windows":
|
||||
libName = "lib_limbo_go.dll"
|
||||
default:
|
||||
extractErr = fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
return
|
||||
}
|
||||
|
||||
// Determine architecture suffix
|
||||
var archSuffix string
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
archSuffix = "amd64"
|
||||
case "arm64":
|
||||
archSuffix = "arm64"
|
||||
case "386":
|
||||
archSuffix = "386"
|
||||
default:
|
||||
extractErr = fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
|
||||
return
|
||||
}
|
||||
|
||||
// Create platform directory string
|
||||
platformDir = fmt.Sprintf("%s_%s", runtime.GOOS, archSuffix)
|
||||
|
||||
// Create a unique temporary directory for the current user
|
||||
tempDir := filepath.Join(os.TempDir(), fmt.Sprintf("limbo-go-%d", os.Getuid()))
|
||||
if err := os.MkdirAll(tempDir, 0755); err != nil {
|
||||
extractErr = fmt.Errorf("failed to create temp directory: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Path to the library within the embedded filesystem
|
||||
libPath := filepath.Join("libs", platformDir, libName)
|
||||
|
||||
// Where the library will be extracted
|
||||
extractedPath = filepath.Join(tempDir, libName)
|
||||
|
||||
// Check if library already exists and is valid
|
||||
if stat, err := os.Stat(extractedPath); err == nil && stat.Size() > 0 {
|
||||
// Library already exists, nothing to do
|
||||
return
|
||||
}
|
||||
|
||||
// Open the embedded library
|
||||
embeddedLib, err := embeddedLibs.Open(libPath)
|
||||
if err != nil {
|
||||
extractErr = fmt.Errorf("failed to open embedded library %s: %w", libPath, err)
|
||||
return
|
||||
}
|
||||
defer embeddedLib.Close()
|
||||
|
||||
// Create the output file
|
||||
outFile, err := os.Create(extractedPath)
|
||||
if err != nil {
|
||||
extractErr = fmt.Errorf("failed to create output file: %w", err)
|
||||
return
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
// Copy the library to the temporary directory
|
||||
if _, err := io.Copy(outFile, embeddedLib); err != nil {
|
||||
extractErr = fmt.Errorf("failed to extract library: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// On Unix systems, make the library executable
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := os.Chmod(extractedPath, 0755); err != nil {
|
||||
extractErr = fmt.Errorf("failed to make library executable: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return extractedPath, extractErr
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue