Bridge logs: add bold blue UNITY-MCP prefix; gate PortManager logs behind Debug Logs toggle; improve Python and UV detection on Windows (flex versions, where.exe/Path scan); tidy installer messages
parent
f24e124c15
commit
4c72309dc8
|
|
@ -25,18 +25,18 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Debug.Log("Unity MCP: Installing Python server...");
|
Debug.Log("<b><color=#2EA3FF>UNITY-MCP</color></b>: Installing Python server...");
|
||||||
ServerInstaller.EnsureServerInstalled();
|
ServerInstaller.EnsureServerInstalled();
|
||||||
|
|
||||||
// Mark as installed
|
// Mark as installed
|
||||||
EditorPrefs.SetBool(InstallationFlagKey, true);
|
EditorPrefs.SetBool(InstallationFlagKey, true);
|
||||||
|
|
||||||
Debug.Log("Unity MCP: Python server installation completed successfully.");
|
Debug.Log("<b><color=#2EA3FF>UNITY-MCP</color></b>: Python server installation completed successfully.");
|
||||||
}
|
}
|
||||||
catch (System.Exception ex)
|
catch (System.Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogError($"Unity MCP: Failed to install Python server: {ex.Message}");
|
Debug.LogError($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Failed to install Python server: {ex.Message}");
|
||||||
Debug.LogWarning("Unity MCP: You may need to manually install the Python server. Check the Unity MCP Editor Window for instructions.");
|
Debug.LogWarning("<b><color=#2EA3FF>UNITY-MCP</color></b>: You may need to manually install the Python server. Check the Unity MCP Editor Window for instructions.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
@ -15,6 +16,12 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class PortManager
|
public static class PortManager
|
||||||
{
|
{
|
||||||
|
private static bool IsDebugEnabled()
|
||||||
|
{
|
||||||
|
try { return EditorPrefs.GetBool("UnityMCP.DebugLogs", false); }
|
||||||
|
catch { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
private const int DefaultPort = 6400;
|
private const int DefaultPort = 6400;
|
||||||
private const int MaxPortAttempts = 100;
|
private const int MaxPortAttempts = 100;
|
||||||
private const string RegistryFileName = "unity-mcp-port.json";
|
private const string RegistryFileName = "unity-mcp-port.json";
|
||||||
|
|
@ -41,7 +48,7 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
string.Equals(storedConfig.project_path ?? string.Empty, Application.dataPath ?? string.Empty, StringComparison.OrdinalIgnoreCase) &&
|
string.Equals(storedConfig.project_path ?? string.Empty, Application.dataPath ?? string.Empty, StringComparison.OrdinalIgnoreCase) &&
|
||||||
IsPortAvailable(storedConfig.unity_port))
|
IsPortAvailable(storedConfig.unity_port))
|
||||||
{
|
{
|
||||||
Debug.Log($"Using stored port {storedConfig.unity_port} for current project");
|
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Using stored port {storedConfig.unity_port} for current project");
|
||||||
return storedConfig.unity_port;
|
return storedConfig.unity_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +57,7 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
{
|
{
|
||||||
if (WaitForPortRelease(storedConfig.unity_port, 1500))
|
if (WaitForPortRelease(storedConfig.unity_port, 1500))
|
||||||
{
|
{
|
||||||
Debug.Log($"Stored port {storedConfig.unity_port} became available after short wait");
|
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Stored port {storedConfig.unity_port} became available after short wait");
|
||||||
return storedConfig.unity_port;
|
return storedConfig.unity_port;
|
||||||
}
|
}
|
||||||
// Prefer sticking to the same port; let the caller handle bind retries/fallbacks
|
// Prefer sticking to the same port; let the caller handle bind retries/fallbacks
|
||||||
|
|
@ -71,7 +78,7 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
{
|
{
|
||||||
int newPort = FindAvailablePort();
|
int newPort = FindAvailablePort();
|
||||||
SavePort(newPort);
|
SavePort(newPort);
|
||||||
Debug.Log($"Discovered and saved new port: {newPort}");
|
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Discovered and saved new port: {newPort}");
|
||||||
return newPort;
|
return newPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,18 +91,18 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
// Always try default port first
|
// Always try default port first
|
||||||
if (IsPortAvailable(DefaultPort))
|
if (IsPortAvailable(DefaultPort))
|
||||||
{
|
{
|
||||||
Debug.Log($"Using default port {DefaultPort}");
|
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Using default port {DefaultPort}");
|
||||||
return DefaultPort;
|
return DefaultPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Log($"Default port {DefaultPort} is in use, searching for alternative...");
|
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Default port {DefaultPort} is in use, searching for alternative...");
|
||||||
|
|
||||||
// Search for alternatives
|
// Search for alternatives
|
||||||
for (int port = DefaultPort + 1; port < DefaultPort + MaxPortAttempts; port++)
|
for (int port = DefaultPort + 1; port < DefaultPort + MaxPortAttempts; port++)
|
||||||
{
|
{
|
||||||
if (IsPortAvailable(port))
|
if (IsPortAvailable(port))
|
||||||
{
|
{
|
||||||
Debug.Log($"Found available port {port}");
|
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Found available port {port}");
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,7 +211,7 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
string legacy = Path.Combine(GetRegistryDirectory(), RegistryFileName);
|
string legacy = Path.Combine(GetRegistryDirectory(), RegistryFileName);
|
||||||
File.WriteAllText(legacy, json);
|
File.WriteAllText(legacy, json);
|
||||||
|
|
||||||
Debug.Log($"Saved port {port} to storage");
|
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Saved port {port} to storage");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,7 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Log("Unity MCP: Python environment repaired successfully.");
|
Debug.Log("<b><color=#2EA3FF>UNITY-MCP</color></b>: Python environment repaired successfully.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -305,47 +305,100 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty;
|
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty;
|
||||||
string[] candidates =
|
|
||||||
|
// Platform-specific candidate lists
|
||||||
|
string[] candidates;
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
"/opt/homebrew/bin/uv",
|
candidates = new[]
|
||||||
"/usr/local/bin/uv",
|
{
|
||||||
"/usr/bin/uv",
|
// Common per-user installs
|
||||||
"/opt/local/bin/uv",
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python313\Scripts\uv.exe"),
|
||||||
Path.Combine(home, ".local", "bin", "uv"),
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python312\Scripts\uv.exe"),
|
||||||
"/opt/homebrew/opt/uv/bin/uv",
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python311\Scripts\uv.exe"),
|
||||||
// Framework Python installs
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python310\Scripts\uv.exe"),
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.13/bin/uv",
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python313\Scripts\uv.exe"),
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/bin/uv",
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python312\Scripts\uv.exe"),
|
||||||
// Fallback to PATH resolution by name
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python311\Scripts\uv.exe"),
|
||||||
"uv"
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python310\Scripts\uv.exe"),
|
||||||
};
|
// Program Files style installs (if a native installer was used)
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) ?? string.Empty, @"uv\uv.exe"),
|
||||||
|
// Try simple name resolution later via PATH
|
||||||
|
"uv.exe",
|
||||||
|
"uv"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
candidates = new[]
|
||||||
|
{
|
||||||
|
"/opt/homebrew/bin/uv",
|
||||||
|
"/usr/local/bin/uv",
|
||||||
|
"/usr/bin/uv",
|
||||||
|
"/opt/local/bin/uv",
|
||||||
|
Path.Combine(home, ".local", "bin", "uv"),
|
||||||
|
"/opt/homebrew/opt/uv/bin/uv",
|
||||||
|
// Framework Python installs
|
||||||
|
"/Library/Frameworks/Python.framework/Versions/3.13/bin/uv",
|
||||||
|
"/Library/Frameworks/Python.framework/Versions/3.12/bin/uv",
|
||||||
|
// Fallback to PATH resolution by name
|
||||||
|
"uv"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
foreach (string c in candidates)
|
foreach (string c in candidates)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (ValidateUvBinary(c)) return c;
|
if (File.Exists(c) && ValidateUvBinary(c)) return c;
|
||||||
}
|
}
|
||||||
catch { /* ignore */ }
|
catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try which uv (explicit path)
|
// Use platform-appropriate which/where to resolve from PATH
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var whichPsi = new System.Diagnostics.ProcessStartInfo
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
FileName = "/usr/bin/which",
|
var wherePsi = new System.Diagnostics.ProcessStartInfo
|
||||||
Arguments = "uv",
|
{
|
||||||
UseShellExecute = false,
|
FileName = "where",
|
||||||
RedirectStandardOutput = true,
|
Arguments = "uv.exe",
|
||||||
RedirectStandardError = true,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true
|
RedirectStandardOutput = true,
|
||||||
};
|
RedirectStandardError = true,
|
||||||
using var wp = System.Diagnostics.Process.Start(whichPsi);
|
CreateNoWindow = true
|
||||||
string output = wp.StandardOutput.ReadToEnd().Trim();
|
};
|
||||||
wp.WaitForExit(3000);
|
using var wp = System.Diagnostics.Process.Start(wherePsi);
|
||||||
if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output) && File.Exists(output))
|
string output = wp.StandardOutput.ReadToEnd().Trim();
|
||||||
|
wp.WaitForExit(3000);
|
||||||
|
if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output))
|
||||||
|
{
|
||||||
|
foreach (var line in output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
string path = line.Trim();
|
||||||
|
if (File.Exists(path) && ValidateUvBinary(path)) return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (ValidateUvBinary(output)) return output;
|
var whichPsi = new System.Diagnostics.ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "/usr/bin/which",
|
||||||
|
Arguments = "uv",
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
using var wp = System.Diagnostics.Process.Start(whichPsi);
|
||||||
|
string output = wp.StandardOutput.ReadToEnd().Trim();
|
||||||
|
wp.WaitForExit(3000);
|
||||||
|
if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output) && File.Exists(output))
|
||||||
|
{
|
||||||
|
if (ValidateUvBinary(output)) return output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
@ -359,8 +412,11 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string candidate = Path.Combine(part, "uv");
|
// Check both uv and uv.exe
|
||||||
if (File.Exists(candidate) && ValidateUvBinary(candidate)) return candidate;
|
string candidateUv = Path.Combine(part, "uv");
|
||||||
|
string candidateUvExe = Path.Combine(part, "uv.exe");
|
||||||
|
if (File.Exists(candidateUv) && ValidateUvBinary(candidateUv)) return candidateUv;
|
||||||
|
if (File.Exists(candidateUvExe) && ValidateUvBinary(candidateUvExe)) return candidateUvExe;
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ namespace UnityMcpBridge.Editor
|
||||||
// Don't restart if already running on a working port
|
// Don't restart if already running on a working port
|
||||||
if (isRunning && listener != null)
|
if (isRunning && listener != null)
|
||||||
{
|
{
|
||||||
Debug.Log($"UnityMcpBridge already running on port {currentUnityPort}");
|
Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: UnityMcpBridge already running on port {currentUnityPort}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,7 +194,7 @@ namespace UnityMcpBridge.Editor
|
||||||
|
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
isAutoConnectMode = false;
|
isAutoConnectMode = false;
|
||||||
Debug.Log($"UnityMcpBridge started on port {currentUnityPort}.");
|
Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: UnityMcpBridge started on port {currentUnityPort}.");
|
||||||
Task.Run(ListenerLoop);
|
Task.Run(ListenerLoop);
|
||||||
EditorApplication.update += ProcessCommands;
|
EditorApplication.update += ProcessCommands;
|
||||||
// Write initial heartbeat immediately
|
// Write initial heartbeat immediately
|
||||||
|
|
@ -226,7 +226,7 @@ namespace UnityMcpBridge.Editor
|
||||||
listener?.Stop();
|
listener?.Stop();
|
||||||
listener = null;
|
listener = null;
|
||||||
EditorApplication.update -= ProcessCommands;
|
EditorApplication.update -= ProcessCommands;
|
||||||
Debug.Log("UnityMcpBridge stopped.");
|
Debug.Log("<b><color=#2EA3FF>UNITY-MCP</color></b>: UnityMcpBridge stopped.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1990,37 +1990,91 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Common absolute paths
|
// Windows-specific Python detection
|
||||||
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty;
|
if (Application.platform == RuntimePlatform.WindowsEditor)
|
||||||
string[] candidates =
|
|
||||||
{
|
{
|
||||||
"/opt/homebrew/bin/python3",
|
// Common Windows Python installation paths
|
||||||
"/usr/local/bin/python3",
|
string[] windowsCandidates =
|
||||||
"/usr/bin/python3",
|
{
|
||||||
"/opt/local/bin/python3",
|
@"C:\Python313\python.exe",
|
||||||
Path.Combine(home, ".local", "bin", "python3"),
|
@"C:\Python312\python.exe",
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.13/bin/python3",
|
@"C:\Python311\python.exe",
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/bin/python3",
|
@"C:\Python310\python.exe",
|
||||||
};
|
@"C:\Python39\python.exe",
|
||||||
foreach (string c in candidates)
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python313\python.exe"),
|
||||||
{
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python312\python.exe"),
|
||||||
if (File.Exists(c)) return true;
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python311\python.exe"),
|
||||||
}
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python310\python.exe"),
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39\python.exe"),
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python313\python.exe"),
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python312\python.exe"),
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python311\python.exe"),
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python310\python.exe"),
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python39\python.exe"),
|
||||||
|
};
|
||||||
|
|
||||||
// Try 'which python3'
|
foreach (string c in windowsCandidates)
|
||||||
var psi = new ProcessStartInfo
|
{
|
||||||
|
if (File.Exists(c)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try 'where python' command (Windows equivalent of 'which')
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "where",
|
||||||
|
Arguments = "python",
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
using var p = Process.Start(psi);
|
||||||
|
string outp = p.StandardOutput.ReadToEnd().Trim();
|
||||||
|
p.WaitForExit(2000);
|
||||||
|
if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp))
|
||||||
|
{
|
||||||
|
string[] lines = outp.Split('\n');
|
||||||
|
foreach (string line in lines)
|
||||||
|
{
|
||||||
|
string trimmed = line.Trim();
|
||||||
|
if (File.Exists(trimmed)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
FileName = "/usr/bin/which",
|
// macOS/Linux detection (existing code)
|
||||||
Arguments = "python3",
|
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty;
|
||||||
UseShellExecute = false,
|
string[] candidates =
|
||||||
RedirectStandardOutput = true,
|
{
|
||||||
RedirectStandardError = true,
|
"/opt/homebrew/bin/python3",
|
||||||
CreateNoWindow = true
|
"/usr/local/bin/python3",
|
||||||
};
|
"/usr/bin/python3",
|
||||||
using var p = Process.Start(psi);
|
"/opt/local/bin/python3",
|
||||||
string outp = p.StandardOutput.ReadToEnd().Trim();
|
Path.Combine(home, ".local", "bin", "python3"),
|
||||||
p.WaitForExit(2000);
|
"/Library/Frameworks/Python.framework/Versions/3.13/bin/python3",
|
||||||
if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp) && File.Exists(outp)) return true;
|
"/Library/Frameworks/Python.framework/Versions/3.12/bin/python3",
|
||||||
|
};
|
||||||
|
foreach (string c in candidates)
|
||||||
|
{
|
||||||
|
if (File.Exists(c)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try 'which python3'
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "/usr/bin/which",
|
||||||
|
Arguments = "python3",
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
using var p = Process.Start(psi);
|
||||||
|
string outp = p.StandardOutput.ReadToEnd().Trim();
|
||||||
|
p.WaitForExit(2000);
|
||||||
|
if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp) && File.Exists(outp)) return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue