vergen/feature/
cargo.rs

1// Copyright (c) 2022 vergen developers
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9#[cfg(feature = "cargo_metadata")]
10use self::cargo_builder::SetDependencies;
11use self::cargo_builder::{SetDebug, SetFeatures, SetOptLevel, SetTargetTriple};
12#[cfg(feature = "cargo_metadata")]
13use anyhow::anyhow;
14use anyhow::{Error, Result};
15use bon::Builder;
16#[cfg(feature = "cargo_metadata")]
17use cargo_metadata::{DepKindInfo, DependencyKind, MetadataCommand, Package, PackageId};
18#[cfg(feature = "cargo_metadata")]
19use regex::Regex;
20use std::env;
21#[cfg(feature = "cargo_metadata")]
22use vergen_lib::constants::CARGO_DEPENDENCIES;
23use vergen_lib::{
24    AddEntries, CargoRerunIfChanged, CargoRustcEnvMap, CargoWarning, DefaultConfig, VergenKey,
25    add_default_map_entry, add_map_entry,
26    constants::{CARGO_DEBUG, CARGO_FEATURES, CARGO_OPT_LEVEL, CARGO_TARGET_TRIPLE},
27};
28
29/// Configure the emission of `VERGEN_CARGO_*` instructions
30///
31/// **NOTE** - All cargo instructions are considered deterministic.  If you change
32/// the version of cargo you are compiling with, these values should change if
33/// being used in the generated binary.
34///
35/// | Variable | Sample |
36/// | -------  | ------ |
37/// | `VERGEN_CARGO_DEBUG` | true |
38/// | `VERGEN_CARGO_FEATURES` | git,build |
39/// | `VERGEN_CARGO_OPT_LEVEL` | 1 |
40/// | `VERGEN_CARGO_TARGET_TRIPLE` | x86_64-unknown-linux-gnu |
41///
42/// # Example
43/// Emit all of the cargo instructions
44///
45/// ```
46/// # use anyhow::Result;
47/// # use std::env;
48/// # use vergen::Emitter;
49/// # use vergen::Cargo;
50/// #
51/// fn main() -> Result<()> {
52///     temp_env::with_vars([
53///         ("CARGO_FEATURE_BUILD", Some("")),
54///         ("DEBUG", Some("true")),
55///         ("OPT_LEVEL", Some("1")),
56///         ("TARGET", Some("x86_64-unknown-linux-gnu"))
57///     ], || {
58/// #        let result = || -> Result<()> {
59///         let cargo = Cargo::all_cargo();
60///         Emitter::default().add_instructions(&cargo)?.emit()?;
61/// #        Ok(())
62/// #        }();
63///     });
64/// #    Ok(())
65/// # }
66/// ```
67/// Emit some of the cargo instructions
68///
69/// ```
70/// # use anyhow::Result;
71/// # use vergen::Emitter;
72/// # use vergen::Cargo;
73/// #
74/// # fn main() -> Result<()> {
75///     temp_env::with_vars([
76///         ("OPT_LEVEL", Some("1")),
77///         ("TARGET", Some("x86_64-unknown-linux-gnu"))
78///     ], || {
79/// #        let result = || -> Result<()> {
80///         let cargo = Cargo::builder().opt_level(true).build();
81///         Emitter::default().add_instructions(&cargo)?.emit()?;
82/// #        Ok(())
83/// #        }();
84///     });
85/// #    Ok(())
86/// # }
87/// ```
88///
89/// Override output with your own value
90///
91/// ```
92/// # use anyhow::Result;
93/// # use std::env;
94/// # use vergen::Emitter;
95/// # use vergen::Cargo;
96/// #
97/// fn main() -> Result<()> {
98///     temp_env::with_vars([
99///         ("CARGO_FEATURE_BUILD", Some("")),
100///         ("VERGEN_CARGO_DEBUG", Some("my own debug value")),
101///         ("OPT_LEVEL", Some("1")),
102///         ("TARGET", Some("x86_64-unknown-linux-gnu"))
103///      ], || {
104/// #        let result = || -> Result<()> {
105///          let cargo = Cargo::all_cargo();
106///          Emitter::default().add_instructions(&cargo)?.emit()?;
107/// #        Ok(())
108/// #        }();
109///      });
110/// #     Ok(())
111/// # }
112/// ```
113///
114#[derive(Builder, Clone, Copy, Debug, PartialEq)]
115#[allow(clippy::struct_excessive_bools)]
116pub struct Cargo {
117    /// Configures the default values.
118    /// If set to `true` all defaults are in "enabled" state.
119    /// If set to `false` all defaults are in "disabled" state.
120    #[builder(field)]
121    all: bool,
122    /// Emit the DEBUG value set by cargo
123    ///
124    /// ```text
125    /// cargo:rustc-env=VERGEN_CARGO_DEBUG=true|false
126    /// ```
127    ///
128    #[builder(default = all)]
129    debug: bool,
130    /// Emit the `CARGO_FEATURE_*` values set by cargo
131    ///
132    /// ```text
133    /// cargo:rustc-env=VERGEN_CARGO_FEATURES=<features>
134    /// ```
135    ///
136    #[builder(default = all)]
137    features: bool,
138    /// Emit the `OPT_LEVEL` value set by cargo
139    ///
140    /// ```text
141    /// cargo:rustc-env=VERGEN_CARGO_OPT_LEVEL=<opt_level>
142    /// ```
143    ///
144    #[builder(default = all)]
145    opt_level: bool,
146    /// Emit the TARGET value set by cargo
147    ///
148    /// ```text
149    /// cargo:rustc-env=VERGEN_CARGO_TARGET_TRIPLE=<target_triple>
150    /// ```
151    ///
152    #[builder(default = all)]
153    target_triple: bool,
154    /// Emit the dependencies value derived from `Cargo.toml`
155    ///
156    /// ```text
157    /// cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=<dependencies>
158    /// ```
159    ///
160    #[cfg(feature = "cargo_metadata")]
161    #[builder(default = all)]
162    dependencies: bool,
163    /// Add a name [`Regex`](regex::Regex) filter for cargo dependencies
164    ///
165    /// ```text
166    /// cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=<deps_filtered_by_name>
167    /// ```
168    ///
169    #[cfg(feature = "cargo_metadata")]
170    #[builder(into)]
171    name_filter: Option<&'static str>,
172    /// Add a [`DependencyKind`](cargo_metadata::DependencyKind) filter for cargo dependencies
173    ///
174    /// ```text
175    /// cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=<deps_filtered_by_kind>
176    /// ```
177    ///
178    #[cfg(feature = "cargo_metadata")]
179    #[builder(into)]
180    dep_kind_filter: Option<DependencyKind>,
181}
182
183impl<S: cargo_builder::State> CargoBuilder<S> {
184    /// Convenience method that switches the defaults of [`CargoBuilder`]
185    /// to enable all of the `VERGEN_CARGO_*` instructions. It can only be
186    /// called at the start of the building process, i.e. when no config
187    /// has been set yet to avoid overwrites.
188    fn all(mut self) -> Self {
189        self.all = true;
190        self
191    }
192}
193
194impl Cargo {
195    /// Emit all of the `VERGEN_CARGO_*` instructions
196    #[must_use]
197    pub fn all_cargo() -> Self {
198        Self::builder().all().build()
199    }
200
201    /// Emit all of the `VERGEN_CARGO_*` instructions and return the builder
202    #[cfg(not(feature = "cargo_metadata"))]
203    pub fn all_cargo_builder() -> CargoBuilder<SetTargetTriple<SetOptLevel<SetFeatures<SetDebug>>>>
204    {
205        Self::builder()
206            .debug(true)
207            .features(true)
208            .opt_level(true)
209            .target_triple(true)
210    }
211
212    /// Emit all of the `VERGEN_CARGO_*` instructions and return the builder
213    #[cfg(feature = "cargo_metadata")]
214    pub fn all_cargo_builder()
215    -> CargoBuilder<SetDependencies<SetTargetTriple<SetOptLevel<SetFeatures<SetDebug>>>>> {
216        Self::builder()
217            .debug(true)
218            .features(true)
219            .opt_level(true)
220            .target_triple(true)
221            .dependencies(true)
222    }
223
224    #[cfg(not(feature = "cargo_metadata"))]
225    fn any(self) -> bool {
226        self.debug || self.features || self.opt_level || self.target_triple
227    }
228
229    #[cfg(feature = "cargo_metadata")]
230    fn any(self) -> bool {
231        self.debug || self.features || self.opt_level || self.target_triple || self.dependencies
232    }
233
234    fn is_cargo_feature(var: (String, String)) -> Option<String> {
235        let (k, _) = var;
236        if k.starts_with("CARGO_FEATURE_") {
237            Some(k.replace("CARGO_FEATURE_", "").to_lowercase())
238        } else {
239            None
240        }
241    }
242
243    #[cfg(feature = "cargo_metadata")]
244    fn get_dependencies(
245        name_filter: Option<&'static str>,
246        dep_kind_filter: Option<DependencyKind>,
247    ) -> Result<String> {
248        let metadata = MetadataCommand::new().exec()?;
249        let resolved_crates = metadata.resolve.ok_or_else(|| anyhow!("No resolve"))?;
250        let root_id = resolved_crates.root.ok_or_else(|| anyhow!("No root id"))?;
251        let root = resolved_crates
252            .nodes
253            .into_iter()
254            .find(|node| node.id == root_id)
255            .ok_or_else(|| anyhow!("No root node"))?;
256        let package_ids: Vec<(PackageId, Vec<DepKindInfo>)> = root
257            .deps
258            .into_iter()
259            .map(|node_dep| (node_dep.pkg, node_dep.dep_kinds))
260            .collect();
261
262        let packages: Vec<(&Package, &Vec<DepKindInfo>)> = package_ids
263            .iter()
264            .filter_map(|(package_id, dep_kinds)| {
265                metadata
266                    .packages
267                    .iter()
268                    .find(|&package| package.id == *package_id)
269                    .map(|package| (package, dep_kinds))
270            })
271            .collect();
272
273        let regex_opt = if let Some(name_regex) = name_filter {
274            Regex::new(name_regex).ok()
275        } else {
276            None
277        };
278        let results: Vec<String> = packages
279            .iter()
280            .filter_map(|(package, dep_kind_info)| {
281                if let Some(regex) = &regex_opt {
282                    if regex.is_match(&package.name) {
283                        Some((package, dep_kind_info))
284                    } else {
285                        None
286                    }
287                } else {
288                    Some((package, dep_kind_info))
289                }
290            })
291            .filter_map(|(package, dep_kind_info)| {
292                if let Some(dep_kind_filter) = dep_kind_filter {
293                    let kinds: Vec<DependencyKind> = dep_kind_info
294                        .iter()
295                        .map(|dep_kind_info| dep_kind_info.kind)
296                        .collect();
297                    if kinds.contains(&dep_kind_filter) {
298                        Some(package)
299                    } else {
300                        None
301                    }
302                } else {
303                    Some(package)
304                }
305            })
306            .map(|package| format!("{} {}", package.name, package.version))
307            .collect();
308        Ok(results.join(","))
309    }
310
311    /// Add a name [`Regex`](regex::Regex) filter for cargo dependencies
312    ///
313    /// ```text
314    /// cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=<deps_filtered_by_name>
315    /// ```
316    ///
317    #[cfg(feature = "cargo_metadata")]
318    pub fn set_name_filter(&mut self, val: Option<&'static str>) -> &mut Self {
319        self.name_filter = val;
320        self
321    }
322    /// Add a [`DependencyKind`](cargo_metadata::DependencyKind) filter for cargo dependencies
323    ///
324    /// ```text
325    /// cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=<deps_filtered_by_kind>
326    /// ```
327    ///
328    #[cfg(feature = "cargo_metadata")]
329    pub fn set_dep_kind_filter(&mut self, val: Option<DependencyKind>) -> &mut Self {
330        self.dep_kind_filter = val;
331        self
332    }
333
334    #[cfg(not(feature = "cargo_metadata"))]
335    #[allow(
336        clippy::unnecessary_wraps,
337        clippy::unused_self,
338        clippy::trivially_copy_pass_by_ref
339    )]
340    fn add_dependencies(&self, _cargo_rustc_env: &mut CargoRustcEnvMap) -> Result<()> {
341        Ok(())
342    }
343
344    #[cfg(feature = "cargo_metadata")]
345    fn add_dependencies(&self, cargo_rustc_env: &mut CargoRustcEnvMap) -> Result<()> {
346        if self.dependencies {
347            if let Ok(value) = env::var(CARGO_DEPENDENCIES) {
348                add_map_entry(VergenKey::CargoDependencies, value, cargo_rustc_env);
349            } else {
350                let value = Self::get_dependencies(self.name_filter, self.dep_kind_filter)?;
351                if !value.is_empty() {
352                    add_map_entry(VergenKey::CargoDependencies, value, cargo_rustc_env);
353                }
354            }
355        }
356        Ok(())
357    }
358
359    #[cfg(not(feature = "cargo_metadata"))]
360    #[allow(clippy::unused_self, clippy::trivially_copy_pass_by_ref)]
361    fn add_default_dependencies(
362        &self,
363        _config: &DefaultConfig,
364        _cargo_rustc_env_map: &mut CargoRustcEnvMap,
365        _cargo_warning: &mut CargoWarning,
366    ) {
367    }
368
369    #[cfg(feature = "cargo_metadata")]
370    fn add_default_dependencies(
371        &self,
372        config: &DefaultConfig,
373        cargo_rustc_env_map: &mut CargoRustcEnvMap,
374        cargo_warning: &mut CargoWarning,
375    ) {
376        if self.dependencies {
377            add_default_map_entry(
378                *config.idempotent(),
379                VergenKey::CargoDependencies,
380                cargo_rustc_env_map,
381                cargo_warning,
382            );
383        }
384    }
385}
386
387impl AddEntries for Cargo {
388    fn add_map_entries(
389        &self,
390        _idempotent: bool,
391        cargo_rustc_env: &mut CargoRustcEnvMap,
392        _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
393        _cargo_warning: &mut CargoWarning,
394    ) -> Result<()> {
395        if self.any() {
396            if self.debug {
397                if let Ok(value) = env::var(CARGO_DEBUG) {
398                    add_map_entry(VergenKey::CargoDebug, value, cargo_rustc_env);
399                } else {
400                    add_map_entry(VergenKey::CargoDebug, env::var("DEBUG")?, cargo_rustc_env);
401                }
402            }
403
404            if self.features {
405                if let Ok(value) = env::var(CARGO_FEATURES) {
406                    add_map_entry(VergenKey::CargoFeatures, value, cargo_rustc_env);
407                } else {
408                    let features: Vec<String> =
409                        env::vars().filter_map(Self::is_cargo_feature).collect();
410                    let feature_str = features.as_slice().join(",");
411                    add_map_entry(VergenKey::CargoFeatures, feature_str, cargo_rustc_env);
412                }
413            }
414
415            if self.opt_level {
416                if let Ok(value) = env::var(CARGO_OPT_LEVEL) {
417                    add_map_entry(VergenKey::CargoOptLevel, value, cargo_rustc_env);
418                } else {
419                    add_map_entry(
420                        VergenKey::CargoOptLevel,
421                        env::var("OPT_LEVEL")?,
422                        cargo_rustc_env,
423                    );
424                }
425            }
426
427            if self.target_triple {
428                if let Ok(value) = env::var(CARGO_TARGET_TRIPLE) {
429                    add_map_entry(VergenKey::CargoTargetTriple, value, cargo_rustc_env);
430                } else {
431                    add_map_entry(
432                        VergenKey::CargoTargetTriple,
433                        env::var("TARGET")?,
434                        cargo_rustc_env,
435                    );
436                }
437            }
438
439            self.add_dependencies(cargo_rustc_env)?;
440        }
441        Ok(())
442    }
443
444    fn add_default_entries(
445        &self,
446        config: &DefaultConfig,
447        cargo_rustc_env_map: &mut CargoRustcEnvMap,
448        _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
449        cargo_warning: &mut CargoWarning,
450    ) -> Result<()> {
451        if *config.fail_on_error() {
452            let error = Error::msg(format!("{:?}", config.error()));
453            Err(error)
454        } else {
455            if self.debug {
456                add_default_map_entry(
457                    *config.idempotent(),
458                    VergenKey::CargoDebug,
459                    cargo_rustc_env_map,
460                    cargo_warning,
461                );
462            }
463            if self.features {
464                add_default_map_entry(
465                    *config.idempotent(),
466                    VergenKey::CargoFeatures,
467                    cargo_rustc_env_map,
468                    cargo_warning,
469                );
470            }
471            if self.opt_level {
472                add_default_map_entry(
473                    *config.idempotent(),
474                    VergenKey::CargoOptLevel,
475                    cargo_rustc_env_map,
476                    cargo_warning,
477                );
478            }
479            if self.target_triple {
480                add_default_map_entry(
481                    *config.idempotent(),
482                    VergenKey::CargoTargetTriple,
483                    cargo_rustc_env_map,
484                    cargo_warning,
485                );
486            }
487            self.add_default_dependencies(config, cargo_rustc_env_map, cargo_warning);
488            Ok(())
489        }
490    }
491}
492
493#[cfg(test)]
494mod test {
495    use super::Cargo;
496    use crate::Emitter;
497    use anyhow::Result;
498    use serial_test::serial;
499    use std::io::Write;
500    use test_util::{with_cargo_vars, with_cargo_vars_ext};
501    use vergen_lib::count_idempotent;
502
503    #[test]
504    #[serial]
505    #[allow(clippy::clone_on_copy, clippy::redundant_clone)]
506    fn cargo_clone_works() -> Result<()> {
507        let cargo = Cargo::all_cargo();
508        let another = cargo.clone();
509        assert_eq!(another, cargo);
510        Ok(())
511    }
512
513    #[test]
514    #[serial]
515    fn cargo_debug_works() -> Result<()> {
516        let cargo = Cargo::all_cargo();
517        let mut buf = vec![];
518        write!(buf, "{cargo:?}")?;
519        assert!(!buf.is_empty());
520        Ok(())
521    }
522
523    #[test]
524    #[serial]
525    fn cargo_default() -> Result<()> {
526        let cargo = Cargo::builder().build();
527        let emitter = Emitter::default().add_instructions(&cargo)?.test_emit();
528        assert_eq!(0, emitter.cargo_rustc_env_map().len());
529        assert_eq!(0, count_idempotent(emitter.cargo_rustc_env_map()));
530        assert_eq!(0, emitter.cargo_warning().len());
531        Ok(())
532    }
533
534    #[test]
535    #[serial]
536    fn all_idempotent() {
537        let result = with_cargo_vars(|| {
538            let cargo = Cargo::all_cargo();
539            let config = Emitter::default()
540                .idempotent()
541                .add_instructions(&cargo)?
542                .test_emit();
543            #[cfg(feature = "cargo_metadata")]
544            assert_eq!(5, config.cargo_rustc_env_map().len());
545            #[cfg(not(feature = "cargo_metadata"))]
546            assert_eq!(4, config.cargo_rustc_env_map().len());
547            assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
548            assert_eq!(0, config.cargo_warning().len());
549            Ok(())
550        });
551        assert!(result.is_ok());
552    }
553
554    #[test]
555    #[serial]
556    fn all() {
557        let result = with_cargo_vars(|| {
558            let cargo = Cargo::all_cargo();
559            let config = Emitter::default().add_instructions(&cargo)?.test_emit();
560            #[cfg(feature = "cargo_metadata")]
561            assert_eq!(5, config.cargo_rustc_env_map().len());
562            #[cfg(not(feature = "cargo_metadata"))]
563            assert_eq!(4, config.cargo_rustc_env_map().len());
564            assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
565            assert_eq!(0, config.cargo_warning().len());
566            Ok(())
567        });
568        assert!(result.is_ok());
569    }
570
571    #[test]
572    #[serial]
573    fn debug() {
574        let result = with_cargo_vars(|| {
575            let cargo = Cargo::builder().debug(true).build();
576            let config = Emitter::default().add_instructions(&cargo)?.test_emit();
577            assert_eq!(1, config.cargo_rustc_env_map().len());
578            assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
579            assert_eq!(0, config.cargo_warning().len());
580            Ok(())
581        });
582        assert!(result.is_ok());
583    }
584
585    #[test]
586    #[serial]
587    fn features() {
588        let result = with_cargo_vars(|| {
589            let cargo = Cargo::builder().features(true).build();
590            let config = Emitter::default().add_instructions(&cargo)?.test_emit();
591            assert_eq!(1, config.cargo_rustc_env_map().len());
592            assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
593            assert_eq!(0, config.cargo_warning().len());
594            Ok(())
595        });
596        assert!(result.is_ok());
597    }
598
599    #[test]
600    #[serial]
601    fn opt_level() {
602        let result = with_cargo_vars(|| {
603            let cargo = Cargo::builder().opt_level(true).build();
604            let config = Emitter::default().add_instructions(&cargo)?.test_emit();
605            assert_eq!(1, config.cargo_rustc_env_map().len());
606            assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
607            assert_eq!(0, config.cargo_warning().len());
608            Ok(())
609        });
610        assert!(result.is_ok());
611    }
612
613    #[test]
614    #[serial]
615    fn target_triple() {
616        let result = with_cargo_vars(|| {
617            let cargo = Cargo::builder().target_triple(true).build();
618            let config = Emitter::default().add_instructions(&cargo)?.test_emit();
619            assert_eq!(1, config.cargo_rustc_env_map().len());
620            assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
621            assert_eq!(0, config.cargo_warning().len());
622            Ok(())
623        });
624        assert!(result.is_ok());
625    }
626
627    #[test]
628    #[serial]
629    #[cfg(feature = "cargo_metadata")]
630    fn dependencies() {
631        let result = with_cargo_vars(|| {
632            let cargo = Cargo::builder()
633                .dependencies(true)
634                .name_filter("anyhow")
635                .build();
636            let config = Emitter::default().add_instructions(&cargo)?.test_emit();
637            assert_eq!(1, config.cargo_rustc_env_map().len());
638            assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
639            assert_eq!(0, config.cargo_warning().len());
640            Ok(())
641        });
642        assert!(result.is_ok());
643    }
644
645    #[test]
646    #[serial]
647    #[cfg(feature = "cargo_metadata")]
648    fn dependencies_bad_name_filter() {
649        let result = with_cargo_vars(|| {
650            let cargo = Cargo::builder().dependencies(true).name_filter("(").build();
651            let config = Emitter::default().add_instructions(&cargo)?.test_emit();
652            assert_eq!(1, config.cargo_rustc_env_map().len());
653            assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
654            assert_eq!(0, config.cargo_warning().len());
655            Ok(())
656        });
657        assert!(result.is_ok());
658    }
659
660    #[test]
661    #[serial]
662    fn bad_env_fails() -> Result<()> {
663        let cargo = Cargo::all_cargo();
664        assert!(
665            Emitter::default()
666                .fail_on_error()
667                .add_instructions(&cargo)
668                .is_err()
669        );
670        Ok(())
671    }
672
673    #[test]
674    #[serial]
675    fn bad_env_emits_warnings() -> Result<()> {
676        let cargo = Cargo::all_cargo();
677        let config = Emitter::default().add_instructions(&cargo)?.test_emit();
678        assert_eq!(0, config.cargo_rustc_env_map().len());
679        assert_eq!(0, count_idempotent(config.cargo_rustc_env_map()));
680        #[cfg(feature = "cargo_metadata")]
681        assert_eq!(5, config.cargo_warning().len());
682        #[cfg(not(feature = "cargo_metadata"))]
683        assert_eq!(4, config.cargo_warning().len());
684        Ok(())
685    }
686
687    #[test]
688    #[serial]
689    fn bad_env_emits_idempotent() -> Result<()> {
690        let cargo = Cargo::all_cargo();
691        let config = Emitter::default()
692            .idempotent()
693            .add_instructions(&cargo)?
694            .test_emit();
695        #[cfg(feature = "cargo_metadata")]
696        assert_eq!(5, config.cargo_rustc_env_map().len());
697        #[cfg(not(feature = "cargo_metadata"))]
698        assert_eq!(4, config.cargo_rustc_env_map().len());
699        #[cfg(feature = "cargo_metadata")]
700        assert_eq!(5, count_idempotent(config.cargo_rustc_env_map()));
701        #[cfg(not(feature = "cargo_metadata"))]
702        assert_eq!(4, count_idempotent(config.cargo_rustc_env_map()));
703        #[cfg(feature = "cargo_metadata")]
704        assert_eq!(5, config.cargo_warning().len());
705        #[cfg(not(feature = "cargo_metadata"))]
706        assert_eq!(4, config.cargo_warning().len());
707        Ok(())
708    }
709
710    #[test]
711    #[serial]
712    fn cargo_debug_override_works() {
713        let result = with_cargo_vars_ext(
714            &[("VERGEN_CARGO_DEBUG", Some("this is a bad date"))],
715            || {
716                let mut stdout_buf = vec![];
717                let cargo = Cargo::all_cargo();
718                assert!(
719                    Emitter::default()
720                        .add_instructions(&cargo)?
721                        .emit_to(&mut stdout_buf)
722                        .is_ok()
723                );
724                let output = String::from_utf8_lossy(&stdout_buf);
725                assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_DEBUG=this is a bad date"));
726                Ok(())
727            },
728        );
729        assert!(result.is_ok());
730    }
731
732    #[test]
733    #[serial]
734    fn cargo_features_override_works() {
735        let result = with_cargo_vars_ext(
736            &[("VERGEN_CARGO_FEATURES", Some("this is a bad date"))],
737            || {
738                let mut stdout_buf = vec![];
739                let cargo = Cargo::all_cargo();
740                assert!(
741                    Emitter::default()
742                        .add_instructions(&cargo)?
743                        .emit_to(&mut stdout_buf)
744                        .is_ok()
745                );
746                let output = String::from_utf8_lossy(&stdout_buf);
747                assert!(
748                    output.contains("cargo:rustc-env=VERGEN_CARGO_FEATURES=this is a bad date")
749                );
750                Ok(())
751            },
752        );
753        assert!(result.is_ok());
754    }
755
756    #[test]
757    #[serial]
758    fn cargo_opt_level_override_works() {
759        let result = with_cargo_vars_ext(
760            &[("VERGEN_CARGO_OPT_LEVEL", Some("this is a bad date"))],
761            || {
762                let mut stdout_buf = vec![];
763                let cargo = Cargo::all_cargo();
764                assert!(
765                    Emitter::default()
766                        .add_instructions(&cargo)?
767                        .emit_to(&mut stdout_buf)
768                        .is_ok()
769                );
770                let output = String::from_utf8_lossy(&stdout_buf);
771                assert!(
772                    output.contains("cargo:rustc-env=VERGEN_CARGO_OPT_LEVEL=this is a bad date")
773                );
774                Ok(())
775            },
776        );
777        assert!(result.is_ok());
778    }
779
780    #[test]
781    #[serial]
782    fn cargo_target_triple_override_works() {
783        let result = with_cargo_vars_ext(
784            &[("VERGEN_CARGO_TARGET_TRIPLE", Some("this is a bad date"))],
785            || {
786                let mut stdout_buf = vec![];
787                let cargo = Cargo::all_cargo();
788                assert!(
789                    Emitter::default()
790                        .add_instructions(&cargo)?
791                        .emit_to(&mut stdout_buf)
792                        .is_ok()
793                );
794                let output = String::from_utf8_lossy(&stdout_buf);
795                assert!(
796                    output
797                        .contains("cargo:rustc-env=VERGEN_CARGO_TARGET_TRIPLE=this is a bad date")
798                );
799                Ok(())
800            },
801        );
802        assert!(result.is_ok());
803    }
804
805    #[test]
806    #[serial]
807    #[cfg(feature = "cargo_metadata")]
808    fn cargo_dependencies_override_works() {
809        let result = with_cargo_vars_ext(
810            &[("VERGEN_CARGO_DEPENDENCIES", Some("this is a bad date"))],
811            || {
812                let mut stdout_buf = vec![];
813                let cargo = Cargo::all_cargo();
814                assert!(
815                    Emitter::default()
816                        .add_instructions(&cargo)?
817                        .emit_to(&mut stdout_buf)
818                        .is_ok()
819                );
820                let output = String::from_utf8_lossy(&stdout_buf);
821                assert!(
822                    output.contains("cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=this is a bad date")
823                );
824                Ok(())
825            },
826        );
827        assert!(result.is_ok());
828    }
829}