具有基于源文件目录的动态输出目录的Makefile [英] Makefile with Dynamic Output Directory Based on Source File Directory
问题描述
我正在尝试使用 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 cleverpatsubst
in place ofnotdir
, 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.
还将打包和干净打包目标作为通用all
和clean
规则的前提条件添加.
It will also add the package and clean-package targets as prerequisites to the generic all
and clean
rules.
foo
,clean-bar
,all
和clean
等目标的行为将完全符合您的预期.
Targets like foo
, clean-bar
, all
and clean
will behave exactly like you expect.
这篇关于具有基于源文件目录的动态输出目录的Makefile的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!