Workaround for GNU Make 3.80 eval bug

2020-04-07 19:36发布

问题:

I'm trying to create a generic build template for my Makefiles, kind of like they discuss in the eval documentation.

I've run into a known bug with GNU Make 3.80. When $(eval) evaluates a line that is over 193 characters, Make crashes with a "Virtual Memory Exhausted" error.

The code I have that causes the issue looks like this.

SRC_DIR = ./src/

PROG_NAME = test

define PROGRAM_template
  $(1)_SRC_DIR = $$(SRC_DIR)$(1)/
  $(1)_SRC_FILES = $$(wildcard $$($(1)_SRC_DIR)*.c)
  $(1)_OBJ_FILES = $$($(1)_SRC_FILES):.c=.o)

  $$($(1)_OBJ_FILES) : $$($(1)_SRC_FILES) # This is the problem line
endef

$(eval $(call PROGRAM_template,$(PROG_NAME)))

When I run this Makefile, I get

gmake: *** virtual memory exhausted.  Stop.

The expected output is that all .c files in ./src/test/ get compiled into .o files (via an implicit rule).

The problem is that $$($(1)_SRC_FILES) and $$($(1)_OBJ_FILES) are together over 193 characters long (if there are enough source files).

I have tried running the make file on a directory where there is only 2 .c files, and it works fine. It's only when there are many .c files in the SRC directory that I get the error.

I know that GNU Make 3.81 fixes this bug. Unfortunately I do not have the authority or ability to install the newer version on the system I'm working on. I'm stuck with 3.80.

So, is there some workaround? Maybe split $$($(1)_SRC_FILES) up and declare each dependency individually within the eval?

回答1:

lol hacks

ifneq (3.81,$(shell (echo $(MAKE_VERSION); echo 3.81) | sort | head -n1))

make-3.81/make:
        wget -nc http://ftp.gnu.org/pub/gnu/make/make-3.81.tar.gz
        gzip -cd make-3.81.tar.gz | tar xvf -
        cd make-3.81 && ./configure --prefix=$$(pwd)
        $(MAKE) -C make-3.81 make

%: make-3.81/make
        make-3.81/make $@

else

# rest of your makefile

endif

Seriously though, there can't possibly be anything preventing you from installing 3.81, even if it is only locally.



回答2:

Perhaps nobody needs this any more, but I guess clever use of include could overcome this kind of limitation.

Replace define PROGRAM_template with something like:

define PROGRAM_template
__template_arg := $(1)
include PROGRAM_template.mk
endef

Create PROGRAM_template.mk to implement the core of the template macro:

$(__template_arg)_SRC_DIR = $(SRC_DIR)$(__template_arg)/
$(__template_arg)_SRC_FILES = $(wildcard $($(__template_arg)_SRC_DIR)*.c)
$(__template_arg)_OBJ_FILES = $($(__template_arg)_SRC_FILES:.c=.o)

$($(__template_arg)_OBJ_FILES) : $($(__template_arg)_SRC_FILES)
__template_arg :=

Of course, this is a bit ugly (use of global variable to pass argument to what technically is a macro). I like the first answer better... :-)