Within a Makefile on linux, we can excute shell/bash commands with it to move directories or excute another files. However when porting the same Makefile over to macOS, all the commands are not readible (therefore path and execution are broken). Is there a universal command or workflow that can work on both?
Example of Makefile
.ONESHELL:
COMMAND ?= none
GIT_HASH ?= githash
alpine:
#cd images/alpine
#make ${COMMAND} GIT_HASH=${GIT_HASH} ALPINE_VERSION=3.6.5 TAG=3.6
so in a linux box both #CD and #MAKE are executed but not for macOS Catalina. I would like to make it universal so that both system will respect the appropriate command that follows.
Chances are that your MacOS box uses its default GNU make version (3.81). .ONESHELL was introduced with 3.82. Upgrade with Homebrew or MacPort. Anyway, better avoid make in recipes, prefer $(MAKE), and instead of cd; make you can use GNU make's -C option: $(MAKE) -C images/alpine ...
Related
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).
I had learned Makefile before, but there is a problem in Makefile when I typed the command "make" today.
Here is the piece of code:
[ -d _build ] || mkdir _build
And the error:
'[' is not recognized as an internal or external command, operable program or batch file.
and there is my Make version:
GNU Make 3.82.90
Built for i686-pc-mingw32
Can anybody tell me how to fix it?
Your makefile was written for Linux/Unix and you are running it under Windows.
In a makefile, the commands that compose the recipes of the targets are
written in the language of the OS shell. Under Windows the shell is cmd.
Under Linux by default it is bash. Under other Unix variants it is sh or
some other shell.
[ -d _build ] || mkdir _build
is a valid command for bash and some other Linux/Unix shells. Not for
cmd. The same will be true of almost all the other recipe commands in the
makefile. You have just hit the first such problem.
Your makefile is of no use under native Windows. On Windows you have a chance
of running it in a Linux virtual machine, or under the Windows 10 Subsystem for Linux,
or under a Unix-like environment for Windows such as Cygwin or MSYS2. You may
well need to install tools that the makefile recipes require in that Linux/Unix-like
system.
If the makefile is simple you can consider translating the unrecognized shell
commands into cmd commands, if you are able.
I have encountered errors on running the conditional assignment operator in a Makefile in Solaris 11.1. However I encounter no errors on running the same Makefile in Ubuntu.
version ?= 6.1
all:
echo $(version)
Note: Before the echo, there is a tab before the echo in the original code. Using four spaces here just for convenience of editing.
Error encountered in Solaris 11.1:
make: Fatal error in reader: Makefile, line 1: Badly formed macro assignment
However, there are no such errors on Ubuntu.
Better you use gmake because many Makefiles use Gnu-specific features. Solaris make might be using slightly different syntax. You can read the Solaris makefile manual; you will find that there is no ?= operator in it. Hence you are getting that error.
?= is a feature added by GNU make, which is the default version of make on Ubuntu, but on Solaris is installed as gmake or /usr/gnu/bin/make. ?= is not supported by the native make command on Solaris, which is installed as /usr/ccs/bin/make on older releases, /usr/bin/make on Solaris 11 and later.
If you need to use the GNU extensions in your makefiles, make sure you run the GNU make, not the Solaris make.
Given this very simple Makefile:
all:
#mkdir -pv test/{a,b}
I get this output on OS X 10.6.8 and CentOS 5.5:
mkdir: created directory `test'
mkdir: created directory `test/a'
mkdir: created directory `test/b'
But on Ubuntu 11.04 I get this:
mkdir: created directory `test'
mkdir: created directory `test/{a,b}'
Running the command mkdir -pv test/{a,b} manually in the shell on all platforms gives the expected result.
The version of GNU Make is the same on all platforms:
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program is built for [PLATFORM]
What's different under Ubuntu and why doesn't the shell expansion work there?
The problem is probably that Make spawns /bin/sh. It is usually a symlink to your system's default shell.
Option 1
You could make sure it points to bash (as this is a bashism). Probably, it is now /bin/dash or /bin/sh, depending on your version of Ubuntu.
Option 2
Easier option:
SHELL=/bin/bash
all:
#echo a{3,4}
#bash -c 'echo a{3,4}'
This prints the same output twice unless you comment-out the SHELL= line
Option 3
If you can't/don't want to modify the make file, you can invoke it like so:
make SHELL=/bin/bash
beware of interactions with sub-makefiles or includes. You might want to look at the make -e option and the make export keyword: http://www.gnu.org/s/hello/manual/make/Variables_002fRecursion.html
Make has a function to do this:
#mkdir -pv $(addprefix test/,a b)
gets expanded to
#mkdir -pv test/a test/b
The docs are very thorough.
It's been a long, long time since I've used Make...
There are several ways to specify a particular shell to use. The default shell for old Make was the original Bourne shell. If you wanted a different shell, you had to set it yourself.
You're using Linux and GNU, so I'll assume that you're using BASH as the default shell. Try this command in your Make:
echo "random = $RANDOM"
If this simply prints random = and doesn't include a random number, your Make is using Bourne shell as its default shell instead of BASH. (Which is weird because I didn't think there was a real Bourne shell in Linux...) To get around this:
You can add in a SHELL macro pointing to the BASH shell.
You can include the shell in your command.
Instead of:
#mkdir -pv test/{a,b}
Put this:
/bin/bash -c #mkdir -pv test/{a,b}
This specifies you want to use BASH and not the standard /bin/sh Bourne shell.
If the echo random = $RANDOM does print a random number, you're using BASH (or at least Kornshell), but the BRACE EXPANSION might not be set. Try using this in your Makefile:
set -o
And make sure braceexpand is on. It could be off when you run Make.
I'm trying to setup a parallel CMake-based build for my source tree, but when I issue
$ cmake .
$ make -j2
I get:
jobserver unavailable: using -j1. Add '+' to parent make rule
as a warning. Does anyone have an idea if it is possible to fix it somehow?
In the generated Makefile, when calling into a sub-make it needs to either use $(MAKE) (not just 'make') or else precede the line with a +. That is, a rule should look like this:
mysubdir:
$(MAKE) -C mysubdir
or like this:
mysubdir:
+make -C mysubdir
If you don't do it one of those two ways, make will give you that warning.
I don't know anything about cmake, so maybe it's generating Makefiles that aren't correct. Or maybe you did something incorrectly on your end.
In my case (with CMake 3.5.2) the trivial cd build && cmake .. && make -j5 works just fine.
But, I do get the jobserver unavailable error when building custom targets (as dependencies of other targets) via the cmake --build . --target foo idiom.
Like this:
add_custom_target(buildroot
COMMAND ${CMAKE_COMMAND} --build . --target install
COMMENT "Populating buildroot..."
)
add_dependencies(deb buildroot)
add_dependencies(rpm buildroot) #... etc
— so that the user can make deb and it Just Works. CMake will regenerate makefiles if needed, run the compilation, install everything exactly as with make install, and then run my custom scripts to package up the populated buildroot into whatever shape or form I need.
Sure enough, I'd like to make -j15 deb — but that fails.
Now, as explained on the mailing list by CMake devs, the root cause lies, surprisingly (or not), within GNU Make; there is a workaround.
The root cause is that make will not pass its jobserver environment to child processes it thinks aren't make.
To illustrate, here's a process tree (ps -A f) branch:
…
\_ bash
\_ make -j15 deb
\_ make -f CMakeFiles/Makefile2 deb
\_ make -f CMakeFiles/buildroot.dir/build.make CMakeFiles/buildroot.dir/build
\_ /usr/bin/cmake --build . --target install ⦿
\_ /usr/bin/gmake install
…
At ⦿ point, make drops jobserver environment, ultimately causing single-threaded compilation.
The workaround which worked great for me, as given away in the linked email, is to prefix all custom commands with +env. Like this:
add_custom_target(buildroot
#-- this ↓↓↓ here -- https://stackoverflow.com/a/41268443/531179
COMMAND +env ${CMAKE_COMMAND} --build . --target install
COMMENT "Populating buildroot..."
)
add_dependencies(deb buildroot)
add_dependencies(rpm buildroot) #... etc
In the end, this appears in the rule for buildroot in the appropriate makefile (CMake generates a bunch of them), and causes GNU Make to behave properly and respect -j.
Hope this helps.
As pointed out by #Carlo Wood in his comment to this answer, trying to convince cmake to add + to the beginning of the command in the cmake-generated makefile is not possible.
A work-around I found is to shield underlying make command from the make flags coming from cmake. This can be done by setting environment variable MAKEFLAGS to empty string for the custom command:
COMMAND ${CMAKE_COMMAND} -E env
MAKEFLAGS=
make <your target and make options>
Hope this helps.