【问题标题】:GNU make: Extracting argument to -j within MakefileGNU make:在 Makefile 中提取参数到 -j
【发布时间】:2011-03-14 19:40:05
【问题描述】:

我已经搜索了一个小时,这个信息似乎无处...

我希望能够在 Makefile 中提取(并可能使用)通过 -j 选项传递的请求的 make“作业”数量,或者在子 make 的情况下由 Make 本身传递。

到目前为止,我看到的最有希望的是 $(MAKEFLAGS) 变量,但是在我的系统上(如果我这样做,比如说 make -j2)这个变量的内容只有“--jobserver-fds= 3,4 -j”。有什么办法可以得到 -j 传递的实际作业数?

【问题讨论】:

  • 你为什么要这样做?这与 make 的精神背道而驰,我很确定有更好的方法来实现你想做的事情。
  • 您可以在顶级 Makefile 中将作业数设为可变参数,该 Makefile 将在内部调用 make -j
  • @static_rtti,我希望更多地了解 make 的实际执行(特别是当它使用 $(MAKE) 调用自身时)。例如,所有 sub-make 的 job_slots 值是否都为 1,或者 sub-make 是否可以传递顶级可用的 N 个作业的某个子集?按照 Raphael 的建议阅读源代码可能是确定这一点的方法,但我认为尝试 make 本身可能更简单。
  • --jobserver-fds 的参数是用于传递作业令牌的文件描述符。 mad-scientist.net/make/jobserver.html 对整个方案有很好的描述。

标签: gnu-make


【解决方案1】:

实际上有一种方法可以在 *nix 上的 makefile 中完全实现这一点。

MAKE_PID := $(shell echo $$PPID)
JOB_FLAG := $(filter -j%, $(subst -j ,-j,$(shell ps T | grep "^\s*$(MAKE_PID).*$(MAKE)")))
JOBS     := $(subst -j,,$(JOB_FLAG))

ps、grep 也需要安装,但几乎是给定的。也可以进一步改进处理 --jobs

按照 cmets 的要求,使用正则表达式并支持 --jobs 的版本:

MAKE_PID := $(shell echo $$PPID)
JOBS := $(shell ps T | sed -n 's/.*$(MAKE_PID).*$(MAKE).* \(-j\|--jobs=\) *\([0-9][0-9]*\).*/\2/p')

【讨论】:

  • 我刚试过这个,效果很好!如果用户没有通过任何-j 进行设置,我可能会做一个小改动将$JOB_FLAG 设置为1。谢谢!
  • 您也可以阅读 /proc/$PPID/cmdline,而不是 grepping ps 的输出。
  • 这不能很好地处理-j。例如,make -j help 将产生 $(JOB_FLAG)-jhelp
  • 如果您已经在使用psgrep,为什么不直接在正则表达式中完成这一切,而不是使用make 函数?
  • 我认为这不适用于带有作业服务器的递归make;读取并行级别的原因之一是要知道作业服务器已分配给make 的当前实例的作业数量。
【解决方案2】:

很抱歉,如果不编写扫描进程列表以识别调用参数的应用程序或脚本,则无法确定并行作业的数量。

查看http://cvs.savannah.gnu.org/viewvc/make/main.c?revision=1.246&root=make&view=markup 的源代码。搜索job_slots > 1

更新:如果您可以控制操作范围,您可以使用自己的程序/脚本包装 make 应用程序,解析参数,设置专用环境变量,然后调用原始 make。

【讨论】:

  • 嗨拉斐尔,感谢您的回答;这就是我害怕的!现在我可以简单地修改我的 Makefile,这样就不需要 -j 的实际值了……我猜这更像是“本着 make 的精神”;-P
【解决方案3】:

哟,

我想出了一种不同的方法,我更喜欢按照接受的答案中的建议来解析命令行。我遇到的主要问题是它在递归调用的makefile中不起作用,因为jobs参数是通过那里的env传递的,然后它代表jobserver,它不会告诉你它将提供多少工作令牌

所以我决定真正接受作业服务器的事情,并编写一个小的 Python 代码,该代码使用来自作业服务器的所有令牌,计算它们,然后将它们放回。

这是一个巧妙的想法,似乎非常容易实现,而且对我来说似乎非常有效。

如何使用它有多个注意事项:

  1. 必须在配方上使用 + 前缀调用脚本,因为这会启用“submake”功能,该功能可以复制子进程的作业服务器描述符,以便能够“生成工人”
  2. 为提供正确的作业计数,配方中包含此脚本的规则应成为所有目标的通用仅订单虚假先决条件,以便 make 在其他任何事情之前运行它,并且永远不会与其他事情并行, 因为那么计数将关闭

python 脚本:


import argparse, os

def safe_int(s):
    try:
        return int(s)
    except:
        return -1

class JobserverArgs:
    known_names = ["jobserver-fds","jobserver-auth"]
    def __init__(self):
        self.fds = "-1,-1"

    @staticmethod
    def from_argv():
        ja = JobserverArgs()
        parser = argparse.ArgumentParser()
        for name in JobserverArgs.known_names:
            parser.add_argument('--'+name, dest="fds")
        parser.parse_known_args(namespace=ja)
        return ja

    def get_fds(self):
        return tuple([safe_int(fd) for fd in (self.fds+",").split(",")][:2])


fd_in, fd_out = JobserverArgs.from_argv().get_fds()

if fd_in == -1 or fd_out == -1:
    print(1)
else:
    os.set_blocking(fd_in, False)

    tokens = os.read(fd_in, 1024)
    os.write(fd_out, tokens)

    print(len(tokens)+1)

示例生成文件:

TARGETS := a b c d e

.PHONY: $(TARGETS)
$(TARGETS):
    @for i in 1 2; do echo "$@$$i"; sleep 1; done

.PHONY: all
all: $(TARGETS)
    @echo done all

$(TARGETS): | common

.PHONY: common
common:
    +$(eval JOBS:=$$(shell python jobs.py $(MAKEFLAGS)))
    @echo JOBS=$(JOBS)

运行它:

projects/make-fun » make all -j100
JOBS=100
a1
b1
c1
d1
e1
a2
b2
c2
d2
e2
done all

【讨论】:

    猜你喜欢
    • 2010-12-06
    • 1970-01-01
    • 2016-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    • 1970-01-01
    • 2016-04-02
    相关资源
    最近更新 更多