GNU make: Extracting argument to -j within Makefile
I've been searching for an hour, and this information appears to be nowhere...
I'd like to be able to extract (and possibly use) the number of requested make "jobs," as passed via the -j option, or by Make itself in the case of sub-makes, in the Makefile.
The开发者_如何学JAVA most promising thing I've seen so far is the $(MAKEFLAGS) variable, but on my system (if I do, say, make -j2) the contents of this variable are only "--jobserver-fds=3,4 -j". Is there any way to get the actual number of jobs passed with -j?
Actually there is a way to implement this completely inside your makefile on *nix.
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 also need to be installed, but it is almost a given. It can be further improved to handle --jobs too
A version using regex and supporting --jobs, as requested in the comments:
MAKE_PID := $(shell echo $$PPID)
JOBS := $(shell ps T | sed -n 's/.*$(MAKE_PID).*$(MAKE).* \(-j\|--jobs=\) *\([0-9][0-9]*\).*/\2/p')
I'm sorry but there is no way to identify the number of parallel jobs -- without writing an application or script that scan the process list to identify the calling parameters.
Check the source code at http://cvs.savannah.gnu.org/viewvc/make/main.c?revision=1.246&root=make&view=markup . Search for job_slots > 1
.
Update: If you have control over the operating range you could wrap the make
application with your own program/script, parse the parameters, set an dedicated environment variable and call the original make afterwards.
Yo,
I've come up with a different approach that I prefer to parsing the command line as it was suggested in the accepted answer. The main issue I have with that is that it doesn't work in a recursively called makefile, as the jobs parameter is passed via the env there, and then it's representing the jobserver which tells you nothing of how many job tokens it's going to provide
So I have decided to actually embrace the jobserver thing and make a small python code that consumes all tokens from the jobserver, counts them, and then puts them back.
This was a neat idea that appeared to be surprisingly easy to implement, and it seems to work for me very well.
There are multiple notes to how it should be used:
- the script must be called with a
+
prefix on the recipe because that enables the "submake" functionality which duplicates the jobserver descriptors for the child process to be able to "spawn workers" - to give a proper jobcount the rule containing this script in the recipe should be made a common order-only phony prerequisite for all your targets so that it will be run by the make prior to anything else and never in parallel to other things, cause then the count will be off
The python script:
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)
example makefile:
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)
running it:
projects/make-fun » make all -j100
JOBS=100
a1
b1
c1
d1
e1
a2
b2
c2
d2
e2
done all
精彩评论