是什么导致 GNU Make 出壳

Chr*_*ris 3 makefile

GNU make 如何决定是直接运行规则中的一行还是通过批处理文件运行?我在Windows Server 2008平台上使用GNU Make v3.80,所以我的shell是cmd.exe。

打开调试 (-d) 并注意到规则中的某些行不是通过子 shell 运行(在我的例子中使用 cmd.exe 进行批处理),而是似乎由 make 直接调用。

请参阅下面的示例。看看xcopy命令是如何直接运行的:

CreateProcess(C:\Windows\system32\xcopy.exe,xcopy /?,...)
Run Code Online (Sandbox Code Playgroud)

但 echo 命令是通过批处理文件运行的:

Creating temporary batch file C:\Users\fbloggs\AppData\Local\Temp\95\make732002.bat
Run Code Online (Sandbox Code Playgroud)

示例生成文件:

stackoverflow :
    xcopy /?
    @echo Done
Run Code Online (Sandbox Code Playgroud)

完整的 dDebug 输出:

GNU Make 3.80 Tool Version v1.3  build for Linux on 20060503
Copyright (C) 2002  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
find_and_set_shell path search set default_shell = C:/Windows/System32/cmd.exe
Reading makefiles...
Reading makefile `..\..\..\pi_scripts\make\make_shell.mak'...
Updating makefiles....
 Considering target file `..\..\..\pi_scripts\make\make_shell.mak'.
  Looking for an implicit rule for `..\..\..\pi_scripts\make\make_shell.mak'.
  No implicit rule found for `..\..\..\pi_scripts\make\make_shell.mak'.
  Finished prerequisites of target file `..\..\..\pi_scripts\make\make_shell.mak'.
 No need to remake target `..\..\..\pi_scripts\make\make_shell.mak'.
Updating goal targets....
Considering target file `stackoverflow'.
 File `stackoverflow' does not exist.
 Finished prerequisites of target file `stackoverflow'.
Must remake target `stackoverflow'.
xcopy /?
CreateProcess(C:\Windows\system32\xcopy.exe,xcopy /?,...)
Putting child 0x025dc670 (stackoverflow) PID 39739552 on the chain.
Live child 0x025dc670 (stackoverflow) PID 39739552 
Copies files and directory trees.

NOTE: Xcopy is now deprecated, please use Robocopy.

XCOPY source [destination] [/A | /M] [/D[:date]] [/P] [/S [/E]] [/V] [/W]
                           [/C] [/I] [/Q] [/F] [/L] [/G] [/H] [/R] [/T] [/U]
                           [/K] [/N] [/O] [/X] [/Y] [/-Y] [/Z] [/B]
                           [/EXCLUDE:file1[+file2][+file3]...]

  source       Specifies the file(s) to copy.
  destination  Specifies the location and/or name of new files.
<snip>
Reaping winning child 0x025dc670 PID 39739552 
Creating temporary batch file C:\Users\fbloggs\AppData\Local\Temp\95\make732002.bat
CreateProcess(C:\Users\fbloggs\AppData\Local\Temp\95\make732002.bat,C:\Users\fbloggs\AppData\Local\Temp\95\make732002.bat,...)
Live child 0x025dc670 (stackoverflow) PID 39739552 
Done
Reaping winning child 0x025dc670 PID 39739552 
Cleaning up temp batch file C:\Users\fbloggs\AppData\Local\Temp\95\make732002.bat
Removing child 0x025dc670 PID 39739552 from chain.
Successfully remade target file `stackoverflow'.
Run Code Online (Sandbox Code Playgroud)

Rei*_*eek 5

有趣的问题。虽然我没有机会验证我的答案,因为我没有设置重现此问题的环境,但我会根据源代码阅读进行尝试。作为参考,以下行是从 GNU make 源文件job.c复制的。

首先,该代码指示何时直接执行命令以及何时通过 shell 执行命令。

3470 /* Figure out the argument list necessary to run LINE as a command.  Try to
3471    avoid using a shell.  This routine handles only ' quoting, and " quoting
3472    when no backslash, $ or ' characters are seen in the quotes.  Starting
3473    quotes may be escaped with a backslash.  If any of the characters in
3474    sh_chars[] is seen, or any of the builtin commands listed in sh_cmds[]
3475    is the first word of a line, the shell is used.
Run Code Online (Sandbox Code Playgroud)

对于您的环境,这两个字符串数组似乎是:

2625   static char sh_chars_dos[] = "|&<>";
2626   static char *sh_cmds_dos[] = { "assoc", "break", "call", "cd", "chcp",
2627                                  "chdir", "cls", "color", "copy", "ctty",
2628                                  "date", "del", "dir", "echo", "echo.",
2629                                  "endlocal", "erase", "exit", "for", "ftype",
2630                                  "goto", "if", "if", "md", "mkdir", "move",
2631                                  "path", "pause", "prompt", "rd", "rem", "ren",
2632                                  "rename", "rmdir", "set", "setlocal",
2633                                  "shift", "time", "title", "type", "ver",
2634                                  "verify", "vol", ":", 0 };
Run Code Online (Sandbox Code Playgroud)

上面列表中的任何命令都是由“shell”执行的——但是,在您的情况下,这并不意味着调用任何 shell,而是创建您提到的这个临时批处理文件。在函数中construct_command_argv_internal(),可以找到以下注释和代码:

3139 #ifdef WINDOWS32
3140         else    /* non-Posix shell (cmd.exe etc.) */
3141           {
3142             const char *f = line;
3143             char *t = line;
3144             char *tstart = t;
3145             int temp_fd;
3146             FILE* batch = NULL;
3147             int id = GetCurrentProcessId ();
3148             PATH_VAR(fbuf);
3149
3150             /* Generate a file name for the temporary batch file.  */
3151             sprintf (fbuf, "make%d", id);
3152             *batch_filename = create_batch_file (fbuf, 0, &temp_fd);
3153             DB (DB_JOBS, (_("Creating temporary batch file %s\n"),
3154                           *batch_filename));
Run Code Online (Sandbox Code Playgroud)

这与您的运行输出一致make。也许您可以尝试列表中的一些命令sh_cmds_dos来检查这是否是正确的分析......