common.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. """Helper functions for general tasks.
  6. Helps for things such as generating versions, paths, and crate names."""
  7. from __future__ import annotations
  8. import os
  9. import sys
  10. from typing import Any, List
  11. from lib import consts
  12. def _find_chromium_root(cwd: str) -> list[str]:
  13. """Finds and returns the path from the root of the Chromium tree."""
  14. # This file is at tools/crates/lib/common.py, so 4 * '..' will take us up
  15. # to the root chromium dir.
  16. path_components_to_root = [os.path.abspath(__file__)] + [os.pardir] * 4
  17. abs_root_path = os.path.join(*path_components_to_root)
  18. path_from_root = os.path.relpath(cwd, abs_root_path)
  19. def split_path(p: str) -> list[str]:
  20. if not p: return []
  21. head, tail = os.path.split(p)
  22. tail_l = [] if tail == "." else [tail]
  23. return split_path(head) + tail_l
  24. return split_path(path_from_root)
  25. # The path from the root of the chromium tree to the current working directory
  26. # as a list of path components. If chromium's src.git is rooted at `/f/b/src``,
  27. # and the this tool is run from `/f/b/src/in/there`, then the value here would
  28. # be `["in", "there"]`. If the tool is run from the root `/f/b/src` then the
  29. # value here is `[]`.
  30. _PATH_FROM_CHROMIUM_ROOT = _find_chromium_root(os.getcwd())
  31. def crate_name_normalized(crate_name: str) -> str:
  32. """Normalizes a crate name for GN and file paths."""
  33. return crate_name.replace("-", "_").replace(".", "_")
  34. def version_is_complete(version_str: str) -> bool:
  35. """Returns whether the `version_str` is fully resolved or not.
  36. A full version includes MAJOR.MINOR.PATCH components."""
  37. parts = _version_to_parts(version_str)
  38. # This supports semmvers with pre-release and build flags.
  39. # https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions
  40. return len(parts) >= 3
  41. def _version_to_parts(version_str: str) -> list[str]:
  42. """Converts a version string into its MAJOR.MINOR.PATCH parts."""
  43. # TODO(danakj): This does not support pre-release or build versions such as
  44. # 1.0.0-alpha.1 or 1.0.0+1234 at this time. We only need support it if we
  45. # want to include such a crate in our tree.
  46. # https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions
  47. # TODO(danakj): It would be real nice to introduce a SemmVer type instead of
  48. # using strings, which sometimes hold partial versions, and sometimes use
  49. # dots as separators or underscores.
  50. parts = version_str.split(".")
  51. assert len(parts) >= 1 and len(parts) <= 3, \
  52. "The version \"{}\" is an invalid semmver.".format(version_str)
  53. return parts
  54. def version_epoch_dots(version_str: str) -> str:
  55. """Returns a version epoch from a given version string.
  56. Returns a string with `.` as the component separator."""
  57. parts = _version_to_parts(version_str)
  58. if parts[0] != "0":
  59. return ".".join(parts[:1])
  60. elif parts[1] != "0":
  61. return ".".join(parts[:2])
  62. else:
  63. return ".".join(parts[:3])
  64. def version_epoch_normalized(version_str: str) -> str:
  65. """Returns a version epoch from a given version string.
  66. Returns a string with `_` as the component separator."""
  67. parts = _version_to_parts(version_str)
  68. if parts[0] != "0":
  69. return "_".join(parts[:1])
  70. elif parts[1] != "0":
  71. return "_".join(parts[:2])
  72. else:
  73. return "_".join(parts[:3])
  74. def gn_third_party_path(rel_path: list[str] = []) -> str:
  75. """Returns the full GN path to the root of all third_party crates."""
  76. path = _PATH_FROM_CHROMIUM_ROOT + consts.THIRD_PARTY
  77. return "//" + "/".join(path + rel_path)
  78. def gn_crate_path(crate_name: str, version: str,
  79. rel_path: list[str] = []) -> str:
  80. """Returns the full GN path to a crate that is in third_party. This is the
  81. path to the crate's BUILD.gn file."""
  82. name = crate_name_normalized(crate_name)
  83. epoch = "v" + version_epoch_normalized(version)
  84. path = _PATH_FROM_CHROMIUM_ROOT + consts.THIRD_PARTY + [name, epoch]
  85. return "//" + "/".join(path + rel_path)
  86. def os_third_party_dir(rel_path: list[str] = []) -> str:
  87. """The relative OS disk path to the top of the third party Rust directory
  88. where all third party crates are found, along with third_party.toml."""
  89. return os.path.join(*consts.THIRD_PARTY, *rel_path)
  90. def os_crate_name_dir(crate_name: str, rel_path: list[str] = []) -> str:
  91. """The relative OS disk path to a third party crate's top-most directory
  92. where all versions of that crate are found."""
  93. return os_third_party_dir(rel_path=[crate_name_normalized(crate_name)] +
  94. rel_path)
  95. def os_crate_version_dir(crate_name: str,
  96. version: str,
  97. rel_path: list[str] = []) -> str:
  98. """The relative OS disk path to a third party crate's versioned directory
  99. where BUILD.gn and README.chromium are found."""
  100. epoch = "v" + version_epoch_normalized(version)
  101. return os_crate_name_dir(crate_name, rel_path=[epoch] + rel_path)
  102. def os_crate_cargo_dir(crate_name: str, version: str,
  103. rel_path: list[str] = []) -> str:
  104. """The relative OS disk path to a third party crate's Cargo root.
  105. This directory is where Cargo.toml and the Rust source files are found. This
  106. is where the crate is extracted when it is downloaded."""
  107. return os_crate_version_dir(crate_name,
  108. version,
  109. rel_path=consts.CRATE_INNER_DIR + rel_path)
  110. def crate_download_url(crate: str, version: str) -> str:
  111. """Returns the crates.io URL to download the crate."""
  112. return consts.CRATES_IO_DOWNLOAD.format(crate=crate, version=version)
  113. def crate_view_url(crate: str) -> str:
  114. """Returns the crates.io URL to see info about the crate."""
  115. return consts.CRATES_IO_VIEW.format(crate=crate)
  116. def load_toml(path: str) -> dict[str, Any]:
  117. """Loads a file at the path and parses it as a TOML file.
  118. This is a helper for times when you don't need the raw text content of the
  119. TOML file.
  120. Returns:
  121. A dictionary of the contents of the TOML file."""
  122. with open(path, "r") as cargo_file:
  123. toml_string = cargo_file.read()
  124. import toml
  125. return dict(toml.loads(toml_string))
  126. def print_same_line(s: str, fill_num_chars: int, done: bool = False) -> int:
  127. """A helper to repeatedly print to the same line.
  128. Args:
  129. s: The text to be printed.
  130. fill_num_chars: This should be `0` on the first call to
  131. print_same_line() for a series of prints to the same output line. Then
  132. it should be the return value of the previous call to
  133. print_same_line() repeatedly until `done` is True, at which time the
  134. cursor will be moved to the next output line.
  135. done: On the final call to print_same_line() for a given line of output,
  136. pass `True` to have the cursor move to the next line.
  137. Returns:
  138. The number of characters that were written, which should be passed as
  139. `fill_num_chars` on the next call. At the end of printing over the same
  140. line, finish by calling with `done` set to true, which will move to the
  141. next line."""
  142. s += " " * (fill_num_chars - len(s))
  143. if not done:
  144. print("\r" + s, end="")
  145. else:
  146. print("\r" + s)
  147. return len(s)