How to remove duplication in Makefile?

谁都会走 提交于 2019-12-01 05:47:37

问题


Is there a way to simplify this sort of repetition in a Makefile?

duo = ./node_modules/.bin/duo

build: lib/background/build lib/page/build lib/popup/build

lib/background/build: lib/background/build/build.js lib/background/build/build.css
lib/page/build: lib/page/build/build.js lib/page/build/build.css
lib/popup/build: lib/popup/build/build.js lib/popup/build/build.css

lib/background/build/build.js: lib/background/index.js node_modules component.json
  @mkdir -p lib/background/build
  @$(duo) lib/background/index.js > lib/background/build/build.js

lib/page/build/build.js: lib/page/index.js node_modules component.json
  @mkdir -p lib/page/build
  @$(duo) lib/page/index.js > lib/page/build/build.js

lib/popup/build/build.js: lib/popup/index.js node_modules component.json
  @mkdir -p lib/popup/build
  @$(duo) lib/popup/index.js > lib/popup/build/build.js

lib/background/build/build.css: lib/background/index.css node_modules component.json
  @mkdir -p lib/background/build
  @$(duo) lib/background/index.css | $(myth) > lib/background/build/build.css

lib/page/build/build.css: lib/page/index.css node_modules component.json
  @mkdir -p lib/page/build
  @$(duo) lib/page/index.css | $(myth) > lib/page/build/build.css

lib/popup/build/build.css: lib/popup/index.css node_modules component.json
  @mkdir -p lib/popup/build
  @$(duo) lib/popup/index.css | $(myth) > lib/popup/build/build.css

Basically, I want to run a simple make build command from the top level, and it only rebuilds these subprojects if necessary. I would like to not have to use a Makefile for each subproject because that is also repetitive. Everything I've tried in relation to wildcard paths hasn't worked out, so wondering if there is even a way to do this. For example, I tried doing something like this (similar both for js and css) but no luck:

js = $(shell find lib test -type f -name '*.js' ! -path "*build.js")

$(js)/build/build.js: node_modules component.json
  # somehow get the directory such as lib/background based on the make command?
  local dir=$(shell dirname $(shell dirname $@))
  @mkdir -p $(dir)/build
  @$(duo) $(dir)/index.js > $(dir)/build/build.js

Any ideas how to make this DRY?


回答1:


A good first start would be to stop repeating the targets/etc. in the rule bodies themselves and use the automatic variables for them instead. So '$@' for the target filename, '$(@D)' for the directory path (like dirname) of the target filename, etc.

Which gets you:

duo = ./node_modules/.bin/duo

build: lib/background/build lib/page/build lib/popup/build

lib/background/build: lib/background/build/build.js lib/background/build/build.css
lib/page/build: lib/page/build/build.js lib/page/build/build.css
lib/popup/build: lib/popup/build/build.js lib/popup/build/build.css

lib/background/build/build.js: lib/background/index.js node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) lib/background/index.js > '$@'

lib/page/build/build.js: lib/page/index.js node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) lib/page/index.js > '$@'

lib/popup/build/build.js: lib/popup/index.js node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) lib/popup/index.js > '$@'

lib/background/build/build.css: lib/background/index.css node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) lib/background/index.css | $(myth) > '$@'

lib/page/build/build.css: lib/page/index.css node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) lib/page/index.css | $(myth) > '$@'

lib/popup/build/build.css: lib/popup/index.css node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) lib/popup/index.css | $(myth) > '$@'

Then realizing that pattern rules are helpful when you have targets and prerequisites that share filename patterns and similar rule bodies you can start using those too. They also give you an extra automatic variable.

You get this (intermediate) stage:

duo = ./node_modules/.bin/duo

build: lib/background/build lib/page/build lib/popup/build

lib/background/build: lib/background/build/build.js lib/background/build/build.css
lib/page/build: lib/page/build/build.js lib/page/build/build.css
lib/popup/build: lib/popup/build/build.js lib/popup/build/build.css

%/build/build.js: %/index.js node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.js > '$@'

%/build/build.js: %/index.js node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.js > '$@'

%/build/build.js: %/index.js node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.js > '$@'

%/build/build.css: %/index.css node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.css | $(myth) > '$@'

%/build/build.css: %/index.css node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.css | $(myth) > '$@'

%/build/build.css: %/index.css node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.css | $(myth) > '$@'

And them you see that you've really just got two duplicate rules so you combine them.

duo = ./node_modules/.bin/duo

build: lib/background/build lib/page/build lib/popup/build

lib/background/build: lib/background/build/build.js lib/background/build/build.css
lib/page/build: lib/page/build/build.js lib/page/build/build.css
lib/popup/build: lib/popup/build/build.js lib/popup/build/build.css

%/build/build.js: %/index.js node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.js > '$@'

%/build/build.css: %/index.css node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.css | $(myth) > '$@'

And then, since make doesn't handle directory targets/prerequisites as well as one might like, you can drop the lib/background/build, lib/page/build, and lib/popup/build intermediate targets and just list the actual files as the prereqs for build.

duo = ./node_modules/.bin/duo

build: lib/background/build/build.js lib/background/build/build.css \
       lib/page/build/build.js lib/page/build/build.css \
       lib/popup/build/build.js lib/popup/build/build.css

%/build/build.js: %/index.js node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.js > '$@'

%/build/build.css: %/index.css node_modules component.json
        @mkdir -p '$(@D)'
        @$(duo) '$*'/index.css | $(myth) > '$@'

I should probably mention that I didn't test this (for lack of desire to mock up the directory layout/etc.) but the transformations were straight-forward and the concepts are fairly simple so it should work just fine. But anything is possible.

To clean up the build prerequisites you could use something like:

build: $(foreach d,background page popup,$(addprefix lib/$d/build/,build.js build.css))


来源:https://stackoverflow.com/questions/25536683/how-to-remove-duplication-in-makefile

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!