开发者

How to include makefiles dynamically?

Is it possible to include Makefiles dynamically? For example depending on some environment variable? I have the following Makefiles:

makefile
app1.1.mak
app1.2.mak

And there is an environment variable APP_VER which could be set to 1.1.0.1, 1.1.0.2, 1.2.0.1, 1.2.0.2.

But there will be only two different makefiles for 1.1 and 1.2 lines.

I have tried to write the following Makefile:

MAK_VER=$$(echo $(APP_VER) | sed -e 's/^\([0-9]*\.[0-9]*\).*$$/\1/')  
include makefile$(MAK_VER).mak  

all: PROD  
        echo MAK_VER=$(MAK_VER)  

But it does not work:

$ make a开发者_高级运维ll
"makefile$(echo", line 0: make: Cannot open makefile$(echo
make: Fatal errors encountered -- cannot continue.

UPDATE:

As far as I understand make includes files before it calculates macros.

That's why it tries to execute the following statement

include makefile.mak

instead of

include makefile1.1.mak


You have two problems: your method of obtaining the version is too complicated, and your include line has a flaw. Try this:

include app$(APP_VER).mak

If APP_VER is an environmental variable, then this will work. If you also want to include the makefile called makefile (that is, if makefile is not the one we're writing), then try this:

include makefile app$(APP_VER).mak

Please note that this is considered a bad idea. If the makefile depends on environmental variables, it will work for some users and not others, which is considered bad behavior.

EDIT:

This should do it:

MAK_VER := $(subst ., ,$(APP_VER))
MAK_VER := $(word 1, $(MAK_VER)).$(word 2, $(MAK_VER))

include makefile app$(MAK_VER).mak


Try this:

MAK_VER=$(shell echo $(APP_VER) | sed -e 's/^\([0-9]*\.[0-9]*\).*$$/\1/')
MAK_FILE=makefile$(MAK_VER).mak

include $(MAK_FILE)

all:
    echo $(MAK_VER)
    echo $(MAK_FILE)


Modifying the outline solution

Have four makefiles:

  • makefile
  • app1.1.mak
  • app1.2.mak
  • appdummy.mak

The app.dummy.mak makefile can be empty - a symlink to /dev/null if you like. Both app.1.1.mak and app.1.2.mak are unchanged from their current content.

The main makefile changes a little:

MAK_VER = dummy
include makefile$(MAK_VER).mak  

dummy:  
        ${MAKE} MAK_VER=$$(echo $(APP_VER) | sed -e 's/^\([0-9]*\.[0-9]*\).*$$/\1/') all

all: PROD
        ...as now...

If you type make, it will read the (empty) dummy makefile, and then try to build the dummy target because it appears first. To build the dummy target, it will run make again, with APP_VER=1.1 or APP_VER=1.2 on the command line:

make APP_VER=1.1 all

Macros set on the command line cannot be changed within the makefile, so this overrides the line in the makefile. The second invocation of make, therefore, will read the correct version-specific makefile, and then build all.

This technique has limitations, most noticeably that it is fiddly to arrange for each and every target to be treated like this. There are ways around it, but usually not worth it.


Project organization

More seriously, I think you need to review what you're doing altogether. You are, presumably, using a version control system (VCS) to manage the source code. Also, presumably, there are some (significant) differences between the version 1.1 and 1.2 source code. So, to be able to do a build for version 1.1, you have to switch from the version 1.1 maintenance branch to the version 1.2 development branch, or something along those lines. So, why isn't the makefile just versioned for 1.1 or 1.2? If you switch between versions, you need to clean out all the derived files (object files, libraries, executables, etc) that may have been built with the wrong source. You have to change the source code over. So why not change the makefile too?

A build script to invoke make

I also observe that since you have the environment variable APP_VER driving your process, that you can finesse the problem by requiring a standardized 'make invoker' that sorts out the APP_VER value and invokes make correctly. Imagine that the script is called build:

#!/bin/sh
: ${APP_VER:=1.2.0.1}  # Latest version is default
case $APP_VER in
[0-9].[0-9].*)
    MAK_VER=`echo $APP_VER | sed -e 's/^\(...\).*/\1/'`
    ;;
*)  echo "`basename $0 .sh`: APP_VER ($APP_VER) should start with two digits followed by dots" 1>&2;
    exit 1;;
esac
exec make MAK_VER=$MAK_VER "$@"

This script validates that APP_VER is set, giving an appropriate default if it is not. It then processes that value to derive the MAK_VER (or errors out if it is incorrect). You'd need to modify that test after you reach version 10, of course, since you are planning to be so successful that you will reach double-digit version numbers in due course.

Given the correct version information, you can now invoke your makefile with any command line arguments.

The makefile can be quite simple:

MAK_VER = dummy

include app$(MAK_VER).mak  

all: PROD
        ...as now...

The appdummy.mak file now contains a rule:

error:
        echo "You must invoke this makefile via the build script" 1>&2
        exit 1

It simply points out the correct way to do the build.

Note that you can avoid the APP_VER environment variable if you keep the product version number under the VCS in a file, and the script then reads the version number from the file. And there could be all sorts of other work done by the script, ensuring that correct tools are installed, other environment variables are set, and so on.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜