checks.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. use std::{fs, path::Path};
  2. use anyhow::{anyhow, Result};
  3. use semver::{Version, VersionReq};
  4. use crate::{
  5. config::{Config, Manifest, PackageManager, WithPath},
  6. VERSION,
  7. };
  8. /// Check whether `overflow-checks` codegen option is enabled.
  9. ///
  10. /// https://doc.rust-lang.org/rustc/codegen-options/index.html#overflow-checks
  11. pub fn check_overflow(cargo_toml_path: impl AsRef<Path>) -> Result<bool> {
  12. Manifest::from_path(cargo_toml_path)?
  13. .profile
  14. .release
  15. .as_ref()
  16. .and_then(|profile| profile.overflow_checks)
  17. .ok_or(anyhow!(
  18. "`overflow-checks` is not enabled. To enable, add:\n\n\
  19. [profile.release]\n\
  20. overflow-checks = true\n\n\
  21. in workspace root Cargo.toml",
  22. ))
  23. }
  24. /// Check whether there is a mismatch between the current CLI version and:
  25. ///
  26. /// - `anchor-lang` crate version
  27. /// - `@coral-xyz/anchor` package version
  28. ///
  29. /// This function logs warnings in the case of a mismatch.
  30. pub fn check_anchor_version(cfg: &WithPath<Config>) -> Result<()> {
  31. let cli_version = Version::parse(VERSION)?;
  32. // Check lang crate
  33. let mismatched_lang_version = cfg
  34. .get_rust_program_list()?
  35. .into_iter()
  36. .map(|path| path.join("Cargo.toml"))
  37. .map(cargo_toml::Manifest::from_path)
  38. .filter_map(|man| man.ok())
  39. .filter_map(|man| man.dependencies.get("anchor-lang").map(|d| d.to_owned()))
  40. .filter_map(|dep| Version::parse(dep.req()).ok())
  41. .find(|ver| ver != &cli_version); // Only log the warning once
  42. if let Some(ver) = mismatched_lang_version {
  43. eprintln!(
  44. "WARNING: `anchor-lang` version({ver}) and the current CLI version({cli_version}) \
  45. don't match.\n\n\t\
  46. This can lead to unwanted behavior. To use the same CLI version, add:\n\n\t\
  47. [toolchain]\n\t\
  48. anchor_version = \"{ver}\"\n\n\t\
  49. to Anchor.toml\n"
  50. );
  51. }
  52. // Check TS package
  53. let package_json = {
  54. let package_json_path = cfg.path().parent().unwrap().join("package.json");
  55. let package_json_content = fs::read_to_string(package_json_path)?;
  56. serde_json::from_str::<serde_json::Value>(&package_json_content)?
  57. };
  58. let mismatched_ts_version = package_json
  59. .get("dependencies")
  60. .and_then(|deps| deps.get("@coral-xyz/anchor"))
  61. .and_then(|ver| ver.as_str())
  62. .and_then(|ver| VersionReq::parse(ver).ok())
  63. .filter(|ver| !ver.matches(&cli_version));
  64. if let Some(ver) = mismatched_ts_version {
  65. let update_cmd = match cfg.toolchain.package_manager.clone().unwrap_or_default() {
  66. PackageManager::NPM => "npm update",
  67. PackageManager::Yarn => "yarn upgrade",
  68. PackageManager::PNPM => "pnpm update",
  69. PackageManager::Bun => "bun update",
  70. };
  71. eprintln!(
  72. "WARNING: `@coral-xyz/anchor` version({ver}) and the current CLI version\
  73. ({cli_version}) don't match.\n\n\t\
  74. This can lead to unwanted behavior. To fix, upgrade the package by running:\n\n\t\
  75. {update_cmd} @coral-xyz/anchor@{cli_version}\n"
  76. );
  77. }
  78. Ok(())
  79. }
  80. /// Check for potential dependency improvements.
  81. ///
  82. /// The main problem people will run into with Solana v2 is that the `solana-program` version
  83. /// specified in users' `Cargo.toml` might be incompatible with `anchor-lang`'s dependency.
  84. /// To fix this and similar problems, users should use the crates exported from `anchor-lang` or
  85. /// `anchor-spl` when possible.
  86. pub fn check_deps(cfg: &WithPath<Config>) -> Result<()> {
  87. // Check `solana-program`
  88. cfg.get_rust_program_list()?
  89. .into_iter()
  90. .map(|path| path.join("Cargo.toml"))
  91. .map(cargo_toml::Manifest::from_path)
  92. .map(|man| man.map_err(|e| anyhow!("Failed to read manifest: {e}")))
  93. .collect::<Result<Vec<_>>>()?
  94. .into_iter()
  95. .filter(|man| man.dependencies.contains_key("solana-program"))
  96. .for_each(|man| {
  97. eprintln!(
  98. "WARNING: Adding `solana-program` as a separate dependency might cause conflicts.\n\
  99. To solve, remove the `solana-program` dependency and use the exported crate from \
  100. `anchor-lang`.\n\
  101. `use solana_program` becomes `use anchor_lang::solana_program`.\n\
  102. Program name: `{}`\n",
  103. man.package().name()
  104. )
  105. });
  106. Ok(())
  107. }
  108. /// Check whether the `idl-build` feature is being used correctly.
  109. ///
  110. /// **Note:** The check expects the current directory to be a program directory.
  111. pub fn check_idl_build_feature() -> Result<()> {
  112. let manifest_path = Path::new("Cargo.toml").canonicalize()?;
  113. let manifest = Manifest::from_path(&manifest_path)?;
  114. // Check whether the manifest has `idl-build` feature
  115. let has_idl_build_feature = manifest
  116. .features
  117. .iter()
  118. .any(|(feature, _)| feature == "idl-build");
  119. if !has_idl_build_feature {
  120. let anchor_spl_idl_build = manifest
  121. .dependencies
  122. .iter()
  123. .any(|dep| dep.0 == "anchor-spl")
  124. .then_some(r#", "anchor-spl/idl-build""#)
  125. .unwrap_or_default();
  126. return Err(anyhow!(
  127. r#"`idl-build` feature is missing. To solve, add
  128. [features]
  129. idl-build = ["anchor-lang/idl-build"{anchor_spl_idl_build}]
  130. in `{manifest_path:?}`."#
  131. ));
  132. }
  133. // Check if `idl-build` is enabled by default
  134. manifest
  135. .dependencies
  136. .iter()
  137. .filter(|(_, dep)| dep.req_features().contains(&"idl-build".into()))
  138. .for_each(|(name, _)| {
  139. eprintln!(
  140. "WARNING: `idl-build` feature of crate `{name}` is enabled by default. \
  141. This is not the intended usage.\n\n\t\
  142. To solve, do not enable the `idl-build` feature and include crates that have \
  143. `idl-build` feature in the `idl-build` feature list:\n\n\t\
  144. [features]\n\t\
  145. idl-build = [\"{name}/idl-build\", ...]\n"
  146. )
  147. });
  148. // Check `anchor-spl`'s `idl-build` feature
  149. manifest
  150. .dependencies
  151. .get("anchor-spl")
  152. .and_then(|_| manifest.features.get("idl-build"))
  153. .map(|feature_list| !feature_list.contains(&"anchor-spl/idl-build".into()))
  154. .unwrap_or_default()
  155. .then(|| {
  156. eprintln!(
  157. "WARNING: `idl-build` feature of `anchor-spl` is not enabled. \
  158. This is likely to result in cryptic compile errors.\n\n\t\
  159. To solve, add `anchor-spl/idl-build` to the `idl-build` feature list:\n\n\t\
  160. [features]\n\t\
  161. idl-build = [\"anchor-spl/idl-build\", ...]\n"
  162. )
  163. });
  164. Ok(())
  165. }