gen_test.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. # python3
  2. # Copyright 2021 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. from datetime import datetime
  6. import io
  7. import re
  8. import tempfile
  9. import toml
  10. import unittest
  11. from lib import gen
  12. from lib import cargo
  13. from lib import compiler
  14. # An example output from `cargo tree`
  15. # 1. We patch paths into the Cargo.toml files that we feed to `cargo tree` and
  16. # this demonstrates that for the "quote" crate by including the path there.
  17. # 2. We include features in the output, demonstrated by many deps here. And we
  18. # also have an example of a crate without any features with the second
  19. # occurrence of "unicode-xid".
  20. # 3. Some crates get de-duped and that means get a (*) on them, which we can
  21. # see.
  22. # 4. A proc-macro crate is a different kind of crate and is designated by the
  23. # (proc-macro) after the version, and we have an example of that with
  24. # cxxbridge-macro.
  25. # 5. We include direct deps as well as transitive deps, which have more than one
  26. # piece of ascii art on their line.
  27. CARGO_TREE = """
  28. cxx v1.0.56 (/path/to/chromium/src/third_party/rust/cxx/v1/crate)
  29. ├── cxxbridge-macro v1.0.56 (proc-macro)
  30. │ ├── proc-macro2 v1.0.32 default,proc-macro,span-locations
  31. │ │ └── unicode-xid v0.2.2 default
  32. │ ├── quote v1.0.10 (/path/to/chromium/src/third_party/rust/quote/v1/crate) default,proc-macro
  33. │ │ └── proc-macro2 v1.0.32 default,proc-macro,span-locations (*)
  34. │ └── syn v1.0.81 clone-impls,default,derive,full,parsing,printing,proc-macro,quote
  35. │ ├── proc-macro2 v1.0.32 default,proc-macro,span-locations (*)
  36. │ ├── quote v1.0.10 default,proc-macro (*)
  37. │ └── unicode-xid v0.2.2
  38. └── link-cplusplus v1.0.5 default
  39. """.lstrip("\n")
  40. class GenTestCase(unittest.TestCase):
  41. def assertListSortedEqual(self, a, b, msg=None):
  42. a.sort()
  43. b.sort()
  44. if msg:
  45. self.assertListEqual(a, b, msg=msg)
  46. else:
  47. self.assertListEqual(a, b)
  48. def matching_archs(self, matching: str) -> set[str]:
  49. return {
  50. arch
  51. for arch in compiler._RUSTC_ARCH_TO_BUILD_CONDITION
  52. if re.search(matching, arch)
  53. }
  54. def all_archs(self):
  55. return set(compiler._RUSTC_ARCH_TO_BUILD_CONDITION.keys())
  56. def make_args(self):
  57. class Args:
  58. pass
  59. args = Args
  60. args.verbose = False
  61. return args
  62. def test_parse_cargo_tree_dependency_line(self):
  63. args = self.make_args()
  64. lines = CARGO_TREE.split("\n")
  65. # Here we are simulating `cargo tree` on a third-party Cargo.toml file,
  66. # not our special third_party.toml file. So we pass False as
  67. # is_third_party_toml, and we can give an empty parsed toml file as it
  68. # won't be used then.
  69. # cxx v1.0.56 (/path/to/chromium/src/third_party/rust/cxx/v1/crate)
  70. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[0])
  71. self.assertEqual(r, None) # not a dependency
  72. # ├── cxxbridge-macro v1.0.56 (proc-macro)
  73. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[1])
  74. expected = gen.CargoTreeDependency(cargo.CrateKey(
  75. "cxxbridge-macro", "1.0.56"),
  76. full_version="1.0.56")
  77. self.assertEqual(r, expected)
  78. # │ ├── proc-macro2 v1.0.32 default,proc-macro,span-locations
  79. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[2])
  80. expected = gen.CargoTreeDependency(
  81. cargo.CrateKey("proc-macro2", "1.0.32"),
  82. full_version="1.0.32",
  83. features=["default", "proc-macro", "span-locations"])
  84. self.assertEqual(r, expected)
  85. # │ │ └── unicode-xid v0.2.2 default
  86. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[3])
  87. expected = gen.CargoTreeDependency(cargo.CrateKey(
  88. "unicode-xid", "0.2.2"),
  89. full_version="0.2.2",
  90. features=["default"])
  91. self.assertEqual(r, expected)
  92. # │ ├── quote v1.0.10 (/path/to/chromium/src/third_party/rust/quote/v1/crate) default,proc-macro
  93. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[4])
  94. expected = gen.CargoTreeDependency(
  95. cargo.CrateKey("quote", "1.0.10"),
  96. full_version="1.0.10",
  97. crate_path="/path/to/chromium/src/third_party/rust/quote/v1/crate",
  98. features=["default", "proc-macro"])
  99. self.assertEqual(r, expected)
  100. # │ │ └── proc-macro2 v1.0.32 default,proc-macro,span-locations (*)
  101. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[5])
  102. expected = gen.CargoTreeDependency(
  103. cargo.CrateKey("proc-macro2", "1.0.32"),
  104. full_version="1.0.32",
  105. features=["default", "proc-macro", "span-locations"])
  106. self.assertEqual(r, expected)
  107. # │ └── syn v1.0.81 clone-impls,default,derive,full,parsing,printing,proc-macro,quote
  108. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[6])
  109. expected = gen.CargoTreeDependency(cargo.CrateKey("syn", "1.0.81"),
  110. full_version="1.0.81",
  111. features=[
  112. "clone-impls", "default",
  113. "derive", "full", "parsing",
  114. "printing", "proc-macro", "quote"
  115. ])
  116. self.assertEqual(r, expected)
  117. # │ ├── proc-macro2 v1.0.32 default,proc-macro,span-locations (*)
  118. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[7])
  119. expected = gen.CargoTreeDependency(
  120. cargo.CrateKey("proc-macro2", "1.0.32"),
  121. full_version="1.0.32",
  122. features=["default", "proc-macro", "span-locations"])
  123. self.assertEqual(r, expected)
  124. # │ ├── quote v1.0.10 default,proc-macro (*)
  125. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[8])
  126. expected = gen.CargoTreeDependency(cargo.CrateKey("quote", "1.0.10"),
  127. full_version="1.0.10",
  128. features=["default", "proc-macro"])
  129. self.assertEqual(r, expected)
  130. # │ └── unicode-xid v0.2.2
  131. r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[9])
  132. expected = gen.CargoTreeDependency(cargo.CrateKey(
  133. "unicode-xid", "0.2.2"),
  134. full_version="0.2.2")
  135. self.assertEqual(r, expected)
  136. # └── link-cplusplus v1.0.5 default
  137. r = gen._parse_cargo_tree_dependency_line(args, dict(), False,
  138. lines[10])
  139. d = gen.CargoTreeDependency(cargo.CrateKey("link-cplusplus", "1.0.5"),
  140. full_version="1.0.5",
  141. features=["default"])
  142. self.assertEqual(r, d)
  143. def test_third_party_toml(self):
  144. args = self.make_args()
  145. THIRD_PARTY_TOML = """
  146. # This dependency has no extensions
  147. [dependencies]
  148. cxxbridge-macro = "1"
  149. # This dependency has an extension, and is default-visible to first-party code.
  150. [dependencies.link-cplusplus]
  151. build-script-outputs = [ "src/link.rs", "src/cplusplus.rs" ]
  152. # This dependency is not visible to first-party code thanks to the extension.
  153. [dependencies.syn]
  154. allow-first-party-usage = false
  155. """
  156. # Here we are simulating parsing our special third_party.toml file, so
  157. # we need to present the contents of that file to the
  158. # _parse_cargo_tree_dependency_line() function, in order for it to look
  159. # for extensions.
  160. toml_content = toml.loads(THIRD_PARTY_TOML)
  161. line = "├── cxxbridge-macro v1.0.56 (proc-macro)"
  162. r = gen._parse_cargo_tree_dependency_line(args, toml_content, True,
  163. line)
  164. d = gen.CargoTreeDependency(
  165. cargo.CrateKey("cxxbridge-macro", "1.0.56"),
  166. full_version="1.0.56",
  167. # Deps from third_party.toml are visible to first-party code by
  168. # default.
  169. is_for_first_party_code=True)
  170. self.assertEqual(r, d)
  171. line = "├── link-cplusplus v1.0.5 default"
  172. r = gen._parse_cargo_tree_dependency_line(args, toml_content, True,
  173. line)
  174. d = gen.CargoTreeDependency(
  175. cargo.CrateKey("link-cplusplus", "1.0.5"),
  176. full_version="1.0.5",
  177. features=["default"],
  178. is_for_first_party_code=True,
  179. # link-cplusplus has build script outputs listed in our
  180. # third_party.toml.
  181. build_script_outputs={"src/link.rs", "src/cplusplus.rs"})
  182. self.assertEqual(r, d)
  183. line = "└── syn v1.0.81 clone-impls,default"
  184. r = gen._parse_cargo_tree_dependency_line(args, toml_content, True,
  185. line)
  186. d = gen.CargoTreeDependency(
  187. cargo.CrateKey("syn", "1.0.81"),
  188. full_version="1.0.81",
  189. features=["clone-impls", "default"],
  190. # Deps from third_party.toml are visible to first-party code unless
  191. # they opt out explicitly, which our syn dependency has done.
  192. is_for_first_party_code=False)
  193. self.assertEqual(r, d)
  194. def test_third_party_toml_dev_deps(self):
  195. args = self.make_args()
  196. THIRD_PARTY_TOML = """
  197. [dependencies]
  198. # Nothing. We're testing dev-dependencies here.
  199. [dev-dependencies]
  200. cxxbridge-macro = "1"
  201. # This dependency has an extension, and is default-visible to first-party code.
  202. [dev-dependencies.link-cplusplus]
  203. build-script-outputs = [ "src/link.rs", "src/cplusplus.rs" ]
  204. # This dependency is not visible to first-party code thanks to the extension.
  205. [dev-dependencies.syn]
  206. allow-first-party-usage = false
  207. """
  208. # Here we are simulating parsing our special third_party.toml file, so
  209. # we need to present the contents of that file to the
  210. # _parse_cargo_tree_dependency_line() function, in order for it to look
  211. # for extensions.
  212. toml_content = toml.loads(THIRD_PARTY_TOML)
  213. line = "├── cxxbridge-macro v1.0.56 (proc-macro)"
  214. r = gen._parse_cargo_tree_dependency_line(args, toml_content, True,
  215. line)
  216. d = gen.CargoTreeDependency(
  217. cargo.CrateKey("cxxbridge-macro", "1.0.56"),
  218. full_version="1.0.56",
  219. # Deps from third_party.toml are visible to first-party code by
  220. # default.
  221. is_for_first_party_code=True)
  222. self.assertEqual(r, d)
  223. line = "├── link-cplusplus v1.0.5 default"
  224. r = gen._parse_cargo_tree_dependency_line(args, toml_content, True,
  225. line)
  226. d = gen.CargoTreeDependency(
  227. cargo.CrateKey("link-cplusplus", "1.0.5"),
  228. full_version="1.0.5",
  229. features=["default"],
  230. is_for_first_party_code=True,
  231. # link-cplusplus has build script outputs listed in our
  232. # third_party.toml.
  233. build_script_outputs={"src/link.rs", "src/cplusplus.rs"})
  234. self.assertEqual(r, d)
  235. line = "└── syn v1.0.81 clone-impls,default"
  236. r = gen._parse_cargo_tree_dependency_line(args, toml_content, True,
  237. line)
  238. d = gen.CargoTreeDependency(
  239. cargo.CrateKey("syn", "1.0.81"),
  240. full_version="1.0.81",
  241. features=["clone-impls", "default"],
  242. # Deps from third_party.toml are visible to first-party code unless
  243. # they opt out explicitly, which our syn dependency has done.
  244. is_for_first_party_code=False)
  245. self.assertEqual(r, d)
  246. def test_get_archs_third_party_toml(self):
  247. crate_key = None # For third_party.toml there's none.
  248. usage_data = None # For third_party.toml there's none.
  249. # Test what happens when there is a `target` on a dependency.
  250. toml_content = toml.loads("""
  251. [dependencies]
  252. all-platform-crate = "1"
  253. [target."cfg(windows)".dependencies]
  254. windows-only-crate = "2"
  255. """)
  256. # Test case without a target specified on the command line. Since
  257. # there's differences per-architecture in the TOML, we will have to test
  258. # all architectures.
  259. arch_specific = gen._get_archs_of_interest(toml_content, usage_data,
  260. None)
  261. # Not true as there's a arch-specific dependency.
  262. self.assertFalse(arch_specific.is_single_arch())
  263. self.assertSetEqual(self.all_archs(), arch_specific.archs_to_test())
  264. # Test case with a target specified on the command line that matches
  265. # the target. We only need to test the target the user specified.
  266. arch_specific = gen._get_archs_of_interest(toml_content, usage_data,
  267. "x86_64-pc-windows-msvc")
  268. # We only ever care about one arch, so everything is single-arch.
  269. self.assertTrue(arch_specific.is_single_arch())
  270. self.assertSetEqual({"x86_64-pc-windows-msvc"},
  271. arch_specific.archs_to_test())
  272. # Test case with a target specified on the command line that does not
  273. # match the target. We only need to test the target the user specified,
  274. # and we're not smart enough to prune it yet..
  275. arch_specific = gen._get_archs_of_interest(toml_content, usage_data,
  276. "x86_64-unknown-linux-gnu")
  277. # We only ever care about one arch, so everything is single-arch.
  278. self.assertTrue(arch_specific.is_single_arch())
  279. self.assertSetEqual({"x86_64-unknown-linux-gnu"},
  280. arch_specific.archs_to_test())
  281. # Test what happens when there is no `target` on a dependency.
  282. toml_content = toml.loads("""
  283. [dependencies]
  284. all-platform-crate = "1"
  285. """)
  286. # Test case without a target specified on the command line.
  287. arch_specific = gen._get_archs_of_interest(toml_content, usage_data,
  288. None)
  289. # Is true since we can apply a single architecture's result to other
  290. # ones.
  291. self.assertTrue(arch_specific.is_single_arch())
  292. # We only need to test one architecture and copy its results to the
  293. # rest.
  294. self.assertEqual(len(arch_specific.archs_to_test()), 1)
  295. # We don't care which architecture will be tested, but in the next test
  296. # case we do, so make sure the arbitrary architecture here isn't doesn't
  297. # unluckily collide with our next test.
  298. self.assertFalse({"x86_64-pc-windows-msvc"
  299. }.issubset(arch_specific.archs_to_test()))
  300. # Test case with a target specified on the command line.
  301. arch_specific = gen._get_archs_of_interest(toml_content, usage_data,
  302. "x86_64-pc-windows-msvc")
  303. self.assertTrue(arch_specific.is_single_arch())
  304. # We should test the architecture specified.
  305. self.assertSetEqual({"x86_64-pc-windows-msvc"},
  306. arch_specific.archs_to_test())
  307. # Ensure we are OK with a [target] section with no dependencies
  308. toml_content = toml.loads("""
  309. [dependencies]
  310. all-platform-crate = "1"
  311. [target."cfg(windows)"]
  312. """)
  313. arch_specific = gen._get_archs_of_interest(toml_content, usage_data,
  314. None)
  315. self.assertTrue(arch_specific.is_single_arch())
  316. def make_fake_parent(self, child_name: str,
  317. archset_where_parent_used: compiler.ArchSet,
  318. parent_is_arch_specific: bool,
  319. parent_has_arch_specific_deps: bool
  320. ) -> gen.ArchSpecific:
  321. if not parent_has_arch_specific_deps:
  322. toml_content = toml.loads("""
  323. [dependencies]
  324. {} = "1"
  325. """.format(child_name))
  326. else:
  327. toml_content = toml.loads("""
  328. [target."cfg(windows)".dependencies]
  329. {} = "1"
  330. """.format(child_name))
  331. parent_crate_data = gen.PerCrateData()
  332. parent_crate_data.arch_specific = parent_is_arch_specific
  333. parent_crate_data.used_on_archs = archset_where_parent_used
  334. return gen._get_archs_of_interest(toml_content, parent_crate_data, None)
  335. def make_fake_dep(self, dep_key: cargo.CrateKey,
  336. parent_requested_features_of_dep: list[str],
  337. dep_for_first_party_code: bool,
  338. dep_build_script_outputs: set[str]):
  339. return gen.CargoTreeDependency(
  340. dep_key,
  341. features=parent_requested_features_of_dep,
  342. is_for_first_party_code=dep_for_first_party_code,
  343. build_script_outputs=dep_build_script_outputs)
  344. def add_fake_dependency_edges(self, build_data: gen.BuildData,
  345. new_keys: set[cargo.CrateKey], **kwargs):
  346. parent_name: str = kwargs["parent_name"]
  347. parent_arch_specific = self.make_fake_parent(
  348. kwargs["dep_name"], kwargs["archset_where_parent_used"],
  349. kwargs["parent_is_arch_specific"],
  350. kwargs["parent_has_arch_specific_deps"])
  351. dep_key = cargo.CrateKey(kwargs["dep_name"], "1.2.3")
  352. dep = self.make_fake_dep(dep_key,
  353. kwargs["parent_requested_features_of_dep"],
  354. kwargs["dep_for_first_party_code"],
  355. kwargs["dep_build_script_outputs"])
  356. parent_crate_key = cargo.CrateKey(parent_name,
  357. "1.2.3") if parent_name else None
  358. gen._add_edges_for_dep_on_target_arch(build_data, parent_crate_key,
  359. parent_arch_specific,
  360. kwargs["parent_usage"],
  361. kwargs["parent_output"], dep,
  362. kwargs["computing_arch"],
  363. new_keys)
  364. return dep_key
  365. def test_add_edges(self):
  366. build_data = gen.BuildData()
  367. new_keys = set()
  368. for_first_party_code = True
  369. build_script_outputs = {"src/child-outs.rs"}
  370. dep_key = self.add_fake_dependency_edges(
  371. build_data,
  372. new_keys,
  373. parent_name=None, # No parent crate from third_party.toml.
  374. # Parent used everywhere.
  375. archset_where_parent_used=compiler.ArchSet.ALL(),
  376. parent_is_arch_specific=False,
  377. parent_has_arch_specific_deps=False,
  378. # computing_arch is chosen arbitrarily since the result will apply
  379. # to all as parent_has_arch_specific_deps is False
  380. computing_arch="aarch64-apple-darwin",
  381. # Parent used for a binary.
  382. parent_usage=cargo.CrateUsage.FOR_NORMAL,
  383. # Adding a build-dependency.
  384. parent_output=cargo.CrateBuildOutput.BUILDRS,
  385. dep_name="child-name",
  386. parent_requested_features_of_dep=["feature1", "feature2"],
  387. dep_for_first_party_code=for_first_party_code,
  388. dep_build_script_outputs=build_script_outputs)
  389. # The new crate was added to new_keys
  390. self.assertSetEqual(new_keys, {dep_key})
  391. # The child crate should be in the BuildData
  392. self.assertSetEqual(build_data.for_buildrs.all_crates(), {dep_key})
  393. self.assertSetEqual(build_data.for_normal.all_crates(), {dep_key})
  394. self.assertSetEqual(build_data.for_tests.all_crates(), {dep_key})
  395. # In buildrs usage, (since the parent was building BUILDRS output),
  396. # the dependency will be used and have data populated.
  397. with build_data.for_buildrs.per_crate(dep_key) as crate:
  398. # The requested features will be used on _all_ architectures, even
  399. # since the ArchSpecific tests one but applies to all.
  400. features = crate.features.all_archsets()
  401. self.assertListSortedEqual(features, [
  402. ("feature1", compiler.ArchSet.ALL()),
  403. ("feature2", compiler.ArchSet.ALL()),
  404. ])
  405. # Similarly, the crate is used everywhere. This should always be a
  406. # superset of the architectures where features are enabled.
  407. self.assertEqual(crate.used_on_archs, compiler.ArchSet.ALL())
  408. # Since the ArchSpecific said the parent is not arch-specific, and
  409. # there's no arch-specific deps, the dependency will not be arch-
  410. # specific.
  411. self.assertFalse(crate.arch_specific)
  412. self.assertEqual(crate.for_first_party, for_first_party_code)
  413. self.assertEqual(crate.build_script_outputs, build_script_outputs)
  414. # There's no edges from the new dependency since we haven't added
  415. # those.
  416. for o in cargo.CrateBuildOutput:
  417. self.assertSetEqual(crate.deps[o].all_deps(),
  418. set(),
  419. msg="For output type {}".format(o))
  420. # Other usages are not used, and have no features enabled, since the
  421. # parent crate is only using this dependency for tests so far, as those
  422. # are the only edges we added.
  423. with build_data.for_normal.per_crate(dep_key) as crate:
  424. self.assertListSortedEqual(crate.features.all_archsets(), [])
  425. self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY())
  426. self.assertFalse(crate.arch_specific)
  427. self.assertEqual(crate.for_first_party, for_first_party_code)
  428. self.assertEqual(crate.build_script_outputs, build_script_outputs)
  429. with build_data.for_tests.per_crate(dep_key) as crate:
  430. self.assertListSortedEqual(crate.features.all_archsets(), [])
  431. self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY())
  432. self.assertFalse(crate.arch_specific)
  433. self.assertEqual(crate.for_first_party, for_first_party_code)
  434. self.assertEqual(crate.build_script_outputs, build_script_outputs)
  435. # The parent would have edges to the child but this was a dep from the
  436. # third_party.toml, so there's no parent.
  437. # Now add a transitive dependency, which will have a parent: the child
  438. # that we added above. We will only add edges to the grand child on
  439. # Windows.
  440. parent_key = dep_key
  441. for_first_party_code = False
  442. build_script_outputs = {"src/grand-child-outs.rs"}
  443. dep_key = self.add_fake_dependency_edges(
  444. build_data,
  445. new_keys,
  446. parent_name="child-name", # The parent crate of the grand-child.
  447. # Parent used everywhere.
  448. archset_where_parent_used=compiler.ArchSet.ALL(),
  449. parent_is_arch_specific=False,
  450. parent_has_arch_specific_deps=True,
  451. # Act as if cargo-tree sees the grand-child on Windows, so we're
  452. # adding an edge there. Since parent_has_arch_specific_deps is True,
  453. # the edge won't be added to other architectures automatically.
  454. computing_arch="x86_64-pc-windows-msvc",
  455. # Parent was used for buildrs.
  456. parent_usage=cargo.CrateUsage.FOR_BUILDRS,
  457. # Adding a build-dependency.
  458. parent_output=cargo.CrateBuildOutput.BUILDRS,
  459. dep_name="grand-child-name",
  460. parent_requested_features_of_dep=["featureA", "featureB"],
  461. dep_for_first_party_code=False,
  462. dep_build_script_outputs=build_script_outputs)
  463. current_archset = compiler.ArchSet(initial={"x86_64-pc-windows-msvc"})
  464. # The new crate was added to new_keys
  465. self.assertSetEqual(new_keys, {parent_key, dep_key})
  466. # The grand-child crate should be in the BuildData
  467. self.assertSetEqual(build_data.for_buildrs.all_crates(),
  468. {parent_key, dep_key})
  469. self.assertSetEqual(build_data.for_normal.all_crates(),
  470. {parent_key, dep_key})
  471. self.assertSetEqual(build_data.for_tests.all_crates(),
  472. {parent_key, dep_key})
  473. # In buildrs usage, (since its parent was building BUILDRS output),
  474. # the dependency will be used and have data populated.
  475. with build_data.for_buildrs.per_crate(dep_key) as crate:
  476. # The requested features will be used on the architecture we're
  477. # processing, since they can't be generalized to all architectures.
  478. features = crate.features.all_archsets()
  479. self.assertListSortedEqual(features,
  480. [("featureA", current_archset),
  481. ("featureB", current_archset)])
  482. # Similarly, the crate is used on the architecture we're processing.
  483. self.assertEqual(crate.used_on_archs, current_archset)
  484. # Since the dep is in a target rule (parent_has_arch_specific_deps),
  485. # the crate is considered arch-specific.
  486. self.assertTrue(crate.arch_specific)
  487. self.assertEqual(crate.for_first_party, for_first_party_code)
  488. self.assertEqual(crate.build_script_outputs, build_script_outputs)
  489. # There's no edges from the new dependency since we haven't added
  490. # those.
  491. for o in cargo.CrateBuildOutput:
  492. self.assertSetEqual(crate.deps[o].all_deps(),
  493. set(),
  494. msg="For output type {}".format(o))
  495. # Other usages are not used, and have no features enabled, since the
  496. # parent crate is only using this dependency for tests so far, as those
  497. # are the only edges we added.
  498. with build_data.for_normal.per_crate(dep_key) as crate:
  499. self.assertListSortedEqual(crate.features.all_archsets(), [])
  500. self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY())
  501. self.assertFalse(crate.arch_specific)
  502. self.assertEqual(crate.for_first_party, for_first_party_code)
  503. self.assertEqual(crate.build_script_outputs, build_script_outputs)
  504. with build_data.for_tests.per_crate(dep_key) as crate:
  505. self.assertListSortedEqual(crate.features.all_archsets(), [])
  506. self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY())
  507. self.assertFalse(crate.arch_specific)
  508. self.assertEqual(crate.for_first_party, for_first_party_code)
  509. self.assertEqual(crate.build_script_outputs, build_script_outputs)
  510. # The child crate now depends on the grand-child crate on the computed.
  511. # architecture, only for it's buildrs script.
  512. with build_data.for_buildrs.per_crate(parent_key) as crate:
  513. self.assertSetEqual(
  514. crate.deps[cargo.CrateBuildOutput.BUILDRS].all_deps(),
  515. {dep_key})
  516. self.assertSetEqual(
  517. crate.deps[cargo.CrateBuildOutput.NORMAL].all_deps(), set())
  518. self.assertSetEqual(
  519. crate.deps[cargo.CrateBuildOutput.TESTS].all_deps(), set())
  520. # We haven't considered the parent crate being built into a crate's
  521. # lib/bin as a normal dependency, or tests as a dev-dependency so
  522. # there's no edges.
  523. with build_data.for_normal.per_crate(parent_key) as crate:
  524. for o in cargo.CrateBuildOutput:
  525. self.assertSetEqual(crate.deps[o].all_deps(),
  526. set(),
  527. msg="For output type {}".format(o))
  528. with build_data.for_tests.per_crate(parent_key) as crate:
  529. for o in cargo.CrateBuildOutput:
  530. self.assertSetEqual(crate.deps[o].all_deps(),
  531. set(),
  532. msg="For output type {}".format(o))
  533. new_keys = set()
  534. # Now we again compute edges between the child and grand-child, for
  535. # another architecture, and for the child's normal build output. It
  536. # can use different features of the grand-child there.
  537. dep_key = self.add_fake_dependency_edges(
  538. build_data,
  539. new_keys,
  540. parent_name="child-name", # The parent crate of the grand-child.
  541. # Parent used everywhere.
  542. archset_where_parent_used=compiler.ArchSet.ALL(),
  543. parent_is_arch_specific=False,
  544. parent_has_arch_specific_deps=True,
  545. # Act as if cargo-tree sees the grand-child on Fuchsia, so we're
  546. # adding an edge there. Since parent_has_arch_specific_deps is True,
  547. # the edge won't be added to other architectures automatically.
  548. computing_arch="x86_64-fuchsia",
  549. # Parent was used for a lib/bin.
  550. parent_usage=cargo.CrateUsage.FOR_NORMAL,
  551. # Adding a build-dependency.
  552. parent_output=cargo.CrateBuildOutput.BUILDRS,
  553. dep_name="grand-child-name",
  554. parent_requested_features_of_dep=["featureB", "featureC"],
  555. dep_for_first_party_code=False,
  556. dep_build_script_outputs=build_script_outputs)
  557. current_archset = compiler.ArchSet(initial={"x86_64-fuchsia"})
  558. before_archset = compiler.ArchSet(initial={"x86_64-pc-windows-msvc"})
  559. union_archset = compiler.ArchSet(
  560. initial={"x86_64-pc-windows-msvc", "x86_64-fuchsia"})
  561. # The grand-child is being used on a new platform, and is arch-specific,
  562. # so it will need to compute its deps again.
  563. self.assertSetEqual(new_keys, {dep_key})
  564. # In buildrs usage, (since its parent was building BUILDRS output),
  565. # the dependency will be used and have data populated.
  566. with build_data.for_buildrs.per_crate(dep_key) as crate:
  567. # The requested features will be used on the architecture we're
  568. # processing, since they can't be generalized to all architectures.
  569. features = crate.features.all_archsets()
  570. self.assertListSortedEqual(features, [
  571. ("featureA", before_archset),
  572. ("featureB", union_archset),
  573. ("featureC", current_archset),
  574. ])
  575. # The crate is used on the union architectures.
  576. self.assertEqual(crate.used_on_archs, union_archset)
  577. # There's no edges from the new dependency since we haven't added
  578. # those.
  579. for o in cargo.CrateBuildOutput:
  580. self.assertSetEqual(crate.deps[o].all_deps(),
  581. set(),
  582. msg="For output type {}".format(o))
  583. # Other usages are not used, and have no features enabled, since the
  584. # parent crate is only using this dependency for tests so far, as those
  585. # are the only edges we added.
  586. with build_data.for_normal.per_crate(dep_key) as crate:
  587. self.assertListSortedEqual(crate.features.all_archsets(), [])
  588. self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY())
  589. self.assertFalse(crate.arch_specific)
  590. self.assertEqual(crate.for_first_party, for_first_party_code)
  591. self.assertEqual(crate.build_script_outputs, build_script_outputs)
  592. with build_data.for_tests.per_crate(dep_key) as crate:
  593. self.assertListSortedEqual(crate.features.all_archsets(), [])
  594. self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY())
  595. self.assertFalse(crate.arch_specific)
  596. self.assertEqual(crate.for_first_party, for_first_party_code)
  597. self.assertEqual(crate.build_script_outputs, build_script_outputs)
  598. # The child crate still depends on the grand-child crate for its buildrs
  599. # script when building built as a build-dependency.
  600. with build_data.for_buildrs.per_crate(parent_key) as crate:
  601. self.assertSetEqual(
  602. crate.deps[cargo.CrateBuildOutput.BUILDRS].all_deps(),
  603. {dep_key})
  604. self.assertSetEqual(
  605. crate.deps[cargo.CrateBuildOutput.NORMAL].all_deps(), set())
  606. self.assertSetEqual(
  607. crate.deps[cargo.CrateBuildOutput.TESTS].all_deps(), set())
  608. # The child crate now _also_ depends on the grand-child crate for its
  609. # buildrs script when building built as a normal dependency.
  610. with build_data.for_normal.per_crate(parent_key) as crate:
  611. self.assertSetEqual(
  612. crate.deps[cargo.CrateBuildOutput.BUILDRS].all_deps(),
  613. {dep_key})
  614. self.assertSetEqual(
  615. crate.deps[cargo.CrateBuildOutput.NORMAL].all_deps(), set())
  616. self.assertSetEqual(
  617. crate.deps[cargo.CrateBuildOutput.TESTS].all_deps(), set())
  618. # We haven't considered the parent crate being built into a crate's
  619. # tests as a dev-dependency so there's no edges.
  620. with build_data.for_tests.per_crate(parent_key) as crate:
  621. for o in cargo.CrateBuildOutput:
  622. self.assertSetEqual(crate.deps[o].all_deps(),
  623. set(),
  624. msg="For output type {}".format(o))
  625. def test_copyright_year(self):
  626. modern = b"# Copyright 2001 The Chromium Authors. All rights reserved."
  627. with tempfile.NamedTemporaryFile() as f:
  628. f.write(modern)
  629. f.flush()
  630. self.assertEqual(gen._get_copyright_year(f.name), "2001")
  631. ancient = b"# Copyright (c) 2001 The Chromium Authors. All rights reserved."
  632. with tempfile.NamedTemporaryFile() as f:
  633. f.write(ancient)
  634. f.flush()
  635. self.assertEqual(gen._get_copyright_year(f.name), "2001")
  636. self.assertEqual(gen._get_copyright_year("/file/does/not/exist"),
  637. str(datetime.now().year))