AppImage linux releases (#1181)

* AppImage build

* AppImage workflow

* use appimagetool bundled with linuxdeploy

* add appimage update info

* Deploy ffmpeg deps /w linuxdeploy

* change dependencies in gh actions

* fix non-gpl builds

* simplify server build script, set-up for potential future ffmpeg static linking work
This commit is contained in:
m00nwtchr 2022-09-21 20:55:06 +00:00 committed by GitHub
parent 871c6d348a
commit 0c4bd7d8d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 199 additions and 63 deletions

View File

@ -133,16 +133,16 @@ jobs:
RUST_BACKTRACE: 1
run: |
sudo apt-get update
sudo apt-get install build-essential pkg-config nasm libva-dev libdrm-dev libvulkan-dev libx264-dev libx265-dev cmake libasound2-dev libjack-jackd2-dev libgtk-3-dev libunwind-dev libffmpeg-nvenc-dev nvidia-cuda-toolkit
sudo apt-get install build-essential pkg-config nasm libva-dev libdrm-dev libvulkan-dev libx264-dev libx265-dev cmake libasound2-dev libjack-jackd2-dev libxrandr-dev libunwind-dev libffmpeg-nvenc-dev nvidia-cuda-toolkit
cp packaging/deb/cuda.pc /usr/share/pkgconfig
cargo xtask prepare-deps --platform linux
- name: Build and package ALVR (.tar.gz)
id: build
env:
RUST_BACKTRACE: 1
run: |
cargo xtask package-server
cargo xtask package-server --local-ffmpeg
- name: Upload server for Linux
uses: actions/upload-release-asset@v1
@ -154,12 +154,12 @@ jobs:
asset_name: alvr_server_linux.tar.gz
asset_content_type: application/gzip
- name: Build and package ALVR portable (.tar.gz)
- name: Build and package ALVR portable (.AppImage)
id: build_portable
env:
RUST_BACKTRACE: 1
run: |
cargo xtask package-server --gpl
cargo xtask package-server --gpl --appimage --zsync
- name: Upload portable server for Linux
uses: actions/upload-release-asset@v1
@ -167,9 +167,19 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.prepare_release.outputs.upload_url }}
asset_path: ./build/alvr_server_linux.tar.gz
asset_name: alvr_server_linux_portable.tar.gz
asset_content_type: application/gzip
asset_path: ./build/ALVR-x86_64.AppImage
asset_name: ALVR-x86_64.AppImage
asset_content_type: application/appimage
- name: Upload portable server for Linux (zsync)
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.prepare_release.outputs.upload_url }}
asset_path: ./build/ALVR-x86_64.AppImage.zsync
asset_name: ALVR-x86_64.AppImage.zsync
asset_content_type: application/appimage
build_android_client:
# Windows latest has Rust, Android NDK and LLVM already installed.

View File

@ -47,7 +47,7 @@ jobs:
RUST_BACKTRACE: 1
run: |
sudo apt update
sudo apt install build-essential pkg-config nasm libva-dev libdrm-dev libvulkan-dev libx264-dev libx265-dev cmake libasound2-dev libjack-jackd2-dev libgtk-3-dev libunwind-dev
sudo apt install build-essential pkg-config nasm libva-dev libdrm-dev libvulkan-dev libx264-dev libx265-dev cmake libasound2-dev libjack-jackd2-dev libxrandr-dev libunwind-dev
cargo xtask prepare-deps --platform linux --no-nvidia
- name: Build crates

View File

@ -10,7 +10,8 @@ rust-version = "1.58"
crate-type = ["cdylib"]
[features]
gpl = []
gpl = ["local_ffmpeg"]
local_ffmpeg = []
[dependencies]
alvr_audio = { path = "../audio" }

View File

@ -2,23 +2,32 @@
use pkg_config;
use std::{env, path::PathBuf};
// this code must be executed BEFORE the actual cpp build when using bundled ffmpeg,
// as it adds definitions and include flags
// but AFTER the build in other cases because linker flags must appear after.
#[cfg(target_os = "linux")]
fn do_ffmpeg_pkg_config(build: &mut cc::Build) {
let ffmpeg_path = alvr_filesystem::deps_dir().join("linux/ffmpeg");
let alvr_build = if cfg!(feature = "gpl") {
"alvr_build"
} else {
""
};
let ffmpeg_build_dir = ffmpeg_path.join(alvr_build);
fn get_ffmpeg_path() -> PathBuf {
let ffmpeg_path = alvr_filesystem::deps_dir()
.join(if cfg!(target_os = "linux") {
"linux"
} else {
"windows"
})
.join("ffmpeg");
#[cfg(feature = "gpl")]
if cfg!(target_os = "linux") {
ffmpeg_path.join("alvr_build")
} else {
ffmpeg_path
}
}
#[cfg(feature = "local_ffmpeg")]
fn do_ffmpeg_config(build: &mut cc::Build) {
let ffmpeg_path = get_ffmpeg_path();
assert!(ffmpeg_path.join("include").exists());
build.include(ffmpeg_path.join("include"));
#[cfg(all(feature = "gpl", target_os = "linux"))]
{
assert!(ffmpeg_path.exists());
let ffmpeg_pkg_path = ffmpeg_build_dir.join("lib/pkgconfig/");
let ffmpeg_pkg_path = ffmpeg_path.join("lib").join("pkgconfig");
assert!(ffmpeg_pkg_path.exists());
let ffmpeg_pkg_path = ffmpeg_pkg_path.to_string_lossy().to_string();
@ -28,17 +37,14 @@ fn do_ffmpeg_pkg_config(build: &mut cc::Build) {
format!("{ffmpeg_pkg_path}:{old}")
}),
);
}
let pkg = pkg_config::Config::new()
.cargo_metadata(cfg!(not(feature = "gpl")))
.to_owned();
let avutil = pkg.probe("libavutil").unwrap();
let avfilter = pkg.probe("libavfilter").unwrap();
let avcodec = pkg.probe("libavcodec").unwrap();
let swscale = pkg.probe("libswscale").unwrap();
let pkg = pkg_config::Config::new().cargo_metadata(false).to_owned();
let avutil = pkg.probe("libavutil").unwrap();
let avfilter = pkg.probe("libavfilter").unwrap();
let avcodec = pkg.probe("libavcodec").unwrap();
let swscale = pkg.probe("libswscale").unwrap();
if cfg!(feature = "gpl") {
build
.define("AVCODEC_MAJOR", avcodec.version.split(".").next().unwrap())
.define("AVUTIL_MAJOR", avutil.version.split(".").next().unwrap())
@ -48,9 +54,6 @@ fn do_ffmpeg_pkg_config(build: &mut cc::Build) {
)
.define("SWSCALE_MAJOR", swscale.version.split(".").next().unwrap());
assert!(ffmpeg_build_dir.join("include").exists());
build.include(ffmpeg_build_dir.join("include"));
// activate dlopen for libav libraries
build
.define("LIBRARY_LOADER_AVCODEC_LOADER_H_DLOPEN", None)
@ -62,20 +65,35 @@ fn do_ffmpeg_pkg_config(build: &mut cc::Build) {
}
}
#[cfg(all(windows, feature = "gpl"))]
fn do_ffmpeg_windows_config(build: &mut cc::Build) {
let ffmpeg_dir = alvr_filesystem::deps_dir().join("windows/ffmpeg");
fn do_ffmpeg_config_post() {
if cfg!(feature = "local_ffmpeg") {
// TODO: cfg!(feature = "gpl") - switch to static linking
let kind = if false { "static" } else { "dylib" };
build.include(format!("{}/include", ffmpeg_dir.to_string_lossy()));
let ffmpeg_path = get_ffmpeg_path();
let ffmpeg_lib_path = ffmpeg_path.join("lib");
assert!(ffmpeg_lib_path.exists());
println!(
"cargo:rustc-link-search=native={}/lib",
ffmpeg_dir.to_string_lossy()
);
println!("cargo:rustc-link-lib=avcodec");
println!("cargo:rustc-link-lib=avutil");
println!("cargo:rustc-link-lib=avfilter");
println!("cargo:rustc-link-lib=swscale");
println!(
"cargo:rustc-link-search=native={}",
ffmpeg_lib_path.to_string_lossy()
);
println!("cargo:rustc-link-lib={}=avutil", kind);
println!("cargo:rustc-link-lib={}=avfilter", kind);
println!("cargo:rustc-link-lib={}=avcodec", kind);
println!("cargo:rustc-link-lib={}=swscale", kind);
} else {
#[cfg(target_os = "linux")]
{
let pkg = pkg_config::Config::new().to_owned();
pkg.probe("libavutil").unwrap();
pkg.probe("libavfilter").unwrap();
pkg.probe("libavcodec").unwrap();
pkg.probe("libswscale").unwrap();
}
}
}
fn main() {
@ -135,19 +153,15 @@ fn main() {
// #[cfg(debug_assertions)]
// build.define("ALVR_DEBUG_LOG", None);
#[cfg(all(target_os = "linux", feature = "gpl"))]
do_ffmpeg_pkg_config(&mut build);
#[cfg(feature = "local_ffmpeg")]
do_ffmpeg_config(&mut build);
#[cfg(all(windows, feature = "gpl"))]
{
do_ffmpeg_windows_config(&mut build);
build.define("ALVR_GPL", None);
}
build.define("ALVR_GPL", None);
build.compile("bindings");
#[cfg(all(target_os = "linux", not(feature = "gpl")))]
do_ffmpeg_pkg_config(&mut build);
do_ffmpeg_config_post();
bindgen::builder()
.clang_arg("-xc++")

View File

@ -8,6 +8,7 @@ pub fn build_server(
root: Option<String>,
reproducible: bool,
experiments: bool,
local_ffmpeg: bool,
) {
let sh = Shell::new().unwrap();
@ -24,7 +25,9 @@ pub fn build_server(
}
let common_flags_ref = &common_flags;
let gpl_flag = gpl.then(|| vec!["--features", "gpl"]).unwrap_or_default();
let gpl_flag = (gpl || local_ffmpeg)
.then(|| vec!["--features", if gpl { "gpl" } else { "local_ffmpeg" }])
.unwrap_or_default();
let artifacts_dir = afs::target_dir().join(build_type);

View File

@ -36,6 +36,9 @@ FLAGS:
--release Optimized build without debug info. For build subcommands
--gpl Bundle GPL libraries. For build subcommands
--experiments Build unfinished features. For build subcommands
--local-ffmpeg Use local build of ffmpeg in non GPL build. For build subcommands
--appimage Package as AppImage. For package-server subcommand
--zsync For --appimage, create .zsync update file and build AppImage with embedded update information. For package-server subcommand
--nightly Append nightly tag to versions. For bump subcommand
--no-rebuild Do not rebuild the server with run-server
--ci Do some CI related tweaks. Depends on the other flags and subcommand
@ -167,6 +170,11 @@ fn main() {
let no_rebuild = args.contains("--no-rebuild");
let for_ci = args.contains("--ci");
let appimage = args.contains("--appimage");
let zsync = args.contains("--zsync");
let local_ffmpeg = args.contains("--local-ffmpeg");
let platform: Option<String> = args.opt_value_from_str("--platform").unwrap();
let version: Option<String> = args.opt_value_from_str("--version").unwrap();
let root: Option<String> = args.opt_value_from_str("--root").unwrap();
@ -191,16 +199,27 @@ fn main() {
dependencies::build_android_deps(for_ci);
}
}
"build-server" => build::build_server(is_release, gpl, None, false, experiments),
"build-server" => {
build::build_server(is_release, gpl, None, false, experiments, local_ffmpeg)
}
"build-client" => build::build_quest_client(is_release),
"build-client-lib" => build::build_client_lib(is_release),
"run-server" => {
if !no_rebuild {
build::build_server(is_release, gpl, None, false, experiments);
build::build_server(
is_release,
gpl,
None,
false,
experiments,
local_ffmpeg,
);
}
run_server();
}
"package-server" => packaging::package_server(root, gpl),
"package-server" => {
packaging::package_server(root, gpl, local_ffmpeg, appimage, zsync)
}
"package-client" => build::build_quest_client(true),
"package-client-lib" => packaging::package_client_lib(),
"clean" => clean(),

View File

@ -44,10 +44,16 @@ fn build_windows_installer() {
.unwrap();
}
pub fn package_server(root: Option<String>, gpl: bool) {
pub fn package_server(
root: Option<String>,
gpl: bool,
local_ffmpeg: bool,
appimage: bool,
zsync: bool,
) {
let sh = Shell::new().unwrap();
build::build_server(true, gpl, root, true, false);
build::build_server(true, gpl, root, true, false, local_ffmpeg);
// Add licenses
let licenses_dir = afs::server_build_dir().join("licenses");
@ -92,9 +98,92 @@ pub fn package_server(root: Option<String>, gpl: bool) {
build_windows_installer();
} else {
command::targz(&sh, &afs::server_build_dir()).unwrap();
if appimage {
server_appimage(true, gpl, zsync).unwrap();
}
}
}
pub fn server_appimage(release: bool, gpl: bool, update: bool) -> Result<(), xshell::Error> {
let sh = Shell::new().unwrap();
let appdir = &afs::build_dir().join("ALVR.AppDir");
let bin = &afs::build_dir().join("alvr_server_linux");
let icon = &afs::workspace_dir().join("resources/alvr.png");
let desktop = &afs::workspace_dir().join("packaging/freedesktop/alvr.desktop");
let linuxdeploy = &afs::build_dir().join("linuxdeploy-x86_64.AppImage");
if !sh.path_exists(&linuxdeploy) {
command::download(&sh, "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage", &linuxdeploy).ok();
}
cmd!(&sh, "chmod a+x {linuxdeploy}").run().ok();
if sh.path_exists(&appdir) {
sh.remove_path(&appdir).ok();
}
cmd!(&sh, "{linuxdeploy} --appdir={appdir}").run().ok();
sh.cmd("sh")
.arg("-c")
.arg(format!(
"cp -r {}/* {}/usr",
bin.to_string_lossy(),
appdir.to_string_lossy()
))
.run()
.ok();
sh.set_var("ARCH", "x86_64");
sh.set_var("OUTPUT", "ALVR-x86_64.AppImage");
if release {
let version = version::version();
sh.set_var("VERSION", &version);
if update {
let repo = if version.contains("nightly") {
"ALVR-nightly"
} else {
"ALVR"
};
sh.set_var(
"UPDATE_INFORMATION",
format!("gh-releases-zsync|alvr-org|{repo}|latest|ALVR-x86_64.AppImage.zsync"),
);
}
}
sh.set_var("VERBOSE", "1");
sh.set_var("NO_APPSTREAM", "1");
// Faster decompression (gzip) or smaller AppImage size (xz)?
// sh.set_var("APPIMAGE_COMP", "xz"); // Currently uses gzip compression, will take effect when linuxdeploy updates.
sh.change_dir(&afs::build_dir());
let mut cmd = cmd!(&sh, "{linuxdeploy} --appdir={appdir} -i{icon} -d{desktop} --deploy-deps-only={appdir}/usr/lib64/alvr/bin/linux64/driver_alvr_server.so --deploy-deps-only={appdir}/usr/lib64/libalvr_vulkan_layer.so --output appimage");
if gpl {
for lib_path in sh
.read_dir(appdir.join("usr/lib64/alvr"))
.unwrap()
.into_iter()
.filter(|path| path.file_name().unwrap().to_string_lossy().contains(".so."))
{
let file_name = lib_path.file_name().unwrap().to_string_lossy();
if file_name.contains("libx264.so") || file_name.contains("libx265.so") {
sh.remove_path(lib_path).ok();
} else {
cmd = cmd.arg(format!("--deploy-deps-only={}", lib_path.to_string_lossy()));
}
}
}
cmd.run()
}
pub fn package_client_lib() {
let sh = Shell::new().unwrap();

BIN
resources/alvr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB