sdk icon indicating copy to clipboard operation
sdk copied to clipboard

Global tools path not discoverable on macOS via `which`

Open atifaziz opened this issue 5 years ago • 9 comments

Steps to reproduce

  • Install .NET Core SDK 2.1+
  • Install any global tool
  • Run which in a Terminal session on macOS to discover the tool's path

Expected behavior

which should print the absolute path of where the global tool is installed and finish with an exit code of zero.

Actual behavior

which fails with a non-zero exit code.

Environment data

My PATH contains the path to the tools directory:

$ tr ':' '\n' <<< "$PATH" | grep dotnet
/usr/local/share/dotnet
~/.dotnet/tools

Running dotnet --info prints:

.NET Core SDK (reflecting any global.json):
 Version:   2.1.500
 Commit:    b68b931422

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  10.12
 OS Platform: Darwin
 RID:         osx.10.12-x64
 Base Path:   /usr/local/share/dotnet/sdk/2.1.500/

Host (useful for support):
  Version: 2.2.1
  Commit:  878dd11e62

.NET Core SDKs installed:
  1.0.1 [/usr/local/share/dotnet/sdk]
  2.0.0 [/usr/local/share/dotnet/sdk]
  2.1.4 [/usr/local/share/dotnet/sdk]
  2.1.200 [/usr/local/share/dotnet/sdk]
  2.1.300 [/usr/local/share/dotnet/sdk]
  2.1.401 [/usr/local/share/dotnet/sdk]
  2.1.500 [/usr/local/share/dotnet/sdk]
  2.2.103 [/usr/local/share/dotnet/sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.6 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.1 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.6 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.1 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 1.0.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 1.0.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 1.1.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 1.1.1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.7 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.6 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download

atifaziz avatar Mar 23 '19 17:03 atifaziz

You need to restart your shell for the PATH changes to take effect. Have you tried that? Otherwise, it would be good to know which shell you are using.

cc @wli3

livarcocc avatar Mar 24 '19 18:03 livarcocc

You need to restart your shell for the PATH changes to take effect. Have you tried that?

I guess in the steps to reproduce the problem, I should have added “restart shell” after installing the SDK, but yes, I did do that.

Otherwise, it would be good to know which shell you are using.

I am using Bash in the macOS Terminal:

$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.

atifaziz avatar Mar 24 '19 21:03 atifaziz

I think it is related to https://github.com/dotnet/core-setup/issues/5099#issuecomment-476010399

There is no other way to put a user path on $PATH using PATH_HELPER for every user (that I can find).

wli3 avatar Mar 24 '19 23:03 wli3

The bigger (and related) problem here is that Process.Start cannot be used to launch any global tool because of the following algorithm (that's part of Process.Start) that doesn't account for ~:

private static string FindProgramInPath(string program)
{
    string path;
    string pathEnvVar = Environment.GetEnvironmentVariable("PATH");
    if (pathEnvVar != null)
    {
        var pathParser = new StringParser(pathEnvVar, ':', skipEmpty: true);
        while (pathParser.MoveNext())
        {
            string subPath = pathParser.ExtractCurrent();
            path = Path.Combine(subPath, program);
            if (File.Exists(path))
            {
                return path;
            }
        }
    }
    return null;
}

atifaziz avatar Mar 25 '19 20:03 atifaziz

@atifaziz that I am not sure. If you just type the command name, it is resolved by the shell. dotnet should not be in the picture yet.

The code above is used in process.start which is not used in run command.

wli3 avatar Mar 26 '19 01:03 wli3

If you just type the command name, it is resolved by the shell.

Yes but is that the only experience we are optimising for? It seems odd that while shell can launch, other programs like which cannot find global tools and a .NET application cannot launch them. It makes it difficult to write a portable application since “it just works” on Windows.

For example, create a new console application with dotnet new console and replace content of Program.cs with the following:

using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;

static class Program
{
    static int Main(string[] args)
    {
        var psi = new ProcessStartInfo(args[0])
        {
            UseShellExecute        = false,
            CreateNoWindow         = true,
            RedirectStandardOutput = true,
            RedirectStandardError  = true,
        };

        foreach (var arg in args.Skip(1))
            psi.ArgumentList.Add(arg);

        using (var process = Process.Start(psi))
        {
            process.OutputDataReceived += (_, ea) =>
            {
                if (ea.Data != null)
                    Console.WriteLine(ea.Data);
            };

            process.ErrorDataReceived += (_, ea) =>
            {
                if (ea.Data != null)
                    Console.Error.WriteLine(ea.Data);
            };

            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            process.WaitForExit();

            return process.ExitCode;
        }
    }
}

Next use the program to launch any globally installed tool on macOS, e.g.:

$ dotnet tool install -g dotnet-script
You can invoke the tool using the following command: dotnet-script
Tool 'dotnet-script' (version '0.28.0') was successfully installed.
$ which dotnet-script || echo not found
not found
$ dotnet run -- dotnet-script -h
Unhandled Exception: System.ComponentModel.Win32Exception: No such file or directory
   at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
   at Program.Main(String[] args) in /Users/atif/dev/pub/CSharpMinifier/tmp/Program.cs:line 21

atifaziz avatar Mar 26 '19 07:03 atifaziz

Ran into the same issue on macOS Big Sur (v11.1) with the new default Terminal shell, zsh (v5.8), while trying to use a new CLI tool. I tried closing and starting a new Terminal command line window and restarting Terminal entirely, all without any luck.

% dotnet tool install WildernessLabs.Meadow.CLI --global
Tool 'wildernesslabs.meadow.cli' is already installed.
% meadow --Download
zsh: command not found: meadow

I could only work around it by "fully qualifying" the path using the information on this troubleshooting page.

% $HOME/.dotnet/tools/meadow --Download

patridge avatar Jan 16 '21 21:01 patridge

Monterey, too, in ZSH. Would love to see this get fixed.

bryancostanich avatar Jul 20 '22 18:07 bryancostanich

It's likely covered in some of the related issues, but here's what worked for me, if anyone else needs a workaround for this issue in zsh.

After you install the SDK, add the global install tools folder to $PATH manually.

export PATH="$PATH:$HOME/.dotnet/tools"

patridge avatar Aug 03 '22 18:08 patridge