In order to be trapped by this problem your project / environment has to meet following conditions:

  1. You need to use Windows.
  2. Your project uses make as a base build system.
  3. You have available sh.exe as a system command.
  4. [Optional] You use CMake for a project generation.

Conditions 1 and 2 are pretty easy to fulfill, for the instance you try to compile Emscripten or Android based project. Condition 3 might look pretty odd. Why sh.exe on Windows? To me it was a natural ripple effect of having Git installed with Linux like tools in system PATH. I just love to use ‘ls’ instead of ‘dir’ in command line.

If the Condition 4 is satisfied, the problem is slightly harder to control. In case of CMake when Unix Makefiles is used as a generator, then CMake sets a problematic variable (described below) automatically. You might wounder: Why you use Unix Makefiles instead of MSYS Makefiles or MinGW Makefiles? Glad you’ve asked. It’s due a cross platform solutions and simplification of tools and CI configurations – this way is just simpler. If we will check the sources of CMake, we can see:

  if (this->IsWindowsShell()) {
    makefileStream << "SHELL = cmd.exe\n"
                   << "\n";
  } else {
#if !defined(__VMS)
    /* clang-format off */
        << "# The shell in which to execute make rules.\n"
        << "SHELL = /bin/sh\n"
        << "\n";
/* clang-format on */

bool cmLocalGenerator::IsWindowsShell() const
 return this->GetState()->UseWindowsShell();

bool cmState::UseWindowsShell() const
 return this->WindowsShell;

void cmState::SetWindowsShell(bool windowsShell)
 this->WindowsShell = windowsShell;

And according to git search results, it is set only for generators:

And It’s defaulted to false by cmGlobalGenerator.cxx


Going back to Make

The root of the problem is a default command processor (shell) what’s used by GNU Make. It can be defined by a local variable SHELL(What CMake does). When the variable is specified then it’s used what’s defined. If it’s not specified then an internal logic is used to determine what to use. For Windows: it’s a value from COMSPEC System Environment Variable.

This observation leads to the Solution 1, but before that there is one simple method:


Solution 0 – Rename

Rename sh.exe or remove a folder that holds it from system PATH.

It’s not the best solution, especially because a problem with GIT after renaming sh.exe to any different file (git submodule doesn’t work without sh.exe), but for some edge cases I can see that removing problematic folder from system PATH can work.

Solution 1 – Change Makefile

Remove SHELL=/bin/sh from your makefile – This will invoke a default logic for defining shell on your system, should work nicely across all platforms (windows, linux, osx). It’s only valid if you can change the makefile. If you prefer to don’t touch it, or the make file is generated (Condition 4: Usage of CMake), then please check next solutions.

Less drastic solution is to specify SHELL=cmd for Windows

Solution 2 – Override local variable

Make provides a way to override locally defined variables by a special invocation. For the instance if you use command:

make game

(where ‘game’ is tour build target

You can fix the problem with shell by calling:

make SHELL=cmd game

But what if you don’t control a way how the solution is compiled?

Solution 3 – Global default arguments

For this you can utilize an environmental flag MAKEFLAGS and specify SHELL=cmd inside it. Some explanation of consequences you can find in the manual.

Once it’s specified (it can be either globally or just in the cmd session) it should start to ignore sh.exe



Was it useful? Please let me know by leaving a Kudos


41 Kudos