Skip to main content

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