1 //! A build dependency for Cargo libraries to find libraries in a
2 //! [Vcpkg](https://github.com/Microsoft/vcpkg) tree. From a Vcpkg package name
3 //! this build helper will emit cargo metadata to link it and it's dependencies
4 //! (excluding system libraries, which it cannot derive).
5 //!
6 //! **Note:** You must set one of `RUSTFLAGS=-Ctarget-feature=+crt-static` or
7 //! `VCPKGRS_DYNAMIC=1` in your environment or the vcpkg-rs helper
8 //! will not find any libraries. If `VCPKGRS_DYNAMIC` is set, `cargo install` will
9 //! generate dynamically linked binaries, in which case you will have to arrange for
10 //! dlls from your Vcpkg installation to be available in your path.
11 //!
12 //! The simplest possible usage looks like this :-
13 //!
14 //! ```rust,no_run
15 //! vcpkg::find_package("libssh2").unwrap();
16 //! ```
17 //!
18 //! The cargo metadata that is emitted can be changed like this :-
19 //!
20 //! ```rust,no_run
21 //! vcpkg::Config::new()
22 //!     .emit_includes(true)
23 //!     .find_package("zlib").unwrap();
24 //! ```
25 //!
26 //! If the search was successful all appropriate Cargo metadata will be printed
27 //! to stdout.
28 //!
29 //! The decision to choose static variants of libraries is driven by adding
30 //! `RUSTFLAGS=-Ctarget-feature=+crt-static` to the environment. This requires
31 //! at least Rust 1.19.
32 //!
33 //! A number of environment variables are available to globally configure which
34 //! libraries are selected.
35 //!
36 //! * `VCPKG_ROOT` - Set the directory to look in for a vcpkg installation. If
37 //! it is not set, vcpkg will use the user-wide installation if one has been
38 //! set up with `vcpkg integrate install`
39 //!
40 //! * `VCPKGRS_NO_FOO` - if set, vcpkg-rs will not attempt to find the
41 //! library named `foo`.
42 //!
43 //! * `VCPKGRS_DISABLE` - if set, vcpkg-rs will not attempt to find any libraries.
44 //!
45 //! * `VCPKGRS_DYNAMIC` - if set, vcpkg-rs will link to DLL builds of ports.
46 //!
47 //! There is a companion crate `vcpkg_cli` that allows testing of environment
48 //! and flag combinations.
49 //!
50 //! ```Batchfile
51 //! C:\src> vcpkg_cli probe -l static mysqlclient
52 //! Found library mysqlclient
53 //! Include paths:
54 //!         C:\src\[..]\vcpkg\installed\x64-windows-static\include
55 //! Library paths:
56 //!         C:\src\[..]\vcpkg\installed\x64-windows-static\lib
57 //! Cargo metadata:
58 //!         cargo:rustc-link-search=native=C:\src\[..]\vcpkg\installed\x64-windows-static\lib
59 //!         cargo:rustc-link-lib=static=mysqlclient
60 //! ```
61 
62 // The CI will test vcpkg-rs on 1.10 because at this point rust-openssl's
63 // openssl-sys is backward compatible that far. (Actually, the oldest release
64 // crate openssl version 0.10 seems to build against is now Rust 1.24.1?)
65 #![allow(deprecated)]
66 
67 #[cfg(test)]
68 #[macro_use]
69 extern crate lazy_static;
70 
71 #[allow(unused_imports)]
72 use std::ascii::AsciiExt;
73 
74 use std::collections::BTreeMap;
75 use std::env;
76 use std::error;
77 use std::ffi::OsStr;
78 use std::fmt;
79 use std::fs::{self, File};
80 use std::io::{BufRead, BufReader};
81 use std::path::{Path, PathBuf};
82 
83 #[derive(Default)]
84 pub struct Config {
85     /// should the cargo metadata actually be emitted
86     cargo_metadata: bool,
87 
88     /// should cargo:include= metadata be emitted (defaults to false)
89     emit_includes: bool,
90 
91     /// .lib/.a files that must be be found for probing to be considered successful
92     required_libs: Vec<String>,
93 
94     /// .dlls that must be be found for probing to be considered successful
95     required_dlls: Vec<String>,
96 
97     /// should DLLs be copies to OUT_DIR?
98     copy_dlls: bool,
99 }
100 
101 /// Details of a package that was found
102 #[derive(Debug)]
103 pub struct Library {
104     /// Paths for the linker to search for static or import libraries
105     pub link_paths: Vec<PathBuf>,
106 
107     /// Paths to search at runtme to find DLLs
108     pub dll_paths: Vec<PathBuf>,
109 
110     /// Paths to search for
111     pub include_paths: Vec<PathBuf>,
112 
113     /// cargo: metadata lines
114     pub cargo_metadata: Vec<String>,
115 
116     /// libraries found are static
117     pub is_static: bool,
118 
119     /// DLLs found
120     pub found_dlls: Vec<PathBuf>,
121 
122     /// static libs or import libs found
123     pub found_libs: Vec<PathBuf>,
124 
125     /// ports that are providing the libraries to link to, in port link order
126     pub ports: Vec<String>,
127 }
128 
129 enum MSVCTarget {
130     X86Windows,
131     X64Windows,
132     X64Linux,
133     X64MacOS,
134 }
135 
136 impl fmt::Display for MSVCTarget {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result137     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138         match *self {
139             MSVCTarget::X86Windows => write!(f, "x86-windows"),
140             MSVCTarget::X64Windows => write!(f, "x64-windows"),
141             MSVCTarget::X64Linux => write!(f, "x64-linux"),
142             MSVCTarget::X64MacOS => write!(f, "x64-osx"),
143         }
144     }
145 }
146 
147 #[derive(Debug)] // need Display?
148 pub enum Error {
149     /// Aborted because of a `VCPKGRS_NO_*` environment variable.
150     ///
151     /// Contains the name of the responsible environment variable.
152     DisabledByEnv(String),
153 
154     /// Aborted because a required environment variable was not set.
155     RequiredEnvMissing(String),
156 
157     /// On Windows, only MSVC ABI is supported
158     NotMSVC,
159 
160     /// Can't find a vcpkg tree
161     VcpkgNotFound(String),
162 
163     /// Library not found in vcpkg tree
164     LibNotFound(String),
165 
166     /// Could not understand vcpkg installation
167     VcpkgInstallation(String),
168 
169     #[doc(hidden)]
170     __Nonexhaustive,
171 }
172 
173 impl error::Error for Error {
description(&self) -> &str174     fn description(&self) -> &str {
175         match *self {
176             Error::DisabledByEnv(_) => "vcpkg-rs requested to be aborted",
177             Error::RequiredEnvMissing(_) => "a required env setting is missing",
178             Error::NotMSVC => "vcpkg-rs only can only find libraries for MSVC ABI 64 bit builds",
179             Error::VcpkgNotFound(_) => "could not find Vcpkg tree",
180             Error::LibNotFound(_) => "could not find library in Vcpkg tree",
181             Error::VcpkgInstallation(_) => "could not look up details of packages in vcpkg tree",
182             Error::__Nonexhaustive => panic!(),
183         }
184     }
185 
cause(&self) -> Option<&error::Error>186     fn cause(&self) -> Option<&error::Error> {
187         match *self {
188             // Error::Command { ref cause, .. } => Some(cause),
189             _ => None,
190         }
191     }
192 }
193 
194 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>195     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
196         match *self {
197             Error::DisabledByEnv(ref name) => write!(f, "Aborted because {} is set", name),
198             Error::RequiredEnvMissing(ref name) => write!(f, "Aborted because {} is not set", name),
199             Error::NotMSVC => write!(
200                 f,
201                 "the vcpkg-rs Vcpkg build helper can only find libraries built for the MSVC ABI."
202             ),
203             Error::VcpkgNotFound(ref detail) => write!(f, "Could not find Vcpkg tree: {}", detail),
204             Error::LibNotFound(ref detail) => {
205                 write!(f, "Could not find library in Vcpkg tree {}", detail)
206             }
207             Error::VcpkgInstallation(ref detail) => write!(
208                 f,
209                 "Could not look up details of packages in vcpkg tree {}",
210                 detail
211             ),
212             Error::__Nonexhaustive => panic!(),
213         }
214     }
215 }
216 
217 /// Deprecated in favor of the find_package function
218 #[doc(hidden)]
probe_package(name: &str) -> Result<Library, Error>219 pub fn probe_package(name: &str) -> Result<Library, Error> {
220     Config::new().probe(name)
221 }
222 
223 /// Find the package `package` in a Vcpkg tree.
224 ///
225 /// Emits cargo metadata to link to libraries provided by the Vcpkg package/port
226 /// named, and any (non-system) libraries that they depend on.
227 ///
228 /// This will select the architecture and linkage based on environment
229 /// variables and build flags as described in the module docs.
find_package(package: &str) -> Result<Library, Error>230 pub fn find_package(package: &str) -> Result<Library, Error> {
231     Config::new().find_package(package)
232 }
233 
find_vcpkg_root() -> Result<PathBuf, Error>234 fn find_vcpkg_root() -> Result<PathBuf, Error> {
235     // prefer the setting from the environment is there is one
236     if let Some(path) = env::var_os("VCPKG_ROOT") {
237         return Ok(PathBuf::from(path));
238     }
239 
240     // see if there is a per-user vcpkg tree that has been integrated into msbuild
241     // using `vcpkg integrate install`
242     let local_app_data = try!(env::var("LOCALAPPDATA").map_err(|_| Error::VcpkgNotFound(
243         "Failed to read either VCPKG_ROOT or LOCALAPPDATA environment variables".to_string()
244     ))); // not present or can't utf8
245     let vcpkg_user_targets_path = Path::new(local_app_data.as_str())
246         .join("vcpkg")
247         .join("vcpkg.user.targets");
248 
249     let file = try!(File::open(vcpkg_user_targets_path.clone()).map_err(|_| {
250         Error::VcpkgNotFound(
251             "No vcpkg.user.targets found. Set the VCPKG_ROOT environment \
252              variable or run 'vcpkg integrate install'"
253                 .to_string(),
254         )
255     }));
256     let file = BufReader::new(&file);
257 
258     for line in file.lines() {
259         let line = try!(line.map_err(|_| Error::VcpkgNotFound(format!(
260             "Parsing of {} failed.",
261             vcpkg_user_targets_path.to_string_lossy().to_owned()
262         ))));
263         let mut split = line.split("Project=\"");
264         split.next(); // eat anything before Project="
265         if let Some(found) = split.next() {
266             // " is illegal in a Windows pathname
267             if let Some(found) = found.split_terminator('"').next() {
268                 let mut vcpkg_root = PathBuf::from(found);
269                 if !(vcpkg_root.pop() && vcpkg_root.pop() && vcpkg_root.pop() && vcpkg_root.pop()) {
270                     return Err(Error::VcpkgNotFound(format!(
271                         "Could not find vcpkg root above {}",
272                         found
273                     )));
274                 }
275                 return Ok(vcpkg_root);
276             }
277         }
278     }
279 
280     Err(Error::VcpkgNotFound(format!(
281         "Project location not found parsing {}.",
282         vcpkg_user_targets_path.to_string_lossy().to_owned()
283     )))
284 }
285 
validate_vcpkg_root(path: &PathBuf) -> Result<(), Error>286 fn validate_vcpkg_root(path: &PathBuf) -> Result<(), Error> {
287     let mut vcpkg_root_path = path.clone();
288     vcpkg_root_path.push(".vcpkg-root");
289 
290     if vcpkg_root_path.exists() {
291         Ok(())
292     } else {
293         Err(Error::VcpkgNotFound(format!(
294             "Could not find Vcpkg root at {}",
295             vcpkg_root_path.to_string_lossy()
296         )))
297     }
298 }
299 
find_vcpkg_target(msvc_target: &MSVCTarget) -> Result<VcpkgTarget, Error>300 fn find_vcpkg_target(msvc_target: &MSVCTarget) -> Result<VcpkgTarget, Error> {
301     let vcpkg_root = try!(find_vcpkg_root());
302     try!(validate_vcpkg_root(&vcpkg_root));
303 
304     let (static_lib, static_appendage, lib_suffix, strip_lib_prefix) = match msvc_target {
305         &MSVCTarget::X64Windows | &MSVCTarget::X86Windows => {
306             let static_lib = env::var("CARGO_CFG_TARGET_FEATURE")
307                 .unwrap_or(String::new()) // rustc 1.10
308                 .contains("crt-static");
309             let static_appendage = if static_lib { "-static" } else { "" };
310             (static_lib, static_appendage, "lib", false)
311         }
312         _ => (true, "", "a", true),
313     };
314 
315     let mut base = vcpkg_root;
316     base.push("installed");
317     let status_path = base.join("vcpkg");
318 
319     let vcpkg_triple = format!("{}{}", msvc_target.to_string(), static_appendage);
320     base.push(&vcpkg_triple);
321 
322     let lib_path = base.join("lib");
323     let bin_path = base.join("bin");
324     let include_path = base.join("include");
325 
326     Ok(VcpkgTarget {
327         vcpkg_triple: vcpkg_triple,
328         lib_path: lib_path,
329         bin_path: bin_path,
330         include_path: include_path,
331         is_static: static_lib,
332         status_path: status_path,
333         lib_suffix: lib_suffix.to_owned(),
334         strip_lib_prefix: strip_lib_prefix,
335     })
336 }
337 
338 #[derive(Clone, Debug)]
339 struct Port {
340     // dlls if any
341     dlls: Vec<String>,
342 
343     // libs (static or import)
344     libs: Vec<String>,
345 
346     // ports that this port depends on
347     deps: Vec<String>,
348 }
349 
load_port_manifest( path: &PathBuf, port: &str, version: &str, vcpkg_target: &VcpkgTarget, ) -> Result<(Vec<String>, Vec<String>), Error>350 fn load_port_manifest(
351     path: &PathBuf,
352     port: &str,
353     version: &str,
354     vcpkg_target: &VcpkgTarget,
355 ) -> Result<(Vec<String>, Vec<String>), Error> {
356     let manifest_file = path.join("info").join(format!(
357         "{}_{}_{}.list",
358         port, version, vcpkg_target.vcpkg_triple
359     ));
360 
361     let mut dlls = Vec::new();
362     let mut libs = Vec::new();
363 
364     let f = try!(
365         File::open(&manifest_file).map_err(|_| Error::VcpkgInstallation(format!(
366             "Could not open port manifest file {}",
367             manifest_file.display()
368         )))
369     );
370 
371     let file = BufReader::new(&f);
372 
373     let dll_prefix = Path::new(&vcpkg_target.vcpkg_triple).join("bin");
374     let lib_prefix = Path::new(&vcpkg_target.vcpkg_triple).join("lib");
375 
376     for line in file.lines() {
377         let line = line.unwrap();
378 
379         let file_path = Path::new(&line);
380 
381         if let Ok(dll) = file_path.strip_prefix(&dll_prefix) {
382             if dll.extension() == Some(OsStr::new("dll"))
383                 && dll.components().collect::<Vec<_>>().len() == 1
384             {
385                 // match "mylib.dll" but not "debug/mylib.dll" or "manual_link/mylib.dll"
386 
387                 dll.to_str().map(|s| dlls.push(s.to_owned()));
388             }
389         } else if let Ok(lib) = file_path.strip_prefix(&lib_prefix) {
390             if lib.extension() == Some(OsStr::new(&vcpkg_target.lib_suffix))
391                 && lib.components().collect::<Vec<_>>().len() == 1
392             {
393                 if let Some(lib) = vcpkg_target.link_name_for_lib(lib) {
394                     libs.push(lib);
395                 }
396             }
397         }
398     }
399 
400     Ok((dlls, libs))
401 }
402 
403 // load ports from the status file or one of the incremental updates
load_port_file( filename: &PathBuf, port_info: &mut Vec<BTreeMap<String, String>>, ) -> Result<(), Error>404 fn load_port_file(
405     filename: &PathBuf,
406     port_info: &mut Vec<BTreeMap<String, String>>,
407 ) -> Result<(), Error> {
408     let f = try!(
409         File::open(&filename).map_err(|e| Error::VcpkgInstallation(format!(
410             "Could not open status file at {}: {}",
411             filename.display(),
412             e
413         )))
414     );
415     let file = BufReader::new(&f);
416     let mut current: BTreeMap<String, String> = BTreeMap::new();
417     for line in file.lines() {
418         let line = line.unwrap();
419         let parts = line.splitn(2, ": ").clone().collect::<Vec<_>>();
420         if parts.len() == 2 {
421             // a key: value line
422             current.insert(parts[0].trim().into(), parts[1].trim().into());
423         } else if line.len() == 0 {
424             // end of section
425             port_info.push(current.clone());
426             current.clear();
427         } else {
428             // ignore all extension lines of the form
429             //
430             // Description: a package with a
431             //   very long description
432             //
433             // the description key is not used so this is harmless but
434             // this will eat extension lines for any multiline key which
435             // could become an issue in future
436         }
437     }
438 
439     if !current.is_empty() {
440         port_info.push(current);
441     }
442 
443     Ok(())
444 }
445 
load_ports(target: &VcpkgTarget) -> Result<BTreeMap<String, Port>, Error>446 fn load_ports(target: &VcpkgTarget) -> Result<BTreeMap<String, Port>, Error> {
447     let mut ports: BTreeMap<String, Port> = BTreeMap::new();
448 
449     let mut port_info: Vec<BTreeMap<String, String>> = Vec::new();
450 
451     // load the main status file. It is not an error if this file does not
452     // exist. If the only command that has been run in a Vcpkg installation
453     // is a single `vcpkg install package` then there will likely be no
454     // status file, only incremental updates. This is the typical case when
455     // running in a CI environment.
456     let status_filename = target.status_path.join("status");
457     load_port_file(&status_filename, &mut port_info).ok();
458 
459     // load updates to the status file that have yet to be normalized
460     let status_update_dir = target.status_path.join("updates");
461 
462     let paths = try!(
463         fs::read_dir(status_update_dir).map_err(|e| Error::VcpkgInstallation(format!(
464             "could not read status file updates dir: {}",
465             e
466         )))
467     );
468 
469     // get all of the paths of the update files into a Vec<PathBuf>
470     let mut paths = try!(paths
471         .map(|rde| rde.map(|de| de.path())) // Result<DirEntry, io::Error> -> Result<PathBuf, io::Error>
472         .collect::<Result<Vec<_>, _>>() // collect into Result<Vec<PathBuf>, io::Error>
473         .map_err(|e| {
474             Error::VcpkgInstallation(format!(
475                 "could not read status file update filenames: {}",
476                 e
477             ))
478         }));
479 
480     // Sort the paths and read them. This could be done directly from the iterator if
481     // read_dir() guarantees that the files will be read in alpha order but that appears
482     // to be unspecified as the underlying operating system calls used are unspecified
483     // https://doc.rust-lang.org/nightly/std/fs/fn.read_dir.html#platform-specific-behavior
484     paths.sort();
485     for path in paths {
486         //       println!("Name: {}", path.display());
487         try!(load_port_file(&path, &mut port_info));
488     }
489     //println!("{:#?}", port_info);
490 
491     let mut seen_names = BTreeMap::new();
492     for current in &port_info {
493         // store them by name and arch, clobbering older details
494         match (
495             current.get("Package"),
496             current.get("Architecture"),
497             current.get("Feature"),
498         ) {
499             (Some(pkg), Some(arch), feature) => {
500                 seen_names.insert((pkg, arch, feature), current);
501             }
502             _ => {}
503         }
504     }
505 
506     for (&(name, arch, feature), current) in &seen_names {
507         if **arch == target.vcpkg_triple {
508             let mut deps = if let Some(deps) = current.get("Depends") {
509                 deps.split(", ").map(|x| x.to_owned()).collect()
510             } else {
511                 Vec::new()
512             };
513 
514             if current
515                 .get("Status")
516                 .unwrap_or(&String::new())
517                 .ends_with(" installed")
518             {
519                 match (current.get("Version"), feature) {
520                     (Some(version), _) => {
521                         // this failing here and bailing out causes everything to fail
522                         let lib_info = try!(load_port_manifest(
523                             &target.status_path,
524                             &name,
525                             version,
526                             &target
527                         ));
528                         let port = Port {
529                             dlls: lib_info.0,
530                             libs: lib_info.1,
531                             deps: deps,
532                         };
533 
534                         ports.insert(name.to_string(), port);
535                     }
536                     (_, Some(_feature)) => match ports.get_mut(name) {
537                         Some(ref mut port) => {
538                             port.deps.append(&mut deps);
539                         }
540                         _ => {
541                             println!("found a feature that had no corresponding port :-");
542                             println!("current {:+?}", current);
543                             continue;
544                         }
545                     },
546                     (_, _) => {
547                         println!("didn't know how to deal with status file entry :-");
548                         println!("{:+?}", current);
549                         continue;
550                     }
551                 }
552             }
553         }
554     }
555 
556     Ok(ports)
557 }
558 
559 /// paths and triple for the chosen target
560 struct VcpkgTarget {
561     vcpkg_triple: String,
562     lib_path: PathBuf,
563     bin_path: PathBuf,
564     include_path: PathBuf,
565 
566     // directory containing the status file
567     status_path: PathBuf,
568 
569     is_static: bool,
570     lib_suffix: String,
571 
572     /// strip 'lib' from library names in linker args?
573     strip_lib_prefix: bool,
574 }
575 
576 impl VcpkgTarget {
link_name_for_lib(&self, filename: &std::path::Path) -> Option<String>577     fn link_name_for_lib(&self, filename: &std::path::Path) -> Option<String> {
578         if self.strip_lib_prefix {
579             filename.to_str().map(|s| s.to_owned())
580         // filename
581         //     .to_str()
582         //     .map(|s| s.trim_left_matches("lib").to_owned())
583         } else {
584             filename.to_str().map(|s| s.to_owned())
585         }
586     }
587 }
588 
589 impl Config {
new() -> Config590     pub fn new() -> Config {
591         Config {
592             cargo_metadata: true,
593             copy_dlls: true,
594             ..Default::default()
595             // emit_includes: false,
596             // required_libs: Vec::new(),
597         }
598     }
599 
600     /// Override the name of the library to look for if it differs from the package name.
601     ///
602     /// This may be called more than once if multiple libs are required.
603     /// All libs must be found for the probe to succeed. `.probe()` must
604     /// be run with a different configuration to look for libraries under one of several names.
605     /// `.libname("ssleay32")` will look for ssleay32.lib and also ssleay32.dll if
606     /// dynamic linking is selected.
lib_name(&mut self, lib_stem: &str) -> &mut Config607     pub fn lib_name(&mut self, lib_stem: &str) -> &mut Config {
608         self.required_libs.push(lib_stem.to_owned());
609         self.required_dlls.push(lib_stem.to_owned());
610         self
611     }
612 
613     /// Override the name of the library to look for if it differs from the package name.
614     ///
615     /// This may be called more than once if multiple libs are required.
616     /// All libs must be found for the probe to succeed. `.probe()` must
617     /// be run with a different configuration to look for libraries under one of several names.
618     /// `.lib_names("libcurl_imp","curl")` will look for libcurl_imp.lib and also curl.dll if
619     /// dynamic linking is selected.
lib_names(&mut self, lib_stem: &str, dll_stem: &str) -> &mut Config620     pub fn lib_names(&mut self, lib_stem: &str, dll_stem: &str) -> &mut Config {
621         self.required_libs.push(lib_stem.to_owned());
622         self.required_dlls.push(dll_stem.to_owned());
623         self
624     }
625 
626     /// Define whether metadata should be emitted for cargo allowing it to
627     /// automatically link the binary. Defaults to `true`.
cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Config628     pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Config {
629         self.cargo_metadata = cargo_metadata;
630         self
631     }
632 
633     /// Define cargo:include= metadata should be emitted. Defaults to `false`.
emit_includes(&mut self, emit_includes: bool) -> &mut Config634     pub fn emit_includes(&mut self, emit_includes: bool) -> &mut Config {
635         self.emit_includes = emit_includes;
636         self
637     }
638 
639     /// Should DLLs be copied to OUT_DIR?
640     /// Defaults to `true`.
copy_dlls(&mut self, copy_dlls: bool) -> &mut Config641     pub fn copy_dlls(&mut self, copy_dlls: bool) -> &mut Config {
642         self.copy_dlls = copy_dlls;
643         self
644     }
645 
646     /// Find the library `port_name` in a Vcpkg tree.
647     ///
648     /// This will use all configuration previously set to select the
649     /// architecture and linkage.
650     /// Deprecated in favor of the find_package function
651     #[doc(hidden)]
probe(&mut self, port_name: &str) -> Result<Library, Error>652     pub fn probe(&mut self, port_name: &str) -> Result<Library, Error> {
653         // determine the target type, bailing out if it is not some
654         // kind of msvc
655         let msvc_target = try!(msvc_target());
656 
657         // bail out if requested to not try at all
658         if env::var_os("VCPKGRS_DISABLE").is_some() {
659             return Err(Error::DisabledByEnv("VCPKGRS_DISABLE".to_owned()));
660         }
661 
662         // bail out if requested to not try at all (old)
663         if env::var_os("NO_VCPKG").is_some() {
664             return Err(Error::DisabledByEnv("NO_VCPKG".to_owned()));
665         }
666 
667         // bail out if requested to skip this package
668         let abort_var_name = format!("VCPKGRS_NO_{}", envify(port_name));
669         if env::var_os(&abort_var_name).is_some() {
670             return Err(Error::DisabledByEnv(abort_var_name));
671         }
672 
673         // bail out if requested to skip this package (old)
674         let abort_var_name = format!("{}_NO_VCPKG", envify(port_name));
675         if env::var_os(&abort_var_name).is_some() {
676             return Err(Error::DisabledByEnv(abort_var_name));
677         }
678 
679         // if no overrides have been selected, then the Vcpkg port name
680         // is the the .lib name and the .dll name
681         if self.required_libs.is_empty() {
682             self.required_libs.push(port_name.to_owned());
683             self.required_dlls.push(port_name.to_owned());
684         }
685 
686         let vcpkg_target = try!(find_vcpkg_target(&msvc_target));
687 
688         // require explicit opt-in before using dynamically linked
689         // variants, otherwise cargo install of various things will
690         // stop working if Vcpkg is installed.
691         if !vcpkg_target.is_static && !env::var_os("VCPKGRS_DYNAMIC").is_some() {
692             return Err(Error::RequiredEnvMissing("VCPKGRS_DYNAMIC".to_owned()));
693         }
694 
695         let mut lib = Library::new(vcpkg_target.is_static);
696 
697         if self.emit_includes {
698             lib.cargo_metadata.push(format!(
699                 "cargo:include={}",
700                 vcpkg_target.include_path.display()
701             ));
702         }
703         lib.include_paths.push(vcpkg_target.include_path.clone());
704 
705         lib.cargo_metadata.push(format!(
706             "cargo:rustc-link-search=native={}",
707             vcpkg_target
708                 .lib_path
709                 .to_str()
710                 .expect("failed to convert string type")
711         ));
712         lib.link_paths.push(vcpkg_target.lib_path.clone());
713         if !vcpkg_target.is_static {
714             lib.cargo_metadata.push(format!(
715                 "cargo:rustc-link-search=native={}",
716                 vcpkg_target
717                     .bin_path
718                     .to_str()
719                     .expect("failed to convert string type")
720             ));
721             // this path is dropped by recent versions of cargo hence the copies to OUT_DIR below
722             lib.dll_paths.push(vcpkg_target.bin_path.clone());
723         }
724 
725         try!(self.emit_libs(&mut lib, &vcpkg_target));
726 
727         if self.copy_dlls {
728             try!(self.do_dll_copy(&mut lib));
729         }
730 
731         if self.cargo_metadata {
732             for line in &lib.cargo_metadata {
733                 println!("{}", line);
734             }
735         }
736         Ok(lib)
737     }
738 
emit_libs(&mut self, lib: &mut Library, vcpkg_target: &VcpkgTarget) -> Result<(), Error>739     fn emit_libs(&mut self, lib: &mut Library, vcpkg_target: &VcpkgTarget) -> Result<(), Error> {
740         for required_lib in &self.required_libs {
741             // this could use static-nobundle= for static libraries but it is apparently
742             // not necessary to make the distinction for windows-msvc.
743 
744             let link_name = match vcpkg_target.strip_lib_prefix {
745                 true => required_lib.trim_left_matches("lib"),
746                 false => required_lib,
747             };
748 
749             lib.cargo_metadata
750                 .push(format!("cargo:rustc-link-lib={}", link_name));
751 
752             // verify that the library exists
753             let mut lib_location = vcpkg_target.lib_path.clone();
754             lib_location.push(required_lib.clone() + "." + &vcpkg_target.lib_suffix);
755 
756             if !lib_location.exists() {
757                 return Err(Error::LibNotFound(lib_location.display().to_string()));
758             }
759             lib.found_libs.push(lib_location);
760         }
761 
762         if !vcpkg_target.is_static {
763             for required_dll in &self.required_dlls {
764                 let mut dll_location = vcpkg_target.bin_path.clone();
765                 dll_location.push(required_dll.clone() + ".dll");
766 
767                 // verify that the DLL exists
768                 if !dll_location.exists() {
769                     return Err(Error::LibNotFound(dll_location.display().to_string()));
770                 }
771                 lib.found_dlls.push(dll_location);
772             }
773         }
774 
775         Ok(())
776     }
777 
do_dll_copy(&mut self, lib: &mut Library) -> Result<(), Error>778     fn do_dll_copy(&mut self, lib: &mut Library) -> Result<(), Error> {
779         if let Some(target_dir) = env::var_os("OUT_DIR") {
780             if !lib.found_dlls.is_empty() {
781                 for file in &lib.found_dlls {
782                     let mut dest_path = Path::new(target_dir.as_os_str()).to_path_buf();
783                     dest_path.push(Path::new(file.file_name().unwrap()));
784                     try!(
785                         fs::copy(file, &dest_path).map_err(|_| Error::LibNotFound(format!(
786                             "Can't copy file {} to {}",
787                             file.to_string_lossy(),
788                             dest_path.to_string_lossy()
789                         )))
790                     );
791                     println!(
792                         "vcpkg build helper copied {} to {}",
793                         file.to_string_lossy(),
794                         dest_path.to_string_lossy()
795                     );
796                 }
797                 lib.cargo_metadata.push(format!(
798                     "cargo:rustc-link-search=native={}",
799                     env::var("OUT_DIR").unwrap()
800                 ));
801                 // work around https://github.com/rust-lang/cargo/issues/3957
802                 lib.cargo_metadata.push(format!(
803                     "cargo:rustc-link-search={}",
804                     env::var("OUT_DIR").unwrap()
805                 ));
806             }
807         } else {
808             return Err(Error::LibNotFound("Unable to get OUT_DIR".to_owned()));
809         }
810         Ok(())
811     }
812 
813     /// Find the package `port_name` in a Vcpkg tree.
814     ///
815     /// Emits cargo metadata to link to libraries provided by the Vcpkg package/port
816     /// named, and any (non-system) libraries that they depend on.
817     ///
818     /// This will select the architecture and linkage based on environment
819     /// variables and build flags as described in the module docs, and any configuration
820     /// set on the builder.
find_package(&mut self, port_name: &str) -> Result<Library, Error>821     pub fn find_package(&mut self, port_name: &str) -> Result<Library, Error> {
822         // determine the target type, bailing out if it is not some
823         // kind of msvc
824         let msvc_target = try!(msvc_target());
825 
826         // bail out if requested to not try at all
827         if env::var_os("VCPKGRS_DISABLE").is_some() {
828             return Err(Error::DisabledByEnv("VCPKGRS_DISABLE".to_owned()));
829         }
830 
831         // bail out if requested to not try at all (old)
832         if env::var_os("NO_VCPKG").is_some() {
833             return Err(Error::DisabledByEnv("NO_VCPKG".to_owned()));
834         }
835 
836         // bail out if requested to skip this package
837         let abort_var_name = format!("VCPKGRS_NO_{}", envify(port_name));
838         if env::var_os(&abort_var_name).is_some() {
839             return Err(Error::DisabledByEnv(abort_var_name));
840         }
841 
842         // bail out if requested to skip this package (old)
843         let abort_var_name = format!("{}_NO_VCPKG", envify(port_name));
844         if env::var_os(&abort_var_name).is_some() {
845             return Err(Error::DisabledByEnv(abort_var_name));
846         }
847 
848         let vcpkg_target = try!(find_vcpkg_target(&msvc_target));
849 
850         let mut required_port_order = Vec::new();
851 
852         // if no overrides have been selected, then the Vcpkg port name
853         // is the the .lib name and the .dll name
854         if self.required_libs.is_empty() {
855             let ports = try!(load_ports(&vcpkg_target));
856 
857             if ports.get(&port_name.to_owned()).is_none() {
858                 return Err(Error::LibNotFound(port_name.to_owned()));
859             }
860 
861             // the complete set of ports required
862             let mut required_ports: BTreeMap<String, Port> = BTreeMap::new();
863             // working of ports that we need to include
864             //        let mut ports_to_scan: BTreeSet<String> = BTreeSet::new();
865             //        ports_to_scan.insert(port_name.to_owned());
866             let mut ports_to_scan = vec![port_name.to_owned()]; //: Vec<String> = BTreeSet::new();
867 
868             while !ports_to_scan.is_empty() {
869                 let port_name = ports_to_scan.pop().unwrap();
870 
871                 if required_ports.contains_key(&port_name) {
872                     continue;
873                 }
874 
875                 if let Some(port) = ports.get(&port_name) {
876                     for dep in &port.deps {
877                         ports_to_scan.push(dep.clone());
878                     }
879                     required_ports.insert(port_name.clone(), (*port).clone());
880                     remove_item(&mut required_port_order, &port_name);
881                     required_port_order.push(port_name);
882                 } else {
883                     // what?
884                 }
885             }
886 
887             // for port in ports {
888             //     println!("port {:?}", port);
889             // }
890             // println!("== Looking for port {}", port_name);
891             // for port in &required_port_order {
892             //     println!("ordered required port {:?}", port);
893             // }
894             // println!("=============================");
895             // for port in &required_ports {
896             //     println!("required port {:?}", port);
897             // }
898 
899             // if no overrides have been selected, then the Vcpkg port name
900             // is the the .lib name and the .dll name
901             if self.required_libs.is_empty() {
902                 for port_name in &required_port_order {
903                     let port = required_ports.get(port_name).unwrap();
904                     self.required_libs.extend(port.libs.iter().map(|s| {
905                         Path::new(&s)
906                             .file_stem()
907                             .unwrap()
908                             .to_string_lossy()
909                             .into_owned()
910                     }));
911                     self.required_dlls
912                         .extend(port.dlls.iter().cloned().map(|s| {
913                             Path::new(&s)
914                                 .file_stem()
915                                 .unwrap()
916                                 .to_string_lossy()
917                                 .into_owned()
918                         }));
919                 }
920             }
921         }
922         // require explicit opt-in before using dynamically linked
923         // variants, otherwise cargo install of various things will
924         // stop working if Vcpkg is installed.
925         if !vcpkg_target.is_static && !env::var_os("VCPKGRS_DYNAMIC").is_some() {
926             return Err(Error::RequiredEnvMissing("VCPKGRS_DYNAMIC".to_owned()));
927         }
928 
929         let mut lib = Library::new(vcpkg_target.is_static);
930 
931         if self.emit_includes {
932             lib.cargo_metadata.push(format!(
933                 "cargo:include={}",
934                 vcpkg_target.include_path.display()
935             ));
936         }
937         lib.include_paths.push(vcpkg_target.include_path.clone());
938 
939         lib.cargo_metadata.push(format!(
940             "cargo:rustc-link-search=native={}",
941             vcpkg_target
942                 .lib_path
943                 .to_str()
944                 .expect("failed to convert string type")
945         ));
946         lib.link_paths.push(vcpkg_target.lib_path.clone());
947         if !vcpkg_target.is_static {
948             lib.cargo_metadata.push(format!(
949                 "cargo:rustc-link-search=native={}",
950                 vcpkg_target
951                     .bin_path
952                     .to_str()
953                     .expect("failed to convert string type")
954             ));
955             // this path is dropped by recent versions of cargo hence the copies to OUT_DIR below
956             lib.dll_paths.push(vcpkg_target.bin_path.clone());
957         }
958 
959         lib.ports = required_port_order;
960 
961         try!(self.emit_libs(&mut lib, &vcpkg_target));
962 
963         if self.copy_dlls {
964             try!(self.do_dll_copy(&mut lib));
965         }
966 
967         if self.cargo_metadata {
968             for line in &lib.cargo_metadata {
969                 println!("{}", line);
970             }
971         }
972         Ok(lib)
973     }
974 }
975 
remove_item(cont: &mut Vec<String>, item: &String) -> Option<String>976 fn remove_item(cont: &mut Vec<String>, item: &String) -> Option<String> {
977     match cont.iter().position(|x| *x == *item) {
978         Some(pos) => Some(cont.remove(pos)),
979         None => None,
980     }
981 }
982 
983 impl Library {
new(is_static: bool) -> Library984     fn new(is_static: bool) -> Library {
985         Library {
986             link_paths: Vec::new(),
987             dll_paths: Vec::new(),
988             include_paths: Vec::new(),
989             cargo_metadata: Vec::new(),
990             is_static: is_static,
991             found_dlls: Vec::new(),
992             found_libs: Vec::new(),
993             ports: Vec::new(),
994         }
995     }
996 }
997 
envify(name: &str) -> String998 fn envify(name: &str) -> String {
999     name.chars()
1000         .map(|c| c.to_ascii_uppercase())
1001         .map(|c| if c == '-' { '_' } else { c })
1002         .collect()
1003 }
1004 
msvc_target() -> Result<MSVCTarget, Error>1005 fn msvc_target() -> Result<MSVCTarget, Error> {
1006     let target = env::var("TARGET").unwrap_or(String::new());
1007     if target == "x86_64-apple-darwin" {
1008         Ok(MSVCTarget::X64MacOS)
1009     } else if target == "x86_64-unknown-linux-gnu" {
1010         Ok(MSVCTarget::X64Linux)
1011     } else if !target.contains("-pc-windows-msvc") {
1012         Err(Error::NotMSVC)
1013     } else if target.starts_with("x86_64-") {
1014         Ok(MSVCTarget::X64Windows)
1015     } else {
1016         // everything else is x86
1017         Ok(MSVCTarget::X86Windows)
1018     }
1019 }
1020 
1021 #[cfg(test)]
1022 mod tests {
1023 
1024     extern crate tempdir;
1025 
1026     use super::*;
1027     use std::env;
1028     use std::sync::Mutex;
1029 
1030     lazy_static! {
1031         static ref LOCK: Mutex<()> = Mutex::new(());
1032     }
1033 
1034     #[test]
do_nothing_for_unsupported_target()1035     fn do_nothing_for_unsupported_target() {
1036         let _g = LOCK.lock();
1037         env::set_var("VCPKG_ROOT", "/");
1038         env::set_var("TARGET", "x86_64-pc-windows-gnu");
1039         assert!(match ::probe_package("foo") {
1040             Err(Error::NotMSVC) => true,
1041             _ => false,
1042         });
1043 
1044         env::set_var("TARGET", "x86_64-pc-windows-gnu");
1045         assert_eq!(env::var("TARGET"), Ok("x86_64-pc-windows-gnu".to_string()));
1046         assert!(match ::probe_package("foo") {
1047             Err(Error::NotMSVC) => true,
1048             _ => false,
1049         });
1050         env::remove_var("TARGET");
1051         env::remove_var("VCPKG_ROOT");
1052     }
1053 
1054     #[test]
do_nothing_for_bailout_variables_set()1055     fn do_nothing_for_bailout_variables_set() {
1056         let _g = LOCK.lock();
1057         env::set_var("VCPKG_ROOT", "/");
1058         env::set_var("TARGET", "x86_64-pc-windows-msvc");
1059 
1060         for &var in &[
1061             "VCPKGRS_DISABLE",
1062             "VCPKGRS_NO_FOO",
1063             "FOO_NO_VCPKG",
1064             "NO_VCPKG",
1065         ] {
1066             env::set_var(var, "1");
1067             assert!(match ::probe_package("foo") {
1068                 Err(Error::DisabledByEnv(ref v)) if v == var => true,
1069                 _ => false,
1070             });
1071             env::remove_var(var);
1072         }
1073         env::remove_var("TARGET");
1074         env::remove_var("VCPKG_ROOT");
1075     }
1076 
1077     // these tests are good but are leaning on a real vcpkg installation
1078 
1079     #[test]
default_build_refuses_dynamic()1080     fn default_build_refuses_dynamic() {
1081         let _g = LOCK.lock();
1082         clean_env();
1083         env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("no-status"));
1084         env::set_var("TARGET", "x86_64-pc-windows-msvc");
1085         println!("Result is {:?}", ::find_package("libmysql"));
1086         assert!(match ::find_package("libmysql") {
1087             Err(Error::RequiredEnvMissing(ref v)) if v == "VCPKGRS_DYNAMIC" => true,
1088             _ => false,
1089         });
1090         clean_env();
1091     }
1092 
1093     #[test]
static_build_finds_lib()1094     fn static_build_finds_lib() {
1095         let _g = LOCK.lock();
1096         clean_env();
1097         env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("normalized"));
1098         env::set_var("TARGET", "x86_64-pc-windows-msvc");
1099         let tmp_dir = tempdir::TempDir::new("vcpkg_tests").unwrap();
1100         env::set_var("OUT_DIR", tmp_dir.path());
1101 
1102         // CARGO_CFG_TARGET_FEATURE is set in response to
1103         // RUSTFLAGS=-Ctarget-feature=+crt-static. It would
1104         //  be nice to test that also.
1105         env::set_var("CARGO_CFG_TARGET_FEATURE", "crt-static");
1106         println!("Result is {:?}", ::find_package("libmysql"));
1107         assert!(match ::find_package("libmysql") {
1108             Ok(_) => true,
1109             _ => false,
1110         });
1111         clean_env();
1112     }
1113 
1114     #[test]
dynamic_build_finds_lib()1115     fn dynamic_build_finds_lib() {
1116         let _g = LOCK.lock();
1117         clean_env();
1118         env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("no-status"));
1119         env::set_var("TARGET", "x86_64-pc-windows-msvc");
1120         env::set_var("VCPKGRS_DYNAMIC", "1");
1121         let tmp_dir = tempdir::TempDir::new("vcpkg_tests").unwrap();
1122         env::set_var("OUT_DIR", tmp_dir.path());
1123 
1124         println!("Result is {:?}", ::find_package("libmysql"));
1125         assert!(match ::find_package("libmysql") {
1126             Ok(_) => true,
1127             _ => false,
1128         });
1129         clean_env();
1130     }
1131 
1132     #[test]
handle_multiline_description()1133     fn handle_multiline_description() {
1134         let _g = LOCK.lock();
1135         clean_env();
1136         env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("multiline-description"));
1137         env::set_var("TARGET", "i686-pc-windows-msvc");
1138         env::set_var("VCPKGRS_DYNAMIC", "1");
1139         let tmp_dir = tempdir::TempDir::new("vcpkg_tests").unwrap();
1140         env::set_var("OUT_DIR", tmp_dir.path());
1141 
1142         println!("Result is {:?}", ::find_package("graphite2"));
1143         assert!(match ::find_package("graphite2") {
1144             Ok(_) => true,
1145             _ => false,
1146         });
1147         clean_env();
1148     }
1149 
1150     #[test]
link_libs_required_by_optional_features()1151     fn link_libs_required_by_optional_features() {
1152         let _g = LOCK.lock();
1153         clean_env();
1154         env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("normalized"));
1155         env::set_var("TARGET", "i686-pc-windows-msvc");
1156         env::set_var("VCPKGRS_DYNAMIC", "1");
1157         let tmp_dir = tempdir::TempDir::new("vcpkg_tests").unwrap();
1158         env::set_var("OUT_DIR", tmp_dir.path());
1159 
1160         println!("Result is {:?}", ::find_package("harfbuzz"));
1161         assert!(match ::find_package("harfbuzz") {
1162             Ok(lib) => lib
1163                 .cargo_metadata
1164                 .iter()
1165                 .find(|&x| x == "cargo:rustc-link-lib=icuuc")
1166                 .is_some(),
1167             _ => false,
1168         });
1169         clean_env();
1170     }
1171 
1172     #[test]
link_lib_name_is_correct()1173     fn link_lib_name_is_correct() {
1174         let _g = LOCK.lock();
1175 
1176         for target in &[
1177             "x86_64-apple-darwin",
1178             "i686-pc-windows-msvc",
1179             //      "x86_64-pc-windows-msvc",
1180             //    "x86_64-unknown-linux-gnu",
1181         ] {
1182             clean_env();
1183             env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("normalized"));
1184             env::set_var("TARGET", target);
1185             env::set_var("VCPKGRS_DYNAMIC", "1");
1186             let tmp_dir = tempdir::TempDir::new("vcpkg_tests").unwrap();
1187             env::set_var("OUT_DIR", tmp_dir.path());
1188 
1189             println!("Result is {:?}", ::find_package("harfbuzz"));
1190             assert!(match ::find_package("harfbuzz") {
1191                 Ok(lib) => lib
1192                     .cargo_metadata
1193                     .iter()
1194                     .find(|&x| x == "cargo:rustc-link-lib=harfbuzz")
1195                     .is_some(),
1196                 _ => false,
1197             });
1198             clean_env();
1199         }
1200     }
1201 
1202     #[test]
link_dependencies_after_port()1203     fn link_dependencies_after_port() {
1204         let _g = LOCK.lock();
1205         clean_env();
1206         env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("normalized"));
1207         env::set_var("TARGET", "i686-pc-windows-msvc");
1208         env::set_var("VCPKGRS_DYNAMIC", "1");
1209         let tmp_dir = tempdir::TempDir::new("vcpkg_tests").unwrap();
1210         env::set_var("OUT_DIR", tmp_dir.path());
1211 
1212         let lib = ::find_package("harfbuzz").unwrap();
1213 
1214         check_before(&lib, "freetype", "zlib");
1215         check_before(&lib, "freetype", "bzip2");
1216         check_before(&lib, "freetype", "libpng");
1217         check_before(&lib, "harfbuzz", "freetype");
1218         check_before(&lib, "harfbuzz", "ragel");
1219         check_before(&lib, "libpng", "zlib");
1220 
1221         clean_env();
1222 
1223         fn check_before(lib: &Library, earlier: &str, later: &str) {
1224             match (
1225                 lib.ports.iter().position(|x| *x == *earlier),
1226                 lib.ports.iter().position(|x| *x == *later),
1227             ) {
1228                 (Some(earlier_pos), Some(later_pos)) if earlier_pos < later_pos => {
1229                     // ok
1230                 }
1231                 _ => {
1232                     println!(
1233                         "earlier: {}, later: {}\nLibrary found: {:#?}",
1234                         earlier, later, lib
1235                     );
1236                     panic!();
1237                 }
1238             }
1239         }
1240     }
1241     // #[test]
1242     // fn dynamic_build_package_specific_bailout() {
1243     //     clean_env();
1244     //     env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("no-status"));
1245     //     env::set_var("TARGET", "x86_64-pc-windows-msvc");
1246     //     env::set_var("VCPKGRS_DYNAMIC", "1");
1247     //     env::set_var("VCPKGRS_NO_LIBMYSQL", "1");
1248 
1249     //     println!("Result is {:?}", ::find_package("libmysql"));
1250     //     assert!(match ::find_package("libmysql") {
1251     //         Err(Error::DisabledByEnv(ref v)) if v == "VCPKGRS_NO_LIBMYSQL" => true,
1252     //         _ => false,
1253     //     });
1254     //     clean_env();
1255     // }
1256 
1257     // #[test]
1258     // fn dynamic_build_global_bailout() {
1259     //     clean_env();
1260     //     env::set_var("VCPKG_ROOT", vcpkg_test_tree_loc("no-status"));
1261     //     env::set_var("TARGET", "x86_64-pc-windows-msvc");
1262     //     env::set_var("VCPKGRS_DYNAMIC", "1");
1263     //     env::set_var("VCPKGRS_DISABLE", "1");
1264 
1265     //     println!("Result is {:?}", ::find_package("libmysql"));
1266     //     assert!(match ::find_package("libmysql") {
1267     //         Err(Error::DisabledByEnv(ref v)) if v == "VCPKGRS_DISABLE" => true,
1268     //         _ => false,
1269     //     });
1270     //     clean_env();
1271     // }
1272 
clean_env()1273     fn clean_env() {
1274         env::remove_var("TARGET");
1275         env::remove_var("VCPKG_ROOT");
1276         env::remove_var("VCPKGRS_DYNAMIC");
1277         env::remove_var("RUSTFLAGS");
1278         env::remove_var("CARGO_CFG_TARGET_FEATURE");
1279         env::remove_var("VCPKGRS_DISABLE");
1280         env::remove_var("VCPKGRS_NO_LIBMYSQL");
1281     }
1282 
1283     // path to a to vcpkg installation to test against
vcpkg_test_tree_loc(name: &str) -> PathBuf1284     fn vcpkg_test_tree_loc(name: &str) -> PathBuf {
1285         let mut path = PathBuf::new();
1286         path.push(env::var("CARGO_MANIFEST_DIR").unwrap());
1287         path.pop();
1288         path.push("test-data");
1289         path.push(name);
1290         path
1291     }
1292 }
1293