package main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
const qkModulePath = "github.com/marzeq/qk"
func resolveLibraryRoot() (string, error) {
if override := os.Getenv("QK_LIB_DIR"); override != "" {
return requireLibraryRoot(override)
}
executable, err := os.Executable()
if err != nil {
return "", err
}
cwd, err := os.Getwd()
if err != nil {
return "", err
}
return resolveLibraryRootFor(executable, cwd, os.TempDir())
}
func resolveLibraryRootFor(executable, cwd, temporaryRoot string) (string, error) {
if runningViaGoRun(executable, temporaryRoot) {
if root := findQKCheckout(cwd); root != "" {
return filepath.Join(root, "libs"), nil
}
return "", fmt.Errorf("qkc is running through go run but no QK checkout containing libs was found above %s", cwd)
}
executables := []string{executable}
if resolved, err := filepath.EvalSymlinks(executable); err == nil && resolved != executable {
executables = append(executables, resolved)
}
for _, installedExecutable := range executables {
executableDir := filepath.Dir(installedExecutable)
for _, candidate := range []string{
filepath.Join(executableDir, "..", "libs"),
filepath.Join(executableDir, "libs"),
} {
if info, err := os.Stat(candidate); err == nil && info.IsDir() {
return filepath.Abs(candidate)
}
}
}
return "", fmt.Errorf("QK libraries not found beside %s; expected ../libs relative to the installed bin directory or set QK_LIB_DIR", executable)
}
func runningViaGoRun(executable, temporaryRoot string) bool {
relative, err := filepath.Rel(temporaryRoot, executable)
if err != nil || relative == "." || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) {
return false
}
parts := strings.Split(filepath.Clean(relative), string(filepath.Separator))
for _, part := range parts {
if strings.HasPrefix(part, "go-build") {
return true
}
}
return false
}
func findQKCheckout(start string) string {
current, err := filepath.Abs(start)
if err != nil {
return ""
}
for {
moduleFile := filepath.Join(current, "go.mod")
if data, readErr := os.ReadFile(moduleFile); readErr == nil && strings.Contains(string(data), "module "+qkModulePath) {
if info, statErr := os.Stat(filepath.Join(current, "libs")); statErr == nil && info.IsDir() {
return current
}
}
parent := filepath.Dir(current)
if parent == current {
return ""
}
current = parent
}
}
func requireLibraryRoot(value string) (string, error) {
root, err := filepath.Abs(value)
if err != nil {
return "", err
}
info, err := os.Stat(root)
if err != nil {
return "", fmt.Errorf("QK library root %s is unavailable: %w", root, err)
}
if !info.IsDir() {
return "", fmt.Errorf("QK library root %s is not a directory", root)
}
return root, nil
}