diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 80338601..a3d622b1 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -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. diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 539f4b4c..26158098 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 diff --git a/alvr/server/Cargo.toml b/alvr/server/Cargo.toml index bf03025b..34e75d3b 100644 --- a/alvr/server/Cargo.toml +++ b/alvr/server/Cargo.toml @@ -10,7 +10,8 @@ rust-version = "1.58" crate-type = ["cdylib"] [features] -gpl = [] +gpl = ["local_ffmpeg"] +local_ffmpeg = [] [dependencies] alvr_audio = { path = "../audio" } diff --git a/alvr/server/build.rs b/alvr/server/build.rs index 11c8ad2e..917689de 100644 --- a/alvr/server/build.rs +++ b/alvr/server/build.rs @@ -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++") diff --git a/alvr/xtask/src/build.rs b/alvr/xtask/src/build.rs index b2603457..f82f5743 100644 --- a/alvr/xtask/src/build.rs +++ b/alvr/xtask/src/build.rs @@ -8,6 +8,7 @@ pub fn build_server( root: Option, 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); diff --git a/alvr/xtask/src/main.rs b/alvr/xtask/src/main.rs index c70c0663..12eeac08 100644 --- a/alvr/xtask/src/main.rs +++ b/alvr/xtask/src/main.rs @@ -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 = args.opt_value_from_str("--platform").unwrap(); let version: Option = args.opt_value_from_str("--version").unwrap(); let root: Option = 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(), diff --git a/alvr/xtask/src/packaging.rs b/alvr/xtask/src/packaging.rs index c32e8c8c..7913cd63 100644 --- a/alvr/xtask/src/packaging.rs +++ b/alvr/xtask/src/packaging.rs @@ -44,10 +44,16 @@ fn build_windows_installer() { .unwrap(); } -pub fn package_server(root: Option, gpl: bool) { +pub fn package_server( + root: Option, + 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, 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(); diff --git a/resources/alvr.png b/resources/alvr.png new file mode 100644 index 00000000..cebd5d18 Binary files /dev/null and b/resources/alvr.png differ