link.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. #!/usr/bin/python
  2. # Copyright 2014-2015 Steven Watanabe
  3. # Distributed under the Boost Software License, Version 1.0.
  4. # (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
  5. # Tests the link-directory rule used to create the
  6. # common boost/ directory in the new git layout.
  7. import BoostBuild
  8. def ignore_config(t):
  9. """These files are created by the configuration logic in link.jam
  10. They may or may not exist, depending on the system."""
  11. t.ignore("bin/symlink/test-hardlink")
  12. t.ignore("bin/test-hardlink-source")
  13. t.ignore("bin/test-symlink")
  14. t.ignore("bin/test-symlink-source")
  15. def test_basic():
  16. """Test creation of a single link"""
  17. t = BoostBuild.Tester()
  18. t.write("jamroot.jam", """\
  19. import link ;
  20. link-directory dir1-link : src/dir1/include : <location>. ;
  21. """)
  22. t.write("src/dir1/include/file1.h", "file1")
  23. t.run_build_system()
  24. t.expect_addition("include/file1.h")
  25. t.expect_content("include/file1.h", "file1")
  26. ignore_config(t)
  27. t.expect_nothing_more()
  28. t.cleanup()
  29. def test_merge_two():
  30. """Test merging two directories"""
  31. t = BoostBuild.Tester()
  32. t.write("jamroot.jam", """\
  33. import link ;
  34. link-directory dir1-link : src/dir1/include : <location>. ;
  35. link-directory dir2-link : src/dir2/include : <location>. ;
  36. """)
  37. t.write("src/dir1/include/file1.h", "file1")
  38. t.write("src/dir2/include/file2.h", "file2")
  39. t.run_build_system()
  40. t.expect_addition("include/file1.h")
  41. t.expect_content("include/file1.h", "file1")
  42. t.expect_addition("include/file2.h")
  43. t.expect_content("include/file2.h", "file2")
  44. ignore_config(t)
  45. t.expect_nothing_more()
  46. t.cleanup()
  47. def test_merge_existing(group1, group2):
  48. """Test adding a link when a different symlink already exists"""
  49. t = BoostBuild.Tester()
  50. t.write("jamroot.jam", """\
  51. import link ;
  52. link-directory dir1-link : src/dir1/include : <location>. ;
  53. link-directory dir2-link : src/dir2/include : <location>. ;
  54. """)
  55. t.write("src/dir1/include/file1.h", "file1")
  56. t.write("src/dir2/include/file2.h", "file2")
  57. t.run_build_system(group1)
  58. if "dir1-link" in group1:
  59. t.expect_addition("include/file1.h")
  60. t.expect_content("include/file1.h", "file1")
  61. if "dir2-link" in group1:
  62. t.expect_addition("include/file2.h")
  63. t.expect_content("include/file2.h", "file2")
  64. ignore_config(t)
  65. t.expect_nothing_more()
  66. t.run_build_system(group2)
  67. if "dir1-link" in group2:
  68. if "dir1-link" not in group1:
  69. t.expect_addition("include/file1.h")
  70. else:
  71. # When a directory is split the link needs to be recreated.
  72. # On Windows, the test system checks the mod time of the
  73. # link rather than the link target, so this may be seen as
  74. # an update.
  75. t.ignore_touch("include/file1.h")
  76. t.expect_content("include/file1.h", "file1")
  77. else:
  78. t.ignore_removal("include/file1.h")
  79. if "dir2-link" in group2:
  80. if "dir2-link" not in group1:
  81. t.expect_addition("include/file2.h")
  82. else:
  83. t.ignore_touch("include/file2.h")
  84. t.expect_content("include/file2.h", "file2")
  85. else:
  86. t.ignore_removal("include/file2.h")
  87. ignore_config(t)
  88. t.expect_nothing_more()
  89. t.cleanup()
  90. def test_merge_existing_all():
  91. test_merge_existing(["dir1-link"], ["dir2-link"])
  92. test_merge_existing(["dir2-link"], ["dir1-link"])
  93. test_merge_existing(["dir1-link"], ["dir1-link", "dir2-link"])
  94. test_merge_existing(["dir2-link"], ["dir1-link", "dir2-link"])
  95. def test_merge_recursive():
  96. "Test merging several directories including common prefixes"
  97. t = BoostBuild.Tester()
  98. t.write("jamroot.jam", """\
  99. import link ;
  100. link-directory dir1-link : src/dir1/include : <location>. ;
  101. link-directory dir2-link : src/dir2/include : <location>. ;
  102. link-directory dir3-link : src/dir3/include : <location>. ;
  103. """)
  104. t.write("src/dir1/include/file1.h", "file1")
  105. t.write("src/dir2/include/file2.h", "file2")
  106. t.write("src/dir2/include/nested/file3.h", "file3")
  107. t.write("src/dir3/include/nested/file4.h", "file4")
  108. t.run_build_system()
  109. t.expect_addition("include/file1.h")
  110. t.expect_content("include/file1.h", "file1")
  111. t.expect_addition("include/file2.h")
  112. t.expect_content("include/file2.h", "file2")
  113. t.expect_addition("include/nested/file3.h")
  114. t.expect_content("include/nested/file3.h", "file3")
  115. t.expect_addition("include/nested/file4.h")
  116. t.expect_content("include/nested/file4.h", "file4")
  117. ignore_config(t)
  118. t.expect_nothing_more()
  119. t.cleanup()
  120. def test_merge_recursive_existing(group1, group2):
  121. "Test merging several directories including common prefixes."
  122. t = BoostBuild.Tester()
  123. t.write("jamroot.jam", """\
  124. import link ;
  125. link-directory dir1-link : src/dir1/include : <location>. ;
  126. link-directory dir2-link : src/dir2/include : <location>. ;
  127. link-directory dir3-link : src/dir3/include : <location>. ;
  128. link-directory dir4-link : src/dir4/include : <location>. ;
  129. link-directory dir5-link : src/dir5/include : <location>. ;
  130. """)
  131. t.write("src/dir1/include/file1.h", "file1")
  132. t.write("src/dir2/include/nested/file2.h", "file2")
  133. t.write("src/dir3/include/nested/file3.h", "file3")
  134. t.write("src/dir4/include/nested/xxx/yyy/file4.h", "file4")
  135. t.write("src/dir5/include/nested/xxx/yyy/file5.h", "file5")
  136. t.run_build_system(group1)
  137. t.run_build_system(group2 + ["-d+12"])
  138. def check_file(target, file):
  139. if target in group2:
  140. if target in group1:
  141. t.ignore_touch(file)
  142. else:
  143. t.expect_addition(file)
  144. check_file("dir1-link", "include/file1.h")
  145. check_file("dir2-link", "include/nested/file2.h")
  146. check_file("dir3-link", "include/nested/file3.h")
  147. check_file("dir4-link", "include/nested/xxx/yyy/file4.h")
  148. check_file("dir5-link", "include/nested/xxx/yyy/file5.h")
  149. ignore_config(t)
  150. t.expect_nothing_more()
  151. t.cleanup()
  152. def test_merge_recursive_existing_all():
  153. # These should create a link
  154. test_merge_recursive_existing(["dir2-link"], ["dir2-link", "dir1-link"])
  155. test_merge_recursive_existing(["dir2-link"], ["dir1-link", "dir2-link"])
  156. # These should create a directory
  157. test_merge_recursive_existing(["dir2-link"], ["dir2-link", "dir3-link"])
  158. test_merge_recursive_existing(["dir2-link"], ["dir3-link", "dir2-link"])
  159. # It should work even if we have to create many intermediate subdirectories
  160. test_merge_recursive_existing(["dir4-link"], ["dir4-link", "dir5-link"])
  161. test_merge_recursive_existing(["dir4-link"], ["dir5-link", "dir4-link"])
  162. def test_include_scan():
  163. """Make sure that the #include scanner finds the headers"""
  164. t = BoostBuild.Tester()
  165. t.write("jamroot.jam", """\
  166. import link ;
  167. link-directory dir1-link : src/dir1/include : <location>. ;
  168. link-directory dir2-link : src/dir2/include : <location>. ;
  169. obj test : test.cpp :
  170. <include>include
  171. <implicit-dependency>dir1-link
  172. <implicit-dependency>dir2-link ;
  173. """)
  174. t.write("src/dir1/include/file1.h", "#include <file2.h>\n")
  175. t.write("src/dir2/include/file2.h", "int f();\n")
  176. t.write("test.cpp", """\
  177. #include <file1.h>
  178. int main() { f(); }
  179. """);
  180. t.run_build_system(["test"])
  181. t.expect_addition("bin/$toolset/debug*/test.obj")
  182. t.run_build_system()
  183. t.expect_nothing_more()
  184. t.cleanup()
  185. def test_include_scan_merge_existing():
  186. """Make sure that files are replaced if needed when merging in
  187. a new directory"""
  188. t = BoostBuild.Tester()
  189. t.write("jamroot.jam", """\
  190. import link ;
  191. link-directory dir1-link : src/dir1/include : <location>. ;
  192. link-directory dir2-link : src/dir2/include : <location>. ;
  193. obj test : test.cpp :
  194. <include>include
  195. <implicit-dependency>dir1-link
  196. <implicit-dependency>dir2-link ;
  197. """)
  198. t.write("src/dir1/include/file1.h", "int f();")
  199. t.write("src/dir2/include/file2.h", "#include <file1.h>")
  200. t.write("test.cpp", """\
  201. #include <file2.h>
  202. int main() { f(); }
  203. """)
  204. t.run_build_system(["dir2-link"])
  205. t.run_build_system(["test"])
  206. t.expect_addition("include/file1.h")
  207. t.expect_addition("bin/$toolset/debug*/test.obj")
  208. t.ignore_touch("include/file2.h")
  209. t.expect_nothing_more()
  210. t.cleanup()
  211. def test_update_file_link(params1, params2):
  212. """Tests the behavior of updates when changing the link mode.
  213. The link needs to be updated iff the original was a copy."""
  214. t = BoostBuild.Tester()
  215. t.write("jamroot.jam", """\
  216. import link ;
  217. import project ;
  218. import property-set ;
  219. import modules ;
  220. if --no-symlinks in [ modules.peek : ARGV ]
  221. {
  222. modules.poke link : .can-symlink : false ;
  223. }
  224. if --no-hardlinks in [ modules.peek : ARGV ]
  225. {
  226. modules.poke link : .can-hardlink : false ;
  227. }
  228. .project = [ project.current ] ;
  229. .has-files = [ glob include/file1.h ] ;
  230. rule can-link ( properties * ) {
  231. if ( ! [ link.can-symlink $(.project) ] ) &&
  232. ( ! [ link.can-hardlink $(.project) ] )
  233. {
  234. ECHO links unsupported ;
  235. }
  236. }
  237. # Use two directories so that we link to individual files.
  238. link-directory dir1-link : src/dir1/include : <location>. ;
  239. link-directory dir2-link : src/dir2/include : <location>. ;
  240. alias check-linking : : <conditional>@can-link ;
  241. """)
  242. t.write("src/dir1/include/file1.h", "file1")
  243. t.write("src/dir2/include/file2.h", "file2")
  244. t.run_build_system(params1)
  245. ignore_config(t)
  246. t.expect_addition("include/file1.h")
  247. t.expect_addition("include/file2.h")
  248. t.expect_nothing_more()
  249. using_links = "links unsupported" not in t.stdout()
  250. t.touch("src/dir1/include/file1.h")
  251. t.run_build_system(params2)
  252. if not using_links: t.expect_touch("include/file1.h")
  253. ignore_config(t)
  254. t.expect_nothing_more()
  255. t.cleanup()
  256. def test_update_file_link_all():
  257. """Test all nine possible combinations of two runs."""
  258. possible_args = [[], ["--no-symlinks"], ["--no-symlinks", "--no-hardlinks"]]
  259. for arg1 in possible_args:
  260. for arg2 in possible_args:
  261. test_update_file_link(arg1, arg2)
  262. def test_error_duplicate():
  263. """Test that linking a single file from
  264. multiple sources causes a hard error."""
  265. t = BoostBuild.Tester()
  266. t.write("jamroot.jam", """\
  267. import link ;
  268. link-directory dir1-link : src/dir1/include : <location>. ;
  269. link-directory dir2-link : src/dir2/include : <location>. ;
  270. """)
  271. t.write("src/dir1/include/file1.h", "file1")
  272. t.write("src/dir2/include/file1.h", "file2")
  273. t.run_build_system(status=1)
  274. t.expect_output_lines(
  275. ["error: Cannot create link include/file1.h to src/dir2/include/file1.h.",
  276. "error: Link previously defined to another file, src/dir1/include/file1.h."])
  277. t.cleanup()
  278. test_basic()
  279. test_merge_two()
  280. test_merge_existing_all()
  281. test_merge_recursive()
  282. test_merge_recursive_existing_all()
  283. test_include_scan()
  284. test_include_scan_merge_existing()
  285. test_update_file_link_all()
  286. test_error_duplicate()