How to use shell builtin function from a Makefile?

后端 未结 5 1487
一个人的身影
一个人的身影 2020-12-16 22:02

I just stumbled on this problem. I tried to write a very basic Makefile target:

core: myprogram
        ulimit -c 10000
        ./myprogram
             


        
5条回答
  •  爱一瞬间的悲伤
    2020-12-16 22:37

    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.

提交回复
热议问题