summaryrefslogtreecommitdiffstats
path: root/src/boost/tools/build/test/scanner_causing_rebuilds.py
blob: d0b55ee16285dee9db3cce3df72045248598fca8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/python

# Copyright 2012 Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)

#   Tests for a bug causing Boost Build's scanner targets to be rebuilt.
# unnecessarily in the following scenario:
#  * We want to build target X requiring target A.
#  * We have a multi-file action generating targets A & B.
#  * Out action generates target B with a more recent timestamp than target A.
#  * Target A includes target B.
#  * Target A has a registered include scanner.
# Now even if our targets A & B have already been built and are up-to-date
# (e.g. in a state left by a previous successful build run), our scanner target
# tasked with scanning target A will be marked for updating, thus causing any
# targets depending on it to be updated/rebuilt as well.

import BoostBuild

t = BoostBuild.Tester(use_test_config=False)

t.write("foo.jam", r"""
import common ;
import generators ;
import modules ;
import type ;
import types/cpp ;

type.register FOO : foo ;
type.register BAR : bar ;
generators.register-standard foo.foo : FOO : CPP BAR ;

local rule sleep-cmd ( delay )
{
    if [ modules.peek : NT ]
    {
        return ping 127.0.0.1 -n $(delay) -w 1000 >NUL ;
    }
    else
    {
        return sleep $(delay) ;
    }
}

.touch = [ common.file-creation-command ] ;
.sleep = [ sleep-cmd 2 ] ;

rule foo ( cpp bar : foo : properties * )
{
    # We add the INCLUDE relationship between our generated CPP & BAR targets
    # explicitly instead of relying on Boost Jam's internal implementation
    # detail - automatically adding such relationships between all files
    # generated by the same action. This way our test will continue to function
    # correctly even if the related Boost Jam implementation detail changes.
    # Note that adding this relationship by adding an #include directive in our
    # generated CPP file is not good enough as such a relationship would get
    # added only after the scanner target's relationships have already been
    # established and they (as affected by our initial INCLUDE relationship) are
    # the original reason for this test failing.
    INCLUDES $(cpp) : $(bar) ;
}

actions foo
{
    $(.touch) "$(<[1])"
    $(.sleep)
    $(.touch) "$(<[2])"
}
""")

t.write(
    'foo.py',
"""
import os

from b2.build import type as type_, generators
from b2.tools import common
from b2.manager import get_manager

MANAGER = get_manager()
ENGINE = MANAGER.engine()

type_.register('FOO', ['foo'])
type_.register('BAR', ['bar'])
generators.register_standard('foo.foo', ['FOO'], ['CPP', 'BAR'])

def sleep_cmd(delay):
    if os.name == 'nt':
        return 'ping 127.0.0.1 -n {} -w 1000 >NUL'.format(delay)
    return 'sleep {}'.format(delay)

def foo(targets, sources, properties):
    cpp, bar = targets
    foo = sources[0]
    # We add the INCLUDE relationship between our generated CPP & BAR targets
    # explicitly instead of relying on Boost Jam's internal implementation
    # detail - automatically adding such relationships between all files
    # generated by the same action. This way our test will continue to function
    # correctly even if the related Boost Jam implementation detail changes.
    # Note that adding this relationship by adding an #include directive in our
    # generated CPP file is not good enough as such a relationship would get
    # added only after the scanner target's relationships have already been
    # established and they (as affected by our initial INCLUDE relationship) are
    # the original reason for this test failing.
    bjam.call('INCLUDES', cpp, bar)

ENGINE.register_action(
    'foo.foo',
    '''
    {touch} "$(<[1])"
    {sleep}
    {touch} "$(<[2])"
    '''.format(touch=common.file_creation_command(), sleep=sleep_cmd(2))
)
"""
)

t.write("x.foo", "")
t.write("jamroot.jam", """\
import foo ;
lib x : x.foo : <link>static ;
""")


# Get everything built once.
t.run_build_system()

# Simply rerunning the build without touching any of its source target files
# should not cause any files to be affected.
t.run_build_system()
t.expect_nothing_more()