Skip to content

Commit

Permalink
Initial implementation of #254
Browse files Browse the repository at this point in the history
- Can currently set the `telemetry` flag.
- Telemetry status is stored in `MULTIRUST_HOME/telemetry`
- Adds routing for telemetry for rustc calls and target add
- Provides basic command proxying for telemetry
  • Loading branch information
peschkaj committed Apr 10, 2016
1 parent fc175c2 commit ed1b623
Show file tree
Hide file tree
Showing 15 changed files with 318 additions and 38 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ tempdir = "0.3.4"
libc = "0.2.0"
rand = "0.3.11"
scopeguard = "0.1.2"
rustc-serialize = "0.3"

[target.x86_64-pc-windows-gnu.dependencies]
winapi = "0.2.4"
Expand Down
29 changes: 2 additions & 27 deletions src/multirust-cli/common.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
//! Just a dumping ground for cli stuff
use multirust::{Cfg, Result, Notification, Toolchain, Error, UpdateStatus};
use multirust_utils::{self, utils};
use multirust_utils::utils;
use multirust_utils::notify::NotificationLevel;
use self_update;
use std::ffi::OsStr;
use std::io::{Write, Read, BufRead};
use std::process::{self, Command};
use std::process::Command;
use std::{cmp, iter};
use std::str::FromStr;
use std;
Expand Down Expand Up @@ -67,30 +66,6 @@ pub fn set_globals(verbose: bool) -> Result<Cfg> {

}

pub fn run_inner<S: AsRef<OsStr>>(mut command: Command,
args: &[S]) -> Result<()> {
command.args(&args[1..]);
// FIXME rust-lang/rust#32254. It's not clear to me
// when and why this is needed.
command.stdin(process::Stdio::inherit());

let status = command.status();

match status {
Ok(status) => {
// Ensure correct exit code is returned
let code = status.code().unwrap_or(1);
process::exit(code);
}
Err(e) => {
Err(multirust_utils::Error::RunningCommand {
name: args[0].as_ref().to_owned(),
error: multirust_utils::raw::CommandError::Io(e),
}.into())
}
}
}

pub fn show_channel_update(cfg: &Cfg, name: &str,
updated: Result<UpdateStatus>) -> Result<()> {
show_channel_updates(cfg, vec![(name.to_string(), updated)])
Expand Down
2 changes: 2 additions & 0 deletions src/multirust-cli/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(custom_derive, plugin)]

extern crate multirust_dist;
#[macro_use]
extern crate multirust_utils;
Expand Down
8 changes: 4 additions & 4 deletions src/multirust-cli/multirust_mode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clap::ArgMatches;
use cli;
use common::{self, confirm, set_globals, run_inner,
use common::{self, confirm, set_globals,
show_channel_update, show_tool_versions,
update_all_channels};
use multirust::*;
Expand Down Expand Up @@ -79,14 +79,14 @@ fn run(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
let args = m.values_of("command").unwrap();
let args: Vec<_> = args.collect();
let cmd = try!(toolchain.create_command(args[0]));
run_inner(cmd, &args)
command::run_command_for_dir(cmd, &args, &cfg)
}

fn proxy(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
let args = m.values_of("command").unwrap();
let args: Vec<_> = args.collect();
let cmd = try!(cfg.create_command_for_dir(&try!(utils::current_dir()), args[0]));
run_inner(cmd, &args)
command::run_command_for_dir(cmd, &args, &cfg)
}

fn command_requires_metadata() -> Result<bool> {
Expand Down Expand Up @@ -326,7 +326,7 @@ fn add_target(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
pkg: "rust-std".to_string(),
target: TargetTriple::from_str(target),
};
try!(toolchain.add_component(new_component));
try!(toolchain.add_component(new_component, &cfg));

Ok(())
}
Expand Down
6 changes: 4 additions & 2 deletions src/multirust-cli/proxy_mode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use common::{run_inner, set_globals};
use common::set_globals;
use multirust::{Cfg, Result, Error};
use multirust::command::run_command_for_dir;
use multirust_utils::utils;
use std::env;
use std::path::PathBuf;
Expand All @@ -26,6 +27,7 @@ pub fn main() -> Result<()> {
fn direct_proxy(cfg: &Cfg, arg0: &str) -> Result<()> {
let cmd = try!(cfg.create_command_for_dir(&try!(utils::current_dir()), arg0));
let args: Vec<_> = env::args_os().collect();
run_inner(cmd, &args)

run_command_for_dir(cmd, &args, &cfg)
}

19 changes: 17 additions & 2 deletions src/multirust-cli/rustup_mode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clap::{App, Arg, AppSettings, SubCommand, ArgMatches};
use common;
use multirust::{Result, Cfg, Error, Toolchain};
use multirust::command;
use multirust_dist::manifest::Component;
use multirust_dist::dist::TargetTriple;
use multirust_utils::utils;
Expand Down Expand Up @@ -58,6 +59,7 @@ pub fn main() -> Result<()> {
(_ ,_) => unreachable!(),
}
}
("telemetry", Some(m)) => try!(telemetry(&cfg, m)),
(_, _) => unreachable!(),
}

Expand Down Expand Up @@ -160,6 +162,13 @@ pub fn cli() -> App<'static, 'static> {
.short("y")))
.subcommand(SubCommand::with_name("upgrade-data")
.about("Upgrade the internal data format.")))
.subcommand(SubCommand::with_name("telemetry")
.about("Enable or disable rust telemetry")
.arg(Arg::with_name("enabled")
.takes_value(true)
.value_name("telemetry")
.help("Set telemetry 'on' or 'off'")
.possible_values(&["on", "off"])))
}

fn maybe_upgrade_data(cfg: &Cfg, m: &ArgMatches) -> Result<bool> {
Expand Down Expand Up @@ -228,7 +237,7 @@ fn run(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
let args: Vec<_> = args.collect();
let cmd = try!(cfg.create_command_for_toolchain(toolchain, args[0]));

common::run_inner(cmd, &args)
command::run_command_for_dir(cmd, &args, &cfg)
}

fn which(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
Expand Down Expand Up @@ -277,7 +286,7 @@ fn target_add(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
target: TargetTriple::from_str(target),
};

toolchain.add_component(new_component)
toolchain.add_component(new_component, &cfg)
}

fn target_remove(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
Expand Down Expand Up @@ -374,3 +383,9 @@ fn self_uninstall(m: &ArgMatches) -> Result<()> {

self_update::uninstall(no_prompt)
}

fn telemetry(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
let telemetry_string = m.value_of("enabled").unwrap();

cfg.set_telemetry(telemetry_string)
}
1 change: 0 additions & 1 deletion src/multirust-utils/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use winreg;
pub use raw::{is_directory, is_file, path_exists, if_not_empty, random_string, prefix_arg,
has_cmd, find_cmd};


pub fn ensure_dir_exists(name: &'static str,
path: &Path,
notify_handler: NotifyHandler)
Expand Down
109 changes: 109 additions & 0 deletions src/multirust/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std;
use std::env;
use std::ffi::OsStr;
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::{self, Command, Output};
use std::time::Instant;

use Cfg;
use errors::*;
use multirust_utils;
use telemetry;
use telemetry::TelemetryEvent;


pub fn run_command_for_dir<S: AsRef<OsStr>>(cmd: Command,
args: &[S],
cfg: &Cfg) -> Result<()> {
let arg0 = env::args().next().map(|a| PathBuf::from(a));
let arg0 = arg0.as_ref()
.and_then(|a| a.file_name())
.and_then(|a| a.to_str());
let arg0 = try!(arg0.ok_or(Error::NoExeName));
if (arg0 == "rustc" || arg0 == "rustc.exe") && cfg.telemetry_enabled() {
return telemetry_rustc(cmd, &args, &cfg);
}

run_command_for_dir_without_telemetry(cmd, &args)
}

fn telemetry_rustc<S: AsRef<OsStr>>(cmd: Command, args: &[S], cfg: &Cfg) -> Result<()> {
let now = Instant::now();

let output = bare_run_command_for_dir(cmd, &args);

let duration = now.elapsed();

let ms = (duration.as_secs() as u64 * 1000) + (duration.subsec_nanos() as u64 / 1000 / 1000);

match output {
Ok(out) => {
let exit_code = out.status.code().unwrap_or(1);

let errors = match out.status.success() {
true => None,
_ => Some(String::from_utf8_lossy(&out.stderr).to_string()),
};

let _ = io::stdout().write(&out.stdout);
let _ = io::stdout().flush();
let _ = io::stderr().write(&out.stderr);
let _ = io::stderr().flush();

let te = TelemetryEvent::RustcRun { duration_ms: ms,
exit_code: exit_code,
errors: errors };
telemetry::log_telemetry(te, cfg);
process::exit(exit_code);
},
Err(e) => {
let exit_code = e.raw_os_error().unwrap_or(1);
let te = TelemetryEvent::RustcRun { duration_ms: ms,
exit_code: exit_code,
errors: Some(format!("{}", e)) };
telemetry::log_telemetry(te, cfg);
Err(multirust_utils::Error::RunningCommand {
name: args[0].as_ref().to_owned(),
error: multirust_utils::raw::CommandError::Io(e),
}.into())
},
}
}

fn run_command_for_dir_without_telemetry<S: AsRef<OsStr>>(cmd: Command, args: &[S]) -> Result<()> {
let output = bare_run_command_for_dir(cmd, &args);

match output {
Ok(out) => {
let _ = io::stdout().write(&out.stdout);
let _ = io::stdout().flush();
let _ = io::stderr().write(&out.stderr);
let _ = io::stderr().flush();

let status = out.status;
// Ensure correct exit code is returned
let code = status.code().unwrap_or(1);
process::exit(code);
}
Err(e) => {
Err(multirust_utils::Error::RunningCommand {
name: args[0].as_ref().to_owned(),
error: multirust_utils::raw::CommandError::Io(e),
}.into())
}
}
}

fn bare_run_command_for_dir<S: AsRef<OsStr>>(mut cmd: Command, args: &[S]) -> std::result::Result<Output, std::io::Error> {
cmd.args(&args[1..]);

// FIXME rust-lang/rust#32254. It's not clear to me
// when and why this is needed.
cmd.stdin(process::Stdio::inherit());

cmd.output()
}



52 changes: 52 additions & 0 deletions src/multirust/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::env;
use std::io;
use std::process::Command;
use std::fmt::{self, Display};
use std::str::FromStr;

use itertools::Itertools;

Expand All @@ -12,6 +13,7 @@ use multirust_dist::{temp, dist};
use multirust_utils::utils;
use override_db::OverrideDB;
use toolchain::{Toolchain, UpdateStatus};
use telemetry::{TelemetryMode};

// Note: multirust-rs jumped from 2 to 12 to leave multirust.sh room to diverge
pub const METADATA_VERSION: &'static str = "12";
Expand All @@ -22,6 +24,8 @@ pub enum OverrideReason {
OverrideDB(PathBuf),
}



impl Display for OverrideReason {
fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
match *self {
Expand All @@ -46,6 +50,7 @@ pub struct Cfg {
pub env_override: Option<String>,
pub dist_root_url: Cow<'static, str>,
pub notify_handler: SharedNotifyHandler,
pub telemetry_mode: TelemetryMode,
}

impl Cfg {
Expand Down Expand Up @@ -86,6 +91,8 @@ impl Cfg {
.and_then(utils::if_not_empty)
.map_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT), Cow::Owned);

let telemetry_mode = Cfg::find_telemetry(&multirust_dir.join("telemetry"));

Ok(Cfg {
multirust_dir: multirust_dir,
version_file: version_file,
Expand All @@ -97,6 +104,7 @@ impl Cfg {
gpg_key: gpg_key,
notify_handler: notify_handler,
env_override: env_override,
telemetry_mode: telemetry_mode,
dist_root_url: dist_root_url,
})
}
Expand Down Expand Up @@ -402,4 +410,48 @@ impl Cfg {
Ok(name.to_owned())
}
}

pub fn set_telemetry(&self, telemetry: &str) -> Result<()> {
let work_file = try!(self.temp_cfg.new_file());

try!(utils::write_file("temp", &work_file, telemetry));

try!(utils::rename_file("telemetry", &*work_file, &self.multirust_dir.join("telemetry")));

self.notify_handler.call(Notification::SetTelemetry(telemetry));

Ok(())
}

pub fn telemetry_enabled(&self) -> bool {
match self.telemetry_mode {
TelemetryMode::On => true,
TelemetryMode::Off => false,
}
}

fn find_telemetry(telemetry_file: &PathBuf) -> TelemetryMode {
// default telemetry should be off - if no telemetry file is found, it's off
if !utils::is_file(telemetry_file) {
return TelemetryMode::Off;
}

let content = utils::read_file("telemetry", telemetry_file);
let telemetry_string = content.unwrap_or("off".to_string());

// If the telemetry file is empty, for some reason, assume that the user
// would prefer no telemetry and set telemetry to off.
if telemetry_string.is_empty() {
return TelemetryMode::Off;
}

// Finally, match any remaining string - if this is anything but 'on',
// assume that the user would prefer no telemetry be collected and set
// telemetry to off.
if !telemetry_string.starts_with("on") {
return TelemetryMode::Off;
}

TelemetryMode::On
}
}
Loading

0 comments on commit ed1b623

Please sign in to comment.