MinGW Makefile is looking in c:\windows\system32 instead of /usr/bin - windows

The following Makefile:
.PHONY: all
all: $(shell find example/data)
#echo $(shell which find)
When run with gnuwin32 Make in a MinGW bash shell, outputs the following:
$ make
FIND: Parameter format not correct
/usr/bin/find
This output is strange because the "FIND: Parameter format not correct" message comes from Windows' find.exe in C:\Windows\system32.
This means that:
$(shell find example/data) is using C:\Windows\system32\find.exe, even though
$(shell which find) identifies /usr/bin/find (the GNU find)
My question is: What is going on here? Why would $(shell find ...) be looking in C:\Windows\system32 instead of in /usr/bin? Why isn't the path consistent and what can I do to make $(shell find ...) behave the same way that find ... would behave from the MinGW bash shell?
I do not want to hard code the path to find; and I could probably get what I want by using which to find the path but I'm more concerned with the path being incorrect than I am about specifically getting find to work here.

When you say "MinGW bash shell" I assume you mean an MSYS/MSYS2 bash shell.
MSYS and MSYS2 provide a Unix/Linux-like shell, and some commands may exist in Unix/Linux with the same name as different commands in Windows.
The find is such a command.
There are several solutions to your problem:
Use the where command instead (your Makefile will be compatible with other platforms).
Specify the full path to the command you want to use. You could detect it with FINDCMD=$(shell which find) and then run it with $(shell $(FINDCMD) example/data)

Related

How to use `shell find` on Make for windows?

I've got a Makefile that contains a statements like this:
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" -not -path modules/options/bindata.go -not -path modules/public/bindata.go -not -path modules/templates/bindata.go)
I'm trying to use it on windows and I know using this particular file wouldn't be a problem if I just used cygwin, msys2, vagrant, wsl or any other similar terminal. That said, I'd like to know how to use that Makefile with plain cmd.exe terminal (got my own reasons to do so, so please don't suggest using those ;)).
Right now my env var PATH has appended the Git for windows usr\bin path
that contains a nice toolset of ~437 unix commands and find
is amongst them.
So for instance, if I run find on my terminal... D:\software\PortableGit\usr\bin\find.exe will be used instead C:\Windows\System32\find.exe as expected, so far so good up to this point.
Problem is when I run the makefile for some reason I haven't understood yet C:\Windows\System32\find.exe is trying to be used instead on the statement $(shell find ...) so I'll get the error FIND: Parameter format not correct when doing make.
So basically my question would be, how can I instruct make to use the proper unix find that has higher priority on PATH? But if this wouldn't be possible to achieve easily... would it be possible to make that statement to become more cross-platform maybe using wildcards?
I've already checked the make docs:
https://www.gnu.org/software/make/manual/html_node/Wildcard-Function.html
https://www.gnu.org/software/make/manual/make.html#Choosing-the-Shell
but I didn't find anything that could help me out.
Also, the version of make I'm using can be downloaded from http://gnuwin32.sourceforge.net/packages/make.htm, at the moment of asking this question version I'm using is GNU Make 3.81

Stop GNU make from compiling by default

I just want to use GNU make to compress some files.
So I wrote the Makefile as follows:
lib.tar.lzma: $(shell find ~/lib -name "*")
rm -f lib.tar.lzma
tar -cavf lib.tar.lzma -C ~/ lib/
However, after I run make, it automatically compile the c++ source code in that directory.
How can I stop it from compiling them? I just want to compress them.
Update:
I got the following error:
<builtin>: recipe for target '/home/xxx/lib/app' failed
It seems a built-in recipe.
(We don't know your entire Makefile and your full file tree, so this is only a guess; I assume that you have shown us a fragment of your much bigger Makefile)
However, after I run make, it automatically compile the c++ source code in that directory.
This is probably happening because your $(shell find ~/lib -name "*") is expanded to something containing your object files. Since they are in your dependencies their source file is recompiled if it is newer. BTW you might want to use instead $(shell cd .. ; find lib -name "*") or if lib has no subdirectory even $(wildcard ../lib/*)
You probably don't need any dependency for that lib.tar.lzma target, so just have:
lib.tar.lzma:
rm -f lib.tar.lzma
tar -cavf lib.tar.lzma -C ~/ lib/
BTW, that -C ~/ perhaps should be -C $$HOME since make use /bin/sh to run commands, and that POSIX shell don't know about ~ ; perhaps a -C .. might be better ...
Perhaps you might write some shell script make-backup.sh to do a more clever tar and you would then code
lib.tar.lzma: make-backup.sh
./make-backup.sh $#
However, perhaps you do have dependencies (e.g. if you need to archive some generated files). Then you need to list them explicitly and wisely (you certainly don't want to depend on all the files; perhaps only the source ones). Also, you might not need to archive any object files *.o, if you have some (but YMMV).
I recommend using make --trace or remake -x to debug your Makefile.
BTW, having a Makefile only for a backup is useless; write a shell script instead.
I also strongly recommend using some version control system (like git) if you don't use any. Notice that git has an archive subcommand which might be a more clever backup.

Create multple object files at once without using a makefile?

When I try
gcc -c *.c
I get an invalid argument error, and it says no input files.
If you run gcc from a directory where no C source files are present, gcc will receive the *.c argument unexpanded, will try to open a file named *.c and fail, will report this failure and in the absence of further arguments, will complain about the missing input files:
$ gcc -c *.c
gcc: error: *.c: No such file or directory
gcc: fatal error: no input files
compilation terminated.
$
The wildcard expansion is performed by the command line interpreter, aka the shell. On a unix system, there are many different shells, sh, csh, tcsh, bash, zsh... all of which expand unquoted wildcards before running the commands. On Windows, the default shells do not expand wildcards for external commands, some programs do it on their own, but most don't. If you run bash on Windows, with or without cygwin, you will get the Unix behavior, but if you run cmd.exe, you won't.
MinGW is a set of development tools to make Windows native executables. It does not provide a shell and favors using the native libraries and utilities when possible. This is the reason why your command gcc -c *.c does not undergo wildcard expansion on your machine. Install bash or cygwin for a more unix-friendly environment.
This should do what you're looking for.
find . -name '*.c' -print | xargs gcc -c

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 can I make the "find" Command on OS X default to the current directory?

I am a heavy command line user and use the find command extensively in my build system scripts. However on Mac OS X when I am not concentrating I often get output like this:
$ find -name \*.plist
find: illegal option -- n
find: illegal option -- a
find: illegal option -- m
find: illegal option -- e
find: *.plist: No such file or directory
Basically, I forgot to add the little dot:
$ find . -name \*.plist
Because BSD find requires the path and GNU find doesn't (it assumes the current directory if you don't specify one). I use Linux, Mac OS X and Cygwin often all at the same time, so it's of great benefit to me to have all my tools behave the same. I tried writing a bash find function that added "./" if I forgot, but I failed. Thanks for your help. :)
Install GNU find instead.
$ brew install findutils
$ alias find=gfind
Yay, it works!
If you can't discipline yourself to use find 'correctly', then why not install GNU find (from findutils) in a directory on your PATH ahead of the system find command.
I used to have my own private variant of cp that would copy files to the current directory if the last item in the list was not a directory. I kept that in my personal bin directory for many years - but eventually removed it because I no longer used the functionality. (My 'cp.sh' was written in 1987 and edited twice, in 1990 and 1997, as part of changes to version control system notations. I think I removed it around 1998. The primary problem with the script is that cp file1 file2 is ambiguous between copying a file over another and copying two files to the current directory.)
Consider writing your own wrapper to find:
#!/bin/sh
[ ! -d "$1" ] && set -- . "$#"
exec /usr/bin/find "$#"
The second line says "if argument 1 is not a directory, then adjust the command line arguments to include dot ahead of the rest of the command. That will be confusing if you ever type:
~/bin/find /non-existent/directory -name '*.plist' -print
because the non-existent directory isn't a directory and the script will add dot to the command line -- the sort of reason that I stopped using my private cp command.
If you must call it 'find', then you want:
alias find=/usr/bin/find\ .
in your .profile or .bash_profile or …. Substitute the real path (if not /usr/bin/find) on your Mac OSX. Enter the full path to avoid cycles (bash normally would interpret alias find=find without issues, but better be sure).
But you better not name the alias find (findl, myfind etc), because it will become a habit and trouble for you if you try it on another system.
find ./ -name "*.plist"
edit: hmm, i may have misunderstood the question! if you were crazy, how about emulating it via a shell script? i routinely keep random utility scripts in ~/.bin, and that's the first thing in my PATH. if you had a similar setup perhaps you could do something like: (untested!)
#!/bin/sh
# remapping find!
CMD=`echo $1 | cut -c 1`
if [ $CMD = '-' ]
then
# pwd search
/usr/bin/find ./ $*
else
# regular find
/usr/bin/find $*
fi
I would suggest that if you're writing scripts (which are more likely to be migrated from one system to another sometime in the future) that you should try to use the more specific form of the command, that is specifying the "." instead of relying on a default. For the same reason, I might even suggest writing sh scripts instead of relying on bash which might not be installed everywhere.
This is probably not what you want but how about: alias find="find ."
or choose a new name (findl for find local?)
You may want to run the commands found in this link: https://www.topbug.net/blog/2013/04/14/install-and-use-gnu-command-line-tools-in-mac-os-x/
It is a bit outdated, for example I found I did not have to add many commands to my path at all.
This covers your problem by having your system use the Non-BSD find utility from the findutils package, while also installing other tools you may want as well.

Resources