rust/hgcli/src/main.rs
author Augie Fackler <augie@google.com>
Mon, 20 Aug 2018 13:29:48 -0400
changeset 39277 f785073f792c
parent 35632 fa9747e7fc86
child 43073 5c9c71cde1c9
permissions -rw-r--r--
merge: improve interactive one-changed one-deleted message (issue5550) I like the wording from the bug, so I figured I'd package it up in a change and see what people think. Differential Revision: https://phab.mercurial-scm.org/D4336
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
35569
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     1
// main.rs -- Main routines for `hg` program
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     2
//
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     3
// Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     4
//
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     5
// This software may be used and distributed according to the terms of the
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     6
// GNU General Public License version 2 or any later version.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     7
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     8
extern crate libc;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     9
extern crate cpython;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    10
extern crate python27_sys;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    11
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    12
use cpython::{NoArgs, ObjectProtocol, PyModule, PyResult, Python};
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    13
use libc::{c_char, c_int};
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    14
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    15
use std::env;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    16
use std::path::PathBuf;
35631
edbe11cfedcf rust: extract function to convert Path to platform CString
Yuya Nishihara <yuya@tcha.org>
parents: 35604
diff changeset
    17
use std::ffi::{CString, OsStr};
35569
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    18
#[cfg(target_family = "unix")]
35632
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    19
use std::os::unix::ffi::{OsStrExt, OsStringExt};
35569
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    20
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    21
#[derive(Debug)]
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    22
struct Environment {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    23
    _exe: PathBuf,
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    24
    python_exe: PathBuf,
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    25
    python_home: PathBuf,
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    26
    mercurial_modules: PathBuf,
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    27
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    28
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    29
/// Run Mercurial locally from a source distribution or checkout.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    30
///
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    31
/// hg is <srcdir>/rust/target/<target>/hg
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    32
/// Python interpreter is detected by build script.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    33
/// Python home is relative to Python interpreter.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    34
/// Mercurial files are relative to hg binary, which is relative to source root.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    35
#[cfg(feature = "localdev")]
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    36
fn get_environment() -> Environment {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    37
    let exe = env::current_exe().unwrap();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    38
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    39
    let mut mercurial_modules = exe.clone();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    40
    mercurial_modules.pop(); // /rust/target/<target>
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    41
    mercurial_modules.pop(); // /rust/target
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    42
    mercurial_modules.pop(); // /rust
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    43
    mercurial_modules.pop(); // /
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    44
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    45
    let python_exe: &'static str = env!("PYTHON_INTERPRETER");
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    46
    let python_exe = PathBuf::from(python_exe);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    47
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    48
    let mut python_home = python_exe.clone();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    49
    python_home.pop();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    50
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    51
    // On Windows, python2.7.exe exists at the root directory of the Python
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    52
    // install. Everywhere else, the Python install root is one level up.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    53
    if !python_exe.ends_with("python2.7.exe") {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    54
        python_home.pop();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    55
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    56
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    57
    Environment {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    58
        _exe: exe.clone(),
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    59
        python_exe: python_exe,
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    60
        python_home: python_home,
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    61
        mercurial_modules: mercurial_modules.to_path_buf(),
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    62
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    63
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    64
35632
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    65
// On UNIX, platform string is just bytes and should not contain NUL.
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    66
#[cfg(target_family = "unix")]
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    67
fn cstring_from_os<T: AsRef<OsStr>>(s: T) -> CString {
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    68
    CString::new(s.as_ref().as_bytes()).unwrap()
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    69
}
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    70
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    71
// TODO convert to ANSI characters?
fa9747e7fc86 rust: convert Unix path to CString transparently
Yuya Nishihara <yuya@tcha.org>
parents: 35631
diff changeset
    72
#[cfg(target_family = "windows")]
35631
edbe11cfedcf rust: extract function to convert Path to platform CString
Yuya Nishihara <yuya@tcha.org>
parents: 35604
diff changeset
    73
fn cstring_from_os<T: AsRef<OsStr>>(s: T) -> CString {
edbe11cfedcf rust: extract function to convert Path to platform CString
Yuya Nishihara <yuya@tcha.org>
parents: 35604
diff changeset
    74
    CString::new(s.as_ref().to_str().unwrap()).unwrap()
edbe11cfedcf rust: extract function to convert Path to platform CString
Yuya Nishihara <yuya@tcha.org>
parents: 35604
diff changeset
    75
}
edbe11cfedcf rust: extract function to convert Path to platform CString
Yuya Nishihara <yuya@tcha.org>
parents: 35604
diff changeset
    76
35569
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    77
// On UNIX, argv starts as an array of char*. So it is easy to convert
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    78
// to C strings.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    79
#[cfg(target_family = "unix")]
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    80
fn args_to_cstrings() -> Vec<CString> {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    81
    env::args_os()
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    82
        .map(|a| CString::new(a.into_vec()).unwrap())
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    83
        .collect()
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    84
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    85
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    86
// TODO Windows support is incomplete. We should either use env::args_os()
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    87
// (or call into GetCommandLineW() + CommandLinetoArgvW()), convert these to
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    88
// PyUnicode instances, and pass these into Python/Mercurial outside the
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    89
// standard PySys_SetArgvEx() mechanism. This will allow us to preserve the
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    90
// raw bytes (since PySys_SetArgvEx() is based on char* and can drop wchar
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    91
// data.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    92
//
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    93
// For now, we use env::args(). This will choke on invalid UTF-8 arguments.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    94
// But it is better than nothing.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    95
#[cfg(target_family = "windows")]
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    96
fn args_to_cstrings() -> Vec<CString> {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    97
    env::args().map(|a| CString::new(a).unwrap()).collect()
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    98
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    99
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   100
fn set_python_home(env: &Environment) {
35631
edbe11cfedcf rust: extract function to convert Path to platform CString
Yuya Nishihara <yuya@tcha.org>
parents: 35604
diff changeset
   101
    let raw = cstring_from_os(&env.python_home).into_raw();
35569
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   102
    unsafe {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   103
        python27_sys::Py_SetPythonHome(raw);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   104
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   105
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   106
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   107
fn update_encoding(_py: Python, _sys_mod: &PyModule) {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   108
    // Call sys.setdefaultencoding("undefined") if HGUNICODEPEDANTRY is set.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   109
    let pedantry = env::var("HGUNICODEPEDANTRY").is_ok();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   110
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   111
    if pedantry {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   112
        // site.py removes the sys.setdefaultencoding attribute. So we need
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   113
        // to reload the module to get a handle on it. This is a lesser
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   114
        // used feature and we'll support this later.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   115
        // TODO support this
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   116
        panic!("HGUNICODEPEDANTRY is not yet supported");
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   117
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   118
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   119
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   120
fn update_modules_path(env: &Environment, py: Python, sys_mod: &PyModule) {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   121
    let sys_path = sys_mod.get(py, "path").unwrap();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   122
    sys_path
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   123
        .call_method(py, "insert", (0, env.mercurial_modules.to_str()), None)
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   124
        .expect("failed to update sys.path to location of Mercurial modules");
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   125
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   126
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   127
fn run() -> Result<(), i32> {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   128
    let env = get_environment();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   129
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   130
    //println!("{:?}", env);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   131
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   132
    // Tell Python where it is installed.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   133
    set_python_home(&env);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   134
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   135
    // Set program name. The backing memory needs to live for the duration of the
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   136
    // interpreter.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   137
    //
35604
74bec9e74831 rust: add TODO about lifetime of program_name variable
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35569
diff changeset
   138
    // TODO consider storing this in a static or associating with lifetime of
74bec9e74831 rust: add TODO about lifetime of program_name variable
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35569
diff changeset
   139
    // the Python interpreter.
74bec9e74831 rust: add TODO about lifetime of program_name variable
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35569
diff changeset
   140
    //
35569
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   141
    // Yes, we use the path to the Python interpreter not argv[0] here. The
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   142
    // reason is because Python uses the given path to find the location of
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   143
    // Python files. Apparently we could define our own ``Py_GetPath()``
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   144
    // implementation. But this may require statically linking Python, which is
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   145
    // not desirable.
35631
edbe11cfedcf rust: extract function to convert Path to platform CString
Yuya Nishihara <yuya@tcha.org>
parents: 35604
diff changeset
   146
    let program_name = cstring_from_os(&env.python_exe).as_ptr();
35569
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   147
    unsafe {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   148
        python27_sys::Py_SetProgramName(program_name as *mut i8);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   149
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   150
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   151
    unsafe {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   152
        python27_sys::Py_Initialize();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   153
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   154
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   155
    // https://docs.python.org/2/c-api/init.html#c.PySys_SetArgvEx has important
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   156
    // usage information about PySys_SetArgvEx:
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   157
    //
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   158
    // * It says the first argument should be the script that is being executed.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   159
    //   If not a script, it can be empty. We are definitely not a script.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   160
    //   However, parts of Mercurial do look at sys.argv[0]. So we need to set
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   161
    //   something here.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   162
    //
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   163
    // * When embedding Python, we should use ``PySys_SetArgvEx()`` and set
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   164
    //   ``updatepath=0`` for security reasons. Essentially, Python's default
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   165
    //   logic will treat an empty argv[0] in a manner that could result in
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   166
    //   sys.path picking up directories it shouldn't and this could lead to
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   167
    //   loading untrusted modules.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   168
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   169
    // env::args() will panic if it sees a non-UTF-8 byte sequence. And
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   170
    // Mercurial supports arbitrary encodings of input data. So we need to
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   171
    // use OS-specific mechanisms to get the raw bytes without UTF-8
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   172
    // interference.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   173
    let args = args_to_cstrings();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   174
    let argv: Vec<*const c_char> = args.iter().map(|a| a.as_ptr()).collect();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   175
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   176
    unsafe {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   177
        python27_sys::PySys_SetArgvEx(args.len() as c_int, argv.as_ptr() as *mut *mut i8, 0);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   178
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   179
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   180
    let result;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   181
    {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   182
        // These need to be dropped before we call Py_Finalize(). Hence the
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   183
        // block.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   184
        let gil = Python::acquire_gil();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   185
        let py = gil.python();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   186
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   187
        // Mercurial code could call sys.exit(), which will call exit()
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   188
        // itself. So this may not return.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   189
        // TODO this may cause issues on Windows due to the CRT mismatch.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   190
        // Investigate if we can intercept sys.exit() or SystemExit() to
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   191
        // ensure we handle process exit.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   192
        result = match run_py(&env, py) {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   193
            // Print unhandled exceptions and exit code 255, as this is what
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   194
            // `python` does.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   195
            Err(err) => {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   196
                err.print(py);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   197
                Err(255)
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   198
            }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   199
            Ok(()) => Ok(()),
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   200
        };
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   201
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   202
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   203
    unsafe {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   204
        python27_sys::Py_Finalize();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   205
    }
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   206
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   207
    result
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   208
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   209
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   210
fn run_py(env: &Environment, py: Python) -> PyResult<()> {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   211
    let sys_mod = py.import("sys").unwrap();
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   212
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   213
    update_encoding(py, &sys_mod);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   214
    update_modules_path(&env, py, &sys_mod);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   215
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   216
    // TODO consider a better error message on failure to import.
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   217
    let demand_mod = py.import("hgdemandimport")?;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   218
    demand_mod.call(py, "enable", NoArgs, None)?;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   219
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   220
    let dispatch_mod = py.import("mercurial.dispatch")?;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   221
    dispatch_mod.call(py, "run", NoArgs, None)?;
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   222
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   223
    Ok(())
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   224
}
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   225
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   226
fn main() {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   227
    let exit_code = match run() {
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   228
        Err(err) => err,
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   229
        Ok(()) => 0,
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   230
    };
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   231
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   232
    std::process::exit(exit_code);
964212780daf rust: implementation of `hg`
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   233
}