diff --git a/internal/jean/backend_build.go b/internal/jean/backend_build.go index 29bdf92..4438a50 100644 --- a/internal/jean/backend_build.go +++ b/internal/jean/backend_build.go @@ -120,6 +120,15 @@ func buildLlamacpp(repo string, p buildPlan, clean bool) error { // on les réinjecte sinon le configure échoue sur « CUDA Toolkit directory '' ». parts = append(parts, cudaPathEnv(filepath.Dir(cudaBin))...) env = strings.Join(parts, "\x00") + // Générateur Visual Studio : vérifie (et répare si possible) l'intégration + // MSBuild de CUDA AVANT de configurer — sinon CMake échoue sur le cryptique + // « No CUDA toolset found » (cf. issue #10 : CUDA dans un chemin custom sans + // l'option « Visual Studio Integration », ou VS installé après CUDA). + if strings.HasPrefix(p.gen, "Visual Studio") { + if err := ensureCudaVSIntegration(filepath.Dir(cudaBin)); err != nil { + return err + } + } } cfgArgs := []string{"-B", "build", "-S", "."} @@ -164,6 +173,17 @@ func hintMissingBuildDep(p buildPlan, cfgLog string) { // Backend Vulkan : les en-têtes SPIR-V (paquet SPIRV-Headers) sont requis par // la config CMake de ggml-vulkan, mais absents par défaut sur beaucoup de // distros même quand glslc/libvulkan sont là. + // Windows/CUDA : « No CUDA toolset found » = intégration MSBuild de CUDA + // absente de Visual Studio. Normalement intercepté AVANT le configure par + // ensureCudaVSIntegration ; ce filet sert aux cas où la détection n'a pas pu + // conclure (vswhere absent, install VS non standard). + if p.backend == "cuda" && strings.Contains(log, "No CUDA toolset found") { + fmt.Printf("\n%s l'intégration Visual Studio de CUDA est absente (« No CUDA toolset found »).\n", yellow("[dépendance]")) + fmt.Printf(" Relance l'installeur du CUDA Toolkit (installation personnalisée) en cochant « CUDA → Visual Studio Integration »\n") + fmt.Printf(" (Visual Studio avec le workload C++ doit déjà être installé), ou copie les fichiers de\n") + fmt.Printf(" \\extras\\visual_studio_integration\\MSBuildExtensions vers\n") + fmt.Printf(" \\MSBuild\\Microsoft\\VC\\\\BuildCustomizations, puis relance %s.\n", bold("jean llamacpp install")) + } if p.backend == "vulkan" && strings.Contains(log, "SPIRV-Headers") { fmt.Printf("\n%s dépendance manquante pour le backend %s : les en-têtes SPIR-V (paquet « SPIRV-Headers ») sont introuvables.\n", yellow("[dépendance]"), green("Vulkan")) diff --git a/internal/jean/sys_platform_unix.go b/internal/jean/sys_platform_unix.go index 99490a0..f75b4c9 100644 --- a/internal/jean/sys_platform_unix.go +++ b/internal/jean/sys_platform_unix.go @@ -175,6 +175,10 @@ func ensureCompiler() error { // nothing extra to inject. func cudaPathEnv(toolkitDir string) []string { return nil } +// ensureCudaVSIntegration is Windows-specific (MSBuild CUDA integration check); +// no-op on Unix. +func ensureCudaVSIntegration(toolkitDir string) error { return nil } + // ensureAccelerator is a no-op on Unix: CUDA/ROCm toolkits are installed through // the distro (their layout is already probed by findNvcc / detectBuildPlan), and // auto-installing multi-GB GPU toolkits across distros is too varied to do safely. diff --git a/internal/jean/sys_platform_windows.go b/internal/jean/sys_platform_windows.go index 9cb857f..6601c64 100644 --- a/internal/jean/sys_platform_windows.go +++ b/internal/jean/sys_platform_windows.go @@ -297,6 +297,71 @@ func ensureAccelerator() { } } +// ensureCudaVSIntegration vérifie que l'intégration MSBuild de CUDA (fichiers +// « CUDA x.y.props/targets » dans BuildCustomizations de Visual Studio) est en +// place — sans elle, le générateur Visual Studio échoue sur le cryptique +// « No CUDA toolset found » (CMakeDetermineCompilerId). Cas typiques : CUDA +// installé dans un chemin custom (ex. F:\Cuda) sans cocher « Visual Studio +// Integration », ou VS (ré)installé APRÈS CUDA — l'installeur NVIDIA n'intègre +// que les VS présents au moment où il tourne. On tente d'abord de copier les +// fichiers depuis le toolkit (extras\visual_studio_integration\MSBuildExtensions) ; +// si ça échoue (droits), on explique quoi faire au lieu de laisser l'erreur +// CMake brute. Best-effort : sans vswhere/VS détectable on laisse cmake juger. +func ensureCudaVSIntegration(toolkitDir string) error { + vs := vswherePath() + if _, err := os.Stat(vs); err != nil { + return nil + } + out, err := exec.Command(vs, "-latest", "-products", "*", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath").Output() + if err != nil { + return nil + } + installPath := strings.TrimSpace(string(out)) + if installPath == "" { + return nil + } + dsts, _ := filepath.Glob(filepath.Join(installPath, "MSBuild", "Microsoft", "VC", "*", "BuildCustomizations")) + for _, d := range dsts { + if m, _ := filepath.Glob(filepath.Join(d, "CUDA *.props")); len(m) > 0 { + return nil // intégration déjà en place + } + } + // Absente : tentative de réparation depuis le toolkit lui-même. + src := filepath.Join(toolkitDir, "extras", "visual_studio_integration", "MSBuildExtensions") + files, _ := filepath.Glob(filepath.Join(src, "*")) + copied := 0 + for _, dst := range dsts { + ok := len(files) > 0 + for _, f := range files { + b, err := os.ReadFile(f) + if err != nil { + ok = false + break + } + if err := os.WriteFile(filepath.Join(dst, filepath.Base(f)), b, 0o644); err != nil { + ok = false + break + } + } + if ok { + copied++ + } + } + if copied > 0 { + fmt.Printf("%s intégration Visual Studio de CUDA absente — réparée (fichiers copiés depuis %s)\n", yellow("[fix]"), src) + return nil + } + return fmt.Errorf(`l'intégration Visual Studio de CUDA est absente : aucun fichier « CUDA x.y.props » sous + %s\MSBuild\Microsoft\VC\\BuildCustomizations +Sans elle, CMake échoue sur « No CUDA toolset found ». Pour corriger, au choix : + 1. relance l'installeur du CUDA Toolkit (installation personnalisée) et coche « CUDA → Visual Studio Integration » — Visual Studio doit déjà être installé à ce moment-là ; + 2. ou copie (en admin) les fichiers de + %s + vers le dossier BuildCustomizations ci-dessus, puis relance jean llamacpp install`, installPath, src) +} + // cudaPathEnv returns the CUDA toolkit env vars the MSBuild CUDA integration // needs (CUDA_PATH and the version-specific CUDA_PATH_Vx_y), derived from the // toolkit root, e.g. "...\CUDA\v13.3" → CUDA_PATH_V13_3. Returns NUL-free