mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-07-21 11:25:00 +00:00
133 lines
3.9 KiB
Go
133 lines
3.9 KiB
Go
// 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
|
|
}
|