mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Merge branch 'main' into issue-4276
This commit is contained in:
commit
43832e18e2
6 changed files with 113 additions and 63 deletions
|
@ -1,10 +1,5 @@
|
||||||
adding-add-shortcut-ctrlandenter = Add (shortcut: ctrl+enter)
|
adding-add-shortcut-ctrlandenter = Add (shortcut: ctrl+enter)
|
||||||
adding-added = Added
|
adding-added = Added
|
||||||
adding-added-cards =
|
|
||||||
Added { $count ->
|
|
||||||
[one] { $count } card
|
|
||||||
*[other] { $count } cards
|
|
||||||
}
|
|
||||||
adding-discard-current-input = Discard current input?
|
adding-discard-current-input = Discard current input?
|
||||||
adding-keep-editing = Keep Editing
|
adding-keep-editing = Keep Editing
|
||||||
adding-edit = Edit "{ $val }"
|
adding-edit = Edit "{ $val }"
|
||||||
|
|
|
@ -300,7 +300,7 @@ class AddCards(QMainWindow):
|
||||||
|
|
||||||
self.addHistory(note)
|
self.addHistory(note)
|
||||||
|
|
||||||
tooltip(tr.adding_added_cards(count=changes.count), period=500)
|
tooltip(tr.importing_cards_added(count=changes.count), period=500)
|
||||||
av_player.stop_and_clear_queue()
|
av_player.stop_and_clear_queue()
|
||||||
self._load_new_note(sticky_fields_from=note)
|
self._load_new_note(sticky_fields_from=note)
|
||||||
gui_hooks.add_cards_did_add_note(note)
|
gui_hooks.add_cards_did_add_note(note)
|
||||||
|
|
|
@ -30,6 +30,12 @@ lipo -create \
|
||||||
-output "$APP_LAUNCHER/Contents/MacOS/launcher"
|
-output "$APP_LAUNCHER/Contents/MacOS/launcher"
|
||||||
cp "$OUTPUT_DIR/uv" "$APP_LAUNCHER/Contents/MacOS/"
|
cp "$OUTPUT_DIR/uv" "$APP_LAUNCHER/Contents/MacOS/"
|
||||||
|
|
||||||
|
# Build install_name_tool stub
|
||||||
|
clang -arch arm64 -o "$OUTPUT_DIR/stub_arm64" stub.c
|
||||||
|
clang -arch x86_64 -o "$OUTPUT_DIR/stub_x86_64" stub.c
|
||||||
|
lipo -create "$OUTPUT_DIR/stub_arm64" "$OUTPUT_DIR/stub_x86_64" -output "$APP_LAUNCHER/Contents/MacOS/install_name_tool"
|
||||||
|
rm "$OUTPUT_DIR/stub_arm64" "$OUTPUT_DIR/stub_x86_64"
|
||||||
|
|
||||||
# Copy support files
|
# Copy support files
|
||||||
ANKI_VERSION=$(cat ../../../.version | tr -d '\n')
|
ANKI_VERSION=$(cat ../../../.version | tr -d '\n')
|
||||||
sed "s/ANKI_VERSION/$ANKI_VERSION/g" Info.plist > "$APP_LAUNCHER/Contents/Info.plist"
|
sed "s/ANKI_VERSION/$ANKI_VERSION/g" Info.plist > "$APP_LAUNCHER/Contents/Info.plist"
|
||||||
|
@ -40,7 +46,7 @@ cp ../versions.py "$APP_LAUNCHER/Contents/Resources/"
|
||||||
|
|
||||||
# Codesign/bundle
|
# Codesign/bundle
|
||||||
if [ -z "$NODMG" ]; then
|
if [ -z "$NODMG" ]; then
|
||||||
for i in "$APP_LAUNCHER/Contents/MacOS/uv" "$APP_LAUNCHER/Contents/MacOS/launcher" "$APP_LAUNCHER"; do
|
for i in "$APP_LAUNCHER/Contents/MacOS/uv" "$APP_LAUNCHER/Contents/MacOS/install_name_tool" "$APP_LAUNCHER/Contents/MacOS/launcher" "$APP_LAUNCHER"; do
|
||||||
codesign --force -vvvv -o runtime -s "Developer ID Application:" \
|
codesign --force -vvvv -o runtime -s "Developer ID Application:" \
|
||||||
--entitlements entitlements.python.xml \
|
--entitlements entitlements.python.xml \
|
||||||
"$i"
|
"$i"
|
||||||
|
|
6
qt/launcher/mac/stub.c
Normal file
6
qt/launcher/mac/stub.c
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -51,6 +51,8 @@ struct State {
|
||||||
previous_version: Option<String>,
|
previous_version: Option<String>,
|
||||||
resources_dir: std::path::PathBuf,
|
resources_dir: std::path::PathBuf,
|
||||||
venv_folder: std::path::PathBuf,
|
venv_folder: std::path::PathBuf,
|
||||||
|
/// system Python + PyQt6 library mode
|
||||||
|
system_qt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -88,9 +90,13 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run() -> Result<()> {
|
fn run() -> Result<()> {
|
||||||
let uv_install_root = dirs::data_local_dir()
|
let uv_install_root = if let Ok(custom_root) = std::env::var("ANKI_LAUNCHER_VENV_ROOT") {
|
||||||
|
std::path::PathBuf::from(custom_root)
|
||||||
|
} else {
|
||||||
|
dirs::data_local_dir()
|
||||||
.context("Unable to determine data_dir")?
|
.context("Unable to determine data_dir")?
|
||||||
.join("AnkiProgramFiles");
|
.join("AnkiProgramFiles")
|
||||||
|
};
|
||||||
|
|
||||||
let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?;
|
let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?;
|
||||||
|
|
||||||
|
@ -113,6 +119,8 @@ fn run() -> Result<()> {
|
||||||
mirror_path: uv_install_root.join("mirror"),
|
mirror_path: uv_install_root.join("mirror"),
|
||||||
pyproject_modified_by_user: false, // calculated later
|
pyproject_modified_by_user: false, // calculated later
|
||||||
previous_version: None,
|
previous_version: None,
|
||||||
|
system_qt: (cfg!(unix) && !cfg!(target_os = "macos"))
|
||||||
|
&& resources_dir.join("system_qt").exists(),
|
||||||
resources_dir,
|
resources_dir,
|
||||||
venv_folder: uv_install_root.join(".venv"),
|
venv_folder: uv_install_root.join(".venv"),
|
||||||
};
|
};
|
||||||
|
@ -193,8 +201,8 @@ fn extract_aqt_version(state: &State) -> Option<String> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new(&state.uv_path)
|
let output = uv_command(state)
|
||||||
.current_dir(&state.uv_install_root)
|
.ok()?
|
||||||
.env("VIRTUAL_ENV", &state.venv_folder)
|
.env("VIRTUAL_ENV", &state.venv_folder)
|
||||||
.args(["pip", "show", "aqt"])
|
.args(["pip", "show", "aqt"])
|
||||||
.output()
|
.output()
|
||||||
|
@ -261,24 +269,11 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let have_venv = state.venv_folder.exists();
|
|
||||||
if cfg!(target_os = "macos") && !have_developer_tools() && !have_venv {
|
|
||||||
println!("If you see a pop-up about 'install_name_tool', you can cancel it, and ignore the warning below.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare to sync the venv
|
// Prepare to sync the venv
|
||||||
let mut command = Command::new(&state.uv_path);
|
let mut command = uv_command(state)?;
|
||||||
command.current_dir(&state.uv_install_root);
|
|
||||||
|
|
||||||
// remove UV_* environment variables to avoid interference
|
|
||||||
for (key, _) in std::env::vars() {
|
|
||||||
if key.starts_with("UV_") || key == "VIRTUAL_ENV" {
|
|
||||||
command.env_remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if cfg!(target_os = "macos") {
|
||||||
// remove CONDA_PREFIX/bin from PATH to avoid conda interference
|
// remove CONDA_PREFIX/bin from PATH to avoid conda interference
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
if let Ok(conda_prefix) = std::env::var("CONDA_PREFIX") {
|
if let Ok(conda_prefix) = std::env::var("CONDA_PREFIX") {
|
||||||
if let Ok(current_path) = std::env::var("PATH") {
|
if let Ok(current_path) = std::env::var("PATH") {
|
||||||
let conda_bin = format!("{conda_prefix}/bin");
|
let conda_bin = format!("{conda_prefix}/bin");
|
||||||
|
@ -290,6 +285,30 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re
|
||||||
command.env("PATH", new_path);
|
command.env("PATH", new_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// put our fake install_name_tool at the top of the path to override
|
||||||
|
// potential conflicts
|
||||||
|
if let Ok(current_path) = std::env::var("PATH") {
|
||||||
|
let exe_dir = std::env::current_exe()
|
||||||
|
.ok()
|
||||||
|
.and_then(|exe| exe.parent().map(|p| p.to_path_buf()));
|
||||||
|
if let Some(exe_dir) = exe_dir {
|
||||||
|
let new_path = format!("{}:{}", exe_dir.display(), current_path);
|
||||||
|
command.env("PATH", new_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create venv with system site packages if system Qt is enabled
|
||||||
|
if state.system_qt {
|
||||||
|
let mut venv_command = uv_command(state)?;
|
||||||
|
venv_command.args([
|
||||||
|
"venv",
|
||||||
|
"--no-managed-python",
|
||||||
|
"--system-site-packages",
|
||||||
|
"--no-config",
|
||||||
|
]);
|
||||||
|
venv_command.ensure_success()?;
|
||||||
|
}
|
||||||
|
|
||||||
command
|
command
|
||||||
.env("UV_CACHE_DIR", &state.uv_cache_dir)
|
.env("UV_CACHE_DIR", &state.uv_cache_dir)
|
||||||
|
@ -297,25 +316,24 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re
|
||||||
.env(
|
.env(
|
||||||
"UV_HTTP_TIMEOUT",
|
"UV_HTTP_TIMEOUT",
|
||||||
std::env::var("UV_HTTP_TIMEOUT").unwrap_or_else(|_| "180".to_string()),
|
std::env::var("UV_HTTP_TIMEOUT").unwrap_or_else(|_| "180".to_string()),
|
||||||
)
|
);
|
||||||
.args(["sync", "--upgrade", "--managed-python", "--no-config"]);
|
|
||||||
|
|
||||||
// Add python version if .python-version file exists
|
command.args(["sync", "--upgrade", "--no-config"]);
|
||||||
|
if !state.system_qt {
|
||||||
|
command.arg("--managed-python");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add python version if .python-version file exists (but not for system Qt)
|
||||||
if let Some(version) = &python_version_trimmed {
|
if let Some(version) = &python_version_trimmed {
|
||||||
|
if !state.system_qt {
|
||||||
command.args(["--python", version]);
|
command.args(["--python", version]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if state.no_cache_marker.exists() {
|
if state.no_cache_marker.exists() {
|
||||||
command.env("UV_NO_CACHE", "1");
|
command.env("UV_NO_CACHE", "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add mirror environment variable if enabled
|
|
||||||
if let Some((python_mirror, pypi_mirror)) = get_mirror_urls(state)? {
|
|
||||||
command
|
|
||||||
.env("UV_PYTHON_INSTALL_MIRROR", &python_mirror)
|
|
||||||
.env("UV_DEFAULT_INDEX", &pypi_mirror);
|
|
||||||
}
|
|
||||||
|
|
||||||
match command.ensure_success() {
|
match command.ensure_success() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Sync succeeded
|
// Sync succeeded
|
||||||
|
@ -665,9 +683,8 @@ fn filter_and_normalize_versions(
|
||||||
fn fetch_versions(state: &State) -> Result<Vec<String>> {
|
fn fetch_versions(state: &State) -> Result<Vec<String>> {
|
||||||
let versions_script = state.resources_dir.join("versions.py");
|
let versions_script = state.resources_dir.join("versions.py");
|
||||||
|
|
||||||
let mut cmd = Command::new(&state.uv_path);
|
let mut cmd = uv_command(state)?;
|
||||||
cmd.current_dir(&state.uv_install_root)
|
cmd.args(["run", "--no-project", "--no-config", "--managed-python"])
|
||||||
.args(["run", "--no-project", "--no-config", "--managed-python"])
|
|
||||||
.args(["--with", "pip-system-certs,requests[socks]"]);
|
.args(["--with", "pip-system-certs,requests[socks]"]);
|
||||||
|
|
||||||
let python_version = read_file(&state.dist_python_version_path)?;
|
let python_version = read_file(&state.dist_python_version_path)?;
|
||||||
|
@ -680,12 +697,6 @@ fn fetch_versions(state: &State) -> Result<Vec<String>> {
|
||||||
|
|
||||||
cmd.arg(&versions_script);
|
cmd.arg(&versions_script);
|
||||||
|
|
||||||
// Add mirror environment variable if enabled
|
|
||||||
if let Some((python_mirror, pypi_mirror)) = get_mirror_urls(state)? {
|
|
||||||
cmd.env("UV_PYTHON_INSTALL_MIRROR", &python_mirror)
|
|
||||||
.env("UV_DEFAULT_INDEX", &pypi_mirror);
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = match cmd.utf8_output() {
|
let output = match cmd.utf8_output() {
|
||||||
Ok(output) => output,
|
Ok(output) => output,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -738,7 +749,26 @@ fn apply_version_kind(version_kind: &VersionKind, state: &State) -> Result<()> {
|
||||||
&format!("anki-release=={version}\",\n \"anki=={version}\",\n \"aqt=={version}"),
|
&format!("anki-release=={version}\",\n \"anki=={version}\",\n \"aqt=={version}"),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
write_file(&state.user_pyproject_path, &updated_content)?;
|
|
||||||
|
let final_content = if state.system_qt {
|
||||||
|
format!(
|
||||||
|
concat!(
|
||||||
|
"{}\n\n[tool.uv]\n",
|
||||||
|
"override-dependencies = [\n",
|
||||||
|
" \"pyqt6; sys_platform=='never'\",\n",
|
||||||
|
" \"pyqt6-qt6; sys_platform=='never'\",\n",
|
||||||
|
" \"pyqt6-webengine; sys_platform=='never'\",\n",
|
||||||
|
" \"pyqt6-webengine-qt6; sys_platform=='never'\",\n",
|
||||||
|
" \"pyqt6_sip; sys_platform=='never'\"\n",
|
||||||
|
"]\n"
|
||||||
|
),
|
||||||
|
updated_content
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
updated_content
|
||||||
|
};
|
||||||
|
|
||||||
|
write_file(&state.user_pyproject_path, &final_content)?;
|
||||||
|
|
||||||
// Update .python-version based on version kind
|
// Update .python-version based on version kind
|
||||||
match version_kind {
|
match version_kind {
|
||||||
|
@ -930,12 +960,25 @@ fn handle_uninstall(state: &State) -> Result<bool> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn have_developer_tools() -> bool {
|
fn uv_command(state: &State) -> Result<Command> {
|
||||||
Command::new("xcode-select")
|
let mut command = Command::new(&state.uv_path);
|
||||||
.args(["-p"])
|
command.current_dir(&state.uv_install_root);
|
||||||
.output()
|
|
||||||
.map(|output| output.status.success())
|
// remove UV_* environment variables to avoid interference
|
||||||
.unwrap_or(false)
|
for (key, _) in std::env::vars() {
|
||||||
|
if key.starts_with("UV_") || key == "VIRTUAL_ENV" {
|
||||||
|
command.env_remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add mirror environment variable if enabled
|
||||||
|
if let Some((python_mirror, pypi_mirror)) = get_mirror_urls(state)? {
|
||||||
|
command
|
||||||
|
.env("UV_PYTHON_INSTALL_MIRROR", &python_mirror)
|
||||||
|
.env("UV_DEFAULT_INDEX", &pypi_mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_python_command(state: &State, args: &[String]) -> Result<Command> {
|
fn build_python_command(state: &State, args: &[String]) -> Result<Command> {
|
||||||
|
|
|
@ -62,7 +62,7 @@ pub fn prepare_for_launch_after_update(mut cmd: Command, root: &Path) -> Result<
|
||||||
pub fn relaunch_in_terminal() -> Result<()> {
|
pub fn relaunch_in_terminal() -> Result<()> {
|
||||||
let current_exe = std::env::current_exe().context("Failed to get current executable path")?;
|
let current_exe = std::env::current_exe().context("Failed to get current executable path")?;
|
||||||
Command::new("open")
|
Command::new("open")
|
||||||
.args(["-a", "Terminal"])
|
.args(["-na", "Terminal"])
|
||||||
.arg(current_exe)
|
.arg(current_exe)
|
||||||
.ensure_spawn()?;
|
.ensure_spawn()?;
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
|
|
Loading…
Reference in a new issue