build.rs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. //! This build script gets run during every build. Its purpose is to put
  2. //! together the files used for the `--help` and `--version`, which need to
  3. //! come in both coloured and non-coloured variants. The main usage text is
  4. //! contained in `src/usage.txt`; to make it easier to edit, backslashes (\)
  5. //! are used instead of the beginning of ANSI escape codes.
  6. //!
  7. //! The version string is quite complex: we want to show the version,
  8. //! current Git hash, and compilation date when building *debug*
  9. //! versions, but just the version for *release* versions.
  10. //!
  11. //! This script generates the string from the environment variables
  12. //! that Cargo adds (http://doc.crates.io/environment-variables.html)
  13. //! and runs `git` to get the SHA1 hash. It then writes the strings
  14. //! into files, which we can include during compilation.
  15. use std::env;
  16. use std::fs::File;
  17. use std::io::{self, Write};
  18. use std::path::PathBuf;
  19. use datetime::{LocalDateTime, ISO};
  20. /// The build script entry point.
  21. fn main() -> io::Result<()> {
  22. #![allow(clippy::write_with_newline)]
  23. let usage = include_str!("src/usage.txt");
  24. let tagline = "dog \\1;32m●\\0m command-line DNS client";
  25. let url = "https://dns.lookup.dog/";
  26. let ver =
  27. if is_debug_build() {
  28. format!("{}\nv{} \\1;31m(pre-release debug build!)\\0m\n\\1;4;34m{}\\0m", tagline, version_string(), url)
  29. }
  30. else if is_development_version() {
  31. format!("{}\nv{} [{}] built on {} \\1;31m(pre-release!)\\0m\n\\1;4;34m{}\\0m", tagline, version_string(), git_hash(), build_date(), url)
  32. }
  33. else {
  34. format!("{}\nv{}\n\\1;4;34m{}\\0m", tagline, version_string(), url)
  35. };
  36. // We need to create these files in the Cargo output directory.
  37. let out = PathBuf::from(env::var("OUT_DIR").unwrap());
  38. // Pretty version text
  39. let mut f = File::create(&out.join("version.pretty.txt"))?;
  40. writeln!(f, "{}", convert_codes(&ver))?;
  41. // Bland version text
  42. let mut f = File::create(&out.join("version.bland.txt"))?;
  43. writeln!(f, "{}", strip_codes(&ver))?;
  44. // Pretty usage text
  45. let mut f = File::create(&out.join("usage.pretty.txt"))?;
  46. writeln!(f, "{}", convert_codes(&tagline))?;
  47. writeln!(f)?;
  48. write!(f, "{}", convert_codes(&usage))?;
  49. // Bland usage text
  50. let mut f = File::create(&out.join("usage.bland.txt"))?;
  51. writeln!(f, "{}", strip_codes(&tagline))?;
  52. writeln!(f)?;
  53. write!(f, "{}", strip_codes(&usage))?;
  54. Ok(())
  55. }
  56. /// Converts the escape codes in ‘usage.txt’ to ANSI escape codes.
  57. fn convert_codes(input: &str) -> String {
  58. input.replace("\\", "\x1B[")
  59. }
  60. /// Removes escape codes from ‘usage.txt’.
  61. fn strip_codes(input: &str) -> String {
  62. input.replace("\\0m", "")
  63. .replace("\\1m", "")
  64. .replace("\\4m", "")
  65. .replace("\\32m", "")
  66. .replace("\\33m", "")
  67. .replace("\\1;31m", "")
  68. .replace("\\1;32m", "")
  69. .replace("\\1;33m", "")
  70. .replace("\\1;4;34", "")
  71. }
  72. /// Retrieve the project’s current Git hash, as a string.
  73. fn git_hash() -> String {
  74. use std::process::Command;
  75. String::from_utf8_lossy(
  76. &Command::new("git")
  77. .args(&["rev-parse", "--short", "HEAD"])
  78. .output().unwrap()
  79. .stdout).trim().to_string()
  80. }
  81. /// Whether we should show pre-release info in the version string.
  82. ///
  83. /// Both weekly releases and actual releases are --release releases,
  84. /// but actual releases will have a proper version number.
  85. fn is_development_version() -> bool {
  86. cargo_version().ends_with("-pre") || env::var("PROFILE").unwrap() == "debug"
  87. }
  88. /// Whether we are building in debug mode.
  89. fn is_debug_build() -> bool {
  90. env::var("PROFILE").unwrap() == "debug"
  91. }
  92. /// Retrieves the [package] version in Cargo.toml as a string.
  93. fn cargo_version() -> String {
  94. env::var("CARGO_PKG_VERSION").unwrap()
  95. }
  96. /// Returns the version and build parameters string.
  97. fn version_string() -> String {
  98. let mut ver = cargo_version();
  99. let feats = nonstandard_features_string();
  100. if ! feats.is_empty() {
  101. ver.push_str(&format!(" [{}]", &feats));
  102. }
  103. ver
  104. }
  105. /// Finds whether a feature is enabled by examining the Cargo variable.
  106. fn feature_enabled(name: &str) -> bool {
  107. env::var(&format!("CARGO_FEATURE_{}", name))
  108. .map(|e| ! e.is_empty())
  109. .unwrap_or(false)
  110. }
  111. /// A comma-separated list of non-standard feature choices.
  112. fn nonstandard_features_string() -> String {
  113. let mut s = Vec::new();
  114. if ! feature_enabled("WITH_IDNA") {
  115. s.push("-idna");
  116. }
  117. if ! feature_enabled("WITH_TLS") {
  118. s.push("-tls");
  119. }
  120. if ! feature_enabled("WITH_HTTPS") {
  121. s.push("-https");
  122. }
  123. s.join(", ")
  124. }
  125. /// Formats the current date as an ISO 8601 string.
  126. fn build_date() -> String {
  127. let now = LocalDateTime::now();
  128. format!("{}", now.date().iso())
  129. }