Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ installers next to the wheel and the VS Code extension:
settings and logs too.
- **Debian, Ubuntu and their relatives:** `mpftp_<version>_amd64.deb`, which
uses the system's WebKitGTK. Install it with
`sudo apt install ./mpftp_<version>_amd64.deb` and start `mpftp`.
`sudo apt install ./mpftp_<version>_amd64.deb` and start `mpftp`. With
arguments, such as `mpftp --help` or `mpftp ls -d /dev/ttyACM0`, the same
command is the CLI, run with the Python the package carries.

To serial ports on Linux, your user needs to be in the `dialout` group (or
`uucp` on some distributions).
Expand Down
13 changes: 12 additions & 1 deletion desktop/scripts/smoke_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# the window comes up on a virtual display, the app starts its Python host
# and the sidecar with the Python it carries, and closing the app leaves no
# Python behind. CI runs this on every build; it needs xvfb-run and, for a
# .deb, sudo to install it.
# .deb, sudo to install it. It also checks that `mpftp` with arguments is
# the CLI, not the window.
#
# smoke_linux.sh deb path/to/mpftp_X.Y.Z_amd64.deb
# smoke_linux.sh appimage path/to/mpftp_X.Y.Z_amd64.AppImage
Expand Down Expand Up @@ -32,6 +33,16 @@ esac
[[ -x "$python" ]] || { echo "::error::no bundled python3 in the $kind"; exit 1; }
"$python" -I -c "import sys, mpftp.desktop_host, mpftp.sidecar; print(sys.version)"

# With arguments the package's mpftp is the CLI: it prints and exits, and
# no window or Python host starts.
cli_out="$(timeout 60 "$app" --help 2>&1)" \
|| { echo "::error::'mpftp --help' from the $kind failed"; echo "$cli_out"; exit 1; }
grep -q '^usage: mpftp' <<<"$cli_out" \
|| { echo "::error::'mpftp --help' from the $kind didn't print the CLI's help"; echo "$cli_out"; exit 1; }
[[ -z "$(pgrep -f -- '-m mpftp.desktop_host' || true)" ]] \
|| { echo "::error::'mpftp --help' from the $kind started the desktop host"; exit 1; }
timeout 60 "$app" --version

logs="$HOME/.local/share/com.pydevices.mpftp/logs"
rm -rf "$logs"
xvfb-run -a "$app" >"$work/app.out" 2>&1 &
Expand Down
45 changes: 45 additions & 0 deletions desktop/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,52 @@ fn supervise(app: AppHandle, host: Arc<Host>, emu: Arc<Emu>, python: PathBuf, lo
}
}

/// On Linux the package puts this app on PATH as `mpftp`, the CLI's own
/// name. Started with arguments (`mpftp --help`, `mpftp ls -d ...`) it is the
/// CLI: it runs the bundled Python's mpftp in its place and never opens a
/// window. With none, from the launcher or a bare `mpftp`, it is the app.
#[cfg(unix)]
fn run_cli_if_asked() {
use std::os::unix::process::CommandExt;

let args: Vec<std::ffi::OsString> = std::env::args_os().skip(1).collect();
if args.is_empty() {
return;
}
let python = std::env::var_os("MPFTP_DESKTOP_PYTHON")
.map(PathBuf::from)
.or_else(|| {
let exe = std::env::current_exe().ok()?.canonicalize().ok()?;
let dir = exe.parent()?;
// Installed: /usr/bin/mpftp with its files in /usr/lib/mpftp
// (an AppImage keeps the same layout). A dev build keeps them
// beside the binary.
[
dir.join("../lib/mpftp/python/bin/python3"),
dir.join("python/bin/python3"),
]
.into_iter()
.find(|p| p.is_file())
});
let Some(python) = python else {
eprintln!("mpftp: can't find the bundled Python, so the command line isn't available from this install");
std::process::exit(2);
};
let err = Command::new(&python)
.args(["-m", "mpftp"])
.args(&args)
// The CLI starts its sidecar with this same Python.
.env("MPFTP_PYTHON", &python)
.env("PYTHONNOUSERSITE", "1")
.exec();
eprintln!("mpftp: could not run {}: {err}", python.display());
std::process::exit(2);
}

fn main() {
#[cfg(unix)]
run_cli_if_asked();

let host = Arc::new(Host::default());
let host_for_exit = host.clone();
let emu = Arc::new(Emu::default());
Expand Down
Loading