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,67 @@ 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 (name , ":" , f"$(OUTS_{ outsn } )" )
505+ emit (hashfile , ":" )
506+ emit (f"\t @mkdir -p { self .dir } " )
507+ emit (f"\t @touch { hashfile } " )
508+ emit (f"$(OUTS_{ outsn } )" , "&:" ,f"$(INS_{ insn } )" , hashfile , into = lines )
484509
485510 if label :
486- emit ("\t $(hide)" , "$(ECHO) $(PROGRESSINFO)" , label , into = lines )
511+ emit ("\t $(hide)" , "$(ECHO) $(PROGRESSINFO)" + label , into = lines )
487512
488513 sandbox = join (self .dir , "sandbox" )
489514 emit ("\t $(hide)" , f"rm -rf { sandbox } " , into = lines )
490515 emit (
491516 "\t $(hide)" ,
492- f"$(PYTHON) build/_sandbox.py --link -s { sandbox } " ,
493- * fins ,
517+ "$(PYTHON) build/_sandbox.py --link -s" ,
518+ sandbox ,
519+ f"$(INS_{ insn } )" ,
494520 into = lines ,
495521 )
496522 for c in cmds :
497523 emit (f"\t $(hide) cd { sandbox } && (" , c , ")" , into = lines )
498524 emit (
499525 "\t $(hide)" ,
500- f"$(PYTHON) build/_sandbox.py --export -s { sandbox } " ,
501- * fouts ,
526+ "$(PYTHON) build/_sandbox.py --export -s" ,
527+ sandbox ,
528+ f"$(OUTS_{ outsn } )" ,
502529 into = lines ,
503530 )
504531 else :
505532 assert len (cmds ) == 0 , "rules with no outputs cannot have commands"
506533 emit (name , ":" , * fins , into = lines )
507534
508- cmd = "" .join (lines )
509- hash = hashlib .sha1 (bytes (cmd , "utf-8" )).hexdigest ()
510-
511- outputFp .write (cmd .replace ("\x01 " , f"$(OBJ)/.hashes/{ hash } " ))
512-
513- if outs :
514- emit (f"$(OBJ)/.hashes/{ hash } :" )
515- emit (
516- f"\t $(hide) mkdir -p $(OBJ)/.hashes && touch $(OBJ)/.hashes/{ hash } "
517- )
535+ outputFp .write ("" .join (lines ))
518536 emit ("" )
519537
520538
@@ -578,13 +596,12 @@ def export(self, name=None, items: TargetsMap = {}, deps: Targets = []):
578596 )
579597 subrule .materialise ()
580598
581- simplerule (
582- replaces = self ,
583- ins = outs + deps ,
584- outs = ["=sentinel" ],
585- commands = ["touch $[outs[0]]" ],
586- label = "EXPORT" ,
587- )
599+ self .ins = []
600+ self .outs = deps + outs
601+
602+ emit ("" )
603+ emit (".PHONY:" , name )
604+ emit (name , ":" , * filenamesof (outs + deps ))
588605
589606
590607def main ():
0 commit comments