Skip to content
Open
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
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[alias]
xtask = "run --quiet --package xtask --"
vendored-phosh = "run --quiet --config .cargo/vendor.toml --package xtask -- vendored-phosh"
23 changes: 23 additions & 0 deletions .cargo/vendor-runner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Cargo invokes this for vendored test binaries and `cargo run --config ...`.
set -euo pipefail

# The dispatcher must start before libphosh (and its schemas) has been built.
if [[ $(basename "$1") == xtask ]]; then
exec "$@"
fi

profile_dir=$(dirname "$1")
if [[ $(basename "$profile_dir") == deps ]]; then
profile_dir=$(dirname "$profile_dir")
fi
schemas=("$profile_dir"/build/libphosh-sys-*/out/phosh/data/gschemas.compiled)
for schema in "${schemas[@]}"; do
if [[ ! -f "$schema" ]] || ! cmp -s "${schemas[0]}" "$schema"; then
echo 'Missing or inconsistent vendored Phosh schemas; clean libphosh-sys with the vendor configuration and rebuild.' >&2
exit 1
fi
done
export GSETTINGS_SCHEMA_DIR
GSETTINGS_SCHEMA_DIR=$(cd "$(dirname "${schemas[0]}")" && pwd)
exec "$@"
13 changes: 13 additions & 0 deletions .cargo/vendor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Opt in from the repository root. Ordinary builds and publication use crates.io.
[patch.crates-io]
libphosh = { path = "libphosh-rs/libphosh" }
libphosh-sys = { path = "libphosh-rs/libphosh/sys" }

[env]
PHROG_LIBPHOSH_BUILD_INTERNAL = { value = "always", force = true }

[build]
target-dir = "target/vendor"

[target.'cfg(unix)']
runner = ".cargo/vendor-runner.sh"
53 changes: 44 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
push:
branches: [main]
pull_request_target:
branches: [main]
branches: [main, vendor-base]
types: [labeled, opened, reopened, synchronize]
Comment thread
samcday marked this conversation as resolved.
workflow_dispatch: {}

Expand Down Expand Up @@ -51,6 +51,18 @@ jobs:
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
include:
- libphosh: system
cargo: cargo
build_flags: --locked
target: target
- libphosh: bundled
cargo: cargo vendored-phosh
build_flags: ''
Comment thread
samcday marked this conversation as resolved.
target: target/vendor
steps:
- if: |
github.event_name == 'pull_request_target' &&
Expand All @@ -68,34 +80,57 @@ jobs:
- name: Build
run: |
set -uexo pipefail
cargo build --all-targets --verbose
${{ matrix.cargo }} build ${{ matrix.build_flags }} --all-targets --verbose
- name: Check generic vendor commands
if: matrix.libphosh == 'bundled'
run: |
cargo vendored-phosh tree --locked -i libphosh
cargo vendored-phosh run --locked -- --help
cargo vendored-phosh clippy --locked --package xtask --bin xtask --no-deps -- -D warnings
- name: Verify libphosh link mode
run: |
set -euo pipefail
dependencies="$(LC_ALL=C readelf -d ${{ matrix.target }}/debug/phrog)"
case "$dependencies" in
*'Shared library: [libphosh-'*) actual=system ;;
*) actual=bundled ;;
esac
test "$actual" = '${{ matrix.libphosh }}'
- name: Lint
run: |
cargo clippy --all-targets --verbose
${{ matrix.cargo }} clippy --locked --all-targets --no-deps --verbose -- -D warnings
- name: Test embedding build support
if: matrix.libphosh == 'bundled'
run: cargo vendored-phosh test --locked --manifest-path libphosh-rs/Cargo.toml -p libphosh-sys --test native_source
- name: Verify registry package
if: matrix.libphosh == 'system'
run: cargo package --locked --no-verify
- name: Lint (fmt)
run: |
cargo fmt --all --check
${{ matrix.cargo }} fmt --all --check
- uses: actions/upload-artifact@v4
with:
name: x86-64-debug
path: target/debug/phrog
name: x86-64-debug${{ matrix.libphosh == 'bundled' && '-bundled' || '' }}
path: ${{ matrix.target }}/debug/phrog
- name: Test
run: |
set -uexo pipefail

export XDG_RUNTIME_DIR=/tmp

# run tests
export RECORD_TESTS=`pwd`/demo-video/recordings/
if [ '${{ matrix.libphosh }}' = system ]; then
export RECORD_TESTS="$PWD/demo-video/recordings/"
fi
export G_MESSAGES_DEBUG=all
cat > phoc.ini <<HERE
[output:X11-1]
mode = 720x1440
scale = 2
HERE
dbus-run-session xvfb-run -a -s -noreset phoc -C phoc.ini -S -E "cargo test --verbose -- --test-threads=1 --nocapture"
dbus-run-session xvfb-run -a -s -noreset phoc -C phoc.ini -S -E "${{ matrix.cargo }} test --locked --verbose -- --test-threads=1 --nocapture"
- uses: actions/upload-artifact@v4
if: always()
if: always() && matrix.libphosh == 'system'
with:
name: test-recordings
path: demo-video/recordings
Expand Down
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,13 @@
## Configuration & Runtime Notes
- `phrog` is a greetd greeter; packaging in `debian/`, `copr/`, and `APKBUILD` shows how it is wired into services.
- Local testing without greetd uses `--fake` and the default login password `0`.

## Vendored Sources
- Keep patches to `phosh/` and `libphosh-rs/` minimal and limited to changes required for embedding. Do not reformat generated/imported code, add formatting exclusions, or change upstream dependency pins incidentally. Keep phrog-specific tooling outside those trees where practical.
- Import and update these directories with `git subtree add` / `git subtree pull --squash`, keeping local fixes in separate commits scoped to one subtree.
- Preserve the subtree merge commits and `git-subtree-*` trailers when landing or restacking: use merge commits, not GitHub squash/rebase merging.
- Update to an explicit upstream commit, then review conflicts and run the relevant build checks:
- GTK3 Phosh: `git subtree pull --prefix=phosh --squash https://gitlab.gnome.org/World/Phosh/phosh.git <commit>`
- GTK4 Phosh: use `https://gitlab.gnome.org/guidog/phosh.git` with the same command.
- Bindings: `git subtree pull --prefix=libphosh-rs --squash https://gitlab.gnome.org/World/Phosh/libphosh-rs.git <commit>`
- Export downstream fixes with `git format-patch --stdout --relative=<prefix> <import-merge>..HEAD -- <prefix>`. Plain `git subtree split` traverses obsolete imports in phrog's old history and can fail on unavailable fork commits.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description = "Mobile-friendly greeter for greetd"
version = "0.53.0"
edition = "2021"
license = "GPL-3.0-only"
exclude = ["/demo-video"]
exclude = ["/demo-video", "/phosh", "/libphosh-rs", "/.cargo/vendor.toml", "/.cargo/vendor-runner.sh"]
repository = "https://github.com/samcday/phrog"

[workspace]
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ Once `libphosh` is installed, building and running 🐸 should be quite simple:
phoc -S -E "cargo run -- --fake"

phoc -S -E "cargo test"

# Embed the vendored libphosh (requires Phosh build dependencies):
cargo vendored-phosh build # Also accepts run, test, fmt, tree, clippy, etc.
```

## Getting help
Expand Down
1 change: 1 addition & 0 deletions libphosh-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions libphosh-rs/libphosh/sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ package = "libhandy-sys"
version = "0.11"

[build-dependencies]
pkg-config = "0.3.30"
system-deps = "7"

[dev-dependencies]
Expand Down
184 changes: 175 additions & 9 deletions libphosh-rs/libphosh/sys/build.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,184 @@
// Generated by gir (https://github.com/gtk-rs/gir @ be9aa145267c)
// from ../.. (@ b4c1dbc27547+)
// from ../../gir-files (@ 6cd7b656acd6)
// DO NOT EDIT
#[cfg(not(docsrs))]
mod native_source;

#[cfg(not(docsrs))]
use std::process;
use std::{collections::HashSet, env, path::PathBuf, process::Command};

#[cfg(docsrs)]
fn main() {} // prevent linking libraries to avoid documentation failure
fn main() {}

#[cfg(not(docsrs))]
fn main() {
if let Err(s) = system_deps::Config::new().probe() {
println!("cargo:warning={s}");
process::exit(1);
println!("cargo:rerun-if-env-changed=PHOSH_SRC");
println!("cargo:rerun-if-env-changed=PHROG_LIBPHOSH_BUILD_INTERNAL");
match env::var("PHROG_LIBPHOSH_BUILD_INTERNAL").as_deref() {
Ok("always") => build_bundled(),
Err(env::VarError::NotPresent) | Ok("never") => {
system_deps::Config::new()
.probe()
.expect("system libphosh-0.45 is required (or set PHROG_LIBPHOSH_BUILD_INTERNAL=always)");
}
_ => panic!("PHROG_LIBPHOSH_BUILD_INTERNAL must be always or never; implicit fallback is not supported"),
}
}

#[cfg(not(docsrs))]
fn run(command: &mut Command) {
let status = command
.status()
.unwrap_or_else(|err| panic!("{command:?}: {err}"));
assert!(status.success(), "{command:?} failed: {status}");
}

#[cfg(not(docsrs))]
fn build_bundled() {
assert_eq!(env::var("HOST").unwrap(), env::var("TARGET").unwrap(),
"bundled libphosh currently supports native builds only; use system libphosh for cross builds");
let manifest = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
let source = env::var_os("PHOSH_SRC")
.map(PathBuf::from)
.unwrap_or_else(|| manifest.join("../../../phosh"));
let source = source
.canonicalize()
.expect("Phosh source tree not found; set PHOSH_SRC");
assert!(
source.join("meson.build").is_file(),
"PHOSH_SRC must point to a Phosh source tree"
);
println!("cargo:rerun-if-changed={}", source.display());
let mut configuration = format!("source={source:?}\n");
for name in [
"CC",
"CFLAGS",
"CPPFLAGS",
"LDFLAGS",
"PKG_CONFIG_PATH",
"PKG_CONFIG_LIBDIR",
] {
println!("cargo:rerun-if-env-changed={name}");
configuration.push_str(&format!("{name}={:?}\n", env::var_os(name)));
}
let output = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let staged_source = output.join("phosh-source");
native_source::stage(&source, &staged_source).expect("failed to stage Phosh sources");
println!("cargo:rerun-if-env-changed=PHROG_VENDOR_OFFLINE");
match env::var("PHROG_VENDOR_OFFLINE").as_deref() {
Ok("1") => (),
Err(env::VarError::NotPresent) | Ok("0") => {
run(Command::new("meson")
.args(["subprojects", "download", "--sourcedir"])
.arg(&staged_source)
.args(["gvc", "libcall-ui"]));
}
_ => panic!("PHROG_VENDOR_OFFLINE must be 0 or 1"),
}
let build = output.join("phosh");
let inputs = output.join("phosh-build-inputs");
// Meson caches the source directory, compiler and environment flags at setup.
// Recreate only our generated build tree when those inputs change.
if build.exists() && std::fs::read_to_string(&inputs).ok().as_ref() != Some(&configuration) {
std::fs::remove_dir_all(&build).expect("failed to reset the native build directory");
}
run(Command::new("meson")
.args([
"setup",
"--reconfigure",
"--backend=ninja",
"--wrap-mode=nodownload",
"--prefix=/usr",
// Do not load plugins belonging to the system Phosh ABI.
"--libdir=lib/phrog",
"-Dbindings-lib=true",
"-Dtests=false",
"-Dphoc_tests=disabled",
"-Dlockscreen-plugins=false",
"-Dquick-setting-plugins=false",
])
.arg(&build)
.arg(&staged_source));
run(Command::new("meson")
.args(["compile", "-C"])
.arg(&build)
.args([
"phosh-0.45:static_library",
"phosh-tool",
"glib-compile-schemas",
]));

std::fs::write(inputs, configuration).expect("failed to record native build inputs");

// Promote only libphosh's private dependencies. A recursive --static probe
// also exposes private dependencies of shared libraries (e.g. PulseAudio's
// libpulsecommon), which the executable must not link directly.
let metadata_dir = build.join("meson-uninstalled");
let metadata = std::fs::read_to_string(metadata_dir.join("libphosh-0.45-uninstalled.pc"))
.expect("failed to read Meson's uninstalled libphosh metadata");
let mut shared_metadata = metadata.clone();
for field in ["Requires", "Libs"] {
let public = format!("{field}:");
let private = format!("{field}.private:");
let values: Vec<_> = metadata
.lines()
.filter_map(|line| {
line.strip_prefix(&public)
.or_else(|| line.strip_prefix(&private))
})
.collect();
shared_metadata = shared_metadata
.lines()
.filter(|line| !line.starts_with(&public) && !line.starts_with(&private))
.map(|line| format!("{line}\n"))
.collect();
shared_metadata.push_str(&format!("{field}: {}\n", values.join(" ")));
}
std::fs::write(metadata_dir.join("phrog-libphosh.pc"), shared_metadata)
.expect("failed to write embedding metadata");

// This build's metadata must win over any installed libphosh.
let mut paths = vec![metadata_dir];
if let Some(existing) = env::var_os("PKG_CONFIG_PATH") {
paths.extend(env::split_paths(&existing));
}
env::set_var("PKG_CONFIG_PATH", env::join_paths(paths).unwrap());
let lib = pkg_config::Config::new()
.cargo_metadata(false)
.print_system_libs(false)
.statik(false)
.probe("phrog-libphosh")
.expect("failed to read bundled libphosh pkg-config metadata");
// pkg-config can repeat transitive flags many times. Preserve their
// order while avoiding GCC's per-argument/environment size limit.
let mut seen_paths = HashSet::new();
for path in &lib.link_paths {
if !seen_paths.insert(path) {
continue;
}
println!("cargo:rustc-link-search=native={}", path.display());
}
let mut seen_libs = HashSet::new();
for name in &lib.libs {
if !seen_libs.insert(name) {
continue;
}
let bundled = lib
.link_paths
.iter()
.any(|path| path.starts_with(&build) && path.join(format!("lib{name}.a")).is_file());
assert!(
name != "phosh-0.45" || bundled,
"bundled libphosh archive not found"
);
println!(
"cargo:rustc-link-lib={}{}",
if bundled { "static=" } else { "" },
name
);
}
let mut seen_args = HashSet::new();
for args in &lib.ld_args {
if !seen_args.insert(args) {
continue;
}
println!("cargo:rustc-link-arg=-Wl,{}", args.join(","));
}
}
Loading
Loading