3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 from __future__
import annotations
31 from typing
import TYPE_CHECKING
38 from SCons
.Executor
import Executor
47 def _actionAppend(a1
, a2
):
49 for curr_a
in [a1
, a2
]:
50 if isinstance(curr_a
, MyAction
):
52 elif isinstance(curr_a
, MyListAction
):
53 all
.extend(curr_a
.list)
54 elif isinstance(curr_a
, list):
57 raise Exception('Cannot Combine Actions')
58 return MyListAction(all
)
61 def __add__(self
, other
):
62 return _actionAppend(self
, other
)
64 def __radd__(self
, other
):
65 return _actionAppend(other
, self
)
67 class MyAction(MyActionBase
):
68 def __init__(self
) -> None:
71 def __call__(self
, target
, source
, env
, executor
: Executor |
None = None) -> int:
72 global built_it
, built_target
, built_source
, built_args
, built_order
74 target
= executor
.get_all_targets()
75 source
= executor
.get_all_sources()
80 built_order
= built_order
+ 1
81 self
.order
= built_order
84 def get_implicit_deps(self
, target
, source
, env
):
88 def __init__(self
, env
=None, targets
=[], sources
=[]) -> None:
90 self
.targets
= targets
91 self
.sources
= sources
92 def get_build_env(self
):
94 def get_build_scanner_path(self
, scanner
) -> str:
95 return 'executor would call %s' % scanner
96 def cleanup(self
) -> None:
98 def scan_targets(self
, scanner
) -> None:
101 d
= scanner(self
.targets
)
102 for t
in self
.targets
:
104 def scan_sources(self
, scanner
) -> None:
107 d
= scanner(self
.sources
)
108 for t
in self
.targets
:
111 class MyListAction(MyActionBase
):
112 def __init__(self
, list) -> None:
114 def __call__(self
, target
, source
, env
) -> None:
116 A(target
, source
, env
)
119 def __init__(self
, **kw
) -> None:
121 self
._dict
.update(kw
)
122 def __getitem__(self
, key
):
123 return self
._dict
[key
]
124 def get(self
, key
, default
= None):
125 return self
._dict
.get(key
, default
)
126 def Dictionary(self
, *args
):
128 def Override(self
, overrides
):
129 d
= self
._dict
.copy()
131 return Environment(**d
)
132 def _update(self
, dict) -> None:
133 self
._dict
.update(dict)
134 def get_factory(self
, factory
):
135 return factory
or MyNode
136 def get_scanner(self
, scanner_key
):
138 return self
._dict
['SCANNERS'][0]
145 def __init__(self
, env
=None, is_explicit
: bool=True) -> None:
146 if env
is None: env
= Environment()
149 self
.action
= MyAction()
150 self
.source_factory
= MyNode
151 self
.is_explicit
= is_explicit
152 self
.target_scanner
= None
153 self
.source_scanner
= None
154 def targets(self
, t
):
156 def get_actions(self
):
158 def get_contents(self
, target
, source
, env
) -> int:
161 class NoneBuilder(Builder
):
162 def execute(self
, target
, source
, env
):
163 Builder
.execute(self
, target
, source
, env
)
166 class ListBuilder(Builder
):
167 def __init__(self
, *nodes
) -> None:
170 def execute(self
, target
, source
, env
):
171 if hasattr(self
, 'status'):
175 target
= self
.nodes
[0]
176 self
.status
= Builder
.execute(self
, target
, source
, env
)
179 def execute(self
, target
, source
, env
) -> int:
183 def execute(self
, target
, source
, env
):
184 raise SCons
.Errors
.BuildError
186 class ExceptBuilder2
:
187 def execute(self
, target
, source
, env
):
188 raise Exception("foo")
192 def __call__(self
, node
):
194 return node
.GetTag('found_includes')
195 def path(self
, env
, dir=None, target
=None, source
=None, kw
={}):
197 def select(self
, node
):
199 def recurse_nodes(self
, nodes
):
202 class MyNode(SCons
.Node
.Node
):
203 """The base Node class contains a number of do-nothing methods that
204 we expect to be overridden by real, functional Node subclasses. So
205 simulate a real, functional Node subclass.
207 def __init__(self
, name
) -> None:
210 self
.Tag('found_includes', [])
211 def __str__(self
) -> str:
213 def get_found_includes(self
, env
, scanner
, target
):
217 def __init__(self
, val
) -> None:
220 def __init__(self
, val
) -> None:
222 def signature(self
, args
):
224 def collect(self
, args
):
233 class NodeInfoBaseTestCase(unittest
.TestCase
):
234 # The abstract class NodeInfoBase has not enough default slots to perform
235 # the merge and format test (arbitrary attributes do not work). Do it with a
236 # derived class that does provide the slots.
238 def test_merge(self
) -> None:
239 """Test merging NodeInfoBase attributes"""
241 class TestNodeInfo(SCons
.Node
.NodeInfoBase
):
242 __slots__
= ('a1', 'a2', 'a3')
254 assert ni1
.a1
== 1, ni1
.a1
255 assert ni1
.a2
== 222, ni1
.a2
256 assert ni1
.a3
== 333, ni1
.a3
258 def test_update(self
) -> None:
259 """Test the update() method"""
260 ni
= SCons
.Node
.NodeInfoBase()
261 ni
.update(SCons
.Node
.Node())
263 def test_format(self
) -> None:
264 """Test the NodeInfoBase.format() method"""
266 class TestNodeInfo(SCons
.Node
.NodeInfoBase
):
267 __slots__
= ('xxx', 'yyy', 'zzz')
275 assert f
== ['x', 'y', 'z'], f
277 field_list
= ['xxx', 'zzz', 'aaa']
279 f
= ni1
.format(field_list
)
280 assert f
== ['x', 'z', 'None'], f
284 class BuildInfoBaseTestCase(unittest
.TestCase
):
286 def test___init__(self
) -> None:
287 """Test BuildInfoBase initialization"""
288 n
= SCons
.Node
.Node()
289 bi
= SCons
.Node
.BuildInfoBase()
292 def test_merge(self
) -> None:
293 """Test merging BuildInfoBase attributes"""
294 n1
= SCons
.Node
.Node()
295 bi1
= SCons
.Node
.BuildInfoBase()
296 n2
= SCons
.Node
.Node()
297 bi2
= SCons
.Node
.BuildInfoBase()
306 assert bi1
.bsources
== 1, bi1
.bsources
307 assert bi1
.bdepends
== 222, bi1
.bdepends
308 assert bi1
.bact
== 333, bi1
.bact
311 class NodeTestCase(unittest
.TestCase
):
313 def test_build(self
) -> None:
314 """Test building a node
316 global built_it
, built_order
318 # Make sure it doesn't blow up if no builder is set.
321 assert built_it
is None
322 node
.build(extra_kw_argument
= 1)
323 assert built_it
is None
326 node
.builder_set(Builder())
327 node
.env_set(Environment())
329 node
.sources
= ["yyy", "zzz"]
332 assert built_target
== [node
], built_target
333 assert built_source
== ["yyy", "zzz"], built_source
337 node
.builder_set(NoneBuilder())
338 node
.env_set(Environment())
340 node
.sources
= ["rrr", "sss"]
341 node
.builder
.overrides
= { "foo" : 1, "bar" : 2 }
344 assert built_target
== [node
], built_target
345 assert built_source
== ["rrr", "sss"], built_source
346 assert built_args
["foo"] == 1, built_args
347 assert built_args
["bar"] == 2, built_args
351 lb
= ListBuilder(fff
, ggg
)
359 fff
.sources
= ["hhh", "iii"]
360 ggg
.sources
= ["hhh", "iii"]
364 assert built_target
== [fff
], built_target
365 assert built_source
== ["hhh", "iii"], built_source
369 assert built_target
== [ggg
], built_target
370 assert built_source
== ["hhh", "iii"], built_source
376 # NOTE: No env_set()! We should pull the environment from the builder.
377 b
.env
= Environment()
378 b
.overrides
= { "on" : 3, "off" : 4 }
382 assert built_target
[0] == jjj
, built_target
[0]
383 assert built_source
== [], built_source
384 assert built_args
["on"] == 3, built_args
385 assert built_args
["off"] == 4, built_args
387 def test_get_build_scanner_path(self
) -> None:
388 """Test the get_build_scanner_path() method"""
389 n
= SCons
.Node
.Node()
392 p
= n
.get_build_scanner_path('fake_scanner')
393 assert p
== "executor would call fake_scanner", p
395 def test_get_executor(self
) -> None:
396 """Test the get_executor() method"""
397 n
= SCons
.Node
.Node()
401 except AttributeError:
404 self
.fail("did not catch expected AttributeError")
411 n
= SCons
.Node
.Node()
412 n
.builder_set(Builder())
414 assert x
.env
== 'env1', x
.env
416 n
= SCons
.Node
.Node()
417 n
.builder_set(Builder())
420 assert x
.env
== 'env2', x
.env
422 def test_set_executor(self
) -> None:
423 """Test the set_executor() method"""
424 n
= SCons
.Node
.Node()
426 assert n
.executor
== 1, n
.executor
428 def test_executor_cleanup(self
) -> None:
429 """Test letting the executor cleanup its cache"""
430 n
= SCons
.Node
.Node()
436 def test_reset_executor(self
) -> None:
437 """Test the reset_executor() method"""
438 n
= SCons
.Node
.Node()
440 assert n
.executor
== 1, n
.executor
442 assert not hasattr(n
, 'executor'), "unexpected executor attribute"
444 def test_built(self
) -> None:
445 """Test the built() method"""
446 class SubNodeInfo(SCons
.Node
.NodeInfoBase
):
447 __slots__
= ('updated',)
448 def update(self
, node
) -> None:
450 class SubNode(SCons
.Node
.Node
):
451 def clear(self
) -> None:
455 n
.ninfo
= SubNodeInfo()
457 assert n
.cleared
, n
.cleared
458 assert n
.ninfo
.updated
, n
.ninfo
.cleared
460 def test_push_to_cache(self
) -> None:
461 """Test the base push_to_cache() method"""
462 n
= SCons
.Node
.Node()
463 r
= n
.push_to_cache()
466 def test_retrieve_from_cache(self
) -> None:
467 """Test the base retrieve_from_cache() method"""
468 n
= SCons
.Node
.Node()
469 r
= n
.retrieve_from_cache()
472 def test_visited(self
) -> None:
473 """Test the base visited() method
475 Just make sure it's there and we can call it.
477 n
= SCons
.Node
.Node()
480 def test_builder_set(self
) -> None:
481 """Test setting a Node's Builder
483 node
= SCons
.Node
.Node()
486 assert node
.builder
== b
488 def test_has_builder(self
) -> None:
489 """Test the has_builder() method
491 n1
= SCons
.Node
.Node()
492 assert n1
.has_builder() == 0
493 n1
.builder_set(Builder())
494 assert n1
.has_builder() == 1
496 def test_has_explicit_builder(self
) -> None:
497 """Test the has_explicit_builder() method
499 n1
= SCons
.Node
.Node()
500 assert not n1
.has_explicit_builder()
502 assert n1
.has_explicit_builder()
503 n1
.set_explicit(None)
504 assert not n1
.has_explicit_builder()
506 def test_get_builder(self
) -> None:
507 """Test the get_builder() method"""
508 n1
= SCons
.Node
.Node()
511 b
= n1
.get_builder(777)
516 b
= n1
.get_builder(999)
519 def test_multiple_side_effect_has_builder(self
) -> None:
520 """Test the multiple_side_effect_has_builder() method
522 n1
= SCons
.Node
.Node()
523 assert n1
.multiple_side_effect_has_builder() == 0
524 n1
.builder_set(Builder())
525 assert n1
.multiple_side_effect_has_builder() == 1
527 def test_is_derived(self
) -> None:
528 """Test the is_derived() method
530 n1
= SCons
.Node
.Node()
531 n2
= SCons
.Node
.Node()
532 n3
= SCons
.Node
.Node()
534 n2
.builder_set(Builder())
537 assert n1
.is_derived() == 0
538 assert n2
.is_derived() == 1
539 assert n3
.is_derived() == 1
541 def test_alter_targets(self
) -> None:
542 """Test the alter_targets() method
544 n
= SCons
.Node
.Node()
545 t
, m
= n
.alter_targets()
549 def test_is_up_to_date(self
) -> None:
550 """Test the default is_up_to_date() method."""
551 node
= SCons
.Node
.Node()
552 assert not node
.is_up_to_date()
554 def test_children_are_up_to_date(self
) -> None:
555 """Test the children_are_up_to_date() method used by subclasses
557 n1
= SCons
.Node
.Node()
558 n2
= SCons
.Node
.Node()
561 assert n1
.children_are_up_to_date(), "expected up to date"
562 n2
.set_state(SCons
.Node
.executed
)
563 assert not n1
.children_are_up_to_date(), "expected not up to date"
564 n2
.set_state(SCons
.Node
.up_to_date
)
565 assert n1
.children_are_up_to_date(), "expected up to date"
567 assert not n1
.children_are_up_to_date(), "expected not up to date"
569 def test_env_set(self
) -> None:
570 """Test setting a Node's Environment
572 node
= SCons
.Node
.Node()
577 def test_get_actions(self
) -> None:
578 """Test fetching a Node's action list
580 node
= SCons
.Node
.Node()
581 node
.builder_set(Builder())
582 a
= node
.builder
.get_actions()
583 assert isinstance(a
[0], MyAction
), a
[0]
585 def test_get_csig(self
) -> None:
586 """Test generic content signature calculation
589 class TestNodeInfo(SCons
.Node
.NodeInfoBase
):
590 __slots__
= ('csig',)
592 SCons
.Node
.Node
.NodeInfo
= TestNodeInfo
593 def my_contents(obj
) -> int:
595 SCons
.Node
._get
_contents
_map
[4] = my_contents
596 node
= SCons
.Node
.Node()
597 node
._func
_get
_contents
= 4
598 result
= node
.get_csig()
599 assert result
in ('550a141f12de6341fba65b0ad0433500', '9a3e61b6bcc8abec08f195526c3132d5a4a98cc0', '3538a1ef2e113da64249eea7bd068b585ec7ce5df73b2d1e319d8c9bf47eb314'), result
601 SCons
.Node
.Node
.NodeInfo
= SCons
.Node
.NodeInfoBase
603 def test_get_cachedir_csig(self
) -> None:
604 """Test content signature calculation for CacheDir
606 class TestNodeInfo(SCons
.Node
.NodeInfoBase
):
607 __slots__
= ('csig',)
609 SCons
.Node
.Node
.NodeInfo
= TestNodeInfo
610 def my_contents(obj
) -> int:
612 SCons
.Node
._get
_contents
_map
[4] = my_contents
613 node
= SCons
.Node
.Node()
614 node
._func
_get
_contents
= 4
615 result
= node
.get_cachedir_csig()
616 assert result
in ('15de21c670ae7c3f6f3f1f37029303c9', 'cfa1150f1787186742a9a884b73a43d8cf219f9b', '91a73fd806ab2c005c13b4dc19130a884e909dea3f72d46e30266fe1a1f588d8'), result
618 SCons
.Node
.Node
.NodeInfo
= SCons
.Node
.NodeInfoBase
620 def test_get_binfo(self
) -> None:
621 """Test fetching/creating a build information structure
623 class TestNodeInfo(SCons
.Node
.NodeInfoBase
):
624 __slots__
= ('csig',)
625 SCons
.Node
.Node
.NodeInfo
= TestNodeInfo
626 node
= SCons
.Node
.Node()
628 binfo
= node
.get_binfo()
629 assert isinstance(binfo
, SCons
.Node
.BuildInfoBase
), binfo
631 node
= SCons
.Node
.Node()
632 d
= SCons
.Node
.Node()
633 ninfo
= d
.get_ninfo()
634 assert isinstance(ninfo
, SCons
.Node
.NodeInfoBase
), ninfo
635 i
= SCons
.Node
.Node()
636 ninfo
= i
.get_ninfo()
637 assert isinstance(ninfo
, SCons
.Node
.NodeInfoBase
), ninfo
641 binfo
= node
.get_binfo()
642 assert isinstance(binfo
, SCons
.Node
.BuildInfoBase
), binfo
643 assert hasattr(binfo
, 'bsources')
644 assert hasattr(binfo
, 'bsourcesigs')
645 assert binfo
.bdepends
== [d
]
646 assert hasattr(binfo
, 'bdependsigs')
647 assert binfo
.bimplicit
== [i
]
648 assert hasattr(binfo
, 'bimplicitsigs')
650 def test_explain(self
) -> None:
651 """Test explaining why a Node must be rebuilt
653 class testNode(SCons
.Node
.Node
):
654 def __str__(self
) -> str: return 'xyzzy'
656 node
.exists
= lambda: None
657 # Can't do this with new-style classes (python bug #1066490)
658 #node.__str__ = lambda: 'xyzzy'
659 result
= node
.explain()
660 assert result
== "building `xyzzy' because it doesn't exist\n", result
662 class testNode2(SCons
.Node
.Node
):
663 def __str__(self
) -> str: return 'null_binfo'
668 node
.fs
.Top
= SCons
.Node
.Node()
669 result
= node
.explain()
670 assert result
is None, result
673 class Null_SConsignEntry
:
674 class Null_BuildInfo
:
675 def prepare_dependencies(self
) -> None:
677 binfo
= Null_BuildInfo()
678 return Null_SConsignEntry()
680 node
.get_stored_info
= get_null_info
681 #see above: node.__str__ = lambda: 'null_binfo'
682 result
= node
.explain()
683 assert result
== "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result
685 # XXX additional tests for the guts of the functionality some day
687 #def test_del_binfo(self):
688 # """Test deleting the build information from a Node
690 # node = SCons.Node.Node()
693 # assert not hasattr(node, 'binfo'), node
695 def test_store_info(self
) -> None:
696 """Test calling the method to store build information
698 node
= SCons
.Node
.Node()
699 SCons
.Node
.store_info_map
[node
.store_info
](node
)
701 def test_get_stored_info(self
) -> None:
702 """Test calling the method to fetch stored build information
704 node
= SCons
.Node
.Node()
705 result
= node
.get_stored_info()
706 assert result
is None, result
708 def test_set_always_build(self
) -> None:
709 """Test setting a Node's always_build value
711 node
= SCons
.Node
.Node()
712 node
.set_always_build()
713 assert node
.always_build
714 node
.set_always_build(3) # type: ignore[arg-type]
715 assert node
.always_build
717 def test_set_noclean(self
) -> None:
718 """Test setting a Node's noclean value
720 node
= SCons
.Node
.Node()
722 assert node
.noclean
, node
.noclean
723 node
.set_noclean(7) # type: ignore[arg-type]
724 assert node
.noclean
, node
.noclean
725 node
.set_noclean(0) # type: ignore[arg-type]
726 assert not node
.noclean
, node
.noclean
727 node
.set_noclean(None) # type: ignore[arg-type]
728 assert not node
.noclean
, node
.noclean
730 def test_set_precious(self
) -> None:
731 """Test setting a Node's precious value
733 node
= SCons
.Node
.Node()
736 node
.set_precious(7) # type: ignore[arg-type]
739 def test_set_pseudo(self
) -> None:
740 """Test setting a Node's pseudo value
742 node
= SCons
.Node
.Node()
745 node
.set_pseudo(False)
746 assert not node
.pseudo
748 def test_exists(self
) -> None:
749 """Test evaluating whether a Node exists.
751 node
= SCons
.Node
.Node()
755 def test_exists_repo(self
) -> None:
756 """Test evaluating whether a Node exists locally or in a repository.
758 node
= SCons
.Node
.Node()
762 class MyNode(SCons
.Node
.Node
):
763 def exists(self
) -> str:
770 def test_prepare(self
) -> None:
771 """Test preparing a node to be built
773 By extension, this also tests the missing() method.
775 node
= SCons
.Node
.Node()
777 n1
= SCons
.Node
.Node()
778 n1
.builder_set(Builder())
780 node
.implicit_set
= set()
781 node
._add
_child
(node
.implicit
, node
.implicit_set
, [n1
])
783 node
.prepare() # should not throw an exception
785 n2
= SCons
.Node
.Node()
788 node
.implicit_set
= set()
789 node
._add
_child
(node
.implicit
, node
.implicit_set
, [n2
])
791 node
.prepare() # should not throw an exception
793 n3
= SCons
.Node
.Node()
795 node
.implicit_set
= set()
796 node
._add
_child
(node
.implicit
, node
.implicit_set
, [n3
])
798 node
.prepare() # should not throw an exception
800 class MyNode(SCons
.Node
.Node
):
805 node
.implicit_set
= set()
806 node
._add
_child
(node
.implicit
, node
.implicit_set
, [n4
])
810 except SCons
.Errors
.StopError
:
812 assert exc_caught
, "did not catch expected StopError"
814 def test_add_dependency(self
):
815 """Test adding dependencies to a Node's list.
817 node
= SCons
.Node
.Node()
818 assert node
.depends
== []
820 zero
= SCons
.Node
.Node()
822 one
= SCons
.Node
.Node()
823 two
= SCons
.Node
.Node()
824 three
= SCons
.Node
.Node()
825 four
= SCons
.Node
.Node()
826 five
= SCons
.Node
.Node()
827 six
= SCons
.Node
.Node()
829 node
.add_dependency([zero
])
830 assert node
.depends
== [zero
]
831 node
.add_dependency([one
])
832 assert node
.depends
== [zero
, one
]
833 node
.add_dependency([two
, three
])
834 assert node
.depends
== [zero
, one
, two
, three
]
835 node
.add_dependency([three
, four
, one
])
836 assert node
.depends
== [zero
, one
, two
, three
, four
]
839 node
.add_depends([[five
, six
]])
843 raise Exception("did not catch expected exception")
844 assert node
.depends
== [zero
, one
, two
, three
, four
]
847 def test_add_source(self
):
848 """Test adding sources to a Node's list.
850 node
= SCons
.Node
.Node()
851 assert node
.sources
== []
853 zero
= SCons
.Node
.Node()
854 one
= SCons
.Node
.Node()
855 two
= SCons
.Node
.Node()
856 three
= SCons
.Node
.Node()
857 four
= SCons
.Node
.Node()
858 five
= SCons
.Node
.Node()
859 six
= SCons
.Node
.Node()
861 node
.add_source([zero
])
862 assert node
.sources
== [zero
]
863 node
.add_source([one
])
864 assert node
.sources
== [zero
, one
]
865 node
.add_source([two
, three
])
866 assert node
.sources
== [zero
, one
, two
, three
]
867 node
.add_source([three
, four
, one
])
868 assert node
.sources
== [zero
, one
, two
, three
, four
]
871 node
.add_source([[five
, six
]])
875 raise Exception("did not catch expected exception")
876 assert node
.sources
== [zero
, one
, two
, three
, four
], node
.sources
878 def test_add_ignore(self
):
879 """Test adding files whose dependencies should be ignored.
881 node
= SCons
.Node
.Node()
882 assert node
.ignore
== []
884 zero
= SCons
.Node
.Node()
885 one
= SCons
.Node
.Node()
886 two
= SCons
.Node
.Node()
887 three
= SCons
.Node
.Node()
888 four
= SCons
.Node
.Node()
889 five
= SCons
.Node
.Node()
890 six
= SCons
.Node
.Node()
892 node
.add_ignore([zero
])
893 assert node
.ignore
== [zero
]
894 node
.add_ignore([one
])
895 assert node
.ignore
== [zero
, one
]
896 node
.add_ignore([two
, three
])
897 assert node
.ignore
== [zero
, one
, two
, three
]
898 node
.add_ignore([three
, four
, one
])
899 assert node
.ignore
== [zero
, one
, two
, three
, four
]
902 node
.add_ignore([[five
, six
]])
906 raise Exception("did not catch expected exception")
907 assert node
.ignore
== [zero
, one
, two
, three
, four
]
909 def test_get_found_includes(self
) -> None:
910 """Test the default get_found_includes() method
912 node
= SCons
.Node
.Node()
913 target
= SCons
.Node
.Node()
915 deps
= node
.get_found_includes(e
, None, target
)
916 assert deps
== [], deps
918 def test_get_implicit_deps(self
) -> None:
919 """Test get_implicit_deps()
922 target
= MyNode("ttt")
925 # No scanner at all returns []
926 deps
= node
.get_implicit_deps(env
, None, target
)
927 assert deps
== [], deps
932 node
.Tag('found_includes', [d1
, d2
])
934 # Simple return of the found includes
935 deps
= node
.get_implicit_deps(env
, s
, s
.path
)
936 assert deps
== [d1
, d2
], deps
938 # By default, our fake scanner recurses
942 d1
.Tag('found_includes', [e
, f
])
943 d2
.Tag('found_includes', [e
, f
])
944 f
.Tag('found_includes', [g
])
945 deps
= node
.get_implicit_deps(env
, s
, s
.path
)
946 assert deps
== [d1
, d2
, e
, f
, g
], list(map(str, deps
))
948 # Recursive scanning eliminates duplicates
949 e
.Tag('found_includes', [f
])
950 deps
= node
.get_implicit_deps(env
, s
, s
.path
)
951 assert deps
== [d1
, d2
, e
, f
, g
], list(map(str, deps
))
953 # Scanner method can select specific nodes to recurse
955 return [n
for n
in nodes
if str(n
)[0] != 'f']
956 s
.recurse_nodes
= no_fff
957 deps
= node
.get_implicit_deps(env
, s
, s
.path
)
958 assert deps
== [d1
, d2
, e
, f
], list(map(str, deps
))
960 # Scanner method can short-circuit recursing entirely
961 s
.recurse_nodes
= lambda nodes
: []
962 deps
= node
.get_implicit_deps(env
, s
, s
.path
)
963 assert deps
== [d1
, d2
], list(map(str, deps
))
965 def test_get_env_scanner(self
) -> None:
966 """Test fetching the environment scanner for a Node
968 node
= SCons
.Node
.Node()
970 env
= Environment(SCANNERS
= [scanner
])
971 s
= node
.get_env_scanner(env
)
972 assert s
== scanner
, s
973 s
= node
.get_env_scanner(env
, {'X':1})
974 assert s
== scanner
, s
976 def test_get_target_scanner(self
) -> None:
977 """Test fetching the target scanner for a Node
982 n
= SCons
.Node
.Node()
984 x
= n
.get_target_scanner()
987 def test_get_source_scanner(self
) -> None:
988 """Test fetching the source scanner for a Node
990 target
= SCons
.Node
.Node()
991 source
= SCons
.Node
.Node()
992 s
= target
.get_source_scanner(source
)
993 assert isinstance(s
, SCons
.Util
.Null
), s
999 class Builder1(Builder
):
1000 def __call__(self
, source
):
1001 r
= SCons
.Node
.Node()
1004 class Builder2(Builder1
):
1005 def __init__(self
, scanner
) -> None:
1006 self
.source_scanner
= scanner
1008 builder
= Builder2(ts1
)
1010 targets
= builder([source
])
1011 s
= targets
[0].get_source_scanner(source
)
1014 target
.builder_set(Builder2(ts1
))
1015 target
.builder
.source_scanner
= ts2
1016 s
= target
.get_source_scanner(source
)
1019 builder
= Builder1(env
=Environment(SCANNERS
= [ts3
]))
1021 targets
= builder([source
])
1023 s
= targets
[0].get_source_scanner(source
)
1027 def test_scan(self
) -> None:
1028 """Test Scanner functionality
1031 node
= MyNode("nnn")
1032 node
.builder
= Builder()
1034 x
= MyExecutor(env
, [node
])
1038 node
.Tag('found_includes', [d
])
1040 node
.builder
.target_scanner
= s
1041 assert node
.implicit
is None
1045 assert node
.implicit
== [d
], node
.implicit
1047 # Check that scanning a node with some stored implicit
1048 # dependencies resets internal attributes appropriately
1049 # if the stored dependencies need recalculation.
1050 class StoredNode(MyNode
):
1051 def get_stored_implicit(self
):
1052 return [MyNode('implicit1'), MyNode('implicit2')]
1054 save_implicit_cache
= SCons
.Node
.implicit_cache
1055 save_implicit_deps_changed
= SCons
.Node
.implicit_deps_changed
1056 save_implicit_deps_unchanged
= SCons
.Node
.implicit_deps_unchanged
1057 SCons
.Node
.implicit_cache
= 1
1058 SCons
.Node
.implicit_deps_changed
= None
1059 SCons
.Node
.implicit_deps_unchanged
= None
1061 sn
= StoredNode("eee")
1062 sn
.builder_set(Builder())
1063 sn
.builder
.target_scanner
= s
1067 assert sn
.implicit
== [], sn
.implicit
1068 assert sn
.children() == [], sn
.children()
1071 SCons
.Node
.implicit_cache
= save_implicit_cache
1072 SCons
.Node
.implicit_deps_changed
= save_implicit_deps_changed
1073 SCons
.Node
.implicit_deps_unchanged
= save_implicit_deps_unchanged
1075 def test_scanner_key(self
) -> None:
1076 """Test that a scanner_key() method exists"""
1077 assert SCons
.Node
.Node().scanner_key() is None
1079 def test_children(self
) -> None:
1080 """Test fetching the non-ignored "children" of a Node.
1082 node
= SCons
.Node
.Node()
1083 n1
= SCons
.Node
.Node()
1084 n2
= SCons
.Node
.Node()
1085 n3
= SCons
.Node
.Node()
1086 n4
= SCons
.Node
.Node()
1087 n5
= SCons
.Node
.Node()
1088 n6
= SCons
.Node
.Node()
1089 n7
= SCons
.Node
.Node()
1090 n8
= SCons
.Node
.Node()
1091 n9
= SCons
.Node
.Node()
1092 n10
= SCons
.Node
.Node()
1093 n11
= SCons
.Node
.Node()
1094 n12
= SCons
.Node
.Node()
1096 node
.add_source([n1
, n2
, n3
])
1097 node
.add_dependency([n4
, n5
, n6
])
1099 node
.implicit_set
= set()
1100 node
._add
_child
(node
.implicit
, node
.implicit_set
, [n7
, n8
, n9
])
1101 node
._add
_child
(node
.implicit
, node
.implicit_set
, [n10
, n11
, n12
])
1102 node
.add_ignore([n2
, n5
, n8
, n11
])
1104 kids
= node
.children()
1105 for kid
in [n1
, n3
, n4
, n6
, n7
, n9
, n10
, n12
]:
1106 assert kid
in kids
, kid
1107 for kid
in [n2
, n5
, n8
, n11
]:
1108 assert kid
not in kids
, kid
1110 def test_all_children(self
) -> None:
1111 """Test fetching all the "children" of a Node.
1113 node
= SCons
.Node
.Node()
1114 n1
= SCons
.Node
.Node()
1115 n2
= SCons
.Node
.Node()
1116 n3
= SCons
.Node
.Node()
1117 n4
= SCons
.Node
.Node()
1118 n5
= SCons
.Node
.Node()
1119 n6
= SCons
.Node
.Node()
1120 n7
= SCons
.Node
.Node()
1121 n8
= SCons
.Node
.Node()
1122 n9
= SCons
.Node
.Node()
1123 n10
= SCons
.Node
.Node()
1124 n11
= SCons
.Node
.Node()
1125 n12
= SCons
.Node
.Node()
1127 node
.add_source([n1
, n2
, n3
])
1128 node
.add_dependency([n4
, n5
, n6
])
1130 node
.implicit_set
= set()
1131 node
._add
_child
(node
.implicit
, node
.implicit_set
, [n7
, n8
, n9
])
1132 node
._add
_child
(node
.implicit
, node
.implicit_set
, [n10
, n11
, n12
])
1133 node
.add_ignore([n2
, n5
, n8
, n11
])
1135 kids
= node
.all_children()
1136 for kid
in [n1
, n2
, n3
, n4
, n5
, n6
, n7
, n8
, n9
, n10
, n11
, n12
]:
1137 assert kid
in kids
, kid
1139 def test_state(self
) -> None:
1140 """Test setting and getting the state of a node
1142 node
= SCons
.Node
.Node()
1143 assert node
.get_state() == SCons
.Node
.no_state
1144 node
.set_state(SCons
.Node
.executing
)
1145 assert node
.get_state() == SCons
.Node
.executing
1146 assert SCons
.Node
.pending
< SCons
.Node
.executing
1147 assert SCons
.Node
.executing
< SCons
.Node
.up_to_date
1148 assert SCons
.Node
.up_to_date
< SCons
.Node
.executed
1149 assert SCons
.Node
.executed
< SCons
.Node
.failed
1151 def test_walker(self
) -> None:
1152 """Test walking a Node tree.
1157 nw
= SCons
.Node
.Walker(n1
)
1158 assert not nw
.is_done()
1159 assert nw
.get_next().name
== "n1"
1161 assert nw
.get_next() is None
1165 n1
.add_source([n2
, n3
])
1167 nw
= SCons
.Node
.Walker(n1
)
1169 assert n
.name
== "n2", n
.name
1171 assert n
.name
== "n3", n
.name
1173 assert n
.name
== "n1", n
.name
1181 n2
.add_source([n4
, n5
])
1182 n3
.add_dependency([n6
, n7
])
1184 nw
= SCons
.Node
.Walker(n1
)
1185 assert nw
.get_next().name
== "n4"
1186 assert nw
.get_next().name
== "n5"
1187 assert n2
in nw
.history
1188 assert nw
.get_next().name
== "n2"
1189 assert nw
.get_next().name
== "n6"
1190 assert nw
.get_next().name
== "n7"
1191 assert n3
in nw
.history
1192 assert nw
.get_next().name
== "n3"
1193 assert n1
in nw
.history
1194 assert nw
.get_next().name
== "n1"
1195 assert nw
.get_next() is None
1198 n8
.add_dependency([n3
])
1199 n7
.add_dependency([n8
])
1201 def cycle(node
, stack
) -> None:
1202 global cycle_detected
1205 global cycle_detected
1207 nw
= SCons
.Node
.Walker(n3
, cycle_func
= cycle
)
1209 assert n
.name
== "n6", n
.name
1211 assert n
.name
== "n8", n
.name
1212 assert cycle_detected
1213 cycle_detected
= None
1215 assert n
.name
== "n7", n
.name
1217 assert nw
.get_next() is None
1219 def test_abspath(self
) -> None:
1220 """Test the get_abspath() method."""
1222 assert n
.get_abspath() == str(n
), n
.get_abspath()
1224 def test_for_signature(self
) -> None:
1225 """Test the for_signature() method."""
1227 assert n
.for_signature() == str(n
), n
.get_abspath()
1229 def test_get_string(self
) -> None:
1230 """Test the get_string() method."""
1231 class TestNode(MyNode
):
1232 def __init__(self
, name
, sig
) -> None:
1233 super().__init
__(name
)
1236 def for_signature(self
):
1239 n
= TestNode("foo", "bar")
1240 assert n
.get_string(0) == "foo", n
.get_string(0)
1241 assert n
.get_string(1) == "bar", n
.get_string(1)
1243 def test_literal(self
) -> None:
1244 """Test the is_literal() function."""
1246 assert n
.is_literal()
1248 def test_sconscripts(self
) -> None:
1249 """Test the is_sconscript() function."""
1250 # check nodes are not sconscript unless added to the list
1252 n2
=SCons
.Node
.Node()
1253 assert not n
.is_sconscript()
1254 assert not n2
.is_sconscript()
1256 # add node to sconscript list and verify
1257 SCons
.Node
.SConscriptNodes
.add(n2
)
1258 assert not n
.is_sconscript()
1259 assert n2
.is_sconscript()
1261 def test_conftests(self
) -> None:
1262 """Test the is_conftest() function."""
1263 # check nodes are not sconscript unless added to the list
1265 n2
=SCons
.Node
.Node()
1266 assert not n
.is_conftest()
1267 assert not n2
.is_conftest()
1269 # add node to sconscript list and verify
1270 n2
.attributes
.conftest_node
= 1
1271 assert not n
.is_conftest()
1272 assert n2
.is_conftest()
1274 def test_Annotate(self
) -> None:
1275 """Test using an interface-specific Annotate function."""
1276 def my_annotate(node
, self
=self
) -> None:
1277 node
.Tag('annotation', self
.node_string
)
1279 save_Annotate
= SCons
.Node
.Annotate
1280 SCons
.Node
.Annotate
= my_annotate
1283 self
.node_string
= '#1'
1284 n
= SCons
.Node
.Node()
1285 a
= n
.GetTag('annotation')
1288 self
.node_string
= '#2'
1289 n
= SCons
.Node
.Node()
1290 a
= n
.GetTag('annotation')
1293 SCons
.Node
.Annotate
= save_Annotate
1295 def test_clear(self
) -> None:
1296 """Test clearing all cached state information."""
1297 n
= SCons
.Node
.Node()
1301 n
.includes
= 'testincludes'
1302 n
.Tag('found_includes', {'testkey':'testvalue'})
1303 n
.implicit
= 'testimplicit'
1311 assert n
.includes
is None, n
.includes
1312 assert n
.cached
== 0, n
.cached
1315 def test_get_subst_proxy(self
) -> None:
1316 """Test the get_subst_proxy method."""
1319 assert n
.get_subst_proxy() == n
, n
.get_subst_proxy()
1321 def test_new_binfo(self
) -> None:
1322 """Test the new_binfo() method"""
1323 n
= SCons
.Node
.Node()
1324 result
= n
.new_binfo()
1325 assert isinstance(result
, SCons
.Node
.BuildInfoBase
), result
1327 def test_get_suffix(self
) -> None:
1328 """Test the base Node get_suffix() method"""
1329 n
= SCons
.Node
.Node()
1333 def test_postprocess(self
) -> None:
1334 """Test calling the base Node postprocess() method"""
1335 n
= SCons
.Node
.Node()
1336 n
.waiting_parents
= {'foo', 'bar'}
1339 assert n
.waiting_parents
== set(), n
.waiting_parents
1341 def test_add_to_waiting_parents(self
) -> None:
1342 """Test the add_to_waiting_parents() method"""
1343 n1
= SCons
.Node
.Node()
1344 n2
= SCons
.Node
.Node()
1345 assert n1
.waiting_parents
== set(), n1
.waiting_parents
1346 r
= n1
.add_to_waiting_parents(n2
)
1348 assert n1
.waiting_parents
== {n2}
, n1
.waiting_parents
1349 r
= n1
.add_to_waiting_parents(n2
)
1353 class NodeListTestCase(unittest
.TestCase
):
1354 def test___str__(self
) -> None:
1359 nl
= SCons
.Node
.NodeList([n3
, n2
, n1
])
1362 ul
= collections
.UserList([2])
1364 assert s
== "['n3', 'n2', 'n1']", s
1367 r
= re
.sub(r
'at (0[xX])?[0-9a-fA-F]+', 'at 0x', r
)
1368 # Don't care about ancestry: just leaf value of MyNode
1369 r
= re
.sub(r
'<.*?\.MyNode', '<MyNode', r
)
1370 # New-style classes report as "object"; classic classes report
1372 r
= re
.sub("object", "instance", r
)
1373 l
= ", ".join(["<MyNode instance at 0x>"]*3)
1374 assert r
== '[%s]' % l
, r
1378 if __name__
== "__main__":
1384 # indent-tabs-mode:nil
1386 # vim: set expandtab tabstop=4 shiftwidth=4: