2
votes

How does GNU make decide if it runs a line from a rule directly or via a batch file? I'm using GNU Make v3.80 on Windows Server 2008 platform, so my shell is cmd.exe.

Switching on debugging (-d) and noticed that some lines from within a rule are not run via a sub-shell (batch in my case as using cmd.exe) but appear to be called directly by make.

See example below. See how the xcopy command is run directly:

CreateProcess(C:\Windows\system32\xcopy.exe,xcopy /?,...)

But the echo command is run via a batch file:

Creating temporary batch file C:\Users\fbloggs\AppData\Local\Temp\95\make732002.bat

Example makefile:

stackoverflow :
    xcopy /?
    @echo Done

Full dDebug output:

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'.
1
Windows don't have separate executable for echo, does it?keltar
No echo.exe on my machine but is that important? I only picked xcopy and echo as examples so people would be familiar with them. I could have picked ar for an example of a direct call and Perl.exe as going via a batch file.Chris
I meant it can't run echo without cmd, because there is no such program. But anyway, - makefile recipes uses shell syntax; if make is completely sure it can avoid using shell here, it is free to do so.keltar

1 Answers

4
votes

Interesting question. Although I do not have the opportunity to verify my answer because I do not have the environment set up to reproduce this, I will give it a try based on source code reading. For reference, the lines below are copied from the GNU make source file job.c.

First of all, that code indicates when a command is executed directly and when it is executed via the 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.

and for your environment, these two arrays of strings seem to be:

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 };

Any command in the list above is executed by the "shell" -- however, in your case this does not really mean invoking any shell, but creating this temporary batch file as you mentioned. In the function construct_command_argv_internal(), the following comment and code can be found:

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

This is in line with the output of your make run. Maybe you can try some of the commands in the sh_cmds_dos list in order to check whether this is a correct analysis...