From 894b2ea0ae5153e3809922723cb72969aaf6ceec Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 6 Nov 2025 03:14:32 +0900 Subject: [PATCH 1/3] pkg/usrlocalsharelima: fix a typo (preserve -> reserve) Signed-off-by: Akihiro Suda --- pkg/usrlocalsharelima/usrlocalsharelima.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/usrlocalsharelima/usrlocalsharelima.go b/pkg/usrlocalsharelima/usrlocalsharelima.go index a2d27c7ec18..a7545b9a9c4 100644 --- a/pkg/usrlocalsharelima/usrlocalsharelima.go +++ b/pkg/usrlocalsharelima/usrlocalsharelima.go @@ -194,7 +194,7 @@ func LibexecLima() ([]string, error) { // candidate: /opt/homebrew/lib/lima // // Note that there is no /opt/homebrew/libexec directory, - // as Homebrew preserves libexec for private use. + // as Homebrew reserves libexec for private use. // https://github.com/lima-vm/lima/issues/4295#issuecomment-3490680651 candidate = filepath.Join(prefix, "lib", "lima") if ents, err := os.ReadDir(candidate); err == nil && len(ents) > 0 { From 74ca9608d249be8fa999399cc917a8198b731ac9 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 5 Nov 2025 20:48:14 +0900 Subject: [PATCH 2/3] pkg/usrlocalsharelima: simplify and allow multiple paths Old: usrlocalsharelima.Dir() (string, error) New: usrlocal.ShareLima() ([]string, error) Signed-off-by: Akihiro Suda --- cmd/limactl/guest-install.go | 4 +- pkg/instance/start.go | 4 +- pkg/limainfo/limainfo.go | 4 +- pkg/plugins/plugins.go | 6 +- pkg/registry/registry.go | 4 +- pkg/templatestore/templatestore.go | 13 +- .../usrlocal.go} | 114 +++++++++--------- 7 files changed, 77 insertions(+), 72 deletions(-) rename pkg/{usrlocalsharelima/usrlocalsharelima.go => usrlocal/usrlocal.go} (65%) diff --git a/cmd/limactl/guest-install.go b/cmd/limactl/guest-install.go index 87ee095225c..49c5a7b82cf 100644 --- a/cmd/limactl/guest-install.go +++ b/cmd/limactl/guest-install.go @@ -21,7 +21,7 @@ import ( "github.com/lima-vm/lima/v2/pkg/limatype" "github.com/lima-vm/lima/v2/pkg/limatype/filenames" "github.com/lima-vm/lima/v2/pkg/store" - "github.com/lima-vm/lima/v2/pkg/usrlocalsharelima" + "github.com/lima-vm/lima/v2/pkg/usrlocal" ) func newGuestInstallCommand() *cobra.Command { @@ -81,7 +81,7 @@ func guestInstallAction(cmd *cobra.Command, args []string) error { prefix := *inst.Config.GuestInstallPrefix // lima-guestagent - guestAgentBinary, err := usrlocalsharelima.GuestAgentBinary(*inst.Config.OS, *inst.Config.Arch) + guestAgentBinary, err := usrlocal.GuestAgentBinary(*inst.Config.OS, *inst.Config.Arch) if err != nil { return err } diff --git a/pkg/instance/start.go b/pkg/instance/start.go index d837c633f3c..8e5c3806f81 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -31,7 +31,7 @@ import ( "github.com/lima-vm/lima/v2/pkg/limatype/filenames" "github.com/lima-vm/lima/v2/pkg/registry" "github.com/lima-vm/lima/v2/pkg/store" - "github.com/lima-vm/lima/v2/pkg/usrlocalsharelima" + "github.com/lima-vm/lima/v2/pkg/usrlocal" ) // DefaultWatchHostAgentEventsTimeout is the duration to wait for the instance @@ -48,7 +48,7 @@ type Prepared struct { func Prepare(ctx context.Context, inst *limatype.Instance, guestAgent string) (*Prepared, error) { if !*inst.Config.Plain && guestAgent == "" { var err error - guestAgent, err = usrlocalsharelima.GuestAgentBinary(*inst.Config.OS, *inst.Config.Arch) + guestAgent, err = usrlocal.GuestAgentBinary(*inst.Config.OS, *inst.Config.Arch) if err != nil { return nil, err } diff --git a/pkg/limainfo/limainfo.go b/pkg/limainfo/limainfo.go index bfeb2ad2258..95b1f916d92 100644 --- a/pkg/limainfo/limainfo.go +++ b/pkg/limainfo/limainfo.go @@ -20,7 +20,7 @@ import ( "github.com/lima-vm/lima/v2/pkg/plugins" "github.com/lima-vm/lima/v2/pkg/registry" "github.com/lima-vm/lima/v2/pkg/templatestore" - "github.com/lima-vm/lima/v2/pkg/usrlocalsharelima" + "github.com/lima-vm/lima/v2/pkg/usrlocal" "github.com/lima-vm/lima/v2/pkg/version" ) @@ -97,7 +97,7 @@ func New(ctx context.Context) (*LimaInfo, error) { } info.IdentityFile = filepath.Join(configDir, filenames.UserPrivateKey) for _, arch := range limatype.ArchTypes { - bin, err := usrlocalsharelima.GuestAgentBinary(limatype.LINUX, arch) + bin, err := usrlocal.GuestAgentBinary(limatype.LINUX, arch) if err != nil { if errors.Is(err, fs.ErrNotExist) { logrus.WithError(err).Debugf("Failed to resolve the guest agent binary for %q", arch) diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index 010f457371b..20d17f747e6 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -18,7 +18,7 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/v2/pkg/osutil" - "github.com/lima-vm/lima/v2/pkg/usrlocalsharelima" + "github.com/lima-vm/lima/v2/pkg/usrlocal" ) const defaultPathExt = ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL" @@ -51,7 +51,7 @@ var Discover = sync.OnceValues(func() ([]Plugin, error) { }) var getPluginDirectories = sync.OnceValue(func() []string { - dirs := usrlocalsharelima.SelfDirs() + dirs := usrlocal.SelfDirs() pathEnv := os.Getenv("PATH") if pathEnv != "" { @@ -59,7 +59,7 @@ var getPluginDirectories = sync.OnceValue(func() []string { dirs = append(dirs, pathDirs...) } - libexecDirs, err := usrlocalsharelima.LibexecLima() + libexecDirs, err := usrlocal.LibexecLima() if err == nil { dirs = append(dirs, libexecDirs...) } diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index 9658d8eaa09..8786361f6c6 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -16,7 +16,7 @@ import ( "github.com/lima-vm/lima/v2/pkg/driver" "github.com/lima-vm/lima/v2/pkg/driver/external/client" - "github.com/lima-vm/lima/v2/pkg/usrlocalsharelima" + "github.com/lima-vm/lima/v2/pkg/usrlocal" ) const ( @@ -124,7 +124,7 @@ func discoverDrivers() error { } } - stdDriverDirs, err := usrlocalsharelima.LibexecLima() + stdDriverDirs, err := usrlocal.LibexecLima() if err != nil { return err } diff --git a/pkg/templatestore/templatestore.go b/pkg/templatestore/templatestore.go index 9591a59aa04..466f618f7b9 100644 --- a/pkg/templatestore/templatestore.go +++ b/pkg/templatestore/templatestore.go @@ -15,7 +15,7 @@ import ( "unicode" "github.com/lima-vm/lima/v2/pkg/limatype/dirnames" - "github.com/lima-vm/lima/v2/pkg/usrlocalsharelima" + "github.com/lima-vm/lima/v2/pkg/usrlocal" ) type Template struct { @@ -31,14 +31,15 @@ func templatesPaths() ([]string, error) { if err != nil { return nil, err } - shareDir, err := usrlocalsharelima.Dir() + shareLimaDirs, err := usrlocal.ShareLima() if err != nil { return nil, err } - return []string{ - limaTemplatesDir, - filepath.Join(shareDir, "templates"), - }, nil + res := []string{limaTemplatesDir} + for _, shareLimaDir := range shareLimaDirs { + res = append(res, filepath.Join(shareLimaDir, "templates")) + } + return res, nil } // Read searches for template `name` in all template directories and returns the diff --git a/pkg/usrlocalsharelima/usrlocalsharelima.go b/pkg/usrlocal/usrlocal.go similarity index 65% rename from pkg/usrlocalsharelima/usrlocalsharelima.go rename to pkg/usrlocal/usrlocal.go index a7545b9a9c4..4eb5f47fbee 100644 --- a/pkg/usrlocalsharelima/usrlocalsharelima.go +++ b/pkg/usrlocal/usrlocal.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright The Lima Authors // SPDX-License-Identifier: Apache-2.0 -package usrlocalsharelima +package usrlocal import ( "errors" @@ -10,7 +10,7 @@ import ( "os" "os/exec" "path/filepath" - "runtime" + "strings" "sync" "github.com/sirupsen/logrus" @@ -73,61 +73,57 @@ func SelfDirs() []string { return selfPaths } -// Dir returns the location of the /lima/share directory, relative to the location -// of the current executable. It checks for multiple possible filesystem layouts and returns -// the first candidate that contains the native guest agent binary. -func Dir() (string, error) { - selfDirs := SelfDirs() +func delveDebugExe() string { + exe, err := os.Executable() + if err != nil { + return "" + } + exeBase := filepath.Base(exe) + if strings.HasPrefix(exeBase, "__debug_bin") { + return exe + } + return "" +} - ostype := limatype.NewOS("linux") - arch := limatype.NewArch(runtime.GOARCH) - if arch == "" { - return "", fmt.Errorf("failed to get arch for %q", runtime.GOARCH) +func delveWorkspace() string { + self := delveDebugExe() + if self == "" { + return "" } + // https://github.com/lima-vm/lima/pull/2651/commits/644c11373cb79aaebd8520706f7d51bd3ee5fbe4 + // launched by `~/go/bin/dlv dap` + // - self: ${workspaceFolder}/cmd/limactl/__debug_bin_XXXXXX + return filepath.Dir(filepath.Dir(filepath.Dir(self))) +} - gaCandidates := []string{} +// ShareLima returns the /share/lima directories. +func ShareLima() ([]string, error) { + var candidates []string + selfDirs := SelfDirs() for _, selfDir := range selfDirs { // selfDir: /usr/local/bin - selfDirDir := filepath.Dir(selfDir) - gaCandidates = append(gaCandidates, - // candidate 0: - // - self: /Applications/Lima.app/Contents/MacOS/limactl - // - agent: /Applications/Lima.app/Contents/MacOS/lima-guestagent.Linux-x86_64 - // - dir: /Applications/Lima.app/Contents/MacOS - filepath.Join(selfDir, "lima-guestagent."+ostype+"-"+arch), - // candidate 1: - // - self: /usr/local/bin/limactl - // - agent: /usr/local/share/lima/lima-guestagent.Linux-x86_64 - // - dir: /usr/local/share/lima - filepath.Join(selfDirDir, "share/lima/lima-guestagent."+ostype+"-"+arch), - // TODO: support custom path - ) - if debugutil.Debug { - // candidate 2: launched by `~/go/bin/dlv dap` + // prefix: /usr/local + // candidate: /usr/local/share/lima + prefix := filepath.Dir(selfDir) + candidate := filepath.Join(prefix, "share", "lima") + if ents, err := os.ReadDir(candidate); err == nil && len(ents) > 0 { + candidates = append(candidates, candidate) + } + } + if debugutil.Debug { + if workspace := delveWorkspace(); workspace != "" { + // https://github.com/lima-vm/lima/pull/2651/commits/644c11373cb79aaebd8520706f7d51bd3ee5fbe4 + // launched by `~/go/bin/dlv dap` // - self: ${workspaceFolder}/cmd/limactl/__debug_bin_XXXXXX // - agent: ${workspaceFolder}/_output/share/lima/lima-guestagent.Linux-x86_64 // - dir: ${workspaceFolder}/_output/share/lima - candidateForDebugBuild := filepath.Join(filepath.Dir(selfDirDir), "_output/share/lima/lima-guestagent."+ostype+"-"+arch) - gaCandidates = append(gaCandidates, candidateForDebugBuild) - logrus.Infof("debug mode detected, adding more guest agent candidates: %v", candidateForDebugBuild) - } - } - - for _, gaCandidate := range gaCandidates { - if _, err := os.Stat(gaCandidate); err == nil { - return filepath.Dir(gaCandidate), nil - } else if !errors.Is(err, os.ErrNotExist) { - return "", err - } - if _, err := os.Stat(gaCandidate + ".gz"); err == nil { - return filepath.Dir(gaCandidate), nil - } else if !errors.Is(err, os.ErrNotExist) { - return "", err + candidate := filepath.Join(workspace, "_output", "share", "lima") + if ents, err := os.ReadDir(candidate); err == nil && len(ents) > 0 { + candidates = append(candidates, candidate) + } } } - - return "", fmt.Errorf("failed to find \"lima-guestagent.%s-%s\" binary for %v, attempted %v", - ostype, arch, selfDirs, gaCandidates) + return candidates, nil } // GuestAgentBinary returns the absolute path of the guest agent binary, possibly with ".gz" suffix. @@ -138,18 +134,26 @@ func GuestAgentBinary(ostype limatype.OS, arch limatype.Arch) (string, error) { if arch == "" { return "", errors.New("arch must be set") } - dir, err := Dir() + shareLimaDirs, err := ShareLima() if err != nil { return "", err } - uncomp := filepath.Join(dir, "lima-guestagent."+ostype+"-"+arch) - comp := uncomp + ".gz" - res, err := chooseGABinary([]string{comp, uncomp}) - if err != nil { - logrus.Debug(err) - return "", fmt.Errorf("guest agent binary could not be found for %s-%s: %w (Hint: try installing `lima-additional-guestagents` package)", ostype, arch, err) + for _, dir := range shareLimaDirs { + uncomp := filepath.Join(dir, "lima-guestagent."+ostype+"-"+arch) + comp := uncomp + ".gz" + var res string + res, err = chooseGABinary([]string{comp, uncomp}) + if err != nil { + logrus.Debug(err) + continue + } + return res, nil + } + if err == nil { + // caller expects err to be comparable to fs.ErrNotExist + err = fs.ErrNotExist } - return res, nil + return "", fmt.Errorf("guest agent binary could not be found for %s-%s: %w (Hint: try installing `lima-additional-guestagents` package)", ostype, arch, err) } func chooseGABinary(candidates []string) (string, error) { From 608928dc8fc62230b08f237f9d7212a2e2817ba5 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 6 Nov 2025 03:21:50 +0900 Subject: [PATCH 3/3] pkg/usrlocal.LibexecLima: add delve path Signed-off-by: Akihiro Suda --- pkg/usrlocal/usrlocal.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/usrlocal/usrlocal.go b/pkg/usrlocal/usrlocal.go index 4eb5f47fbee..fc11f7336f5 100644 --- a/pkg/usrlocal/usrlocal.go +++ b/pkg/usrlocal/usrlocal.go @@ -205,5 +205,13 @@ func LibexecLima() ([]string, error) { candidates = append(candidates, candidate) } } + if debugutil.Debug { + if workspace := delveWorkspace(); workspace != "" { + candidate := filepath.Join(workspace, "_output", "libexec", "lima") + if ents, err := os.ReadDir(candidate); err == nil && len(ents) > 0 { + candidates = append(candidates, candidate) + } + } + } return candidates, nil }