1#!/usr/local/bin/python3.8
2
3# Copyright 2012 Jurko Gospodnetic
4# Distributed under the Boost Software License, Version 1.0.
5# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
6
7#   Tests for a bug causing Boost Build's scanner targets to be rebuilt.
8# unnecessarily in the following scenario:
9#  * We want to build target X requiring target A.
10#  * We have a multi-file action generating targets A & B.
11#  * Out action generates target B with a more recent timestamp than target A.
12#  * Target A includes target B.
13#  * Target A has a registered include scanner.
14# Now even if our targets A & B have already been built and are up-to-date
15# (e.g. in a state left by a previous successful build run), our scanner target
16# tasked with scanning target A will be marked for updating, thus causing any
17# targets depending on it to be updated/rebuilt as well.
18
19import BoostBuild
20
21t = BoostBuild.Tester(use_test_config=False)
22
23t.write("foo.jam", r"""
24import common ;
25import generators ;
26import modules ;
27import type ;
28import types/cpp ;
29
30type.register FOO : foo ;
31type.register BAR : bar ;
32generators.register-standard foo.foo : FOO : CPP BAR ;
33
34local rule sleep-cmd ( delay )
35{
36    if [ modules.peek : NT ]
37    {
38        return ping 127.0.0.1 -n $(delay) -w 1000 >NUL ;
39    }
40    else
41    {
42        return sleep $(delay) ;
43    }
44}
45
46.touch = [ common.file-creation-command ] ;
47.sleep = [ sleep-cmd 2 ] ;
48
49rule foo ( cpp bar : foo : properties * )
50{
51    # We add the INCLUDE relationship between our generated CPP & BAR targets
52    # explicitly instead of relying on Boost Jam's internal implementation
53    # detail - automatically adding such relationships between all files
54    # generated by the same action. This way our test will continue to function
55    # correctly even if the related Boost Jam implementation detail changes.
56    # Note that adding this relationship by adding an #include directive in our
57    # generated CPP file is not good enough as such a relationship would get
58    # added only after the scanner target's relationships have already been
59    # established and they (as affected by our initial INCLUDE relationship) are
60    # the original reason for this test failing.
61    INCLUDES $(cpp) : $(bar) ;
62}
63
64actions foo
65{
66    $(.touch) "$(<[1])"
67    $(.sleep)
68    $(.touch) "$(<[2])"
69}
70""")
71
72t.write(
73    'foo.py',
74"""
75import os
76
77from b2.build import type as type_, generators
78from b2.tools import common
79from b2.manager import get_manager
80
81MANAGER = get_manager()
82ENGINE = MANAGER.engine()
83
84type_.register('FOO', ['foo'])
85type_.register('BAR', ['bar'])
86generators.register_standard('foo.foo', ['FOO'], ['CPP', 'BAR'])
87
88def sleep_cmd(delay):
89    if os.name == 'nt':
90        return 'ping 127.0.0.1 -n {} -w 1000 >NUL'.format(delay)
91    return 'sleep {}'.format(delay)
92
93def foo(targets, sources, properties):
94    cpp, bar = targets
95    foo = sources[0]
96    # We add the INCLUDE relationship between our generated CPP & BAR targets
97    # explicitly instead of relying on Boost Jam's internal implementation
98    # detail - automatically adding such relationships between all files
99    # generated by the same action. This way our test will continue to function
100    # correctly even if the related Boost Jam implementation detail changes.
101    # Note that adding this relationship by adding an #include directive in our
102    # generated CPP file is not good enough as such a relationship would get
103    # added only after the scanner target's relationships have already been
104    # established and they (as affected by our initial INCLUDE relationship) are
105    # the original reason for this test failing.
106    bjam.call('INCLUDES', cpp, bar)
107
108ENGINE.register_action(
109    'foo.foo',
110    '''
111    {touch} "$(<[1])"
112    {sleep}
113    {touch} "$(<[2])"
114    '''.format(touch=common.file_creation_command(), sleep=sleep_cmd(2))
115)
116"""
117)
118
119t.write("x.foo", "")
120t.write("jamroot.jam", """\
121import foo ;
122lib x : x.foo : <link>static ;
123""")
124
125
126# Get everything built once.
127t.run_build_system()
128
129# Simply rerunning the build without touching any of its source target files
130# should not cause any files to be affected.
131t.run_build_system()
132t.expect_nothing_more()
133