mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-24 07:28:13 +02:00
v0.2.5 : build llama.cpp 100% automatique et propre sur Windows et Linux
Windows : `jean llamacpp install` part de zéro et provisionne toute la chaîne sans intervention : - install Windows ajoute jean au PATH (copie dans %JEAN_HOME%\bin) - auto-install des outils manquants via winget (cmake, git, ninja) + refresh du PATH du process depuis le registre - compilateur MSVC Build Tools auto-installé ; générateur Visual Studio (localise MSVC sans Developer Prompt) ; binaire multi-config (Release\) - détection GPU + auto-install du CUDA Toolkit quand une carte NVIDIA est vue sans nvcc ; CUDA_PATH/CUDA_PATH_Vx_y injectés pour MSBuild ; DLL runtime CUDA (bin + bin\x64) ajoutées au PATH de llama-server - console UTF-8 + VT pour un affichage correct Multi-OS : - auto-install des outils aussi sous Unix (apt/dnf/pacman/brew) - désactive l'UI web embarquée de llama-server (LLAMA_BUILD_UI=OFF + LLAMA_USE_PREBUILT_UI=OFF) : supprime la dépendance npm/HuggingFace qui cassait le build ; jean fournit sa propre UI - build CUDA allégé (sans FA_ALL_QUANTS) : bien plus rapide - sortie de build propre : configure/build passent par un runner qui écrit le détail dans un log fichier et n'affiche qu'un spinner animé + le compteur de fichiers (formats MSBuild et Make/Ninja) + les erreurs réelles ; tests unitaires du filtre Validé en exécution réelle : Windows (GTX 1650 Ti, sm_75) et serveur Linux multi-GPU (sm_75;120) jusqu'au serveur GPU opérationnel.
This commit is contained in:
1 parent
747032cbf6
commit
949e261538
7 files changed
+809
-27
No files matched your search
+122
-1
@@ -4,8 +4,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// On Windows there's no systemd unit, sudoers, or /usr/local/bin to populate.
|
||||
@@ -59,16 +62,128 @@ func cmdInstall(args []string) error {
|
||||
fmt.Printf(" %s config.env déjà présent — inchangé\n", dim("•"))
|
||||
}
|
||||
|
||||
// 3. Install the binary into JEAN_HOME\bin and put that dir on the user PATH,
|
||||
// so `jean` is callable from any shell (this is the Windows analogue of the
|
||||
// /usr/local/bin symlink the Unix installer creates).
|
||||
binDir := filepath.Join(jeanHome, "bin")
|
||||
if err := os.MkdirAll(binDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
onPath := false
|
||||
if dst, err := installSelf(binDir); err != nil {
|
||||
fmt.Printf(" %s copie du binaire impossible (%v) — ajoute-le au PATH à la main\n", dim("•"), err)
|
||||
} else {
|
||||
fmt.Printf(" %s binaire installé %s\n", green("✓"), dst)
|
||||
added, err := addToUserPath(binDir)
|
||||
switch {
|
||||
case err != nil:
|
||||
fmt.Printf(" %s mise à jour du PATH impossible (%v)\n", dim("•"), err)
|
||||
case added:
|
||||
fmt.Printf(" %s %s ajouté au PATH utilisateur\n", green("✓"), binDir)
|
||||
onPath = true
|
||||
default:
|
||||
fmt.Printf(" %s %s déjà dans le PATH\n", dim("•"), binDir)
|
||||
onPath = true
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s installation terminée.\n", green("[ok]"))
|
||||
fmt.Printf("\nProchaines étapes :\n")
|
||||
fmt.Printf(" 1. édite la config : %s (renseigne BIN, MODEL)\n", bold("jean edit"))
|
||||
fmt.Printf(" 2. démarre le service: %s\n", bold("jean start"))
|
||||
fmt.Printf(" 3. UI web : %s\n", bold("jean web"))
|
||||
fmt.Printf("\n%s pour exécuter 'jean' depuis n'importe où, ajoute son dossier au PATH.\n", dim("[info]"))
|
||||
if onPath {
|
||||
fmt.Printf("\n%s ouvre un NOUVEAU terminal pour que 'jean' soit reconnu (le PATH n'est lu qu'au démarrage du shell).\n", dim("[info]"))
|
||||
} else {
|
||||
fmt.Printf("\n%s pour exécuter 'jean' depuis n'importe où, ajoute son dossier au PATH.\n", dim("[info]"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// installSelf copies the currently running executable into binDir as jean.exe
|
||||
// and returns the destination path. If the running exe already lives there
|
||||
// (re-install), it's a no-op.
|
||||
func installSelf(binDir string) (string, error) {
|
||||
src, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
src, _ = filepath.EvalSymlinks(src)
|
||||
dst := filepath.Join(binDir, "jean.exe")
|
||||
if strings.EqualFold(src, dst) {
|
||||
return dst, nil
|
||||
}
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer in.Close()
|
||||
// Can't overwrite a running exe, but jean.exe in binDir isn't the one we're
|
||||
// running (src != dst here), so a plain create is fine.
|
||||
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := io.Copy(out, in); err != nil {
|
||||
out.Close()
|
||||
return "", err
|
||||
}
|
||||
if err := out.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// addToUserPath appends dir to the per-user PATH (HKCU\Environment) persistently,
|
||||
// without admin rights and without setx's 1024-char truncation. Returns false if
|
||||
// dir was already present. The change applies to newly launched shells.
|
||||
func addToUserPath(dir string) (bool, error) {
|
||||
// Read the *user* PATH (not the process PATH, which is User+Machine merged),
|
||||
// edit it, and write it back, all via PowerShell's environment API which
|
||||
// handles the registry REG_EXPAND_SZ type and the WM_SETTINGCHANGE broadcast.
|
||||
ps := fmt.Sprintf(`$d=%s
|
||||
$p=[Environment]::GetEnvironmentVariable('Path','User')
|
||||
if (-not $p) { $p='' }
|
||||
$parts=$p.Split(';') | Where-Object { $_ -ne '' }
|
||||
if ($parts -contains $d) { Write-Output 'present'; exit 0 }
|
||||
$new=(@($parts) + $d) -join ';'
|
||||
[Environment]::SetEnvironmentVariable('Path',$new,'User')
|
||||
Write-Output 'added'`, psQuote(dir))
|
||||
cmd := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command", ps)
|
||||
outBytes, err := cmd.CombinedOutput()
|
||||
out := strings.TrimSpace(string(outBytes))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%v: %s", err, out)
|
||||
}
|
||||
return strings.Contains(out, "added"), nil
|
||||
}
|
||||
|
||||
// removeFromUserPath drops dir from the per-user PATH if present. Returns false
|
||||
// if it wasn't there.
|
||||
func removeFromUserPath(dir string) (bool, error) {
|
||||
ps := fmt.Sprintf(`$d=%s
|
||||
$p=[Environment]::GetEnvironmentVariable('Path','User')
|
||||
if (-not $p) { Write-Output 'absent'; exit 0 }
|
||||
$parts=$p.Split(';') | Where-Object { $_ -ne '' -and $_ -ne $d }
|
||||
if (($p.Split(';') | Where-Object { $_ -eq $d }).Count -eq 0) { Write-Output 'absent'; exit 0 }
|
||||
[Environment]::SetEnvironmentVariable('Path',($parts -join ';'),'User')
|
||||
Write-Output 'removed'`, psQuote(dir))
|
||||
cmd := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command", ps)
|
||||
outBytes, err := cmd.CombinedOutput()
|
||||
out := strings.TrimSpace(string(outBytes))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%v: %s", err, out)
|
||||
}
|
||||
return strings.Contains(out, "removed"), nil
|
||||
}
|
||||
|
||||
// psQuote wraps s in a PowerShell single-quoted string literal (doubling any
|
||||
// embedded single quotes), safe against spaces and metacharacters in the path.
|
||||
func psQuote(s string) string {
|
||||
return "'" + strings.ReplaceAll(s, "'", "''") + "'"
|
||||
}
|
||||
|
||||
func cmdUninstall(args []string) error {
|
||||
keepData := true
|
||||
for _, a := range args {
|
||||
@@ -82,6 +197,12 @@ func cmdUninstall(args []string) error {
|
||||
// Stop the background server if it's running.
|
||||
_ = svcStop(false)
|
||||
|
||||
// Pull JEAN_HOME\bin off the user PATH (best-effort).
|
||||
binDir := filepath.Join(JeanHome(), "bin")
|
||||
if removed, err := removeFromUserPath(binDir); err == nil && removed {
|
||||
fmt.Printf(" %s %s retiré du PATH utilisateur\n", green("✓"), binDir)
|
||||
}
|
||||
|
||||
if !keepData {
|
||||
jeanHome := JeanHome()
|
||||
if err := os.RemoveAll(jeanHome); err != nil {
|
||||
|
||||
Reference in new issue
Block a user