具有基于源文件目录的动态输出目录的Makefile [英] Makefile with Dynamic Output Directory Based on Source File Directory

查看:33
本文介绍了具有基于源文件目录的动态输出目录的Makefile的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 monorepo ,并根据源文件的目录来处理动态输出目录.

I'm trying to create a Makefile using a monorepo and am struggling a bit with dynamic output directories based on the source files' directories.

我的项目的布局如下:

% 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).

I want to process each *.js file inside of each package (e.g., ./packages/foo/src/index.js) and emit the output into another directory (specifically, ./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
  • Process ./packages/bar/src/index.js -> ./packages/bar/lib/index.js
  • Process ./packages/bar/src/other-file.js -> ./packages/bar/lib/other-file.js

我还可能希望将此模式用于./src中的其他类型的文件,例如处理.less文件.

I would also potentially like to use this pattern for other sorts of files in ./src such as processing .less files.

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

$(packages):
    @echo "$@"

clean:
    rm -rf $(package_dest_dir)

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

I'm not sure if I need to use VPATH or what ... help?

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

Note: The Makefile currently is just enough to make and remove all destination directories.

推荐答案

类似的事情应该可以完成:

Something like this should do the job:

# 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.

The individual functions/helpers/manipulation shouldn't be hard to understand. I abstracted directories into the first three helpers to avoid having to change a ton of occurrences should the need arise. Let's dive in pkg-rules where the actual dynamic rules are defined.

pkg-rules通过foo.它将创建以下规则:

Say pkg-rules gets passed foo. It will create the following rules:

  • 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:在注释中,我将此称为干净包装目标/规则".这是一个伪造的规则,它删除了程序包的输出目录.
  • packages/foo/lib/%.js: packages/foo/src/%.js | packages/foo/lib: this is the pattern rule that builds the output .js file. It also has the output directory as an order-only prerequisite (which has nothing to do with execution order - it means the directory target will be built only if it doesn't exist, without looking at the timestamp).
  • packages/foo/lib: this rule creates the output directory. You don't have nested directories and the recipe won't be executed if the directory exists, so -p is not needed. By the way, it's possible to have nested directories and replicate the source tree, but it requires some more trickery (likely secondary expansion, $(@D), using a more clever patsubst in place of notdir, using the shell for recursive wildcards, extracting dirs, etc).
  • foo: packages/foo/lib/index.js: in the comments I call this the "package target/rule". It's an empty phony rule which has all the package outputs as prerequisites.
  • clean-foo: in the comments I call this "clean-package target/rule". It's a phony rule which removes the package output directory.

还将打包和干净打包目标作为通用allclean规则的前提条件添加.

It will also add the package and clean-package targets as prerequisites to the generic all and clean rules.

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

Targets like foo, clean-bar, all and clean will behave exactly like you expect.

这篇关于具有基于源文件目录的动态输出目录的Makefile的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆