在运行Makefile时,使用go命令出现“Permission denied”错误

3

我遇到了一些奇怪的权限被拒绝错误,但我不知道这些错误是从哪里引起的。

$ go run .
Hello from go

$ make run
go run .
make: go: Permission denied
make: *** [Makefile:2: run] Error 127

$ make run2
echo "Make says hello" ; go run .
Make says hello
Hello from go

$ cat Makefile 
run:
    go run .

run2:
    echo "Make says hello" ; go run .

$ cat main.go 
package main

import "fmt"

func main() {
    fmt.Println("Hello from go")
}

我的终端是在Ubuntu 22.04上运行的bash。

我的run目标和直接运行go之间有什么区别,可能会导致权限被拒绝的错误?

runrun2之间有什么区别,使得其中一个可以工作而另一个不能?

编辑:使用-d / --trace运行make。

$ make -d run
<...snip...>
 No need to remake target 'Makefile'.
Updating goal targets....
Considering target file 'run'.
 File 'run' does not exist.
 Finished prerequisites of target file 'run'.
Must remake target 'run'.
go run .
make: go: Permission denied
make: *** [Makefile:2: run] Error 127

$ make --trace run
Makefile:2: target 'run' does not exist
go run .
make: go: Permission denied
make: *** [Makefile:2: run] Error 127

$ make --trace run2
Makefile:5: target 'run2' does not exist
echo "Make says hello"; go run .
Make says hello
Hello from go

那么,如果您在“run”目标中的go run .之前添加echo“foo”;会怎样呢? - kostix
另外,如果使用 --trace 运行,它会打印出任何“有趣”的内容吗? - kostix
@kostix 在运行中添加 echo "foo"; 使其工作并返回 foo\nHello from go"。我已经添加了使用 -d 运行 make 的输出。我不确定如何使用 --trace 运行它。 - Kasper Middelboe Petersen
我无法重现这个问题。我倾向于认为它是由一些情况的组合引起的,这些情况还没有被问题完全捕捉到。你可以尝试以下几点:(i)创建一个新目录,在其中工作,并只放置你的 Makefilemain.go;(ii)显式运行 main.gogo run main.go),而不是运行目录中的所有 .go 文件。此外,在执行运行时,请确保不要在编辑器中打开 .go 文件,至少如果你使用 go run . 变体的话。 - John Bollinger
这非常有趣。在目标下的每个命令都应该由单独的 shell 实例运行。看起来好像 make 看到可以解释为简单 shell 命令的内容时,使用了一些 —— 让我们称之为 "默认" shell,而当它看到可以被认为是脚本的内容(比如用 ; 分隔的两个命令),它会生成一个新的 shell,但这个 shell 的初始化方式与默认 shell 不同。可能是您在登录会话开始后调整了 ~/.bashrc,并且没有重新启动您正在尝试运行 make 的 shell?这可能可以解释这个问题。 - kostix
也就是说,当一个新的 shell 被产生时,它会被初始化,以便找到“可运行”的 go,而 go make 看到的内容是不同的。你可以尝试将 go run . 改为 which go 并查看结果。嗯,我承认这些只是瞎猜,但是… - kostix
2个回答

9
由于GNU make中存在一个错误(实际上是gnulib中的一个错误),导致您在PATH上的某个目录中具有名为go的目录(在包含go可执行文件的实际目录之前)。
因此,如果您有一个目录/usr/bin/go /。并且您的PATH中有/usr/bin,则会出现此问题。
您应该检查您的PATH,并确保删除任何包含此类子目录的目录。如果您无法从PATH中删除该目录(通常不需要在PATH中添加包含子目录的目录,但我想这是可能的),并且无法将go目录重命名为其他名称,则必须确保GNU make调用shell,方法是添加特殊字符。只需使用;就足够了:
run:
         go run . ;

1
只是提醒一下,我相信这个 bug 在 GNU make 的最新版本(4.4 或更高版本)中已经修复了。 - MadScientist

1
您遇到的问题可能是由于您的shell和Makefile执行的shell之间存在不同的环境所致。例如,如果您为go设置了一个shell别名,则该别名对Makefile不可见;或者如果您在shell rc文件中设置了自定义路径,则该路径对Makefile也不可见。很难猜测差异可能出现在哪里。
您可以尝试在Makefile中尝试以下调试方法:
echo $(PATH)
command -v go

运行相同的命令并比较结果。

请注意,Makefile 的默认 shell 是 /bin/sh,而你可能有 bashzsh

以下是一些方便的默认设置,可用于配置 Makefile 构建:

LANG=en_US.UTF-8
SHELL=/bin/bash
.SHELLFLAGS=--norc --noprofile -e -u -o pipefail -c

1
Makefile 的 shell 不是同时运行 runrun2 吗?当 run 无法工作时,run2 如何运行? - Kasper Middelboe Petersen
我找不出这两个代码之间的任何区别。我期望 runrun2 的行为是相同的。 - Kare Nuorteva
他们不会。将command -v goecho $(PATH)添加到运行目标中,返回的确切值与从终端手动执行相同。 - Kasper Middelboe Petersen
请在Makefile文件中添加.PHONY: run run2 - Kare Nuorteva
默认情况下,make 使用的 shell 执行配方是 /bin/sh,但这并不妨碍它继承从中启动 make 的交互式 shell 的环境。在这个意义上,环境是一个进程特性,而不是特定于 shell 的特性。 - John Bollinger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接