【问题标题】:Makefile with Dynamic Output Directory Based on Source File Directory具有基于源文件目录的动态输出目录的 Makefile
【发布时间】:2016-09-20 00:52:16
【问题描述】:

我正在尝试使用 monorepo 创建一个 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 文件。

生成文件

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 $@

$(packages):
    @echo "$@"

clean:
    rm -rf $(package_dest_dir)

我不确定是否需要使用VPATH 或什么...帮助?

注意:Makefile 当前足以创建和删除所有目标目录。

【问题讨论】:

  • 虽然您的问题与this one 不完全重复,但您可以在这里找到一些不同的方法来实现您想要做的事情。花时间阅读并理解答案。
  • 感谢您为我指明正确的方向。我实际上已经尝试过动态规则生成,但我的define 从未被调用过。再试一次,并用它更新我的问题(并希望得到答案)。

标签: makefile gnu-make


【解决方案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 $$@ from $$^
    @cp $$^ $$@
$(call pkg-libdir,$1):
    @mkdir $$@
$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:在 cmets 中我称之为“包目标/规则”。这是一个空的虚假规则,它将所有包输出作为先决条件。
  • clean-foo:在 cmets 中,我称之为“干净包目标/规则”。这是删除包输出目录的虚假规则。

它还将 package 和 clean-package 目标添加为通用 allclean 规则的先决条件。

fooclean-barallclean 等目标的行为将完全符合您的预期。

【讨论】:

    【解决方案2】:

    你可以生成一个临时的 makefile:

    packages:= $(shell ls $(PACKAGES_ROOT))
    
    packages.mk : Makefile $(abspath .)
        for package in $(packages); do \
            echo "packages/$(package)/lib/%.js:packages/$(package)/src/%.js" >> $@ ; \
            echo "    echo recipe for $(dir)" >> $@; \
            echo >> $@; \
        done
    
    -include "packages.mk"
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多