66from copy import copy
77import functools
88import importlib
9- import importlib .abc
109import importlib .util
1110from importlib .machinery import (
1211 SourceFileLoader ,
2120import ast
2221from collections import namedtuple
2322
23+ VERBOSE_MK_FILE = False
24+
2425verbose = False
2526quiet = False
2627cwdStack = ["" ]
2728targets = {}
2829unmaterialisedTargets = {} # dict, not set, to get consistent ordering
2930materialisingStack = []
3031defaultGlobals = {}
32+ globalId = 1
33+ wordCache = {}
3134
3235RE_FORMAT_SPEC = re .compile (
3336 r"(?:(?P<fill>[\s\S])?(?P<align>[<>=^]))?"
@@ -157,7 +160,8 @@ def wrapper(*, name=None, replaces=None, **kwargs):
157160 t .callback = func
158161 t .traits .add (func .__name__ )
159162 if "args" in kwargs :
160- t .args .update (kwargs ["args" ])
163+ t .explicit_args = kwargs ["args" ]
164+ t .args .update (t .explicit_args )
161165 del kwargs ["args" ]
162166 if "traits" in kwargs :
163167 t .traits |= kwargs ["traits" ]
@@ -406,8 +410,17 @@ def _removesuffix(self, suffix):
406410
407411
408412def loadbuildfile (filename ):
409- filename = _removesuffix (filename .replace ("/" , "." ), ".py" )
410- builtins .__import__ (filename )
413+ modulename = _removesuffix (filename .replace ("/" , "." ), ".py" )
414+ if modulename not in sys .modules :
415+ spec = importlib .util .spec_from_file_location (
416+ name = modulename ,
417+ location = filename ,
418+ loader = BuildFileLoaderImpl (fullname = modulename , path = filename ),
419+ submodule_search_locations = [],
420+ )
421+ module = importlib .util .module_from_spec (spec )
422+ sys .modules [modulename ] = module
423+ spec .loader .exec_module (module )
411424
412425
413426def flatten (items ):
@@ -433,6 +446,7 @@ def filenamesof(items):
433446 def generate (xs ):
434447 for x in xs :
435448 if isinstance (x , Target ):
449+ x .materialise ()
436450 yield from generate (x .outs )
437451 else :
438452 yield x
@@ -458,63 +472,74 @@ def emit(*args, into=None):
458472
459473def emit_rule (self , ins , outs , cmds = [], label = None ):
460474 name = self .name
461- fins = set (filenamesof (ins ))
475+ fins_list = filenamesof (ins )
476+ fins = set (fins_list )
462477 fouts = filenamesof (outs )
463478 nonobjs = [f for f in fouts if not f .startswith ("$(OBJ)" )]
464479
465480 emit ("" )
481+ if VERBOSE_MK_FILE :
482+ for k , v in self .args .items ():
483+ emit (f"# { k } = { v } " )
466484
467485 lines = []
468486 if nonobjs :
469487 emit ("clean::" , into = lines )
470488 emit ("\t $(hide) rm -f" , * nonobjs , into = lines )
471489
490+ hashable = cmds + fins_list + fouts
491+ hash = hashlib .sha1 (bytes ("\n " .join (hashable ), "utf-8" )).hexdigest ()
492+ hashfile = join (self .dir , f"hash_{ hash } " )
493+
494+ global globalId
472495 emit (".PHONY:" , name , into = lines )
473496 if outs :
474- emit (name , ":" , * fouts , into = lines )
475- if len (fouts ) == 1 :
476- emit (* fouts , ":" , * fins , "\x01 " , into = lines )
477- else :
478- emit ("ifeq ($(MAKE4.3),yes)" , into = lines )
479- emit (* fouts , "&:" , * fins , "\x01 " , into = lines )
480- emit ("else" , into = lines )
481- emit (* (fouts [1 :]), ":" , fouts [0 ], into = lines )
482- emit (fouts [0 ], ":" , * fins , "\x01 " , into = lines )
483- emit ("endif" , into = lines )
497+ outsn = globalId
498+ globalId = globalId + 1
499+ insn = globalId
500+ globalId = globalId + 1
501+
502+ emit (f"OUTS_{ outsn } " , "=" , * fouts , into = lines )
503+ emit (f"INS_{ insn } " , "=" , * fins , into = lines )
504+ emit (
505+ name ,
506+ ":" ,
507+ hashfile ,
508+ f"$(OUTS_{ outsn } )" ,
509+ into = lines ,
510+ )
511+ emit (f"$(OUTS_{ outsn } )" , ":" , hashfile , into = lines )
512+ emit (hashfile , ":" , f"$(INS_{ insn } )" , into = lines )
484513
485514 if label :
486- emit ("\t $(hide)" , "$(ECHO) $(PROGRESSINFO)" , label , into = lines )
515+ emit ("\t $(hide)" , "$(ECHO) $(PROGRESSINFO)" + label , into = lines )
487516
488517 sandbox = join (self .dir , "sandbox" )
489518 emit ("\t $(hide)" , f"rm -rf { sandbox } " , into = lines )
490519 emit (
491520 "\t $(hide)" ,
492- f"$(PYTHON) build/_sandbox.py --link -s { sandbox } " ,
493- * fins ,
521+ "$(PYTHON) build/_sandbox.py --link -s" ,
522+ sandbox ,
523+ f"$(INS_{ insn } )" ,
494524 into = lines ,
495525 )
496526 for c in cmds :
497527 emit (f"\t $(hide) cd { sandbox } && (" , c , ")" , into = lines )
498528 emit (
499529 "\t $(hide)" ,
500- f"$(PYTHON) build/_sandbox.py --export -s { sandbox } " ,
501- * fouts ,
530+ "$(PYTHON) build/_sandbox.py --export -s" ,
531+ sandbox ,
532+ f"$(OUTS_{ outsn } )" ,
502533 into = lines ,
503534 )
504535 else :
505536 assert len (cmds ) == 0 , "rules with no outputs cannot have commands"
506537 emit (name , ":" , * fins , into = lines )
507538
508- cmd = "" .join (lines )
509- hash = hashlib .sha1 (bytes (cmd , "utf-8" )).hexdigest ()
510-
511- outputFp .write (cmd .replace ("\x01 " , f"$(OBJ)/.hashes/{ hash } " ))
539+ outputFp .write ("" .join (lines ))
512540
513541 if outs :
514- emit (f"$(OBJ)/.hashes/{ hash } :" )
515- emit (
516- f"\t $(hide) mkdir -p $(OBJ)/.hashes && touch $(OBJ)/.hashes/{ hash } "
517- )
542+ emit (f"\t $(hide) touch { hashfile } " )
518543 emit ("" )
519544
520545
@@ -578,13 +603,12 @@ def export(self, name=None, items: TargetsMap = {}, deps: Targets = []):
578603 )
579604 subrule .materialise ()
580605
581- simplerule (
582- replaces = self ,
583- ins = outs + deps ,
584- outs = ["=sentinel" ],
585- commands = ["touch $[outs[0]]" ],
586- label = "EXPORT" ,
587- )
606+ self .ins = []
607+ self .outs = deps + outs
608+
609+ emit ("" )
610+ emit (".PHONY:" , name )
611+ emit (name , ":" , * filenamesof (outs + deps ))
588612
589613
590614def main ():
0 commit comments