Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellcode-Bibliothek smoosh_tools.rs   Sprache: unbekannt

 
Untersuchungsergebnis.rs Download desUnknown {[0] [0] [0]}zum Wurzelverzeichnis wechseln

use std::collections::HashMap;
use std::env::{self, Args};
use std::fs::{create_dir_all, File};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::{self, exit, Command};
use std::str::FromStr;

static USAGE_STRING: &'static str = r#"Tools for jsparagus + SmooshMonkey development

USAGE:
    cargo run --bin smoosh_tools [COMMAND] [OPTIONS]

COMMAND:
    build [--opt] [MOZILLA_CENTRAL]
        Build SpiderMonkey JS shell with SmooshMonkey enabled, using this
        jsparagus clone instead of vendored one
    shell [--opt] [MOZILLA_CENTRAL]
        Run SpiderMonkey JS shell binary built by "build" command
    test [--opt] [MOZILLA_CENTRAL]
        Run jstests/jit-test with SpiderMonkey JS shell binary built by
        "build" command
    bench [--opt] [--samples-dir=REAL_JS_SAMPLES/DATE] [MOZILLA_CENTRAL]
        Compare SpiderMonkey parser performance against SmooshMonkey on a
        collection of JavaScript files, using the JS shell binary built by
        "build" command.
    bump [MOZILLA_CENTRAL]
        Bump jsparagus version referred by mozilla-central to the latest
        "ci_generated" branch HEAD, and re-vendor jsparagus
    try [--remote=REMOTE] [MOZILLA_CENTRAL]
        Push to try with current jsparagus branch
        This pushes current jsparagus branch to "generated" branch, and
        modifies the reference in mozilla-central to it, and pushes to try
        This requires L1 Commit Access for hg.mozilla.org,
        and mozilla-central should be a Git repository
    gen [--remote=REMOTE]
        Push current jsparagus branch to "generated" branch, with generated
        files included, to refer from mozilla-central

OPTIONS:
    MOZILLA_CENTRAL Path to mozilla-central or mozilla-unified clone
                    This can be omitted if mozilla-central or mozilla-unified
                    is placed next to jsparagus clone directory
    --opt           Use optimized build configuration, instead of debug build
    --remote=REMOTE The name of remote to push the generated branch to
                    Defaults to "origin"
    --concat-mozconfig For building mozilla-central, concatenates the content
                    of the MOZCONFIG environment variable with the content of
                    smoosh_tools mozconfig.
    --samples-dir=DIR Directory containing thousands of JavaScripts to be used
                    for measuring the performance of SmooshMonkey.
"#;

macro_rules! try_finally {
    ({$($t: tt)*} {$($f: tt)*}) => {
        let result = (|| -> Result<(), Error> {
            $($t)*
            Ok(())
        })();
        $($f)*
        result?
    }
}

/// Simple wrapper for logging.
///
/// Do not use env_logger etc, to avoid adding extra dependency to library.
/// See https://github.com/rust-lang/rfcs/pull/2887
macro_rules! log_info {
    ($($t: tt)*) => {
        print!("[INFO] ");
        println!($($t)*);
    }
}

#[derive(Debug)]
enum Error {
    Generic(String),
    SubProcessError(String, Option<i32>),
    IO(String, std::io::Error),
    Encode(String, std::str::Utf8Error),
    EnvVar(&'static str, std::env::VarError),
}

impl Error {
    fn dump(&self) {
        match self {
            Error::Generic(message) => {
                println!("{}", message);
            }
            Error::SubProcessError(message, code) => {
                println!("{}", message);
                match code {
                    Some(code) => println!("Subprocess exit with exit status: {}", code),
                    None => println!("Subprocess terminated by signal"),
                }
            }
            Error::IO(message, e) => {
                println!("{}", message);
                println!("{}", e);
            }
            Error::Encode(message, e) => {
                println!("{}", message);
                println!("{}", e);
            }
            Error::EnvVar(var, e) => {
                println!("Error while reading {}:", var);
                println!("{}", e);
            }
        }
    }
}

#[derive(Debug, Copy, Clone)]
enum CommandType {
    Build,
    Shell,
    Test,
    Bench,
    Bump,
    Gen,
    Try,
}

#[derive(Debug, Copy, Clone)]
enum BuildType {
    Opt,
    Debug,
}

/// Parse command line arguments.
///
/// Do not use `clap` here, to avoid adding extra dependency to library.
/// See https://github.com/rust-lang/rfcs/pull/2887
#[derive(Debug)]
struct SimpleArgs {
    command: CommandType,
    build_type: BuildType,
    moz_path: String,
    realjs_path: String,
    remote: String,
    concat_mozconfig: bool,
}

impl SimpleArgs {
    fn parse(mut args: Args) -> Self {
        // Skip binary path.
        let _ = args.next().unwrap();

        let command = match args.next() {
            Some(command) => match command.as_str() {
                "build" => CommandType::Build,
                "test" => CommandType::Test,
                "shell" => CommandType::Shell,
                "bench" => CommandType::Bench,
                "bump" => CommandType::Bump,
                "gen" => CommandType::Gen,
                "try" => CommandType::Try,
                _ => Self::show_usage(),
            },
            None => Self::show_usage(),
        };

        let mut plain_args = Vec::new();

        let mut remote = "origin".to_string();
        let mut moz_path = Self::guess_moz();
        let mut realjs_path = Self::guess_realjs();
        let mut build_type = BuildType::Debug;
        let mut concat_mozconfig = false;

        for arg in args {
            if arg.starts_with("-") {
                if arg.contains("=") {
                    let mut split = arg.split("=");
                    let name = match split.next() {
                        Some(s) => s,
                        None => Self::show_usage(),
                    };
                    let value = match split.next() {
                        Some(s) => s,
                        None => Self::show_usage(),
                    };

                    match name {
                        "--remote" => {
                            remote = value.to_string();
                        }
                        "--samples-dir" => {
                            realjs_path = value.to_string();
                        }
                        _ => {
                            Self::show_usage();
                        }
                    }
                } else {
                    match arg.as_str() {
                        "--opt" => {
                            build_type = BuildType::Opt;
                        }
                        "--concat-mozconfig" => {
                            concat_mozconfig = true;
                        }
                        _ => {
                            Self::show_usage();
                        }
                    }
                }
            } else {
                plain_args.push(arg);
            }
        }

        if !plain_args.is_empty() {
            moz_path = plain_args.remove(0);
        }

        if !plain_args.is_empty() {
            Self::show_usage();
        }

        SimpleArgs {
            command,
            build_type,
            moz_path,
            realjs_path,
            remote,
            concat_mozconfig,
        }
    }

    fn show_usage() -> ! {
        print!("{}", USAGE_STRING);
        process::exit(-1)
    }

    fn guess_moz() -> String {
        let cwd = match env::current_dir() {
            Ok(cwd) => cwd,
            _ => return "../mozilla-central".to_string(),
        };

        for path in vec!["../mozilla-central", "../mozilla-unified"] {
            let topsrcdir = Path::new(&cwd).join(path);
            if topsrcdir.exists() {
                return path.to_string();
            }
        }

        return "../mozilla-central".to_string();
    }

    fn guess_realjs() -> String {
        return "../real-js-samples/20190416".to_string();
    }
}

#[derive(Debug)]
struct MozillaTree {
    topsrcdir: PathBuf,
    smoosh_cargo: PathBuf,
}

impl MozillaTree {
    fn try_new(path: &String) -> Result<Self, Error> {
        let rel_topsrcdir = Path::new(path);
        let cwd = env::current_dir().unwrap();
        let topsrcdir = Path::new(&cwd).join(rel_topsrcdir);
        if !topsrcdir.exists() {
            return Err(Error::Generic(format!(
                "{:?} doesn't exist. Please specify a path to mozilla-central\n
For more information, see https://github.com/mozilla-spidermonkey/jsparagus/wiki/SpiderMonkey",
                topsrcdir
            )));
        }
        let topsrcdir = topsrcdir.canonicalize().unwrap();
        let cargo = topsrcdir
            .join("js")
            .join("src")
            .join("frontend")
            .join("smoosh")
            .join("Cargo.toml");
        if !cargo.exists() {
            return Err(Error::Generic(format!(
                "{:?} doesn't exist. Please specify a path to mozilla-central",
                cargo
            )));
        }

        Ok(Self {
            topsrcdir: topsrcdir.to_path_buf(),
            smoosh_cargo: cargo.to_path_buf(),
        })
    }
}

#[derive(Debug)]
struct JsparagusTree {
    topsrcdir: PathBuf,
    mozconfigs: PathBuf,
}

impl JsparagusTree {
    fn try_new() -> Result<Self, Error> {
        let cwd = env::current_dir().unwrap();
        let topsrcdir = Path::new(&cwd);
        let cargo = topsrcdir.join("Cargo.toml");
        if !cargo.exists() {
            return Err(Error::Generic(format!(
                "{:?} doesn't exist. Please run smoosh_tools in jsparagus top level directory",
                cargo
            )));
        }

        let mozconfigs = topsrcdir.join("mozconfigs");
        if !mozconfigs.exists() {
            return Err(Error::Generic(format!(
                "{:?} doesn't exist. Please run smoosh_tools in jsparagus top level directory",
                mozconfigs
            )));
        }

        Ok(Self {
            topsrcdir: topsrcdir.to_path_buf(),
            mozconfigs: mozconfigs.to_path_buf(),
        })
    }

    fn mozconfig(&self, build_type: BuildType) -> PathBuf {
        self.mozconfigs.join(match build_type {
            BuildType::Opt => "smoosh-opt",
            BuildType::Debug => "smoosh-debug",
        })
    }

    fn compare_parsers_js(&self) -> PathBuf {
        self.topsrcdir
            .join("benchmarks")
            .join("compare-spidermonkey-parsers.js")
    }
}

struct ObjDir(String);
impl FromStr for ObjDir {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let header = "mk_add_options";
        let s = match s.starts_with(header) {
            true => &s[header.len()..],
            false => return Err(Error::Generic("unexpected start".into())),
        };
        if Some(0) != s.find(char::is_whitespace) {
            return Err(Error::Generic(
                "expected whitespace after mk_add_options".into(),
            ));
        }
        let s = s.trim_start();
        let eq_idx = s.find('=').ok_or(Error::Generic(
            "equal sign not found after mk_add_option".into(),
        ))?;
        let var_name = &s[..eq_idx];
        if var_name != "MOZ_OBJDIR" {
            return Err(Error::Generic(format!(
                "{}: unexpected variable, expected MOZ_OBJDIR",
                var_name
            )));
        }
        let s = &s[(eq_idx + 1)..];
        let s = s.trim();

        Ok(ObjDir(s.into()))
    }
}

#[derive(Debug)]
struct BuildTree {
    moz: MozillaTree,
    #[allow(dead_code)]
    jsp: JsparagusTree,
    mozconfig: PathBuf,
}

impl BuildTree {
    fn try_new(args: &SimpleArgs) -> Result<Self, Error> {
        let moz = MozillaTree::try_new(&args.moz_path)?;
        let jsp = JsparagusTree::try_new()?;

        let jsp_mozconfig = jsp.mozconfig(args.build_type);
        let mozconfig = if args.concat_mozconfig {
            // Create a MOZCONFIG file which concatenate the content of the
            // environmenet variable with the content provided by jsparagus.
            // This is useful to add additional compilation variants for
            // mozilla-central.
            let env = env::var("MOZCONFIG").map_err(|e| Error::EnvVar("MOZCONFIG", e))?;
            let env_config = read_file(&env.into())?;
            let jsp_config = read_file(&jsp_mozconfig)?;
            let config = env_config + &jsp_config;

            // Extract the object directory, in which the mozconfig file would
            // be created.
            let mut objdir = None;
            for line in config.lines() {
                match line.parse() {
                    Ok(ObjDir(meta_path)) => objdir = Some(meta_path),
                    Err(_error) => (),
                }
            }
            let objdir = objdir.ok_or(Error::Generic("MOZ_OBJDIR must exists".into()))?;
            let topsrcdir = moz
                .topsrcdir
                .to_str()
                .ok_or(())
                .map_err(|_| Error::Generic("topsrcdir cannot be encoded in UTF-8.".into()))?;
            let objdir = objdir.replace("@TOPSRCDIR@", topsrcdir);

            // Create the object direcotry.
            let objdir: PathBuf = objdir.into();
            if !objdir.is_dir() {
                create_dir_all(&objdir).map_err(|e| {
                    Error::IO(format!("Failed to create directory {:?}", objdir), e)
                })?;
            }

            // Create MOZCONFIG file.
            let mozconfig = objdir.join("mozconfig");
            write_file(&mozconfig, config)?;

            mozconfig
        } else {
            jsp_mozconfig
        };

        Ok(Self {
            moz,
            jsp,
            mozconfig,
        })
    }
}

/// Run `command`, and check if the exit code is successful.
/// Returns Err if failed to run the command, or the exit code is non-zero.
fn check_command(command: &mut Command) -> Result<(), Error> {
    log_info!("$ {:?}", command);
    let status = command
        .status()
        .map_err(|e| Error::IO(format!("Failed to run {:?}", command), e))?;
    if !status.success() {
        return Err(Error::SubProcessError(
            format!("Failed to run {:?}", command),
            status.code(),
        ));
    }

    Ok(())
}

/// Run `command`, and returns its status code.
/// Returns Err if failed to run the command, or the subprocess is terminated
/// by signal.
fn get_retcode(command: &mut Command) -> Result<i32, Error> {
    log_info!("$ {:?}", command);
    let status = command
        .status()
        .map_err(|e| Error::IO(format!("Failed to run {:?}", command), e))?;
    if !status.success() {
        match status.code() {
            Some(code) => return Ok(code),
            None => {
                return Err(Error::SubProcessError(
                    format!("Failed to run {:?}", command),
                    None,
                ))
            }
        }
    }

    Ok(0)
}

/// Run `command`, and returns its stdout
/// Returns Err if failed to run the command.
fn get_output(command: &mut Command) -> Result<String, Error> {
    log_info!("$ {:?}", command);
    let output = command
        .output()
        .map_err(|e| Error::IO(format!("Failed to run {:?}", command), e))?;
    let stdout = std::str::from_utf8(output.stdout.as_slice())
        .map_err(|e| Error::Encode(format!("Failed to decode the output of {:?}", command), e))?
        .to_string();
    Ok(stdout)
}

struct GitRepository {
    topsrcdir: PathBuf,
}

impl GitRepository {
    fn try_new(topsrcdir: PathBuf) -> Result<Self, Error> {
        if !topsrcdir.join(".git").as_path().exists() {
            return Err(Error::Generic(format!(
                "{:?} is not Git repository",
                topsrcdir
            )));
        }

        Ok(Self { topsrcdir })
    }

    fn run(&self, args: &[&str]) -> Result<(), Error> {
        check_command(
            Command::new("git")
                .args(args)
                .current_dir(self.topsrcdir.clone()),
        )
    }

    fn get_retcode(&self, args: &[&str]) -> Result<i32, Error> {
        get_retcode(
            Command::new("git")
                .args(args)
                .current_dir(self.topsrcdir.clone()),
        )
    }

    fn get_output(&self, args: &[&str]) -> Result<String, Error> {
        get_output(
            Command::new("git")
                .args(args)
                .current_dir(self.topsrcdir.clone()),
        )
    }

    /// Checks if there's no uncommitted changes.
    fn assert_clean(&self) -> Result<(), Error> {
        log_info!("Checking {} is clean", self.topsrcdir.to_str().unwrap());
        let code = self.get_retcode(&["diff-index", "--quiet", "HEAD", "--"])?;
        if code != 0 {
            return Err(Error::Generic(format!(
                "Uncommitted changes found in {}",
                self.topsrcdir.to_str().unwrap()
            )));
        }

        let code = self.get_retcode(&["diff-index", "--cached", "--quiet", "HEAD", "--"])?;
        if code != 0 {
            return Err(Error::Generic(format!(
                "Uncommitted changes found in {}",
                self.topsrcdir.to_str().unwrap()
            )));
        }

        Ok(())
    }

    /// Returns the current branch, or "HEAD" if it's detached head..
    fn branch(&self) -> Result<String, Error> {
        Ok(self
            .get_output(&["rev-parse", "--abbrev-ref", "HEAD"])?
            .trim()
            .to_string())
    }

    /// Ensure a remote with `name` exists.
    /// If it doesn't exist, add remote with `name` and `url`.
    fn ensure_remote(&self, name: &'static str, url: &'static str) -> Result<(), Error> {
        for line in self.get_output(&["remote"])?.split("\n") {
            if line == name {
                return Ok(());
            }
        }

        self.run(&["remote", "add", name, url])?;

        Ok(())
    }

    /// Returns a map of remote branches.
    fn ls_remote(&self, remote: &'static str) -> Result<HashMap<String, String>, Error> {
        let mut map = HashMap::new();
        for line in self.get_output(&["ls-remote", remote])?.split("\n") {
            let mut split = line.split("\t");
            let sha = match split.next() {
                Some(s) => s,
                None => continue,
            };
            let ref_name = match split.next() {
                Some(s) => s,
                None => continue,
            };
            map.insert(ref_name.to_string(), sha.to_string());
        }

        Ok(map)
    }
}

/// Trait for replacing dependencies in Cargo.toml.
trait DependencyLineReplacer {
    /// Receives `line` for official jsparagus reference,
    /// and adds modified jsparagus reference to `lines`.
    fn on_official(&self, line: &str, lines: &mut Vec<String>);
}

/// Replace jsparagus reference to `sha` in official ci_generated branch.
struct OfficialDependencyLineReplacer {
    sha: String,
}

impl DependencyLineReplacer for OfficialDependencyLineReplacer {
    fn on_official(&self, _line: &str, lines: &mut Vec<String>) {
        let newline = format!("jsparagus = {{ git = \"https://github.com/mozilla-spidermonkey/jsparagus\", rev = \"{}\" }}", self.sha);
        log_info!("Rewriting jsparagus reference: {}", newline);
        lines.push(newline);
    }
}

/// Replace jsparagus reference to local clone.
struct LocalDependencyLineReplacer {
    jsparagus: PathBuf,
}

impl DependencyLineReplacer for LocalDependencyLineReplacer {
    fn on_official(&self, line: &str, lines: &mut Vec<String>) {
        lines.push(format!("# {}", line));
        let newline = format!(
            "jsparagus = {{ path = \"{}\" }}",
            self.jsparagus.to_str().unwrap()
        );
        log_info!("Rewriting jsparagus reference: {}", newline);
        lines.push(newline);
    }
}

/// Replace jsparagus reference to a remote branch in forked repository.
struct ForkDependencyLineReplacer {
    github_user: String,
    branch: String,
}

impl DependencyLineReplacer for ForkDependencyLineReplacer {
    fn on_official(&self, line: &str, lines: &mut Vec<String>) {
        lines.push(format!("# {}", line));
        let newline = format!(
            "jsparagus = {{ git = \"https://github.com/{}/jsparagus\", branch = \"{}\" }}",
            self.github_user, self.branch
        );
        log_info!("Rewriting jsparagus reference: {}", newline);
        lines.push(newline);
    }
}

fn read_file(path: &PathBuf) -> Result<String, Error> {
    let mut file = File::open(path.as_path())
        .map_err(|e| Error::IO(format!("Couldn't open {}", path.to_str().unwrap()), e))?;
    let mut content = String::new();
    file.read_to_string(&mut content)
        .map_err(|e| Error::IO(format!("Couldn't read {}", path.to_str().unwrap()), e))?;

    Ok(content)
}

fn write_file(path: &PathBuf, content: String) -> Result<(), Error> {
    let mut file = File::create(path.as_path()).map_err(|e| {
        Error::IO(
            format!("Couldn't open {} in write mode", path.to_str().unwrap()),
            e,
        )
    })?;
    file.write_all(content.as_bytes())
        .map_err(|e| Error::IO(format!("Couldn't write {}", path.to_str().unwrap()), e))?;

    Ok(())
}

fn update_cargo<T>(cargo: &PathBuf, replacer: T) -> Result<(), Error>
where
    T: DependencyLineReplacer,
{
    let content = read_file(cargo)?;
    let mut filtered_lines = Vec::new();
    for line in content.split("\n") {
        if line.starts_with(
            "# jsparagus = { git = \"https://github.com/mozilla-spidermonkey/jsparagus\",",
        ) || line.starts_with(
            "jsparagus = { git = \"https://github.com/mozilla-spidermonkey/jsparagus\",",
        ) {
            let orig_line = if line.starts_with("# ") {
                &line[2..]
            } else {
                line
            };
            replacer.on_official(orig_line, &mut filtered_lines)
        } else if line.starts_with("jsparagus = ") {
        } else {
            filtered_lines.push(line.to_string());
        }
    }
    write_file(cargo, filtered_lines.join("\n"))
}

fn update_gkrust(args: &SimpleArgs) -> Result<(), Error> {
    log_info!("Updating gkrust-shared");

    let build = BuildTree::try_new(args)?;

    check_command(
        Command::new("cargo")
            .args(["update", "-p", "gkrust-shared"])
            .current_dir(build.moz.topsrcdir),
    )
}

fn run_mach(command_args: &[&str], args: &SimpleArgs) -> Result<(), Error> {
    let build = BuildTree::try_new(args)?;

    check_command(
        Command::new(build.moz.topsrcdir.join("mach").to_str().unwrap())
            .args(command_args)
            .current_dir(build.moz.topsrcdir)
            .env("MOZCONFIG", build.mozconfig.to_str().unwrap()),
    )
}

fn build(args: &SimpleArgs) -> Result<(), Error> {
    let moz = MozillaTree::try_new(&args.moz_path)?;
    let jsparagus = JsparagusTree::try_new()?;

    update_cargo(
        &moz.smoosh_cargo,
        LocalDependencyLineReplacer {
            jsparagus: jsparagus.topsrcdir,
        },
    )?;

    update_gkrust(args)?;

    run_mach(&["build"], args)
}

fn shell(args: &SimpleArgs) -> Result<(), Error> {
    run_mach(&["run", "--smoosh"], args)
}

fn bench(args: &SimpleArgs) -> Result<(), Error> {
    let jsparagus = JsparagusTree::try_new()?;
    let cmp_parsers = jsparagus.compare_parsers_js();
    let cmp_parsers: &str = cmp_parsers.to_str().ok_or(Error::Generic(
        "Unable to serialize benchmark script path".into(),
    ))?;
    let realjs_path = jsparagus.topsrcdir.join(&args.realjs_path);
    let realjs_path: &str = realjs_path.to_str().ok_or(Error::Generic(
        "Unable to serialize benchmark script path".into(),
    ))?;

    run_mach(
        &["run", "-f", cmp_parsers, "--", "--", "--dir", realjs_path],
        args,
    )
}

fn test(args: &SimpleArgs) -> Result<(), Error> {
    run_mach(&["jstests", "--args=--smoosh"], args)?;
    run_mach(&["jit-test", "--args=--smoosh"], args)
}

fn vendor(moz: &MozillaTree) -> Result<(), Error> {
    check_command(
        Command::new(moz.topsrcdir.join("mach").to_str().unwrap())
            .arg("vendor")
            .arg("rust")
            .current_dir(moz.topsrcdir.clone()),
    )
}

fn bump(args: &SimpleArgs) -> Result<(), Error> {
    let moz = MozillaTree::try_new(&args.moz_path)?;
    let jsparagus = JsparagusTree::try_new()?;

    let jsparagus_repo = GitRepository::try_new(jsparagus.topsrcdir.clone())?;

    log_info!("Checking ci_generated branch HEAD");

    let remotes =
        jsparagus_repo.ls_remote("https://github.com/mozilla-spidermonkey/jsparagus.git")?;

    let branch = "refs/heads/ci_generated";

    let ci_generated_sha = match remotes.get(branch) {
        Some(sha) => sha,
        None => {
            return Err(Error::Generic(format!("{} not found in upstream", branch)));
        }
    };

    log_info!("ci_generated branch HEAD = {}", ci_generated_sha);

    update_cargo(
        &moz.smoosh_cargo,
        OfficialDependencyLineReplacer {
            sha: ci_generated_sha.clone(),
        },
    )?;

    vendor(&moz)?;

    log_info!("Please add updated files and commit them.");

    Ok(())
}

/// Parse remote string and get GitHub username.
/// Currently this supports only SSH format.
fn parse_github_username(remote: String) -> Result<String, Error> {
    let git_prefix = "git@github.com:";
    let git_suffix = "/jsparagus.git";

    if remote.starts_with(git_prefix) && remote.ends_with(git_suffix) {
        return Ok(remote.replace(git_prefix, "").replace(git_suffix, ""));
    }

    Err(Error::Generic(format!(
        "Failed to get GitHub username: {}",
        remote
    )))
}

struct BranchInfo {
    github_user: String,
    branch: String,
}

/// Create "generated" branch and push to remote, and returns
/// GitHub username and branch name.
fn push_to_gen_branch(args: &SimpleArgs) -> Result<BranchInfo, Error> {
    let jsparagus = JsparagusTree::try_new()?;

    let jsparagus_repo = GitRepository::try_new(jsparagus.topsrcdir.clone())?;
    jsparagus_repo.assert_clean()?;

    log_info!("Getting GitHub username and current branch");

    let origin = jsparagus_repo
        .get_output(&["remote", "get-url", args.remote.as_str()])?
        .trim()
        .to_string();
    let github_user = parse_github_username(origin)?;

    let branch = jsparagus_repo.branch()?;
    if branch == "HEAD" {
        return Err(Error::Generic(format!(
            "Detached HEAD is not supported. Please checkout a branch"
        )));
    }

    let gen_branch = format!("{}-generated-branch", branch);

    log_info!("Creating {} branch", gen_branch);

    jsparagus_repo.run(&["checkout", "-b", gen_branch.as_str()])?;

    try_finally!({
        log_info!("Updating generated files");

        check_command(
            Command::new("make")
                .arg("all")
                .current_dir(jsparagus.topsrcdir.clone()),
        )?;

        log_info!("Committing generated files");

        jsparagus_repo.run(&["add", "--force", "*_generated.rs"])?;

        try_finally!({
            jsparagus_repo.run(&["commit", "-m", "Add generated files"])?;

            try_finally!({
                log_info!("Pushing to {}", gen_branch);
                jsparagus_repo.run(&["push", "-f", args.remote.as_str(), gen_branch.as_str()])?;
            } {
                // Revert the commit, wihtout removing *_generated.rs.
                jsparagus_repo.run(&["reset", "--soft", "HEAD^"])?;
            });
        } {
            // Forget *_generated.rs files.
            jsparagus_repo.run(&["reset"])?;
        });
    } {
        jsparagus_repo.run(&["checkout", branch.as_str()])?;
        jsparagus_repo.run(&["branch", "-D", gen_branch.as_str()])?;
    });

    Ok(BranchInfo {
        github_user,
        branch: gen_branch,
    })
}

fn gen_branch(args: &SimpleArgs) -> Result<(), Error> {
    push_to_gen_branch(args)?;

    Ok(())
}

fn push_try(args: &SimpleArgs) -> Result<(), Error> {
    let moz = MozillaTree::try_new(&args.moz_path)?;

    let moz_repo = GitRepository::try_new(moz.topsrcdir.clone())?;
    moz_repo.assert_clean()?;

    let branch_info = push_to_gen_branch(args)?;

    moz_repo.ensure_remote("try", "hg::https://hg.mozilla.org/try")?;

    update_cargo(
        &moz.smoosh_cargo,
        ForkDependencyLineReplacer {
            github_user: branch_info.github_user,
            branch: branch_info.branch,
        },
    )?;

    vendor(&moz)?;

    moz_repo.run(&["add", "."])?;
    moz_repo.run(&["commit", "-m", "Update vendored crates for jsparagus"])?;
    try_finally!({
        let syntax = "try: -b do -p sm-smoosh-linux64,sm-nonunified-linux64 -u none -t none";
        moz_repo.run(&["commit", "--allow-empty", "-m", syntax])?;
        try_finally!({
            moz_repo.run(&["push", "try"])?;
        } {
            moz_repo.run(&["reset", "--hard", "HEAD^"])?;
        });
    } {
        moz_repo.run(&["reset", "--hard", "HEAD^"])?;
    });

    Ok(())
}

fn main() {
    let args = SimpleArgs::parse(env::args());

    let result = match args.command {
        CommandType::Build => build(&args),
        CommandType::Shell => shell(&args),
        CommandType::Test => test(&args),
        CommandType::Bench => bench(&args),
        CommandType::Bump => bump(&args),
        CommandType::Gen => gen_branch(&args),
        CommandType::Try => push_try(&args),
    };

    match result {
        Ok(_) => {}
        Err(e) => {
            e.dump();
            exit(1)
        }
    }
}

[ 0.67Quellennavigators  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge