Makefile 如何优化生成文件,以避免重复

Makefile 如何优化生成文件,以避免重复,makefile,gnu-make,Makefile,Gnu Make,我不熟悉使用make,一直在尝试了解如何正确配置make文件。特别是,我认为有一种更好的方式来处理遵循某种模式的规则和受抚养人。但我不能真正理解这方面的手册 我现在有了一个可以工作的makefile(在OSX和Linux上,我假设是GNUMake),但是我不理解的技术可以大大缩短它。它有不同目录的重复模式。你能告诉我如何使以下更好,并让我知道哪些设施是用于每个缩短 # Include this file NOT stored in repository which defines which e

我不熟悉使用
make
,一直在尝试了解如何正确配置make文件。特别是,我认为有一种更好的方式来处理遵循某种模式的规则和受抚养人。但我不能真正理解这方面的手册

我现在有了一个可以工作的makefile(在OSX和Linux上,我假设是GNUMake),但是我不理解的技术可以大大缩短它。它有不同目录的重复模式。你能告诉我如何使以下更好,并让我知道哪些设施是用于每个缩短

# Include this file NOT stored in repository which defines which environment to use
include Makefile.local
# Work out the pas version
PAS=${shell git describe --abbrev=0}
# Version of node being used
VERSION = 7.6.0

\.dockerimage: access/.dockerimage evening/.dockerimage server/.dockerimage
    touch .dockerimage
server/.dockerimage: client/client/.dockerimage server/.env $(shell find server  \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)
    docker image build -t server:${PAS} -t server:latest server
    touch server/.dockerimage
server/.env: environments/common/server.env environments/${enviro}/server.env
    cat environments/common/server.env > server/.env
    cat environments/${enviro}/server.env >> server/.env
access/.dockerimage: request/.dockerimage access/.env $(shell find access  \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)
    docker image build -t access:${PAS} -t access:latest access
    touch access/.dockerimage
access/.env: environments/common/access.env environments/${enviro}/access.env
    cat environments/common/access.env > access/.env
    cat environments/${enviro}/access.env >> access/.env
evening/.dockerimage: request/.dockerimage evening/.env $(shell find evening  \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)
    docker image build -t evening:${PAS} -t evening:latest evening
    touch evening/.dockerimage
evening/.env: environments/common/evening.env environments/${enviro}/evening.env
    cat environments/common/evening.env > evening/.env
    cat environments/${enviro}/evening.env >> evening/.env
pcode/.dockerimage: libs/.dockerimage pcode/.env $(shell find pcode  \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)
    docker image build -t pcode:${PAS} -t pcode:latest pcode
    touch pcode/.dockerimage
pcode/.env: environments/common/pcode.env environments/${enviro}/pcode.env
    cat environments/common/pcode.env > pcode/.env
    cat environments/${enviro}/pcode.env >> pcode/.env
test-client/.dockerimage: client/client/.dockerimage
    docker image build -t test-client:${PAS} test-client:latest test-client
    touch test-client/.dockerimage
test-server/.dockerimage: services/.dockerimage
    docker image build -t test-server:${PAS} -t test-server:latest test-server
    touch test-server/.dockerimage
client/client/.dockerimage: services/.dockerimage $(shell find client/client \! -name .dockerimage -type f)
    docker image build -t client:${PAS} -t client:latest client/client
    touch client/client/.dockerimage
services/.dockerimage: client/.dockerimage $(shell find services \! -name .dockerimage -type f -print0)
    docker image build -t services:${PAS} -t services:latest services  --build-arg PAS_VERSION=${PAS}
    touch services/.dockerimage
client/.dockerimage: libs/.dockerimage $(shell find client \! -name .dockerimage -depth 1 -type f)
    docker image build -t components:${PAS} -t components:latest client
    touch client/.dockerimage
libs/.dockerimage: request/.dockerimage $(shell find libs \! -name .dockerimage -type f)
    docker image build -t libs:${PAS} -t libs:latest libs
    touch libs/.dockerimage
request/.dockerimage: node/.dockerimage $(shell find request \! -name .dockerimage -type f)
    docker image build -t request:${PAS} -t request:latest request
    touch request/.dockerimage
node/.dockerimage: node/Dockerfile-${ARCH} node/.dockerignore
    docker image build -f node/Dockerfile-${ARCH} -t node:${VERSION} -t node:latest node
    touch node/.dockerimage
clean: clean-images clean-above clean-env
    rm node/.dockerimage
    docker image rm -f node:latest
    docker image rm -f node:${VERSION}
clean-above:
    for dir in server access evening pcode client/client services test-client test-server servies client libs request; \
        do rm $$dir/.dockerimage; done
clean-env:
    for dir in access evening pcode server; do rm $$dir/.env; done
clean-images: clean-above
    for dir in access access evening pcode client/client services test-client test-server services client libs request; \
     do docker image rm -f $$dir:latest;  docker image rm -f $$dir:${PAS}; done
.PHONY: run clean clean-above clean-images clean-env

在windows上的git bash(向其中添加make.exe)下运行也会很有用,尽管这不是必需的。

StackOverflow并不是要求别人重写代码的地方

我将说两件事:首先,为了减少重复,您需要了解:您可以将大量重复的项放在变量中,然后使用这些项,而不是多次写入所有内容。这很简单

第二,你应该调查,尤其是。看起来规则之间有足够的重叠,您应该能够创建一些不同的模式规则,而不是为每个目标编写单独的显式规则。这是一个稍微高级一点的主题

您可以尝试修改makefile,看看会发生什么:反复试验通常是最好的学习方法,而且成本低廉,易于多次运行
make
。如果遇到无法解决的问题,现在您有了一个关于StackOverflow的适当、特定的问题:)


哦,最后一件事:你应该考虑使用变量(即,使用<代码>:= /代码>赋值而不是<代码> = />代码>,特别是对于你的<代码> $(shell)调用;它将更加有效。

简化生成文件在迭代时会产生良好的结果。从较小的步骤开始,在函数/变量/隐式规则中包装两个重复的模式,并一次又一次地这样做。 以下是我试图简化makefile中的一些规则的尝试:

some_prerequisites = $1/.env $(shell find $1 \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)

environment_recipe = cat environments/common/$1.env environments/${enviro}/$1.env > $@
dockerimage_recipe = docker image build -t $1:${PAS} -t $1:latest $1 && touch $@

\.dockerimage: access/.dockerimage evening/.dockerimage server/.dockerimage
    touch $@
server/.dockerimage: client/client/.dockerimage $(call some_prerequisites, server)
    $(call dockerimage_recipe,server)
server/.env: environments/common/server.env environments/${enviro}/server.env
    $(call environment_recipe,server)
access/.dockerimage: request/.dockerimage $(call some_prerequisites, access)
    $(call dockerimage_recipe,access)
access/.env: environments/common/access.env environments/${enviro}/access.env
    $(call environment_recipe,access)
evening/.dockerimage: request/.dockerimage $(call some_prerequisites, evening)
    $(call dockerimage_recipe,evening)
evening/.env: environments/common/evening.env environments/${enviro}/evening.env
    $(call environment_recipe,evening)
pcode/.dockerimage: libs/.dockerimage $(call some_prerequisites, pcode)
    $(call dockerimage_recipe,pcode)
pcode/.env: environments/common/pcode.env environments/${enviro}/pcode.env
    $(call environment_recipe,pcode)
在第一次迭代中,我引入了
一些前提条件
函数。然后,当makefile变得稍微不那么混乱时,我添加了
environment\u recipe
dockerimage\u recipe

这是#2次迭代:

some_prerequisites = $1/.env $(shell find $1 \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)

environment_recipe = cat environments/common/$1.env environments/${enviro}/$1.env > $@
dockerimage_recipe = docker image build -t $1:${PAS} -t $1:latest $1 && touch $@

\.dockerimage: access/.dockerimage evening/.dockerimage server/.dockerimage
    touch $@
server/.dockerimage: client/client/.dockerimage $(call some_prerequisites, server)
    $(call dockerimage_recipe,server)
access/.dockerimage: request/.dockerimage $(call some_prerequisites, access)
    $(call dockerimage_recipe,access)
evening/.dockerimage: request/.dockerimage $(call some_prerequisites, evening)
    $(call dockerimage_recipe,evening)
pcode/.dockerimage: libs/.dockerimage $(call some_prerequisites, pcode)
    $(call dockerimage_recipe,pcode)

$(foreach e, pcode evening access server \
    $(eval $e/.env: environments/common/$e.env environments/${enviro}/$e.env; \
        $$(call environment_recipe,$e) \
    ) \
)
some_prerequisites = $1/.env $(shell find $1 \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)

environment_recipe = cat environments/common/$1.env environments/${enviro}/$1.env > $@
dockerimage_recipe = docker image build -t $1:${PAS} -t $1:latest $1 && touch $@

\.dockerimage: access/.dockerimage evening/.dockerimage server/.dockerimage
    touch $@
server/.dockerimage: client/client/.dockerimage
access/.dockerimage: request/.dockerimage
evening/.dockerimage: request/.dockerimage
pcode/.dockerimage: libs/.dockerimage

$(foreach e, pcode evening access server \
    $(eval $e/.env: environments/common/$e.env environments/${enviro}/$e.env; \
        $$(call environment_recipe,$e) \
    ) \
    $(eval $e/.dockerimage: $(call some_prerequisites, $e); \
        $$(call dockerimage_recipe,$e) \
    )\
)
这是#3次迭代:

some_prerequisites = $1/.env $(shell find $1 \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)

environment_recipe = cat environments/common/$1.env environments/${enviro}/$1.env > $@
dockerimage_recipe = docker image build -t $1:${PAS} -t $1:latest $1 && touch $@

\.dockerimage: access/.dockerimage evening/.dockerimage server/.dockerimage
    touch $@
server/.dockerimage: client/client/.dockerimage $(call some_prerequisites, server)
    $(call dockerimage_recipe,server)
access/.dockerimage: request/.dockerimage $(call some_prerequisites, access)
    $(call dockerimage_recipe,access)
evening/.dockerimage: request/.dockerimage $(call some_prerequisites, evening)
    $(call dockerimage_recipe,evening)
pcode/.dockerimage: libs/.dockerimage $(call some_prerequisites, pcode)
    $(call dockerimage_recipe,pcode)

$(foreach e, pcode evening access server \
    $(eval $e/.env: environments/common/$e.env environments/${enviro}/$e.env; \
        $$(call environment_recipe,$e) \
    ) \
)
some_prerequisites = $1/.env $(shell find $1 \! -name pacakge.json \! -name .dockerimage -depth 1 -type f)

environment_recipe = cat environments/common/$1.env environments/${enviro}/$1.env > $@
dockerimage_recipe = docker image build -t $1:${PAS} -t $1:latest $1 && touch $@

\.dockerimage: access/.dockerimage evening/.dockerimage server/.dockerimage
    touch $@
server/.dockerimage: client/client/.dockerimage
access/.dockerimage: request/.dockerimage
evening/.dockerimage: request/.dockerimage
pcode/.dockerimage: libs/.dockerimage

$(foreach e, pcode evening access server \
    $(eval $e/.env: environments/common/$e.env environments/${enviro}/$e.env; \
        $$(call environment_recipe,$e) \
    ) \
    $(eval $e/.dockerimage: $(call some_prerequisites, $e); \
        $$(call dockerimage_recipe,$e) \
    )\
)

在我看来,这对于makefile中的一些规则来说已经足够简化了。其他的可以按照相同的模式简化。

多亏了@MadScientist,我一直在探索隐式模式。我有一个关于扩展的问题,在另一个SO问题/答案中得到了解决。我想,为了完整性起见,我会在我必须发布的地方发布。由于最初的问题,我在makefile中添加了很多选项,所以它不是一对一的比较

# Include this file NOT stored in repository which defines which environment to use
include Makefile.local
#work out architecture from envonment
ARCH := ${shell cat environments/${enviro}/arch}
# Work out the pas version
PAS := ${shell git describe --abbrev=0}
# None standard dependancies for each of the images
server-IMAGEDEPS := libs/database/index.js libs/log/index.js libs/utils/index.js\
    $(shell find services/manager -type f -not -name package.json -print0) $(shell find services/web -type f -not -name package.json) \
    $(shell find client -type f -not -name .bowerrc -not -name bower.json -not -name package.json -not -name DOCKBUILDfile -print0)
server-BASEDEPS := services/.dockerimage
server-LINKDEPS := pasv5-database pasv5-manager pasv5-web
access-IMAGEDEPS := libs/request/index.js
access-BASEDEPS := libs/.dockerimage
access-LINKDEPS := pasv5-request
evening-IMAGEDEPS := libs/request/index.js libs/log/index.js
evening-BASEDEPS := libs/.dockerimage
evening-LINKDEPS := pasv5-request pasv5-log
pcode-IMAGEDEPS := libs/database/index.js libs/log/index.js libs/utils/index.js
pcode-BASEDEPS := libs/.dockerimage
pcode-LINKDEPS := pasv5-database pasv5-log pasv5-utils
manage-LINKDEPS := pasv5-utils pasv5-log
web-LINKDEPS := pasv5-log
daily-LINKDEPS := pasv5-database

LINKMODULEDIRS := libs/database libs/log libs/utils libs/request services/manager services/web
NODEMODULEDIRS := $(LINKMODULEDIRS) access evening pcode server daily

# Standard docker build command
DOCKBUILD = docker image build -t $(@D)-base:stable $(@D); touch $@

all: server/.dockerimage evening/.dockerimage pcode/.dockerimage access/.dockerimage

daily/.env: pcode/.env
    cp pcode/.env daily/.env
services/.dockerimage: client/.dockerimage services/Dockerfile services/package.json services/manager/package.json services/web/package.json
    @(DOCKBUILD)
client/.dockerimage: libs/.dockerimage client/Dockerfile client/package.json client/.bowerrc client/bower.json
    @(DOCKBUILD)
libs/.dockerimage: node/.dockerimage libs/Dockerfile libs/package.json libs/database/package.docker libs/log/package.json libs/utils/package.json \
    libs/request/package.json libs/request/akc-crt.pem
    @(DOCKBUILD)
node/.dockerimage: node/Dockerfile-${ARCH} node/.dockerignore
    docker image build -f node/Dockerfile-${ARCH} -t node:latest node
    $(eval VERSION := $(shell docker run node:latest node --version | cut -c 2- ))
    docker image tag node:latest node:${VERSION}
    docker image tag node:latest docker.hartley-consultants.com/node:${VERSION}
    touch $@
# stable version of node
node-stable:
    docker image tag node:latest node:stable
clean: clean-images clean-above clean-env
    rm node/.dockerimage
    docker image rm -f node:latest
    docker image rm -f node:${VERSION}
clean-above:
    for dir in server access evening pcode client services libs; do [ -f $$dir/.dockerimage ] && rm $$dir/.dockerimage; [ -f $$dir/.dockerbase ] && rm $$dir/.dockerbase; done; exit 0
clean-env:
    for dir in access evening pcode server; do rm $$dir/.env; done
clean-images: clean-above
    for img in access evening pcode server; \
     do docker image rm -f $$img-base:stable;  docker image rm -f $$img-${enviro}:${PAS}; rm $$img/.dockerimage; rm $$img/.dockerbase; done
    for img in services client libs; do docker image rm -f $$img:stable; rm $$img/.dockerimage; done

# sets up to run system without docker images
local: server/.env evening/.env pcode/.env access/.env daily/.env $(foreach dir,$(NODEMODULEDIRS), $(dir)/node_modules)
    npm install -g bower && cd client && bower install

pasv5-database:
    cd libs/database; npm link
pasv5-log:
    cd libs/log; npm link
pasv5-utils:
    cd libs/utils; npm link
pasv5-request:
    cd libs/request; npm link
pasv5-manager:
    cd services/manager; npm-link
pasv5-web:
    cd services/web; npm-link
clean-local:
    $(foreach dir,$(NODEMODULEDIRS), $(shell rm -rf $(dir)/node_modules))
.SECONDEXPANSION:
# pattern rules
%/.env: envionments/common/%.env environments/${enviro}/%.env; cat $^ > $@
%/.dockerbase: %/Dockerfile %/package.docker %/.env $$(%-BASEDEPS)
    $(DOCKBUILD)
%/.dockerimage: %/.dockerbase %/server.js Dockerfile-% $$(%-IMAGEDEPS)
    docker image build -t $(@D)-${enviro}:${PAS} --build-arg PAS_VERSION=${PAS} -f Dockerfile-${@D} .
ifeq ($(enviro), production)
    docker tag $(@D)-production:${PAS} docker.hartley-consultants.com/pas/$(@D):${PAS}
endif
    touch $@
%/node_modules: $$(%-LINKDEPS)
    cd $*; npm install; for link in $^ ; do npm link $$link ; done


.PHONY: all clean clean-above clean-images clean-env node-stable local clean-local pasv5-database pasv5-log pasv5-utils pasv5-request pasv5-manager pasv5-web

我不想我的代码被重写!这就是我一直在寻找的答案。对于一个全新的人来说,即使知道该看哪个部分也很难找到。我认为隐含规则是C和C++的某些规则生成的,而不是我控制的。告诉我如何让它变得更好。。。它在git bash下运行非常有用,就像你要求别人重写它一样,但我可能只是误解了这一点。R/C/C++规则内置于make,这些调用被称为(在GNU制作手册)“内置规则”。所有的内置规则都必须是隐式的,但是您可以定义自己的隐式规则,如果这些规则不充分或不合适的话,这些规则将扩充或替换内置规则。祝你好运