@@ -26,6 +26,15 @@ var knownDotnetToolErrors = []dotnetToolFriendlyError{
2626 },
2727}
2828
29+ const DotnetSourceUserOverride = "user-override"
30+ const DotnetSourceArchMatched = "arch-matched"
31+ const DotnetSourceDefault = "default"
32+
33+ type DotnetToolResolution struct {
34+ Path string
35+ Source string // "user-override", "arch-matched", "default"
36+ }
37+
2938// wrapDotnetToolStartError wraps a command-start error, appending a
3039// user-friendly message when the error matches a known pattern. The original
3140// error message is always preserved for debugging.
@@ -39,30 +48,61 @@ func wrapDotnetToolStartError(err error, cmdArgs []string) error {
3948 return fmt .Errorf ("failed to start dotnet tool %v: %w" , cmdArgs , err )
4049}
4150
42- // ensureDotnetToolResolved lazily resolves DotnetToolPath if it was not set
43- // during validation (e.g. when runtime was auto-detected rather than explicit).
44- func ensureDotnetToolResolved () (string , error ) {
45- if path := config .GlobalConfig .DotnetToolPath ; path != "" {
46- return path , nil
51+ func resolveDotnetToolForPid (pid int ) (DotnetToolResolution , error ) {
52+ // user override
53+ if config .GlobalConfig .DotnetToolPath != "" {
54+ resolvedPath , err := config .ResolveDotnetToolOverride ()
55+ if err != nil {
56+ return DotnetToolResolution {}, err
57+ }
58+ return DotnetToolResolution {Path : resolvedPath , Source : DotnetSourceUserOverride }, nil
4759 }
48- resolved , err := config .ResolveDotnetToolPath ()
49- if err != nil {
50- return "" , err
60+
61+ // arch matched
62+ arch , detectErr := detectTargetArch (pid )
63+ if detectErr != nil {
64+ logger .Warn ().Err (detectErr ).Int ("pid" , pid ).Msg ("could not detect target arch" )
65+ }
66+ if arch != "" {
67+ name := config .DotnetToolNameForArch (arch )
68+ if p , ok := config .FindDotnetToolNearYcOrPath (name ); ok {
69+ return DotnetToolResolution {Path : p , Source : DotnetSourceArchMatched }, nil
70+ }
71+
72+ return DotnetToolResolution {}, fmt .Errorf (".NET tool for PID %d (%s) not found. expected %s next to yc or on PATH" , pid , arch , name )
73+ }
74+
75+ // No arch info — fall back to default tool name
76+ if p , ok := config .FindDotnetToolNearYcOrPath (config .DefaultDotnetToolName ); ok {
77+ if detectErr != nil {
78+ logger .Warn ().
79+ Err (detectErr ).
80+ Int ("pid" , pid ).
81+ Str ("path" , p ).
82+ Msg ("using legacy .NET tool path because target arch detection failed" )
83+ }
84+ return DotnetToolResolution {Path : p , Source : DotnetSourceDefault }, nil
85+ }
86+
87+ if detectErr != nil {
88+ return DotnetToolResolution {}, fmt .Errorf (
89+ ".NET tool path %q not found near yc or on PATH (target arch detection for PID %d failed: %w)" ,
90+ config .DefaultDotnetToolName , pid , detectErr )
5191 }
52- config . GlobalConfig . DotnetToolPath = resolved
53- return resolved , nil
92+ return DotnetToolResolution {}, fmt . Errorf (
93+ ".NET tool path %q not found near yc or on PATH" , config . DefaultDotnetToolName )
5494}
5595
56- // executeDotnetTool runs the configured .NET helper executable with the given arguments
96+ // executeDotnetTool runs the configured .NET tool executable with the given arguments
5797// and captures the output to a file. Returns the file handle and any error.
58- func executeDotnetTool (args []string , outputPath string ) (* os.File , error ) {
59- toolPath , err := ensureDotnetToolResolved ( )
98+ func executeDotnetTool (pid int , args []string , outputPath string ) (* os.File , error ) {
99+ toolResolution , err := resolveDotnetToolForPid ( pid )
60100 if err != nil {
61101 return nil , err
62102 }
63103
64104 // Build the command: [toolPath, args...]
65- cmdArgs := append ([]string {toolPath }, args ... )
105+ cmdArgs := append ([]string {toolResolution . Path }, args ... )
66106
67107 logger .Log ("Executing dotnet tool: %v" , cmdArgs )
68108
@@ -144,15 +184,15 @@ func executeDotnetTool(args []string, outputPath string) (*os.File, error) {
144184 return file , nil
145185}
146186
147- // startDotnetToolInBackground starts the configured .NET helper executable with the
187+ // startDotnetToolInBackground starts the configured .NET tool executable with the
148188// given arguments and returns the running command handle without waiting.
149- func startDotnetToolInBackground (args []string , hookers ... executils.Hooker ) (executils.CmdManager , error ) {
150- toolPath , err := ensureDotnetToolResolved ( )
189+ func startDotnetToolInBackground (pid int , args []string , hookers ... executils.Hooker ) (executils.CmdManager , error ) {
190+ toolResolution , err := resolveDotnetToolForPid ( pid )
151191 if err != nil {
152192 return nil , err
153193 }
154194
155- cmdArgs := append ([]string {toolPath }, args ... )
195+ cmdArgs := append ([]string {toolResolution . Path }, args ... )
156196 logger .Log ("Starting dotnet tool in background: %v" , cmdArgs )
157197
158198 cmd , err := executils .CommandStartInBackground (cmdArgs , hookers ... )
0 commit comments