]> git.lizzy.rs Git - rust.git/blob - src/config.rs
struct_lit_multiline_style -> struct_lit_single_line (and make it a bool)
[rust.git] / src / config.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 extern crate toml;
12
13 use std::{env, fs};
14 use std::cell::Cell;
15 use std::fs::File;
16 use std::io::{Error, ErrorKind, Read};
17 use std::path::{Path, PathBuf};
18
19 use file_lines::FileLines;
20 use lists::{ListTactic, SeparatorPlace, SeparatorTactic};
21 use Summary;
22
23
24 macro_rules! is_nightly_channel {
25     () => {
26     env::var("CFG_RELEASE_CHANNEL")
27         .map(|c| c == "nightly")
28         .unwrap_or(false)
29     }
30 }
31
32 macro_rules! configuration_option_enum{
33     ($e:ident: $( $x:ident ),+ $(,)*) => {
34         #[derive(Copy, Clone, Eq, PartialEq, Debug)]
35         pub enum $e {
36             $( $x ),+
37         }
38
39         impl_enum_serialize_and_deserialize!($e, $( $x ),+);
40     }
41 }
42
43 configuration_option_enum! { NewlineStyle:
44     Windows, // \r\n
45     Unix, // \n
46     Native, // \r\n in Windows, \n on other platforms
47 }
48
49 configuration_option_enum! { BraceStyle:
50     AlwaysNextLine,
51     PreferSameLine,
52     // Prefer same line except where there is a where clause, in which case force
53     // the brace to the next line.
54     SameLineWhere,
55 }
56
57 configuration_option_enum! { ControlBraceStyle:
58     // K&R style, Rust community default
59     AlwaysSameLine,
60     // Stroustrup style
61     ClosingNextLine,
62     // Allman style
63     AlwaysNextLine,
64 }
65
66 configuration_option_enum! { IndentStyle:
67     // First line on the same line as the opening brace, all lines aligned with
68     // the first line.
69     Visual,
70     // First line is on a new line and all lines align with block indent.
71     Block,
72 }
73
74 configuration_option_enum! { Density:
75     // Fit as much on one line as possible.
76     Compressed,
77     // Use more lines.
78     Tall,
79     // Try to compress if the body is empty.
80     CompressedIfEmpty,
81     // Place every item on a separate line.
82     Vertical,
83 }
84
85 configuration_option_enum! { TypeDensity:
86     // No spaces around "=" and "+"
87     Compressed,
88     // Spaces around " = " and " + "
89     Wide,
90 }
91
92
93 impl Density {
94     pub fn to_list_tactic(self) -> ListTactic {
95         match self {
96             Density::Compressed => ListTactic::Mixed,
97             Density::Tall | Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
98             Density::Vertical => ListTactic::Vertical,
99         }
100     }
101 }
102
103 configuration_option_enum! { ReportTactic:
104     Always,
105     Unnumbered,
106     Never,
107 }
108
109 configuration_option_enum! { WriteMode:
110     // Backs the original file up and overwrites the original.
111     Replace,
112     // Overwrites original file without backup.
113     Overwrite,
114     // Writes the output to stdout.
115     Display,
116     // Writes the diff to stdout.
117     Diff,
118     // Displays how much of the input file was processed
119     Coverage,
120     // Unfancy stdout
121     Plain,
122     // Outputs a checkstyle XML file.
123     Checkstyle,
124 }
125
126 configuration_option_enum! { Color:
127     // Always use color, whether it is a piped or terminal output
128     Always,
129     // Never use color
130     Never,
131     // Automatically use color, if supported by terminal
132     Auto,
133 }
134
135 /// Trait for types that can be used in `Config`.
136 pub trait ConfigType: Sized {
137     /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
138     /// pipe-separated list of variants; for other types it returns "<type>".
139     fn doc_hint() -> String;
140 }
141
142 impl ConfigType for bool {
143     fn doc_hint() -> String {
144         String::from("<boolean>")
145     }
146 }
147
148 impl ConfigType for usize {
149     fn doc_hint() -> String {
150         String::from("<unsigned integer>")
151     }
152 }
153
154 impl ConfigType for isize {
155     fn doc_hint() -> String {
156         String::from("<signed integer>")
157     }
158 }
159
160 impl ConfigType for String {
161     fn doc_hint() -> String {
162         String::from("<string>")
163     }
164 }
165
166 impl ConfigType for FileLines {
167     fn doc_hint() -> String {
168         String::from("<json>")
169     }
170 }
171
172 pub struct ConfigHelpItem {
173     option_name: &'static str,
174     doc_string: &'static str,
175     variant_names: String,
176     default: &'static str,
177 }
178
179 impl ConfigHelpItem {
180     pub fn option_name(&self) -> &'static str {
181         self.option_name
182     }
183
184     pub fn doc_string(&self) -> &'static str {
185         self.doc_string
186     }
187
188     pub fn variant_names(&self) -> &String {
189         &self.variant_names
190     }
191
192     pub fn default(&self) -> &'static str {
193         self.default
194     }
195 }
196
197 macro_rules! create_config {
198     ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
199         #[derive(Clone)]
200         pub struct Config {
201             // For each config item, we store a bool indicating whether it has
202             // been accessed and the value, and a bool whether the option was
203             // manually initialised, or taken from the default,
204             $($i: (Cell<bool>, bool, $ty, bool)),+
205         }
206
207         // Just like the Config struct but with each property wrapped
208         // as Option<T>. This is used to parse a rustfmt.toml that doesn't
209         // specify all properties of `Config`.
210         // We first parse into `PartialConfig`, then create a default `Config`
211         // and overwrite the properties with corresponding values from `PartialConfig`.
212         #[derive(Deserialize, Serialize, Clone)]
213         pub struct PartialConfig {
214             $(pub $i: Option<$ty>),+
215         }
216
217         impl PartialConfig {
218             pub fn to_toml(&self) -> Result<String, String> {
219                 // file_lines can't be specified in TOML
220                 let mut cloned = self.clone();
221                 cloned.file_lines = None;
222
223                 toml::to_string(&cloned)
224                     .map_err(|e| format!("Could not output config: {}", e.to_string()))
225             }
226         }
227
228         // Macro hygiene won't allow us to make `set_$i()` methods on Config
229         // for each item, so this struct is used to give the API to set values:
230         // `config.get().option(false)`. It's pretty ugly. Consider replacing
231         // with `config.set_option(false)` if we ever get a stable/usable
232         // `concat_idents!()`.
233         pub struct ConfigSetter<'a>(&'a mut Config);
234
235         impl<'a> ConfigSetter<'a> {
236             $(
237             pub fn $i(&mut self, value: $ty) {
238                 (self.0).$i.2 = value;
239             }
240             )+
241         }
242
243         // Query each option, returns true if the user set the option, false if
244         // a default was used.
245         pub struct ConfigWasSet<'a>(&'a Config);
246
247         impl<'a> ConfigWasSet<'a> {
248             $(
249             pub fn $i(&self) -> bool {
250                 (self.0).$i.1
251             }
252             )+
253         }
254
255         impl Config {
256             pub fn version_meets_requirement(&self, error_summary: &mut Summary) -> bool {
257                 if self.was_set().required_version() {
258                     let version = env!("CARGO_PKG_VERSION");
259                     let required_version = self.required_version();
260                     if version != required_version {
261                         println!(
262                             "Error: rustfmt version ({}) doesn't match the required version ({})",
263                             version,
264                             required_version,
265                         );
266                         error_summary.add_formatting_error();
267                         return false;
268                     }
269                 }
270
271                 true
272             }
273
274             $(
275             pub fn $i(&self) -> $ty {
276                 self.$i.0.set(true);
277                 self.$i.2.clone()
278             }
279             )+
280
281             pub fn set<'a>(&'a mut self) -> ConfigSetter<'a> {
282                 ConfigSetter(self)
283             }
284
285             pub fn was_set<'a>(&'a self) -> ConfigWasSet<'a> {
286                 ConfigWasSet(self)
287             }
288
289             fn fill_from_parsed_config(mut self, parsed: PartialConfig) -> Config {
290             $(
291                 if let Some(val) = parsed.$i {
292                     if !self.$i.3 {
293                         self.$i.1 = true;
294                         self.$i.2 = val;
295                     } else {
296                         if is_nightly_channel!() {
297                             self.$i.1 = true;
298                             self.$i.2 = val;
299                         } else {
300                             println!("Warning: can't set some features as unstable \
301                                     features are only available in nightly channel.");
302                         }
303                     }
304                 }
305             )+
306                 self
307             }
308
309             pub fn from_toml(toml: &str) -> Result<Config, String> {
310                 let parsed: toml::Value =
311                     toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
312                 let mut err: String = String::new();
313                 {
314                     let table = parsed
315                         .as_table()
316                         .ok_or(String::from("Parsed config was not table"))?;
317                     for key in table.keys() {
318                         match &**key {
319                             $(
320                                 stringify!($i) => (),
321                             )+
322                                 _ => {
323                                     let msg =
324                                         &format!("Warning: Unknown configuration option `{}`\n",
325                                                  key);
326                                     err.push_str(msg)
327                                 }
328                         }
329                     }
330                 }
331                 match parsed.try_into() {
332                     Ok(parsed_config) =>
333                         Ok(Config::default().fill_from_parsed_config(parsed_config)),
334                     Err(e) => {
335                         err.push_str("Error: Decoding config file failed:\n");
336                         err.push_str(format!("{}\n", e).as_str());
337                         err.push_str("Please check your config file.\n");
338                         Err(err)
339                     }
340                 }
341             }
342
343             pub fn used_options(&self) -> PartialConfig {
344                 PartialConfig {
345                     $(
346                         $i: if self.$i.0.get() {
347                                 Some(self.$i.2.clone())
348                             } else {
349                                 None
350                             },
351                     )+
352                 }
353             }
354
355             pub fn all_options(&self) -> PartialConfig {
356                 PartialConfig {
357                     $(
358                         $i: Some(self.$i.2.clone()),
359                     )+
360                 }
361             }
362
363             pub fn override_value(&mut self, key: &str, val: &str)
364             {
365                 match key {
366                     $(
367                         stringify!($i) => {
368                             self.$i.2 = val.parse::<$ty>()
369                                 .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
370                                                  stringify!($i),
371                                                  val,
372                                                  stringify!($ty)));
373                         }
374                     )+
375                     _ => panic!("Unknown config key in override: {}", key)
376                 }
377             }
378
379             /// Construct a `Config` from the toml file specified at `file_path`.
380             ///
381             /// This method only looks at the provided path, for a method that
382             /// searches parents for a `rustfmt.toml` see `from_resolved_toml_path`.
383             ///
384             /// Return a `Config` if the config could be read and parsed from
385             /// the file, Error otherwise.
386             pub fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
387                 let mut file = File::open(&file_path)?;
388                 let mut toml = String::new();
389                 file.read_to_string(&mut toml)?;
390                 Config::from_toml(&toml).map_err(|err| Error::new(ErrorKind::InvalidData, err))
391             }
392
393             /// Resolve the config for input in `dir`.
394             ///
395             /// Searches for `rustfmt.toml` beginning with `dir`, and
396             /// recursively checking parents of `dir` if no config file is found.
397             /// If no config file exists in `dir` or in any parent, a
398             /// default `Config` will be returned (and the returned path will be empty).
399             ///
400             /// Returns the `Config` to use, and the path of the project file if there was
401             /// one.
402             pub fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> {
403
404                 /// Try to find a project file in the given directory and its parents.
405                 /// Returns the path of a the nearest project file if one exists,
406                 /// or `None` if no project file was found.
407                 fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
408                     let mut current = if dir.is_relative() {
409                         env::current_dir()?.join(dir)
410                     } else {
411                         dir.to_path_buf()
412                     };
413
414                     current = fs::canonicalize(current)?;
415
416                     loop {
417                         match get_toml_path(&current) {
418                             Ok(Some(path)) => return Ok(Some(path)),
419                             Err(e) => return Err(e),
420                             _ => ()
421                         }
422
423                         // If the current directory has no parent, we're done searching.
424                         if !current.pop() {
425                             return Ok(None);
426                         }
427                     }
428                 }
429
430                 match resolve_project_file(dir)? {
431                     None => Ok((Config::default(), None)),
432                     Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))),
433                 }
434             }
435
436
437             pub fn print_docs() {
438                 use std::cmp;
439                 let max = 0;
440                 $( let max = cmp::max(max, stringify!($i).len()+1); )+
441                 let mut space_str = String::with_capacity(max);
442                 for _ in 0..max {
443                     space_str.push(' ');
444                 }
445                 println!("Configuration Options:");
446                 $(
447                     let name_raw = stringify!($i);
448                     let mut name_out = String::with_capacity(max);
449                     for _ in name_raw.len()..max-1 {
450                         name_out.push(' ')
451                     }
452                     name_out.push_str(name_raw);
453                     name_out.push(' ');
454                     println!("{}{} Default: {:?}",
455                              name_out,
456                              <$ty>::doc_hint(),
457                              $def);
458                     $(
459                         println!("{}{}", space_str, $dstring);
460                     )+
461                     println!();
462                 )+
463             }
464         }
465
466         // Template for the default configuration
467         impl Default for Config {
468             fn default() -> Config {
469                 Config {
470                     $(
471                         $i: (Cell::new(false), false, $def, $stb),
472                     )+
473                 }
474             }
475         }
476     )
477 }
478
479 /// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
480 ///
481 /// Return the path if a config file exists, empty if no file exists, and Error for IO errors
482 pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
483     const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
484     for config_file_name in &CONFIG_FILE_NAMES {
485         let config_file = dir.join(config_file_name);
486         match fs::metadata(&config_file) {
487             // Only return if it's a file to handle the unlikely situation of a directory named
488             // `rustfmt.toml`.
489             Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
490             // Return the error if it's something other than `NotFound`; otherwise we didn't
491             // find the project file yet, and continue searching.
492             Err(e) => if e.kind() != ErrorKind::NotFound {
493                 return Err(e);
494             },
495             _ => {}
496         }
497     }
498     Ok(None)
499 }
500
501
502
503 create_config! {
504     indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items.";
505     unstable_features: bool, false, true,
506             "Enables unstable features. Only available on nightly channel";
507     verbose: bool, false, false, "Use verbose output";
508     disable_all_formatting: bool, false, false, "Don't reformat anything";
509     skip_children: bool, false, false, "Don't reformat out of line modules";
510     file_lines: FileLines, FileLines::all(), false,
511         "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
512          via the --file-lines option";
513     max_width: usize, 100, false, "Maximum width of each line";
514     error_on_line_overflow: bool, true, false, "Error if unable to get all lines within max_width";
515     error_on_line_overflow_comments: bool, true, false,
516         "Error if unable to get comments within max_width";
517     tab_spaces: usize, 4, false, "Number of spaces per tab";
518     fn_call_width: usize, 60, false,
519         "Maximum width of the args of a function call before falling back to vertical formatting";
520     struct_lit_width: usize, 18, false,
521         "Maximum width in the body of a struct lit before falling back to vertical formatting";
522     struct_variant_width: usize, 35, false,
523         "Maximum width in the body of a struct variant before falling back to vertical formatting";
524     force_explicit_abi: bool, true, false, "Always print the abi for extern items";
525     newline_style: NewlineStyle, NewlineStyle::Unix, false, "Unix or Windows line endings";
526     brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
527     control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
528         "Brace style for control flow constructs";
529     impl_empty_single_line: bool, true, false, "Put empty-body implementations on a single line";
530     trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
531         "How to handle trailing commas for lists";
532     trailing_semicolon: bool, true, false,
533         "Add trailing semicolon after break, continue and return";
534     fn_empty_single_line: bool, true, false, "Put empty-body functions on a single line";
535     fn_single_line: bool, false, false, "Put single-expression functions on a single line";
536     fn_args_density: Density, Density::Tall, false, "Argument density in functions";
537     array_width: usize, 60, false,
538         "Maximum width of an array literal before falling back to vertical formatting";
539     array_horizontal_layout_threshold: usize, 0, false,
540         "How many elements array must have before rustfmt uses horizontal layout.";
541     type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
542         "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
543     // TODO:
544     // 1. Should we at least try to put the where clause on the same line as the rest of the
545     // function decl?
546     // 2. Currently options `Tall` and `Vertical` produce the same output.
547     where_density: Density, Density::Vertical, false, "Density of a where clause";
548     where_single_line: bool, false, false, "To force single line where layout";
549     where_layout: ListTactic, ListTactic::Vertical, false, "Element layout inside a where clause";
550     struct_lit_single_line: bool, true, false,
551         "Put small struct literals on a single line";
552     report_todo: ReportTactic, ReportTactic::Never, false,
553         "Report all, none or unnumbered occurrences of TODO in source file comments";
554     report_fixme: ReportTactic, ReportTactic::Never, false,
555         "Report all, none or unnumbered occurrences of FIXME in source file comments";
556     chain_width: usize, 60, false, "Maximum length of a chain to fit on a single line";
557     imports_indent: IndentStyle, IndentStyle::Visual, false, "Indent of imports";
558     imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
559     reorder_extern_crates: bool, true, false, "Reorder extern crate statements alphabetically";
560     reorder_extern_crates_in_group: bool, true, false, "Reorder extern crate statements in group";
561     reorder_imports: bool, false, false, "Reorder import statements alphabetically";
562     reorder_imports_in_group: bool, false, false, "Reorder import statements in group";
563     reorder_imported_names: bool, true, false,
564         "Reorder lists of names in import statements alphabetically";
565     single_line_if_else_max_width: usize, 50, false, "Maximum line length for single line if-else \
566                                                 expressions. A value of zero means always break \
567                                                 if-else expressions.";
568     format_strings: bool, false, false, "Format string literals where necessary";
569     hard_tabs: bool, false, false, "Use tab characters for indentation, spaces for alignment";
570     wrap_comments: bool, false, false, "Break comments to fit on the line";
571     comment_width: usize, 80, false,
572         "Maximum length of comments. No effect unless wrap_comments = true";
573     normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible";
574     wrap_match_arms: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
575                                   the same line with the pattern of arms";
576     match_block_trailing_comma: bool, false, false,
577         "Put a trailing comma after a block based match arm (non-block arms are not affected)";
578     match_arm_forces_newline: bool, false, false,
579         "Force match arm bodies to be in a new lines";
580     indent_match_arms: bool, true, false, "Indent match arms instead of keeping them at the same \
581                                     indentation level as the match keyword";
582     space_before_colon: bool, false, false, "Leave a space before the colon";
583     space_after_colon: bool, true, false, "Leave a space after the colon";
584     spaces_around_ranges: bool, false, false, "Put spaces around the  .. and ... range operators";
585     spaces_within_parens_and_brackets: bool, false, false,
586         "Put spaces within non-empty parentheses or brackets";
587     use_try_shorthand: bool, false, false, "Replace uses of the try! macro by the ? shorthand";
588     write_mode: WriteMode, WriteMode::Overwrite, false,
589         "What Write Mode to use when none is supplied: \
590          Replace, Overwrite, Display, Plain, Diff, Coverage";
591     color: Color, Color::Auto, false,
592         "What Color option to use when none is supplied: Always, Never, Auto";
593     condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
594                                               in tuple patterns";
595     combine_control_expr: bool, true, false, "Combine control expressions with function calls.";
596     struct_field_align_threshold: usize, 0, false, "Align struct fields if their diffs fits within \
597                                              threshold.";
598     remove_blank_lines_at_start_or_end_of_block: bool, true, false,
599         "Remove blank lines at start or end of a block";
600     same_line_attributes: bool, true, false,
601         "Try to put attributes on the same line as fields and variants.";
602     multiline_closure_forces_block: bool, false, false,
603         "Force multiline closure bodies to be wrapped in a block";
604     multiline_match_arm_forces_block: bool, false, false,
605         "Force multiline match arm bodies to be wrapped in a block";
606     merge_derives: bool, true, false, "Merge multiple `#[derive(...)]` into a single one";
607     binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
608         "Where to put a binary operator when a binary expression goes multiline.";
609     required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
610         "Require a specific version of rustfmt."
611 }
612
613 #[cfg(test)]
614 mod test {
615     use super::Config;
616
617     #[test]
618     fn test_config_set() {
619         let mut config = Config::default();
620         config.set().verbose(false);
621         assert_eq!(config.verbose(), false);
622         config.set().verbose(true);
623         assert_eq!(config.verbose(), true);
624     }
625
626     #[test]
627     fn test_config_used_to_toml() {
628         let config = Config::default();
629
630         let verbose = config.verbose();
631         let skip_children = config.skip_children();
632
633         let used_options = config.used_options();
634         let toml = used_options.to_toml().unwrap();
635         assert_eq!(
636             toml,
637             format!("verbose = {}\nskip_children = {}\n", verbose, skip_children)
638         );
639     }
640
641     #[test]
642     fn test_was_set() {
643         let config = Config::from_toml("hard_tabs = true").unwrap();
644
645         assert_eq!(config.was_set().hard_tabs(), true);
646         assert_eq!(config.was_set().verbose(), false);
647     }
648
649     #[test]
650     fn test_as_not_nightly_channel() {
651         let mut config = Config::default();
652         assert_eq!(config.was_set().unstable_features(), false);
653         config.set().unstable_features(true);
654         assert_eq!(config.was_set().unstable_features(), false);
655     }
656
657     #[test]
658     fn test_as_nightly_channel() {
659         let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
660         ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
661         let mut config = Config::default();
662         config.set().unstable_features(true);
663         assert_eq!(config.was_set().unstable_features(), false);
664         config.set().unstable_features(true);
665         assert_eq!(config.unstable_features(), true);
666         ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
667     }
668
669     #[test]
670     fn test_unstable_from_toml() {
671         let mut config = Config::from_toml("unstable_features = true").unwrap();
672         assert_eq!(config.was_set().unstable_features(), false);
673         let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
674         ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
675         config = Config::from_toml("unstable_features = true").unwrap();
676         assert_eq!(config.was_set().unstable_features(), true);
677         assert_eq!(config.unstable_features(), true);
678         ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
679     }
680
681 }