project_id.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. #!/usr/bin/python
  2. # Copyright (C) 2012. Jurko Gospodnetic
  3. # Distributed under the Boost Software License, Version 1.0.
  4. # (See accompanying file LICENSE.txt or copy at
  5. # https://www.bfgroup.xyz/b2/LICENSE.txt)
  6. # Tests Boost Build's project-id handling.
  7. import BoostBuild
  8. import sys
  9. def test_assigning_project_ids():
  10. t = BoostBuild.Tester(pass_toolset=False)
  11. t.write("jamroot.jam", """\
  12. import assert ;
  13. import modules ;
  14. import notfile ;
  15. import project ;
  16. rule assert-project-id ( id ? : module-name ? )
  17. {
  18. module-name ?= [ CALLER_MODULE ] ;
  19. assert.result $(id) : project.attribute $(module-name) id ;
  20. }
  21. # Project rule modifies the main project id.
  22. assert-project-id ; # Initial project id is empty
  23. project foo ; assert-project-id /foo ;
  24. project ; assert-project-id /foo ;
  25. project foo ; assert-project-id /foo ;
  26. project bar ; assert-project-id /bar ;
  27. project /foo ; assert-project-id /foo ;
  28. project "" ; assert-project-id /foo ;
  29. # Calling the use-project rule does not modify the project's main id.
  30. use-project id1 : a ;
  31. # We need to load the 'a' Jamfile module manually as the use-project rule will
  32. # only schedule the load to be done after the current module load finishes.
  33. a-module = [ project.load a ] ;
  34. assert-project-id : $(a-module) ;
  35. use-project id2 : a ;
  36. assert-project-id : $(a-module) ;
  37. modules.call-in $(a-module) : project baz ;
  38. assert-project-id /baz : $(a-module) ;
  39. use-project id3 : a ;
  40. assert-project-id /baz : $(a-module) ;
  41. # Make sure the project id still holds after all the scheduled use-project loads
  42. # complete. We do this by scheduling the assert for the Jam action scheduling
  43. # phase.
  44. notfile x : @assert-a-rule ;
  45. rule assert-a-rule ( target : : properties * )
  46. {
  47. assert-project-id /baz : $(a-module) ;
  48. }
  49. """)
  50. t.write("a/jamfile.jam", """\
  51. # Initial project id for this module is empty.
  52. assert-project-id ;
  53. """)
  54. t.run_build_system()
  55. t.cleanup()
  56. def test_using_project_ids_in_target_references():
  57. t = BoostBuild.Tester()
  58. __write_appender(t, "appender.jam")
  59. t.write("jamroot.jam", """\
  60. import type ;
  61. type.register AAA : _a ;
  62. type.register BBB : _b ;
  63. import appender ;
  64. appender.register aaa-to-bbb : AAA : BBB ;
  65. use-project id1 : a ;
  66. use-project /id2 : a ;
  67. bbb b1 : /id1//target ;
  68. bbb b2 : /id2//target ;
  69. bbb b3 : /id3//target ;
  70. bbb b4 : a//target ;
  71. bbb b5 : /project-a1//target ;
  72. bbb b6 : /project-a2//target ;
  73. bbb b7 : /project-a3//target ;
  74. use-project id3 : a ;
  75. """)
  76. t.write("a/source._a", "")
  77. t.write("a/jamfile.jam", """\
  78. project project-a1 ;
  79. project /project-a2 ;
  80. import alias ;
  81. alias target : source._a ;
  82. project /project-a3 ;
  83. """)
  84. t.run_build_system()
  85. t.expect_addition("bin/b%d._b" % x for x in range(1, 8))
  86. t.expect_nothing_more()
  87. t.cleanup()
  88. def test_repeated_ids_for_different_projects():
  89. t = BoostBuild.Tester()
  90. t.write("a/jamfile.jam", "")
  91. t.write("jamroot.jam", "project foo ; use-project foo : a ;")
  92. t.run_build_system(status=1)
  93. t.expect_output_lines("""\
  94. error: Attempt to redeclare already registered project id '/foo'.
  95. error: Original project:
  96. error: Name: Jamfile<*>
  97. error: Module: Jamfile<*>
  98. error: Main id: /foo
  99. error: File: jamroot.jam
  100. error: Location: .
  101. error: New project:
  102. error: Module: Jamfile<*>
  103. error: File: a*jamfile.jam
  104. error: Location: a""")
  105. t.write("jamroot.jam", "use-project foo : a ; project foo ;")
  106. t.run_build_system(status=1)
  107. t.expect_output_lines("""\
  108. error: Attempt to redeclare already registered project id '/foo'.
  109. error: Original project:
  110. error: Name: Jamfile<*>
  111. error: Module: Jamfile<*>
  112. error: Main id: /foo
  113. error: File: jamroot.jam
  114. error: Location: .
  115. error: New project:
  116. error: Module: Jamfile<*>
  117. error: File: a*jamfile.jam
  118. error: Location: a""")
  119. t.write("jamroot.jam", """\
  120. import modules ;
  121. import project ;
  122. modules.call-in [ project.load a ] : project foo ;
  123. project foo ;
  124. """)
  125. t.run_build_system(status=1)
  126. t.expect_output_lines("""\
  127. error: at jamroot.jam:4
  128. error: Attempt to redeclare already registered project id '/foo'.
  129. error: Original project:
  130. error: Name: Jamfile<*>
  131. error: Module: Jamfile<*>
  132. error: Main id: /foo
  133. error: File: a*jamfile.jam
  134. error: Location: a
  135. error: New project:
  136. error: Module: Jamfile<*>
  137. error: File: jamroot.jam
  138. error: Location: .""")
  139. t.cleanup()
  140. def test_repeated_ids_for_same_project():
  141. t = BoostBuild.Tester()
  142. t.write("jamroot.jam", "project foo ; project foo ;")
  143. t.run_build_system()
  144. t.write("jamroot.jam", "project foo ; use-project foo : . ;")
  145. t.run_build_system()
  146. t.write("jamroot.jam", "project foo ; use-project foo : ./. ;")
  147. t.run_build_system()
  148. t.write("jamroot.jam", """\
  149. project foo ;
  150. use-project foo : . ;
  151. use-project foo : ./aaa/.. ;
  152. use-project foo : ./. ;
  153. """)
  154. t.run_build_system()
  155. # On Windows we have a case-insensitive file system and we can use
  156. # backslashes as path separators.
  157. # FIXME: Make a similar test pass on Cygwin.
  158. if sys.platform in ['win32']:
  159. t.write("a/fOo bAr/b/jamfile.jam", "")
  160. t.write("jamroot.jam", r"""
  161. use-project bar : "a/foo bar/b" ;
  162. use-project bar : "a/foO Bar/b" ;
  163. use-project bar : "a/foo BAR/b/" ;
  164. use-project bar : "a\\.\\FOO bar\\b\\" ;
  165. """)
  166. t.run_build_system()
  167. t.rm("a")
  168. t.write("bar/jamfile.jam", "")
  169. t.write("jamroot.jam", """\
  170. use-project bar : bar ;
  171. use-project bar : bar/ ;
  172. use-project bar : bar// ;
  173. use-project bar : bar/// ;
  174. use-project bar : bar//// ;
  175. use-project bar : bar/. ;
  176. use-project bar : bar/./ ;
  177. use-project bar : bar/////./ ;
  178. use-project bar : bar/../bar/xxx/.. ;
  179. use-project bar : bar/..///bar/xxx///////.. ;
  180. use-project bar : bar/./../bar/xxx/.. ;
  181. use-project bar : bar/.////../bar/xxx/.. ;
  182. use-project bar : bar/././../bar/xxx/.. ;
  183. use-project bar : bar/././//////////../bar/xxx/.. ;
  184. use-project bar : bar/.///.////../bar/xxx/.. ;
  185. use-project bar : bar/./././xxx/.. ;
  186. use-project bar : bar/xxx////.. ;
  187. use-project bar : bar/xxx/.. ;
  188. use-project bar : bar///////xxx/.. ;
  189. """)
  190. t.run_build_system()
  191. t.rm("bar")
  192. # On Windows we have a case-insensitive file system and we can use
  193. # backslashes as path separators.
  194. # FIXME: Make a similar test pass on Cygwin.
  195. if sys.platform in ['win32']:
  196. t.write("baR/jamfile.jam", "")
  197. t.write("jamroot.jam", r"""
  198. use-project bar : bar ;
  199. use-project bar : BAR ;
  200. use-project bar : bAr ;
  201. use-project bar : bAr/ ;
  202. use-project bar : bAr\\ ;
  203. use-project bar : bAr\\\\ ;
  204. use-project bar : bAr\\\\///// ;
  205. use-project bar : bAr/. ;
  206. use-project bar : bAr/./././ ;
  207. use-project bar : bAr\\.\\.\\.\\ ;
  208. use-project bar : bAr\\./\\/.\\.\\ ;
  209. use-project bar : bAr/.\\././ ;
  210. use-project bar : Bar ;
  211. use-project bar : BaR ;
  212. use-project bar : BaR/./../bAr/xxx/.. ;
  213. use-project bar : BaR/./..\\bAr\\xxx/.. ;
  214. use-project bar : BaR/xxx/.. ;
  215. use-project bar : BaR///\\\\\\//xxx/.. ;
  216. use-project bar : Bar\\xxx/.. ;
  217. use-project bar : BAR/xXx/.. ;
  218. use-project bar : BAR/xXx\\\\/\\/\\//\\.. ;
  219. """)
  220. t.run_build_system()
  221. t.rm("baR")
  222. t.cleanup()
  223. def test_unresolved_project_references():
  224. t = BoostBuild.Tester()
  225. __write_appender(t, "appender.jam")
  226. t.write("a/source._a", "")
  227. t.write("a/jamfile.jam", "import alias ; alias target : source._a ;")
  228. t.write("jamroot.jam", """\
  229. import type ;
  230. type.register AAA : _a ;
  231. type.register BBB : _b ;
  232. import appender ;
  233. appender.register aaa-to-bbb : AAA : BBB ;
  234. use-project foo : a ;
  235. bbb b1 : a//target ;
  236. bbb b2 : /foo//target ;
  237. bbb b-invalid : invalid//target ;
  238. bbb b-root-invalid : /invalid//target ;
  239. bbb b-missing-root : foo//target ;
  240. bbb b-invalid-target : /foo//invalid ;
  241. """)
  242. t.run_build_system(["b1", "b2"])
  243. t.expect_addition("bin/b%d._b" % x for x in range(1, 3))
  244. t.expect_nothing_more()
  245. t.run_build_system(["b-invalid"], status=1)
  246. t.expect_output_lines("""\
  247. error: Unable to find file or target named
  248. error: 'invalid//target'
  249. error: referred to from project at
  250. error: '.'
  251. error: could not resolve project reference 'invalid'""")
  252. t.run_build_system(["b-root-invalid"], status=1)
  253. t.expect_output_lines("""\
  254. error: Unable to find file or target named
  255. error: '/invalid//target'
  256. error: referred to from project at
  257. error: '.'
  258. error: could not resolve project reference '/invalid'""")
  259. t.run_build_system(["b-missing-root"], status=1)
  260. t.expect_output_lines("""\
  261. error: Unable to find file or target named
  262. error: 'foo//target'
  263. error: referred to from project at
  264. error: '.'
  265. error: could not resolve project reference 'foo' - possibly missing a """
  266. "leading slash ('/') character.")
  267. t.run_build_system(["b-invalid-target"], status=1)
  268. t.expect_output_lines("""\
  269. error: Unable to find file or target named
  270. error: '/foo//invalid'
  271. error: referred to from project at
  272. error: '.'""")
  273. t.expect_output_lines("*could not resolve project reference*", False)
  274. t.cleanup()
  275. def __write_appender(t, name):
  276. t.write(name,
  277. r"""# Copyright 2012 Jurko Gospodnetic
  278. # Distributed under the Boost Software License, Version 1.0.
  279. # (See accompanying file LICENSE.txt or copy at
  280. # https://www.bfgroup.xyz/b2/LICENSE.txt)
  281. # Support for registering test generators that construct their targets by
  282. # simply appending their given input data, e.g. list of sources & targets.
  283. import "class" : new ;
  284. import generators ;
  285. import modules ;
  286. import sequence ;
  287. rule register ( id composing ? : source-types + : target-types + )
  288. {
  289. local caller-module = [ CALLER_MODULE ] ;
  290. id = $(caller-module).$(id) ;
  291. local g = [ new generator $(id) $(composing) : $(source-types) :
  292. $(target-types) ] ;
  293. $(g).set-rule-name $(__name__).appender ;
  294. generators.register $(g) ;
  295. return $(id) ;
  296. }
  297. if [ modules.peek : NT ]
  298. {
  299. X = ")" ;
  300. ECHO_CMD = (echo. ;
  301. }
  302. else
  303. {
  304. X = \" ;
  305. ECHO_CMD = "echo $(X)" ;
  306. }
  307. local appender-runs ;
  308. # We set up separate actions for building each target in order to avoid having
  309. # to iterate over them in action (i.e. shell) code. We have to be extra careful
  310. # though to achieve the exact same effect as if doing all the work in just one
  311. # action. Otherwise Boost Jam might, under some circumstances, run only some of
  312. # our actions. To achieve this we register a series of actions for all the
  313. # targets (since they all have the same target list - either all or none of them
  314. # get run independent of which target actually needs to get built), each
  315. # building only a single target. Since all our actions use the same targets, we
  316. # can not use 'on-target' parameters to pass data to a specific action so we
  317. # pass them using the second 'sources' parameter which our actions then know how
  318. # to interpret correctly. This works well since Boost Jam does not automatically
  319. # add dependency relations between specified action targets & sources and so the
  320. # second argument, even though most often used to pass in a list of sources, can
  321. # actually be used for passing in any type of information.
  322. rule appender ( targets + : sources + : properties * )
  323. {
  324. appender-runs = [ CALC $(appender-runs:E=0) + 1 ] ;
  325. local target-index = 0 ;
  326. local target-count = [ sequence.length $(targets) ] ;
  327. local original-targets ;
  328. for t in $(targets)
  329. {
  330. target-index = [ CALC $(target-index) + 1 ] ;
  331. local appender-run = $(appender-runs) ;
  332. if $(targets[2])-defined
  333. {
  334. appender-run += [$(target-index)/$(target-count)] ;
  335. }
  336. append $(targets) : $(appender-run:J=" ") $(t) $(sources) ;
  337. }
  338. }
  339. actions append
  340. {
  341. $(ECHO_CMD)-------------------------------------------------$(X)
  342. $(ECHO_CMD)Appender run: $(>[1])$(X)
  343. $(ECHO_CMD)Appender run: $(>[1])$(X)>> "$(>[2])"
  344. $(ECHO_CMD)Target group: $(<:J=' ')$(X)
  345. $(ECHO_CMD)Target group: $(<:J=' ')$(X)>> "$(>[2])"
  346. $(ECHO_CMD) Target: '$(>[2])'$(X)
  347. $(ECHO_CMD) Target: '$(>[2])'$(X)>> "$(>[2])"
  348. $(ECHO_CMD) Sources: '$(>[3-]:J=' ')'$(X)
  349. $(ECHO_CMD) Sources: '$(>[3-]:J=' ')'$(X)>> "$(>[2])"
  350. $(ECHO_CMD)=================================================$(X)
  351. $(ECHO_CMD)-------------------------------------------------$(X)>> "$(>[2])"
  352. }
  353. """)
  354. test_assigning_project_ids()
  355. test_using_project_ids_in_target_references()
  356. test_repeated_ids_for_same_project()
  357. test_repeated_ids_for_different_projects()
  358. test_unresolved_project_references()