GNU make behaves different on another machine - makefile

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

Related

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

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.

CMake: Generate Unix Makefiles which ignore errors

In Unix Makefile I can prefix a recipe line with - to ignore any error that will occur (as describe in Errors in Recipes).
hello_world: hello_world.cxx
-$(CXX) $(CXXFLAGS) $^ -o $#
I converted my Makefile to CMake:
cmake_minimum_required(VERSION 3.0)
project(HelloWorld)
add_executable(hello_world hello_world.cxx)
and run cmake and the generated Makefile looking fine, except the missing -.
Is it possible to generate Unix Makefile with CMake that will ignore errors (prefix the recipe line with -)?
The best would be to specify it per target level. I know I can run make -i to have the same behaviour but it isn't that convenient.
You cannot.
make is designed to give the user a fine control over commands it runs. CMake's under-the-hood commands are supposed to always succeed.
As a hack, you can generate makefiles and run make --ignore-errors.
But I advice making each of your examples that would fail a separate project, and run them from an external script.

How to Change the Default Command That make Executes?

By default, when running make to compile a C source code file named prog.c
make prog
the default command that executes is
cc prog.c -o prog
Sometimes I really need to include some additional flags. I know that when there are no Makefiles, make relies on some environment variables.
On Ubuntu 14.04, how to configure these variables to change the command that gets executed by default?
Step by step answers will be appreciated!
When no makefile is present (or no rule exists in that makefile) make relies on a default built-in database of rules. Run make -p to get make to spit out all the rules it knows about (in the no makefile case that will be the default ones).
When you look at that list you will find a pattern rule for building C source into object files or executables. Those rules have variables in them (like CFLAGS, LDFLAGS, etc.) that can be used to control exactly what you are trying to. That's why they are there (and are why that default command has such funny spacing, in case you ever wondered about that).

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.

How to stop MinGW and MSYS from mangling path names given at the command line

On Windows, I'm cross-compiling a program for ARM/Linux using CodeSourcery's cross-compiler suite. I use MinGW MSYS as my command interpreter, and very often it will mangle my paths and pathnames. For example, to build my program, I invoke
arm-none-linux-gnueabi-gcc.exe -Wall -g \
-Wl,--dynamic-linker=/usr/lib/myrpath/ld-linux.so.3 \
-Wl,-rpath=/usr/lib/myrpath \
-I../targetsysroot/usr/include \
myprogram.c -o myprogram
Of course, I want /usr/lib/myrpath inserted verbatim into the myprogram executable - the ARM Linux target I'm compiling for doesn't use MinGW or MSYS. But here's what ends up going into it:
...
0x0000000f (RPATH) Library rpath: [C:/MinGW/msys/1.0/lib/myrpath]
...
Not exactly what I wanted. If I invoke GCC on the cmd.exe command line directly, I get the right rpath in the executable. If I invoke GCC on the MSYS command line, I get the mangled rpath. If I invoke GCC with a Makefile that is run with make from the cmd.exe command line, I still get a mangled rpath (!)
Any ideas how I might turn off this annoying behavior?
There is a way to suppress the path translation by setting MSYS_NO_PATHCONV=1 in Windows Git MSys or MSYS2_ARG_CONV_EXCL="*" in MSYS2.
Alternatively, you can set the variable only temporarily just for that command by putting the assignment just before the command itself:
MSYS_NO_PATHCONV=1 arm-none-linux-gnueabi-gcc.exe -Wall -g \
-Wl,--dynamic-linker=/usr/lib/myrpath/ld-linux.so.3 \
-Wl,-rpath=/usr/lib/myrpath \
-I../targetsysroot/usr/include \
myprogram.c -o myprogram
I just discovered a neat trick to avoid MSYS/MinGW translating the paths for you.
If you use double-slash to start the path, then MSYS won't translate the path to DOS format. So in OP's example, the -rpath switch should be specified like this:
-Wl,-rpath=//usr/lib/myrpath
All Unix/Linux tools seem to handle such spurious slashes without any problem, so even though your binary's rpath will start with //usr/... I think the loader will do the right thing.
I don't think there's a way to switch this off. MSYS is a fork of an old Cygwin version with a number of tweaks aimed at improved Windows integration, whereby the automatic POSIX path translation when invoking native Windows programs is arguably the most significant. The trouble with that is that it isn't always possible to tell whether an argument is a path or something else, or whether, as in this case, it is in fact a path that nevertheless shouldn't be translated. The translation is guided by a set of heuristics.
You could try using MinGW make instead of MSYS make (yes, they're different things), which is a native Windows build of make without POSIX path support and conversion. Install with mingw-get install mingw32-make and invoke as mingw32-make.
Or you could try Cygwin, ideally with a Cygwin build of the toolchain.
Indeed, in the original MSYS project provided by MinGW.org, there is no way to disable the Posix path conversion.
That's why I made a little fork of the msys-core runtime which supports the MSYS_NO_PATHCONV flag introduced with the Git for Windows fork. In that way, you may use MSYS_NO_PATHCONV environment variable as in the Git for Windows but in the original MinGW/MSYS.
So in summary, to disable this Posix path convesion:
For MSYS2 (built-in): MSYS2_ARG_CONV_EXCL="*"
For Git for Windows (built-in): MSYS_NO_PATHCONV=1
For MinGW.org (with msys-core-extended): MSYS_NO_PATHCONV=1.
export MSYS_NO_PATHCONV=1 was necessary in my case on git-bash on windows (as noted by dx_over_dt above. )
Unfortunately putting two forward slashes for this example doesn't work as expected.
rsync -rvztn --delete --exclude="/application/logs/" ...
I want 'rsync' to exclude files only at /application/logs which is at the top level, hence the leading forward slash. Adding two forward slashes will not cause it to exclude this directory. I have to resort to the less accurate --exclude="application/logs/".

Resources