1616import inspect
1717import string
1818import sys
19+ import hashlib
1920
2021verbose = False
2122quiet = False
@@ -131,6 +132,12 @@ def wrapper(*, name=None, replaces=None, **kwargs):
131132 return wrapper
132133
133134
135+ def _isiterable (xs ):
136+ return isinstance (xs , Iterable ) and not isinstance (
137+ xs , (str , bytes , bytearray )
138+ )
139+
140+
134141class Target :
135142 def __init__ (self , cwd , name ):
136143 if verbose :
@@ -166,7 +173,7 @@ def format_field(self, value, format_spec):
166173 return ""
167174 if type (value ) == str :
168175 return value
169- if isinstance (value , ( set , tuple ) ):
176+ if _isiterable (value ):
170177 value = list (value )
171178 if type (value ) != list :
172179 value = [value ]
@@ -210,9 +217,23 @@ def materialise(self, replacing=False):
210217 # Actually call the callback.
211218
212219 cwdStack .append (self .cwd )
213- self .callback (
214- ** {k : self .args [k ] for k in self .binding .arguments .keys ()}
215- )
220+ if "kwargs" in self .binding .arguments .keys ():
221+ # If the caller wants kwargs, return all arguments except the standard ones.
222+ cbargs = {
223+ k : v for k , v in self .args .items () if k not in {"dir" }
224+ }
225+ else :
226+ # Otherwise, just call the callback with the ones it asks for.
227+ cbargs = {}
228+ for k in self .binding .arguments .keys ():
229+ if k != "kwargs" :
230+ try :
231+ cbargs [k ] = self .args [k ]
232+ except KeyError :
233+ error (
234+ f"invocation of { self } failed because { k } isn't an argument"
235+ )
236+ self .callback (** cbargs )
216237 cwdStack .pop ()
217238 except BaseException as e :
218239 print (f"Error materialising { self } : { self .callback } " )
@@ -308,9 +329,7 @@ class Targets:
308329 def convert (value , target ):
309330 if not value :
310331 return []
311- assert isinstance (
312- value , (list , tuple )
313- ), "cannot convert non-list to Targets"
332+ assert _isiterable (value ), "cannot convert non-list to Targets"
314333 return [target .targetof (x ) for x in flatten (value )]
315334
316335
@@ -334,7 +353,7 @@ def loadbuildfile(filename):
334353def flatten (items ):
335354 def generate (xs ):
336355 for x in xs :
337- if isinstance ( x , Iterable ) and not isinstance ( x , ( str , bytes ) ):
356+ if _isiterable ( x ):
338357 yield from generate (x )
339358 else :
340359 yield x
@@ -343,15 +362,13 @@ def generate(xs):
343362
344363
345364def targetnamesof (items ):
346- if not isinstance (items , (list , tuple , set )):
347- error ("argument of filenamesof is not a list/tuple/set" )
365+ assert _isiterable (items ), "argument of filenamesof is not a collection"
348366
349367 return [t .name for t in items ]
350368
351369
352370def filenamesof (items ):
353- if not isinstance (items , (list , tuple , set )):
354- error ("argument of filenamesof is not a list/tuple/set" )
371+ assert _isiterable (items ), "argument of filenamesof is not a collection"
355372
356373 def generate (xs ):
357374 for x in xs :
@@ -371,9 +388,12 @@ def filenameof(x):
371388 return xs [0 ]
372389
373390
374- def emit (* args ):
375- outputFp .write (" " .join (args ))
376- outputFp .write ("\n " )
391+ def emit (* args , into = None ):
392+ s = " " .join (args ) + "\n "
393+ if into is not None :
394+ into += [s ]
395+ else :
396+ outputFp .write (s )
377397
378398
379399def emit_rule (name , ins , outs , cmds = [], label = None ):
@@ -382,26 +402,35 @@ def emit_rule(name, ins, outs, cmds=[], label=None):
382402 nonobjs = [f for f in fouts if not f .startswith ("$(OBJ)" )]
383403
384404 emit ("" )
405+
406+ lines = []
385407 if nonobjs :
386- emit ("clean::" )
387- emit ("\t $(hide) rm -f" , * nonobjs )
408+ emit ("clean::" , into = lines )
409+ emit ("\t $(hide) rm -f" , * nonobjs , into = lines )
388410
389- emit (".PHONY:" , name )
411+ emit (".PHONY:" , name , into = lines )
390412 if outs :
391- emit (name , ":" , * fouts )
392- if cmds :
393- emit (* fouts , "&:" , * fins )
394- else :
395- emit (* fouts , ":" , * fins )
413+ emit (name , ":" , * fouts , into = lines )
414+ emit (* fouts , "&:" , * fins , "\x01 " , into = lines )
396415
397416 if label :
398- emit ("\t $(hide)" , "$(ECHO)" , label )
417+ emit ("\t $(hide)" , "$(ECHO) $(PROGRESSINFO) " , label , into = lines )
399418 for c in cmds :
400- emit ("\t $(hide)" , c )
419+ emit ("\t $(hide)" , c , into = lines )
401420 else :
402421 assert len (cmds ) == 0 , "rules with no outputs cannot have commands"
403- emit (name , ":" , * fins )
422+ emit (name , ":" , * fins , into = lines )
423+
424+ cmd = "" .join (lines )
425+ hash = hashlib .sha1 (bytes (cmd , "utf-8" )).hexdigest ()
404426
427+ outputFp .write (cmd .replace ("\x01 " , f"$(OBJ)/.hashes/{ hash } " ))
428+
429+ if outs :
430+ emit (f"$(OBJ)/.hashes/{ hash } :" )
431+ emit (
432+ f"\t $(hide) mkdir -p $(OBJ)/.hashes && touch $(OBJ)/.hashes/{ hash } "
433+ )
405434 emit ("" )
406435
407436
0 commit comments