I just stumbled on this problem. I tried to write a very basic Makefile
target:
core: myprogram
ulimit -c 10000
./myprogram
ulimit -c 0
The idea is to set up the core size limit to a proper value, make the program crash and then reset the core size limit back to zero. When I am calling this rule, I get the following error message:
$ make core
cc -Wall -Wextra -m32 -g -o example example.c
ulimit -c 100000
make: ulimit: Command not found
make: *** [core] Error 127
First, I have been a bit surprised, but I think that the problem is coming from the fact that ulimit
is a shell builtin. And, suprisingly (at least for me), these builtin functions cannot be called from a Makefile
.
Moreover, ulimit
can be both a builtin function only (it is the case on my Debian) or a binary program only (/usr/bin/ulimit
).
So, my question is simple, how to workaround this problem, if possible in an elegant and portable manner, and call the builtin function from the inside of a Makefile ?
@anon58192932 That specific iteration, to execute a command, is usually done in a shell syntax fragment in a build recipe, so it occurs in the shell, rather than in make: for x in $(FILES); do command $$x; done . Note the doubled up $$ which passes a single $ to the shell.
$(shell) is a special function in gmake that runs an external command and captures the output for use in the makefile.
?= indicates to set the KDIR variable only if it's not set/doesn't have a value. For example: KDIR ?= "foo" KDIR ?= "bar" test: echo $(KDIR) Would print "foo"
In computing, a shell builtin is a command or a function, called from a shell, that is executed directly in the shell itself, instead of an external executable program which the shell would load and execute. Shell builtins work significantly faster than external programs, because there is no program loading overhead.
The reason you're getting this error is that make (GNU make in particular) tries to perform a number of optimizations. One of those is that if the command appears to be a simple command that does not require the shell, then make will simply invoke it directly via fork/exec and not run a shell. If the command only exists as a shell built-in, then this will not work. Your command line ulimit -c 10000
is a simple command and ulimit is not defined as only a shell-builtin that make knows about, so make will try to fork/exec ulimit
directly. So a way to get around your immediate issue is to simply add a character that's special to the shell (the most obvious one is ;
), which will hint to make that this command needs to be sent to the shell.
However, this will not work for you.
Exactly contrary to H2CO3's comment above: How possibly could it be [a shell builtin], given the functionality it provides? the real question you have to ask yourself is the opposite: how possibly could it NOT be one, given the functionality it provides? The man page for ulimit clearly states: The ulimit utility shall set or report the file-size writing limit imposed on files written by the shell and its child processes, and further: Since ulimit affects the current shell execution environment, it is always provided as a shell regular built-in.
You have to remember that it's virtually impossible for a process in UNIX to modify ANY aspect of its parent process. It can only modify itself, or any child processes that it invokes. This includes the environment variables, working directory, and it also includes ulimit settings.
So, good, how does this apply to your situation? You have to remember that each logical line in a make recipe is invokes in a separate shell. So for a command like:
core: myprogram
ulimit -c 10000 ;
./myprogram
ulimit -c 0 ;
(adding the semicolons to force a shell) what make basically invokes is this:
core: myprogram
/bin/sh -c 'ulimit -c 10000 ;'
/bin/sh -c './myprogram'
/bin/sh -c 'ulimit -c 0 ;'
As you can see, each ulimit is invoked in its own shell, so it's effectively useless. It will modify the core file size limit for that shell, then the shell exits and the change is lost, then your program is invoked in a new shell with the original ulimit, then a third shell is invoked and ulimit for cores is set to 0 (also useless).
What you need to do is put all of these commands on a single logical line, like this:
core: myprogram
ulimit -c 10000; ./myprogram
(you don't need to set the limit back, because the shell will exit anyway).
As a side note, this is why make doesn't worry too much about these shell builtins. A builtin like this is basically impossible to use to any effect in a context where you don't need to use some special shell character like a semicolon.
The default shell for any Makefile
is sh
. If you have to use builtins of a specific shell, specify that shell in your Makefile
:
SHELL:=bash
Note that this is bad practice, because the idea is that your Makefile
should work on any machine, not only those with bash
installed.
If you want to support either variant (external vs. builtin), you can check for the availability of bash
resp. ulimit
via which
, and set a variable containing the command to use (ulimit
vs. bash -c "ulimit"
) depending on the outcome of that check.
Edit: MadScientist is absolutely right about the "current shell only" aspect of ulimit
. I'll keep this answer intact for documentation purposes, but it doesn't help with perror's specific problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With