清理我的 makefile 片段吗?

发布于 2024-09-28 23:57:27 字数 877 浏览 4 评论 0原文

我正在使用 GNU Make 来运行科学数据分析,其中我们有一堆 Matlab 脚本,可以思考一些输入数据,然后生成几个文件。它以相当复杂的方式完成此操作,因此在尝试向 Make 描述其功能时,我不得不使用一些令人讨厌的 Makefile 技巧。这是一个例子:

seg_cams_nozero := cam1 cam2 cam3
seg_per_camera := $(shell echo {,dyn_}{hands,obj{1,2,3,4,5}}.mat)
# the complete list of things we want
segmentation_outputs := $(foreach cam,$(seg_cams_nozero),$(foreach product,$(seg_per_camera),derived/cont_$(cam)_$(product)))

# how to make some product, independent of what camera
define seg_per_product
derived/cont_cam%_$$(product): /path/to/input/file_%.mat
        run_a_script $$*
endef

$(foreach product,$(seg_per_camera),$(eval $(seg_per_product)))

segmentation: $(segmentation_outputs)

所以,这基本上是可怕的,因为我还没有弄清楚如何在这种情况下有效地使用模式规则,并且因为我必须使用 shell 生成大量文件名。

你会怎么写这样的东西?您会预先生成文件名并将它们放入包含的 Makefile 中吗?想出一个使用模式规则的好方法吗?有没有办法不用 $(eval ...) 来做到这一点?

I'm using GNU Make to run scientific data analysis, where we have a bunch of Matlab scripts that ponder some input data and then spit out several files. It does this in a fairly complex way, so I've had to do some nasty Makefile tricks when trying to describe its function to Make. Here's an example:

seg_cams_nozero := cam1 cam2 cam3
seg_per_camera := $(shell echo {,dyn_}{hands,obj{1,2,3,4,5}}.mat)
# the complete list of things we want
segmentation_outputs := $(foreach cam,$(seg_cams_nozero),$(foreach product,$(seg_per_camera),derived/cont_$(cam)_$(product)))

# how to make some product, independent of what camera
define seg_per_product
derived/cont_cam%_$(product): /path/to/input/file_%.mat
        run_a_script $*
endef

$(foreach product,$(seg_per_camera),$(eval $(seg_per_product)))

segmentation: $(segmentation_outputs)

So, this is basically horrible because I haven't figured out how to use pattern rules effectively in this situation, and because I have to generate a ton of filenames using the shell.

How would you write something like this? Would you pre-generate the filenames and put them in an included Makefile? Figure out a great way to use pattern rules? Is there any way to do it without $(eval ...)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

扭转时空 2024-10-05 23:57:27

有几种方法可以做到这一点,但没有一种方法是完美的(直到有人将正则表达式处理放入 Make 中)。

首先,我注意到一个命令为给定的摄像机创建了所有目标,但它针对每个目标运行,这是一种浪费。因此,让我们从 cam1 的目标开始

cam1_outputs := $(addprefix derived/cont_cam1_, $(seg_per_camera))

,将第一个目标设为“主要目标”(并将其从列表中删除)。这将是其余部分的先决条件,并且是唯一实际需要运行该脚本的部分。 (使用更高级的方法可能有稍微更优雅的方法,但现在就这样了。)

cam1_primary := $(firstword $(cam1_outputs))
cam1_outputs := $(filter-out $(cam1_primary), $(cam1_outputs))

$(cam1_outputs): $(cam1_primary)

$(cam1_primary): /path/to/input/file_1.mat 
    run_a_script 1

现在将其扩展到其他两个相机。我们可以将“主要”规则重写为模式规则:

$(cam1_primary) $(cam2_primary) $(cam3_primary): derived/cont_cam%_hands.mat: /path/to/input/file_%.mat
    run_a_script $*

其余的我们可以为所有三个摄像头拼写出来,但这意味着大量冗余代码。我们可以定义一个模板并评估它,但我希望尽可能避免这种情况。因此,我们将使用一个小技巧:(

cam2_primary := $(subst cam1,cam2,$(cam1_primary))
# ...and the same for the rest...

这有点多余,但并不太可怕。)

将它们放在一起,我们得到:

# Mention this first so it'll be the default target
segmentation:

seg_cams_nozero := cam1 cam2 cam3 

# seg_per_camera := $(shell echo {,dyn_}{hands,obj{1,2,3,4,5}}.mat) 
# Let's do this without the shell:
seg_per_camera := hands $(addprefix obj, 1 2 3 4 5)
seg_per_camera += $(addprefix dyn_, $(seg_per_camera))
seg_per_camera := $(addsuffix .mat, $(seg_per_camera))

cam1_outputs := $(addprefix derived/cont_cam1_, $(seg_per_camera))

# Now's a good time for this.
segmentation_outputs := $(cam1_outputs)
segmentation_outputs += $(subst cam1,cam2,$(cam1_outputs))
segmentation_outputs += $(subst cam1,cam3,$(cam1_outputs))

cam1_primary := $(firstword $(cam1_outputs))
cam1_outputs := $(filter-out $(cam1_primary), $(cam1_outputs)) 
$(cam1_outputs): $(cam1_primary)

cam2_primary := $(subst cam1,cam2,$(cam1_primary))
cam2_outputs := $(subst cam1,cam2,$(cam1_outputs))
$(cam2_outputs): $(cam2_primary)

cam3_primary := $(subst cam1,cam3,$(cam1_primary))
cam3_outputs := $(subst cam1,cam3,$(cam1_outputs))
$(cam3_outputs): $(cam3_primary)

$(cam1_primary) $(cam2_primary) $(cam3_primary): derived/cont_cam%_hands.mat: /path/to/input/file_%.mat
    run_a_script $*

segmentation: $(segmentation_outputs)

There are several ways to do this, none of them perfect (until someone puts regex handling into Make).

First, I notice that one command makes all of the targets for a given camera, but it's run for each target, which is a waste. So let's start with the targets for cam1

cam1_outputs := $(addprefix derived/cont_cam1_, $(seg_per_camera))

and make the first one the "primary one" (and remove it from the list). This will be a prerequisite for the rest, and will be the only one that actually requires that the script be run. (There may be slightly more elegant ways using more advanced methods, but this'll do for now.)

cam1_primary := $(firstword $(cam1_outputs))
cam1_outputs := $(filter-out $(cam1_primary), $(cam1_outputs))

$(cam1_outputs): $(cam1_primary)

$(cam1_primary): /path/to/input/file_1.mat 
    run_a_script 1

Now to extend this to the other two cameras. We can rewrite the "primary" rule as a pattern rule:

$(cam1_primary) $(cam2_primary) $(cam3_primary): derived/cont_cam%_hands.mat: /path/to/input/file_%.mat
    run_a_script $*

The rest we could just spell out for all three cameras, but that would mean a lot of redundant code. We could define a template and eval it, but I like to avoid that if possible. So we'll just use a little trick:

cam2_primary := $(subst cam1,cam2,$(cam1_primary))
# ...and the same for the rest...

(That's a little redundant, but not too terrible.)

Put it all together and we get:

# Mention this first so it'll be the default target
segmentation:

seg_cams_nozero := cam1 cam2 cam3 

# seg_per_camera := $(shell echo {,dyn_}{hands,obj{1,2,3,4,5}}.mat) 
# Let's do this without the shell:
seg_per_camera := hands $(addprefix obj, 1 2 3 4 5)
seg_per_camera += $(addprefix dyn_, $(seg_per_camera))
seg_per_camera := $(addsuffix .mat, $(seg_per_camera))

cam1_outputs := $(addprefix derived/cont_cam1_, $(seg_per_camera))

# Now's a good time for this.
segmentation_outputs := $(cam1_outputs)
segmentation_outputs += $(subst cam1,cam2,$(cam1_outputs))
segmentation_outputs += $(subst cam1,cam3,$(cam1_outputs))

cam1_primary := $(firstword $(cam1_outputs))
cam1_outputs := $(filter-out $(cam1_primary), $(cam1_outputs)) 
$(cam1_outputs): $(cam1_primary)

cam2_primary := $(subst cam1,cam2,$(cam1_primary))
cam2_outputs := $(subst cam1,cam2,$(cam1_outputs))
$(cam2_outputs): $(cam2_primary)

cam3_primary := $(subst cam1,cam3,$(cam1_primary))
cam3_outputs := $(subst cam1,cam3,$(cam1_outputs))
$(cam3_outputs): $(cam3_primary)

$(cam1_primary) $(cam2_primary) $(cam3_primary): derived/cont_cam%_hands.mat: /path/to/input/file_%.mat
    run_a_script $*

segmentation: $(segmentation_outputs)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文