dependency_test.py 6.9 KB

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