Prepending ENVVARs in command line of make target doesn't work consistently on Windows - makefile

I have a Makefile with a target that prepends an environment variable to the shell call (with the usual bash syntax). This is the gist of it:
mytest:
ANSWER=42 echo Hello!
(the real deal is a programme that does something with the ENV ANSWER, but that's irrelevant here)
This works as expected in a linux/bash environment. In windows/cmd.exe environments it works to my surprise in some machines, but fails in others with this error:
> make mytest
MYVAR=42 echo Hello!
'MYVAR' is not recognized as an internal or external command,
operable program or batch file.
make: *** [Makefile:332: mytest] Error 1
Which is what I'd normally expect, since ENVVAR=<value> <command> isn't valid syntax in the windows shell. Apparently Make does some magic that I don't understand.
If I pre-export the ENV this way, it works as expected:
mytest: export ANSWER:=42
mytest:
echo Hello!
But since it works on some windows environments, I'd like to know why and maybe adapt those instead of changing a lot of Makefiles.
All of the environments are using GNU Make version 4.3.

Running on Windows is complicated because GNU make can be built in different ways there. Sometimes it is built to use Windows cmd.exe as its shell. Sometimes it's built to use an installation of sh.exe as its shell (note, GNU make never comes with a shell: the shell is a separate facility provided on the system). And sometimes it's built to use sh.exe if it can find one, else use cmd.exe.
If you're seeing different behaviors on different systems, then the way make was built is different between those systems, and/or different systems have different extra software installed so that some have sh.exe and some don't.

Related

GNU make behaves different on another machine

I've created a makefile for GNU make 3.80 which works fine on my main development machine running Windows. I've some experience when to use '\' instead of '/' or when a '\\' is applicable.
This time there is '\' in paths as the makefile gets generated from a VS .vxproj via a Perl script.
Strange thing is now that a 100% working makefile behaves different on another machine running exactly the same make.exe binary which is part of my repository.
A rule like this
$(OBJ_DIR)\Atomics.obj : ..\BSW\Atomics\src\Atomics.c
$(CC) $(CFLAGS) $<
on the other machine produces the error message
Cannot open source file: '..BSWAtomicssrcAtomics.c'
OK, solution is make Perl toggle the '\' into '/' when creating the makefile.
But still I wonder if there is some Windows setting which causes this problem?
In a cmd.exe in same directory the same make.exe is called just like:
D:\project\XYZ\make>..\tool\make\make.exe
Yes indeed the presence of some shell binary (msys, mingw, etc.) broke my makefile for the other host. So the only way for my makefile to stick to its MS-DOS commands is to hide the UN*X tools by PATH-settings (e.g. start sub-shell with mini environment).

GNU Make Under Windows: Check for cygwin in PATH

I have been putting together a makefile in a Windows environment for my team to use. I decided to use MinGW's version of make for Windows. I put that executable with its dependencies into a repository location that should be in everyone's PATH variable. The executable was renamed "make.exe" for simplicity.
Then I realized that I have to account for the case when someone has cygwin's bin folder in their path. Commands like echo, rmdir, and mkdir will call echo.exe, rmdir.exe, and mkdir.exe from cygwin's bin folder. This means that I need to appropriately catch this scenario and use different flags for each command.
I see three cases here:
Cygwin's bin path comes before the path where make.exe is located in the repository. When a team member executes make.exe, they will be executing cygwin's make. Unix-style commands must be used.
Cygwin's bin path comes after the path where make.exe is located in the repository. The correct make.exe will be executed, but I still have to use Unix-style commands.
Cygwin is not installed or not in the PATH. I can use all Windows commands in this case.
I am fine with treating cases 1 and 2 the same. Since MinGW's make and cygwin's make are both based on GNU Make, then I don't see this being much of an issue other than incompatibility issues between versions of GNU Make. Let's just assume that isn't a problem for now.
I have come up with the following check in my makefile.
ifneq (,$(findstring cygdrive,$(PATH))$(findstring cygwin,$(PATH))$(findstring Cygwin,$(PATH)))
#Use Unix style command variables
else
#Use Windows style command variables
endif
Finding "cygdrive" in the path variable means that we are most likely in case 1. Finding "cygwin" or "Cygwin" in the path variable most likely means that we are in case 2. Not finding either string in the path most likely means that we are in case 3.
I am not completely fond of this solution because the cygwin's folder can be renamed or the string "cygwin" or "cygdrive" can be put in the PATH variable without having cygwin installed. One team member is still having issues as he has cygwin's bin path in the PATH variable, but the above does not catch that. I am assuming that he renamed the folder to something else, but I haven't been able to check on that.
So is there a better way to figure out what syntax that I should be using?
Here is another solution that I thought up.
ifeq (a,$(shell echo "a"))
#Use Unix style command variables
else
#Use Windows style command variables
endif
This is based on the fact that 'echo "a"' in Unix will print a (without quotes) but windows will print "a" (with the quotes). If I am using the Unix style echo then I can assume that I am using all Unix commands.
I don't find this solution very elegant though, so I am not marking it as the solution for this question. I think this is better than what I originally had though.
Cygwin make v. MinGW make: Does mingw make support the jobserver, as in can you do make -j5? If not, ${.FEATURES} has jobserver for cygwin make. Maybe load is a good test too.
Cygwin before non-cygwin on path: cygpath.exe is unique to cygwin. You could just look for this in ${PATH}. Unfortunately, Windows users like using spaces in folder names, and there's no way of dealing with this in pure make. $(shell which make) will return /usr/bin/make for cygwin, though a shell invocation on every make run is very smelly.
You don't install a compiler from a repository, is not make a similar case? Just get your users to install cygwin and be done with it.

git-slave for windows

git-slave documentation only has the following not-so-helpful comment regarding installation on Windows:
* Limited windows support
Multiple users have been successful in using gitslave with Windows.
This is "supported" only in the sense that people have reported it to
work and we accept bugfixes, not that you can `make` install or check,
or that it is QAed on this platform prior to release."
When I try to download and run 'nmake install' I get the equally cryptic error:
makefile(2) : fatal error U1001: syntax error : illegal character '{' in macro
Stop.
Does anyone have any experience with this and can point me in the right direction?
The Makefile for git-slave has only been used with GNU Make - as it is a rather simple makefile, there is no reason it shouldn't work with Microsoft nmake as well, except for "gratuitous" use of Make extensions that are not supported by Microsoft nmake. ( How similar/different are gnu make, microsoft nmake and posix standard make? has a good summary of the differences).
On lines 2-4 of gitslave/Makefile, if you replace ${prefix} with $(prefix) and ${mandir} with $(mandir) [essentially replace braces with parentheses (brackets)] nmake should no longer choke on Makefile. However, unless you have installed a bunch of POSIX utilities or something that allows commands like mkdir -p, rm -f, and sed to work, fixing the nmake incompatibility would only allow (at best) make testcheck to work.
None of the gitslave developers have regular(?) access to Windows development machines, so like the documentation says: "we accept bugfixes, [but do] not [claim] that you can make install or check,
or that it is QAed on this platform."
I imagine that the other people who have used git-slave on windows just made sure that Perl and gitslave and any POSIX utilities that gitslave depends on (e.g. grep and rm) are installed somewhere in PATH.
On Windows you can download and install the free unix tool kit including all necessary programs:
https://sourceforge.net/projects/unxutils/
You also need a perl tool kit because "pod2man" is used in the make process.
Furthermore the script "gits" is a perl script which runs under *ix because of the "she-bang" instruction in the first line ("#!/usr/bin/perl") - this doesn't work on Windows.
I created a small wrapper batch scripts that uses my perl to start the original script:
gits.bat:
perl gits %*
Hope this helps.

Why does MinGW/MSys change the binary path?

I'm using Mingw to build a C/C++ project. This project has makefiles beyond my comprehension, and relies on a custom and quite sophisticated toolchain to compile it. It's quite convenient to have GNU tools available on Windows, especially from Windows's cmd shell, but while invoking the tools (make in particular), MinGW seems to change my PATH around.
Cmd does it normally:
echo %PATH% > ... c:\Apps\msys\bin ... (from cmd)
but msys changes this address to :
echo $PATH > ... /usr/bin ...
in msys, even when I print the PATH from a makefile. As a result, make complains that it can't find commands like make, uname, echo, you name it (no pun intended).
Strangely, I managed to get this environment working ages ago without a hitch, but this is the first time I remember seeing this path problem. How can I get MinGW/msys to correctly point to its executables?
richard has a point - there were two different shells fighting over environment variables (not to mention running msys) and so each parsed its own and the system's environment variables differently.
Also make sure that variables defined in your user or system environment are properly written - Windows likes "C:\foo\bar" style paths, but Msys treats them as "/c/foo/bar".

autoconf using sh, I need SHELL=BASH, how do I force autoconf to use bash?

I'm running autoconf and configure sets SHELL to '/bin/sh'.
This creates huge problems. How to force SHELL to be '/bin/bash' for autoconf?
I'm trying to get this running on osx, it's working on linux. Linux is using SHELL=/bin/bash. osx defaults to /bin/sh.
I have similar problems on Solaris with GCC - and I use the 'standard' technique:
CONFIG_SHELL=/bin/bash ./configure ...
(Or, actually, I use /bin/ksh, but setting the CONFIG_SHELL env var allows you to tell autoconf scripts which shell to use.)
I checked the configure script for git and gd (they happened to be extracted) to check that this wasn't a GCC peculiar env var.
What are the "huge problems"? autoconf works very hard to generate a configure script that works with a very large percentage of shells. If you have an example of a construct that autoconf is writing that is not portable, please report it to the autoconf mailing list. On the other hand, if the problems you are experiencing are a result of your own shell code in configure.ac not being portable (eg, you're using bashisms) then the solution is to either stop using non-portable code or require the user to explicitly set SHELL or CONFIG_SHELL at configure time.
It sounds like the problem you are experiencing is in the environment of the user running configure. On Linux, your user has SHELL set to /bin/bash, but on OS X it is set to /bin/sh. The configure script generated by autoconf does some initial tests of the shell it is running under and does attempt to re-exec itself using a different shell if the provided shell lacks certain features. However, if you are introducing non-portable shell code in configure.ac, then you are violating one of the main philosophy's of autoconf -- namely that configure scripts should be portable. If you truly want to use bashisms in your shell code, then you are requiring your user to pass SHELL=/bin/bash as an argument to the configure script. This is not a bug in autoconf, but would be considered by many to be a bug in your project's build.
Autoconf is supposed to solve portability problems by generating a script which can run "anywhere". That's why it generates bizarre code like:
if test X$foo = X ; then ... # check if foo is empty
rather than:
if [ "$x" = "" ] ; then ...
That kind of crufty code probably once allowed these scripts to run on some ancient Ultrix system or whatever.
An configure script not running because of shell differences is like coming to a Formula-1 race with 10 liters of gas, and three spare tires.
If you're developing a configure script with Autoconf, and it is sensitive to whether the shell is Bash or the OSX shell, you are doing something wrong, or the Autoconf people broke something. If it's from you, fix whatever shell pieces you are adding to the script by making them portable.
Where is SHELL being set to that? What is being run with /bin/sh when you want /bin/bash?
configure scripts are meant to run anywhere, even on the horribly broken and non-Bash shells that exist in the wild.
Edit: What exactly is the problem?
Another edit: Perhaps you'd like the script to re-execute itself, something like this. It's probably buggy:
if test "$SHELL" = "/bin/sh" && test -x /bin/bash; then
exec /bin/bash -c "$0" "$#"
fi
ln -f /bin/bash /bin/sh
:-P (No, it's not a serious answer. Please don't hose your system by doing it!)

Resources