build.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. //! Build script for Rust modules in Tor.
  2. //!
  3. //! We need to use this because some of our Rust tests need to use some
  4. //! of our C modules, which need to link some external libraries.
  5. //!
  6. //! This script works by looking at a "config.rust" file generated by our
  7. //! configure script, and then building a set of options for cargo to pass to
  8. //! the compiler.
  9. use std::collections::HashMap;
  10. use std::env;
  11. use std::fs::File;
  12. use std::io;
  13. use std::io::prelude::*;
  14. use std::path::PathBuf;
  15. /// Wrapper around a key-value map.
  16. struct Config(HashMap<String, String>);
  17. /// Locate a config.rust file generated by autoconf, starting in the OUT_DIR
  18. /// location provided by cargo and recursing up the directory tree. Note that
  19. /// we need to look in the OUT_DIR, since autoconf will place generated files
  20. /// in the build directory.
  21. fn find_cfg() -> io::Result<String> {
  22. let mut path = PathBuf::from(env::var("OUT_DIR").unwrap());
  23. loop {
  24. path.push("config.rust");
  25. if path.exists() {
  26. return Ok(path.to_str().unwrap().to_owned());
  27. }
  28. path.pop(); // remove config.rust
  29. if !path.pop() {
  30. // can't remove last part of directory
  31. return Err(io::Error::new(io::ErrorKind::NotFound, "No config.rust"));
  32. }
  33. }
  34. }
  35. impl Config {
  36. /// Find the config.rust file and try to parse it.
  37. ///
  38. /// The file format is a series of lines of the form KEY=VAL, with
  39. /// any blank lines and lines starting with # ignored.
  40. fn load() -> io::Result<Config> {
  41. let path = find_cfg()?;
  42. let f = File::open(&path)?;
  43. let reader = io::BufReader::new(f);
  44. let mut map = HashMap::new();
  45. for line in reader.lines() {
  46. let s = line?;
  47. if s.trim().starts_with("#") || s.trim() == "" {
  48. continue;
  49. }
  50. let idx = match s.find("=") {
  51. None => {
  52. return Err(io::Error::new(io::ErrorKind::InvalidData, "missing ="));
  53. }
  54. Some(x) => x,
  55. };
  56. let (var, eq_val) = s.split_at(idx);
  57. let val = &eq_val[1..];
  58. map.insert(var.to_owned(), val.to_owned());
  59. }
  60. Ok(Config(map))
  61. }
  62. /// Return a reference to the value whose key is 'key'.
  63. ///
  64. /// Panics if 'key' is not found in the configuration.
  65. fn get(&self, key: &str) -> &str {
  66. self.0.get(key).unwrap()
  67. }
  68. /// Add a dependency on a static C library that is part of Tor, by name.
  69. fn component(&self, s: &str) {
  70. println!("cargo:rustc-link-lib=static={}", s);
  71. }
  72. /// Add a dependency on a native library that is not part of Tor, by name.
  73. fn dependency(&self, s: &str) {
  74. println!("cargo:rustc-link-lib={}", s);
  75. }
  76. /// Add a link path, relative to Tor's build directory.
  77. fn link_relpath(&self, s: &str) {
  78. let builddir = self.get("BUILDDIR");
  79. println!("cargo:rustc-link-search=native={}/{}", builddir, s);
  80. }
  81. /// Add an absolute link path.
  82. fn link_path(&self, s: &str) {
  83. println!("cargo:rustc-link-search=native={}", s);
  84. }
  85. /// Parse the CFLAGS in s, looking for -l and -L items, and adding
  86. /// rust configuration as appropriate.
  87. fn from_cflags(&self, s: &str) {
  88. let mut next_is_lib = false;
  89. let mut next_is_path = false;
  90. for ent in self.get(s).split_whitespace() {
  91. if next_is_lib {
  92. self.dependency(ent);
  93. next_is_lib = false;
  94. } else if next_is_path {
  95. self.link_path(ent);
  96. next_is_path = false;
  97. } else if ent == "-l" {
  98. next_is_lib = true;
  99. } else if ent == "-L" {
  100. next_is_path = true;
  101. } else if ent.starts_with("-L") {
  102. self.link_path(&ent[2..]);
  103. } else if ent.starts_with("-l") {
  104. self.dependency(&ent[2..]);
  105. }
  106. }
  107. }
  108. }
  109. pub fn main() {
  110. let cfg = Config::load().unwrap();
  111. let package = env::var("CARGO_PKG_NAME").unwrap();
  112. match package.as_ref() {
  113. "crypto" => {
  114. // Right now, I'm having a separate configuration for each Rust
  115. // package, since I'm hoping we can trim them down. Once we have a
  116. // second Rust package that needs to use this build script, let's
  117. // extract some of this stuff into a module.
  118. //
  119. // This is a ridiculous amount of code to be pulling in just
  120. // to test our crypto library: modularity would be our
  121. // friend here.
  122. cfg.from_cflags("TOR_LDFLAGS_zlib");
  123. cfg.from_cflags("TOR_LDFLAGS_openssl");
  124. cfg.from_cflags("TOR_LDFLAGS_libevent");
  125. cfg.link_relpath("src/lib");
  126. cfg.link_relpath("src/ext/keccak-tiny");
  127. cfg.link_relpath("src/ext/ed25519/ref10");
  128. cfg.link_relpath("src/ext/ed25519/donna");
  129. cfg.link_relpath("src/trunnel");
  130. // Note that we can't pull in "libtor-testing", or else we
  131. // will have dependencies on all the other rust packages that
  132. // tor uses. We must be careful with factoring and dependencies
  133. // moving forward!
  134. cfg.component("tor-crypt-ops-testing");
  135. cfg.component("tor-sandbox-testing");
  136. cfg.component("tor-encoding-testing");
  137. cfg.component("tor-fs-testing");
  138. cfg.component("tor-time-testing");
  139. cfg.component("tor-net-testing");
  140. cfg.component("tor-thread-testing");
  141. cfg.component("tor-memarea-testing");
  142. cfg.component("tor-log-testing");
  143. cfg.component("tor-lock-testing");
  144. cfg.component("tor-fdio-testing");
  145. cfg.component("tor-container-testing");
  146. cfg.component("tor-smartlist-core-testing");
  147. cfg.component("tor-string-testing");
  148. cfg.component("tor-malloc");
  149. cfg.component("tor-wallclock");
  150. cfg.component("tor-err-testing");
  151. cfg.component("tor-intmath-testing");
  152. cfg.component("tor-ctime-testing");
  153. cfg.component("curve25519_donna");
  154. cfg.component("keccak-tiny");
  155. cfg.component("ed25519_ref10");
  156. cfg.component("ed25519_donna");
  157. cfg.component("or-trunnel-testing");
  158. cfg.from_cflags("TOR_ZLIB_LIBS");
  159. cfg.from_cflags("TOR_LIB_MATH");
  160. cfg.from_cflags("NSS_LIBS");
  161. cfg.from_cflags("TOR_OPENSSL_LIBS");
  162. cfg.from_cflags("TOR_LIBEVENT_LIBS");
  163. cfg.from_cflags("TOR_LIB_WS32");
  164. cfg.from_cflags("TOR_LIB_GDI");
  165. cfg.from_cflags("TOR_LIB_USERENV");
  166. cfg.from_cflags("CURVE25519_LIBS");
  167. cfg.from_cflags("TOR_LZMA_LIBS");
  168. cfg.from_cflags("TOR_ZSTD_LIBS");
  169. cfg.from_cflags("LIBS");
  170. }
  171. _ => {
  172. panic!("No configuration in build.rs for package {}", package);
  173. }
  174. }
  175. }