dotnet / sdk

Core functionality needed to create .NET Core projects, that is shared between Visual Studio and CLI
https://dot.net/core
MIT License
2.73k stars 1.07k forks source link

Global tools path not discoverable on macOS via `which` #10177

Open atifaziz opened 5 years ago

atifaziz commented 5 years ago

Steps to reproduce

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
livarcocc commented 5 years ago

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

atifaziz commented 5 years ago

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.
wli3 commented 5 years ago

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).

atifaziz commented 5 years ago

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;
}
wli3 commented 5 years ago

@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.

atifaziz commented 5 years ago

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
patridge commented 3 years ago

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
bryancostanich commented 2 years ago

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

patridge commented 2 years ago

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"