2016-09-20 93 views
2

我想创建一个使用monorepo的Makefile,并且正在基于源文件目录的动态输出目录挣扎。基于源文件目录的动态输出目录的Makefile

我的项目被布置像这样:

% tree . 
. 
├── Makefile 
└── packages 
   ├── bar 
   │   └── src 
   │    └── index.js 
   │    └── other-file.js 
   └── foo 
    └── src 
     └── index.js 

5 directories, 4 files 

欲处理每个*.js文件每个包的内部(例如,./packages/foo/src/index.js),并发射输出到另一个目录(具体地,./packages/foo/lib/index.js)。

例如make bar将:

  • 过程./packages/bar/src/index.js - >./packages/bar/lib/index.js
  • 过程./packages/bar/src/other-file.js - >./packages/bar/lib/other-file.js

我也将潜在地喜欢用这种模式用于在./src其他类型的文件的如处理.less文件。

的Makefile

PACKAGES_ROOT = ./packages 
packages := $(shell ls $(PACKAGES_ROOT)) 

package_source_dir := $(addprefix packages/,$(addsuffix /src/,$(packages))) 
package_dest_dir := $(subst src,lib,$(package_source_dir)) 

package_source := $(foreach sdir, $(package_source_dir), $(wildcard $(sdir)*.js*)) 
package_dest := $(subst src,lib,$(package_source)) 

.PHONY: all checkdirs clean 
all: checkdirs 

checkdirs: $(package_dest_dir) 

$(package_dest_dir): 
    mkdir -p [email protected] 

$(packages): 
    @echo "[email protected]" 

clean: 
    rm -rf $(package_dest_dir) 

我不知道我是否需要使用VPATH还是什么...帮助吗?

注意:Makefile目前只是足以制作和删除所有目标目录。

+2

虽然你的问题并不完全是[this one]的重复(http://stackoverflow.com/questions/39015453/building-c-program-out-of-source-tree-with-gnu-make/39033569 #39033569),你可以在这里找到几种不同的方法来实现你想要做的事情。花时间阅读并理解答案。 – Tim

+0

谢谢你指点我正确的方向。我实际上已经尝试过动态规则生成,但是我的'define'永远不会被调用。再给它一次,并更新我的问题(并希望回答)。 –

回答

1

像这样的东西应该做的工作:

# Directories 
PKGS_ROOT := packages 
PKGS_SRCDIR := src 
PKGS_OUTDIR := lib 

# Expands to the source directory for the specified package 
pkg-srcdir = $(PKGS_ROOT)/$1/$(PKGS_SRCDIR) 
# Expands to the output directory for the specified package 
pkg-libdir = $(PKGS_ROOT)/$1/$(PKGS_OUTDIR) 
# Expands to all output targets for the specified package 
pkg-libs = $(addprefix $(call pkg-libdir,$1)/,$(notdir $(wildcard $(call pkg-srcdir,$1)/*.js))) 

# Defines the following rules for the specified package: 
# - build rule for .js files 
# - rule to create the output directory if missing 
# - package rule to build all outputs 
# - clean-package rule to remove the output directory 
# Adds the following prerequisites: 
# - package target to 'all' rule 
# - clean-package target to 'clean' rule 
define pkg-rules 
$(call pkg-libdir,$1)/%.js: $(call pkg-srcdir,$1)/%.js | $(call pkg-libdir,$1) 
    @echo Making [email protected] from $$^ 
    @cp $$^ [email protected] 
$(call pkg-libdir,$1): 
    @mkdir [email protected] 
$1: $(call pkg-libs,$1) 
clean-$1: 
    rm -rf $(call pkg-libdir,$1) 
all: $1 
clean: clean-$1 
.PHONY: $1 clean-$1 
endef 

# Creates rules for the specified package 
add-pkg = $(eval $(call pkg-rules,$1)) 

# Create rules for all packages 
PKGS := $(notdir $(wildcard $(PKGS_ROOT)/*)) 
$(foreach p,$(PKGS),$(call add-pkg,$p)) 

# Will be filled in by pkg-rules 
.PHONY: all clean 

.DEFAULT_GOAL := all 

的各个功能/助理/操作应该不难理解。我将目录抽象为前三名助手,以避免在需要时不得不更改大量事件。让我们深入pkg-rules,其中定义了实际的动态规则。

pkg-rules通过foo。它会创建以下规则:

  • packages/foo/lib/%.js: packages/foo/src/%.js | packages/foo/lib:这是建立在输出.js文件的形式规则。它还将输出目录作为订单唯一的前提条件(与执行顺序无关 - 这意味着目录目标只有在不存在时才会构建,而不会查看时间戳)。
  • packages/foo/lib:此规则创建输出目录。您没有嵌套目录,如果目录存在,配方将不会执行,因此不需要-p。顺便说一下,可能有嵌套的目录并复制源树,但它需要更多的诡计(可能是次要扩展,$(@D),使用更为巧妙的patsubst代替notdir,使用shell进行递归通配符,提取目录等)。
  • foo: packages/foo/lib/index.js:在评论中我称之为“包目标/规则”。这是一个空虚的假冒规则,它将所有包裹输出作为先决条件。
  • clean-foo:在评论中,我称之为“清理包目标/规则”。这是一个假冒规则,它删除了包输出目录。

它还将增加为前提条件的包装和清洁包的目标,以通用allclean规则。

foo,clean-bar,allclean这样的目标的行为与您预期的完全相同。

1

您可以生成一个临时生成文件:

packages:= $(shell ls $(PACKAGES_ROOT)) 

packages.mk : Makefile $(abspath .) 
    for package in $(packages); do \ 
     echo "packages/$(package)/lib/%.js:packages/$(package)/src/%.js" >> [email protected] ; \ 
     echo " echo recipe for $(dir)" >> [email protected]; \ 
     echo >> [email protected]; \ 
    done 

-include "packages.mk" 

这假定目录不是动态的其他规则创建的,因为$(packages)将只包含其中存在的时候makefile文件是第一次读取的目录。