1#!/usr/bin/python 2 3# Copyright 2003 Dave Abrahams 4# Copyright 2002, 2003, 2005, 2006 Vladimir Prus 5# Distributed under the Boost Software License, Version 1.0. 6# (See accompanying file LICENSE_1_0.txt or copy at 7# http://www.boost.org/LICENSE_1_0.txt) 8 9import BoostBuild 10 11 12def test_basic(): 13 t = BoostBuild.Tester(["-d3", "-d+12"], use_test_config=False) 14 15 t.write("a.cpp", """ 16#include <a.h> 17# include "a.h" 18#include <x.h> 19int main() {} 20""") 21 t.write("a.h", "\n") 22 t.write("a_c.c", """\ 23#include <a.h> 24# include "a.h" 25#include <x.h> 26""") 27 t.write("b.cpp", """\ 28#include "a.h" 29int main() {} 30""") 31 t.write("b.h", "\n") 32 t.write("c.cpp", """\ 33#include "x.h" 34int main() {} 35""") 36 t.write("e.cpp", """\ 37#include "x.h" 38int main() {} 39""") 40 t.write("x.foo", "") 41 t.write("y.foo", "") 42 43 t.write("src1/a.h", '#include "b.h"\n') 44 t.write("src1/b.h", '#include "c.h"\n') 45 t.write("src1/c.h", "\n") 46 t.write("src1/z.h", """\ 47extern int dummy_variable_suppressing_empty_file_warning_on_hp_cxx_compiler; 48""") 49 50 t.write("src2/b.h", "\n") 51 52 t.write("jamroot.jam", """\ 53import foo ; 54import types/cpp ; 55import types/exe ; 56 57project test : requirements <include>src1 ; 58 59exe a : x.foo a.cpp a_c.c ; 60exe b : b.cpp ; 61 62# Because of <define>FOO, c.cpp will be compiled to a different directory than 63# everything for main target "a". Therefore, without <implicit-dependency>, C 64# preprocessor processing that module will not find "x.h", which is part of 65# "a"'s dependency graph. 66# 67# -------------------------- 68# More detailed explanation: 69# -------------------------- 70# c.cpp includes x.h which does not exist on the current include path so Boost 71# Jam will try to match it to existing Jam targets to cover cases as this one 72# where the file is generated by the same build. 73# 74# However, as x.h is not part of "c" metatarget's dependency graph, Boost 75# Build will not actualize its target by default, i.e. create its Jam target. 76# 77# To get the Jam target created in time, we use the <implicit-dependency> 78# feature. This tells Boost Build that it needs to actualize the dependency 79# graph for metatarget "a", even though that metatarget has not been directly 80# mentioned and is not a dependency for any of the metatargets mentioned in the 81# current build request. 82# 83# Note that Boost Build does not automatically add a dependency between the 84# Jam targets in question so, if Boost Jam does not add a dependency on a target 85# from that other dependency graph (x.h in our case), i.e. if c.cpp does not 86# actually include x.h, us actualizing it will have no effect in the end as 87# Boost Jam will not have a reason to actually build those targets in spite of 88# knowing about them. 89exe c : c.cpp : <define>FOO <implicit-dependency>a ; 90""") 91 92 t.write("foo.jam", """\ 93import generators ; 94import modules ; 95import os ; 96import print ; 97import type ; 98import types/cpp ; 99 100type.register FOO : foo ; 101 102generators.register-standard foo.foo : FOO : CPP H ; 103 104nl = " 105" ; 106 107rule foo ( targets * : sources * : properties * ) 108{ 109 # On NT, you need an exported symbol in order to have an import library 110 # generated. We will not really use the symbol defined here, just force the 111 # import library creation. 112 if ( [ os.name ] = NT || [ modules.peek : OS ] in CYGWIN ) && 113 <main-target-type>LIB in $(properties) 114 { 115 .decl = "void __declspec(dllexport) foo() {}" ; 116 } 117 print.output $(<[1]) ; 118 print.text $(.decl:E="//")$(nl) ; 119 print.output $(<[2]) ; 120 print.text "#include <z.h>"$(nl) ; 121} 122""") 123 124 t.write("foo.py", 125r"""import bjam 126import b2.build.type as type 127import b2.build.generators as generators 128 129from b2.manager import get_manager 130 131type.register("FOO", ["foo"]) 132generators.register_standard("foo.foo", ["FOO"], ["CPP", "H"]) 133 134def prepare_foo(targets, sources, properties): 135 if properties.get('os') in ['windows', 'cygwin']: 136 bjam.call('set-target-variable', targets, "DECL", 137 "void __declspec(dllexport) foo() {}") 138 139get_manager().engine().register_action("foo.foo", 140 "echo -e $(DECL:E=//)\\n > $(<[1])\n" 141 "echo -e "#include <z.h>\\n" > $(<[2])\n", function=prepare_foo) 142""") 143 144 # Check that main target 'c' was able to find 'x.h' from 'a's dependency 145 # graph. 146 t.run_build_system() 147 t.expect_addition("bin/$toolset/debug*/c.exe") 148 149 # Check handling of first level includes. 150 151 # Both 'a' and 'b' include "a.h" and should be updated. 152 t.touch("a.h") 153 t.run_build_system() 154 155 t.expect_touch("bin/$toolset/debug*/a.exe") 156 t.expect_touch("bin/$toolset/debug*/a.obj") 157 t.expect_touch("bin/$toolset/debug*/a_c.obj") 158 t.expect_touch("bin/$toolset/debug*/b.exe") 159 t.expect_touch("bin/$toolset/debug*/b.obj") 160 t.ignore_touch("bin/*/a.rsp") 161 t.ignore_touch("bin/*/b.rsp") 162 t.expect_nothing_more() 163 164 # Only source files using include <a.h> should be compiled. 165 t.touch("src1/a.h") 166 t.run_build_system() 167 168 t.expect_touch("bin/$toolset/debug*/a.exe") 169 t.expect_touch("bin/$toolset/debug*/a.obj") 170 t.expect_touch("bin/$toolset/debug*/a_c.obj") 171 t.ignore_touch("bin/*/a.rsp") 172 t.expect_nothing_more() 173 174 # "src/a.h" includes "b.h" (in the same dir). 175 t.touch("src1/b.h") 176 t.run_build_system() 177 t.expect_touch("bin/$toolset/debug*/a.exe") 178 t.expect_touch("bin/$toolset/debug*/a.obj") 179 t.expect_touch("bin/$toolset/debug*/a_c.obj") 180 t.ignore_touch("bin/*/a.rsp") 181 t.expect_nothing_more() 182 183 # Included by "src/b.h". We had a bug: file included using double quotes 184 # (e.g. "b.h") was not scanned at all in this case. 185 t.touch("src1/c.h") 186 t.run_build_system() 187 t.expect_touch("bin/$toolset/debug*/a.exe") 188 189 t.touch("b.h") 190 t.run_build_system() 191 t.expect_nothing_more() 192 193 # Test dependency on a generated header. 194 # 195 # TODO: we have also to check that generated header is found correctly if 196 # it is different for different subvariants. Lacking any toolset support, 197 # this check will be implemented later. 198 t.touch("x.foo") 199 t.run_build_system() 200 t.expect_touch("bin/$toolset/debug*/a.obj") 201 t.expect_touch("bin/$toolset/debug*/a_c.obj") 202 203 # Check that generated headers are scanned for dependencies as well. 204 t.touch("src1/z.h") 205 t.run_build_system() 206 t.expect_touch("bin/$toolset/debug*/a.obj") 207 t.expect_touch("bin/$toolset/debug*/a_c.obj") 208 209 t.cleanup() 210 211 212def test_scanned_includes_with_absolute_paths(): 213 """ 214 Regression test: on Windows, <includes> with absolute paths were not 215 considered when scanning dependencies. 216 217 """ 218 t = BoostBuild.Tester(["-d3", "-d+12"]) 219 220 t.write("jamroot.jam", """\ 221path-constant TOP : . ; 222exe app : main.cpp : <include>$(TOP)/include ; 223"""); 224 225 t.write("main.cpp", """\ 226#include <dir/header.h> 227int main() {} 228""") 229 230 t.write("include/dir/header.h", "\n") 231 232 t.run_build_system() 233 t.expect_addition("bin/$toolset/debug*/main.obj") 234 235 t.touch("include/dir/header.h") 236 t.run_build_system() 237 t.expect_touch("bin/$toolset/debug*/main.obj") 238 239 t.cleanup() 240 241 242test_basic() 243test_scanned_includes_with_absolute_paths() 244