Problem
In order to be trapped by this problem your project / environment has to meet following conditions:
- You need to use Windows.
- Your project uses make as a base build system.
- You have available sh.exe as a system command.
- [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:
// https://github.com/Kitware/CMake/blob/78d1a2d0bbda1798ffc71b11a181d376914bdc3c/Source/cmLocalUnixMakefileGenerator3.cxx#L636-L648 if (this->IsWindowsShell()) { makefileStream << "SHELL = cmd.exe\n" << "\n"; } else { #if !defined(__VMS) /* clang-format off */ makefileStream << "# The shell in which to execute make rules.\n" << "SHELL = /bin/sh\n" << "\n"; /* clang-format on */ #endif } // https://github.com/Kitware/CMake/blob/55465be8404d37713c4bece1cc20acb3a7165492/Source/cmLocalGenerator.cxx#L2251-L2254 bool cmLocalGenerator::IsWindowsShell() const { return this->GetState()->UseWindowsShell(); } // https://github.com/Kitware/CMake/blob/c6ea3f01c3e78145b8fd3dd534d8803d8f7e7bd7/Source/cmState.cxx#L537-L540 bool cmState::UseWindowsShell() const { return this->WindowsShell; } // https://github.com/Kitware/CMake/blob/c6ea3f01c3e78145b8fd3dd534d8803d8f7e7bd7/Source/cmState.cxx#L532-L535 void cmState::SetWindowsShell(bool windowsShell) { this->WindowsShell = windowsShell; }
And according to git search results, it is set only for generators:
- cmGlobalWatcomWMakeGenerator.cxx
- cmGlobalBorlandMakefileGenerator.cxx
- cmGlobalJOMMakefileGenerator.cxx
- cmGlobalMinGWMakefileGenerator.cxx
- cmGlobalNMakeMakefileGenerator.cxx
- cmGlobalVisualStudioGenerator.cxx
- cmGlobalNinjaGenerator.cxx
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
I encountered this same problem with all 4 condition met and I use MinGW Makefiles so I just I want to add another solution that I found
1. Simply build it through cmake .. on the shell (or run the task if you have specified it)
2. It will generate an incomplete build. That’s okay because you want to edit the CMakeCache.txt.
3. Open CMakeCache.txt and edit //Path to a program by copying this line CMAKE_SH:FILEPATH=CMAKE_SH-NOTFOUND
4. Run the cmake task again and problem should be solved.
Thank you, indeed it’s another solution for this problem. In our case we’ve constrained ourselves to do not do any postprocessing of CMakeCache.
Cheers!