mirror of
https://github.com/ankitects/anki.git
synced 2025-11-06 12:47:11 -05:00
sys.executable is the bin we're getting sys.executable from 🤦♂️
This commit is contained in:
parent
8ac763ceba
commit
88752aeeb7
3 changed files with 29 additions and 75 deletions
|
|
@ -1,13 +1,12 @@
|
||||||
# Copyright: Ankitects Pty Ltd and contributors
|
# Copyright: Ankitects Pty Ltd and contributors
|
||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
|
||||||
cfg = sysconfig.get_config_var
|
cfg = sysconfig.get_config_var
|
||||||
base = cfg("installed_base") or cfg("installed_platbase")
|
base = cfg("installed_base") or cfg("installed_platbase")
|
||||||
lib = cfg("LDLIBRARY") or cfg("INSTSONAME")
|
lib = cfg("LDLIBRARY") or cfg("INSTSONAME")
|
||||||
version = cfg("py_version_nodot")
|
version = cfg("py_version_nodot")
|
||||||
print(json.dumps([version, os.path.join(base, "lib", lib), sys.executable]))
|
print(version)
|
||||||
|
print(os.path.join(base, "lib", lib))
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
# Copyright: Ankitects Pty Ltd and contributors
|
# Copyright: Ankitects Pty Ltd and contributors
|
||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
|
||||||
cfg = sysconfig.get_config_var
|
cfg = sysconfig.get_config_var
|
||||||
base = cfg("installed_base") or cfg("installed_platbase")
|
base = cfg("installed_base") or cfg("installed_platbase")
|
||||||
version = cfg("py_version_nodot")
|
version = cfg("py_version_nodot")
|
||||||
lib = "python" + version + ".dll"
|
lib = "python" + version + ".dll"
|
||||||
print(json.dumps([version, os.path.join(base, lib), sys.executable]))
|
print(version)
|
||||||
|
print(os.path.join(base, lib))
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@ use std::ffi::CString;
|
||||||
use std::io::stdin;
|
use std::io::stdin;
|
||||||
use std::io::stdout;
|
use std::io::stdout;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Component;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
@ -17,13 +14,13 @@ use std::time::UNIX_EPOCH;
|
||||||
use anki_i18n::I18n;
|
use anki_i18n::I18n;
|
||||||
use anki_io::copy_file;
|
use anki_io::copy_file;
|
||||||
use anki_io::create_dir_all;
|
use anki_io::create_dir_all;
|
||||||
use anki_io::create_file;
|
|
||||||
use anki_io::modified_time;
|
use anki_io::modified_time;
|
||||||
use anki_io::read_file;
|
use anki_io::read_file;
|
||||||
use anki_io::remove_file;
|
use anki_io::remove_file;
|
||||||
use anki_io::write_file;
|
use anki_io::write_file;
|
||||||
use anki_io::ToUtf8Path;
|
use anki_io::ToUtf8Path;
|
||||||
use anki_process::CommandExt as AnkiCommandExt;
|
use anki_process::CommandExt as AnkiCommandExt;
|
||||||
|
use anyhow::anyhow;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
|
@ -1044,8 +1041,8 @@ fn uv_command(state: &State) -> Result<Command> {
|
||||||
Ok(command)
|
Ok(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _build_python_command(state: &State) -> Result<Command> {
|
fn get_venv_bin_path(state: &State) -> std::path::PathBuf {
|
||||||
let python_exe = if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
let show_console = std::env::var("ANKI_CONSOLE").is_ok();
|
let show_console = std::env::var("ANKI_CONSOLE").is_ok();
|
||||||
if show_console {
|
if show_console {
|
||||||
state.venv_folder.join("Scripts/python.exe")
|
state.venv_folder.join("Scripts/python.exe")
|
||||||
|
|
@ -1054,9 +1051,11 @@ fn _build_python_command(state: &State) -> Result<Command> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.venv_folder.join("bin/python")
|
state.venv_folder.join("bin/python")
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut cmd = Command::new(&python_exe);
|
fn _build_python_command(state: &State, python_exe: &std::path::Path) -> Result<Command> {
|
||||||
|
let mut cmd = Command::new(python_exe);
|
||||||
// tell the Python code it was invoked by the launcher, and updating is
|
// tell the Python code it was invoked by the launcher, and updating is
|
||||||
// available
|
// available
|
||||||
cmd.env("ANKI_LAUNCHER", std::env::current_exe()?.utf8()?.as_str());
|
cmd.env("ANKI_LAUNCHER", std::env::current_exe()?.utf8()?.as_str());
|
||||||
|
|
@ -1070,78 +1069,38 @@ fn _build_python_command(state: &State) -> Result<Command> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_python_command(state: &State, args: &[String]) -> Result<Command> {
|
fn build_python_command(state: &State, args: &[String]) -> Result<Command> {
|
||||||
let mut cmd = _build_python_command(state)?;
|
let python_exe = get_venv_bin_path(state);
|
||||||
|
let mut cmd = _build_python_command(state, &python_exe)?;
|
||||||
cmd.args(["-c", "import aqt, sys; sys.argv[0] = 'Anki'; aqt.run()"]);
|
cmd.args(["-c", "import aqt, sys; sys.argv[0] = 'Anki'; aqt.run()"]);
|
||||||
cmd.args(args);
|
cmd.args(args);
|
||||||
Ok(cmd)
|
Ok(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize a path, removing things like `.` and `..` w/o following symlinks
|
|
||||||
/// NOTE: lifted from https://github.com/rust-lang/cargo/blob/28b79ea2b7b6d922d3ee85a935e63deed42db9c1/crates/cargo-util/src/paths.rs#L84
|
|
||||||
pub fn normalize_path(path: &Path) -> PathBuf {
|
|
||||||
let mut components = path.components().peekable();
|
|
||||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
|
|
||||||
components.next();
|
|
||||||
PathBuf::from(c.as_os_str())
|
|
||||||
} else {
|
|
||||||
PathBuf::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
for component in components {
|
|
||||||
match component {
|
|
||||||
Component::Prefix(..) => unreachable!(),
|
|
||||||
Component::RootDir => {
|
|
||||||
ret.push(Component::RootDir);
|
|
||||||
}
|
|
||||||
Component::CurDir => {}
|
|
||||||
Component::ParentDir => {
|
|
||||||
if ret.ends_with(Component::ParentDir) {
|
|
||||||
ret.push(Component::ParentDir);
|
|
||||||
} else {
|
|
||||||
let popped = ret.pop();
|
|
||||||
if !popped && !ret.has_root() {
|
|
||||||
ret.push(Component::ParentDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Component::Normal(c) => {
|
|
||||||
ret.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_python_env_info(state: &State) -> Result<(String, std::path::PathBuf, CString)> {
|
fn get_python_env_info(state: &State) -> Result<(String, std::path::PathBuf, CString)> {
|
||||||
|
let python_exe = get_venv_bin_path(state);
|
||||||
// we can cache this, as it can only change after syncing the project
|
// we can cache this, as it can only change after syncing the project
|
||||||
// as it stands, we already expect there to be a trusted python exe in
|
// as it stands, we already expect there to be a trusted python exe in
|
||||||
// a particular place so this doesn't seem too concerning security-wise
|
// a particular place so this doesn't seem too concerning security-wise
|
||||||
type Cache = (String, PathBuf, PathBuf);
|
|
||||||
// TODO: let-chains...
|
// TODO: let-chains...
|
||||||
if let Ok(cached) = read_file(&state.libpython_info) {
|
if let Ok(cached) = read_file(&state.libpython_info) {
|
||||||
if let Ok(cached) = String::from_utf8(cached) {
|
if let Ok(cached) = String::from_utf8(cached) {
|
||||||
if let Ok((version, lib_path, exec_path)) = serde_json::from_str::<Cache>(&cached) {
|
if let Some((version, lib_path)) = cached.split_once('\n') {
|
||||||
if let Ok(lib_path) = state.uv_install_root.join(lib_path).canonicalize() {
|
if let Ok(lib_path) = state.uv_install_root.join(lib_path).canonicalize() {
|
||||||
// can't use canonicalise here as it follows symlinks,
|
|
||||||
// we need bin to be in the venv for it to know where
|
|
||||||
// to find the pyvenv.cfg that's in the parent dir
|
|
||||||
let exec_path = normalize_path(&state.uv_install_root.join(exec_path));
|
|
||||||
// make sure we're still within AnkiProgramFiles...
|
// make sure we're still within AnkiProgramFiles...
|
||||||
if lib_path.strip_prefix(&state.uv_install_root).is_ok()
|
if lib_path.strip_prefix(&state.uv_install_root).is_ok() {
|
||||||
&& exec_path.strip_prefix(&state.uv_install_root).is_ok()
|
|
||||||
{
|
|
||||||
return Ok((
|
return Ok((
|
||||||
version,
|
version.to_string(),
|
||||||
lib_path,
|
lib_path,
|
||||||
CString::new(exec_path.as_os_str().as_encoded_bytes())?,
|
CString::new(python_exe.as_os_str().as_encoded_bytes())?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let _ = remove_file(&state.libpython_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cmd = _build_python_command(state)?;
|
let mut cmd = _build_python_command(state, &python_exe)?;
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// we can check which sysconfig vars are available
|
// we can check which sysconfig vars are available
|
||||||
// with `sysconfig.get_config_vars()`. very limited on
|
// with `sysconfig.get_config_vars()`. very limited on
|
||||||
|
|
@ -1162,29 +1121,26 @@ fn get_python_env_info(state: &State) -> Result<(String, std::path::PathBuf, CSt
|
||||||
let output = cmd.utf8_output()?;
|
let output = cmd.utf8_output()?;
|
||||||
let output = output.stdout.trim();
|
let output = output.stdout.trim();
|
||||||
|
|
||||||
let (version, lib_path, exec_path): Cache = serde_json::from_str(output)?;
|
let (version, lib_path) = output
|
||||||
|
.split_once('\n')
|
||||||
|
.ok_or_else(|| anyhow!("invalid libpython info"))?;
|
||||||
|
let lib_path = std::path::PathBuf::from(lib_path);
|
||||||
|
|
||||||
if !lib_path.exists() {
|
if !lib_path.exists() {
|
||||||
anyhow::bail!("library path doesn't exist: {lib_path:?}");
|
anyhow::bail!("library path doesn't exist: {lib_path:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exec_path.exists() {
|
if let Ok(lib_path) = lib_path.strip_prefix(&state.uv_install_root) {
|
||||||
anyhow::bail!("exec path doesn't exist: {exec_path:?}");
|
let _ = write_file(
|
||||||
}
|
&state.libpython_info,
|
||||||
|
format!("{version}\n{}", lib_path.display()),
|
||||||
if let Ok(file) = create_file(&state.libpython_info) {
|
|
||||||
let cached = (
|
|
||||||
version.clone(),
|
|
||||||
lib_path.strip_prefix(&state.uv_install_root)?,
|
|
||||||
exec_path.strip_prefix(&state.uv_install_root)?,
|
|
||||||
);
|
);
|
||||||
let _ = serde_json::to_writer(file, &cached);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
version.to_owned(),
|
version.to_owned(),
|
||||||
lib_path,
|
lib_path,
|
||||||
CString::new(exec_path.as_os_str().as_encoded_bytes())?,
|
CString::new(python_exe.as_os_str().as_encoded_bytes())?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue