diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 0000000..f9eec03 --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,39 @@ +name: CI + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + +permissions: + contents: read + id-token: write + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-test: + name: Build and Test + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - name: Enable Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Build + run: nix develop -c zig build -Dnix + + - name: Test + run: nix develop -c zig build test -Dnix diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ab9da32 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,135 @@ +name: Release + +on: + push: + branches: + - main + tags: + - "v*" + +permissions: + contents: write + id-token: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - name: Enable Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Resolve and Validate Release Version + id: version + run: | + # Grab the version from build.zig.zon. + base_version="$(sed -n 's/^[[:space:]]*\.version = "\(.*\)",/\1/p' build.zig.zon | head -n1)" + if [ -z "$base_version" ]; then + echo "Failed to parse version from build.zig.zon" >&2 + exit 1 + fi + + # If triggered by a new tag. + if [ "${GITHUB_REF_TYPE}" = "tag" ]; then + expected_tag="v${base_version}" + actual_tag="${GITHUB_REF_NAME}" + + if [ "$actual_tag" != "$expected_tag" ]; then + echo "Tag/version mismatch. build.zig.zon has ${base_version}, expected tag ${expected_tag}, got ${actual_tag}" >&2 + exit 1 + fi + + release_tag="${actual_tag}" + release_version="${base_version}" + prerelease="false" + # Triggered by merge to main. + else + short_sha="$(git rev-parse --short=8 HEAD)" + release_version="${base_version}+g${short_sha}" + release_tag="nightly" + prerelease="true" + fi + + echo "release_tag=${release_tag}" >> "$GITHUB_OUTPUT" + echo "release_version=${release_version}" >> "$GITHUB_OUTPUT" + echo "prerelease=${prerelease}" >> "$GITHUB_OUTPUT" + + - name: Build and Test + run: | + nix develop -c bash -lc ' + set -euo pipefail + echo "Release version: ${{ steps.version.outputs.release_version }}" + zig build -Dnix + zig build test -Dnix + ' + + - name: Build Release Binaries + run: nix develop -c zig build -Dappimage -Doptimize=ReleaseFast + + # - name: Package Linux + # run: tar -czf spacecap-linux-x86_64.tar.gz -C zig-out linux + + # - name: Package Windows + # run: zip -r spacecap-windows-x86_64.zip zig-out/windows + + - name: Generate Checksums + run: | + # sha256sum spacecap-linux-x86_64.tar.gz > SHA256SUMS.txt + # sha256sum spacecap-windows-x86_64.zip >> SHA256SUMS.txt + sha256sum zig-out/linux/spacecap-linux-x86_64.AppImage >> SHA256SUMS.txt + + - name: Upload Workflow Artifacts + uses: actions/upload-artifact@v4 + with: + name: spacecap-release-assets + # spacecap-linux-x86_64.tar.gz is currently disabled. + # spacecap-windows-x86_64.zip is currently disabled. + path: | + zig-out/linux/spacecap-linux-x86_64.AppImage + SHA256SUMS.txt + + - name: Move Nightly Tag + if: github.ref_type != 'tag' + run: | + git tag -f nightly "${GITHUB_SHA}" + git push --force origin refs/tags/nightly + + - name: Check Existing Release + id: existing_release + env: + GH_TOKEN: ${{ github.token }} + run: | + if gh release view "${{ steps.version.outputs.release_tag }}" > /dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + + - name: Delete Existing Nightly Release + if: github.ref_type != 'tag' && steps.existing_release.outputs.exists == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: gh release delete "${{ steps.version.outputs.release_tag }}" --yes + + - name: Publish GitHub Release Assets + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.version.outputs.release_tag }} + target_commitish: ${{ github.sha }} + prerelease: ${{ steps.version.outputs.prerelease }} + generate_release_notes: ${{ github.ref_type != 'tag' || steps.existing_release.outputs.exists != 'true' }} + append_body: false + # spacecap-linux-x86_64.tar.gz is currently disabled. + # spacecap-windows-x86_64.zip is currently disabled. + files: | + zig-out/linux/spacecap-linux-x86_64.AppImage + SHA256SUMS.txt diff --git a/README.md b/README.md index be62331..a040829 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,64 @@ alternative to OBS for capturing video replays. **NOTE:** I'm testing with an RTX 3080 GPU. I have no idea if AMD works. I don't have one to test on. -## How to compile and run +## Linux Requirements -Currently this only works on Linux with [Nix](https://nixos.org/download/#download-nix). -A GPU that supports Vulkan Video is required for recording. +- vulkan (and related graphics drivers) +- pipewire +- pipewire-pulse + +### Installation + +**NOTE:** A reboot may be required. + +#### NixOS + +There is currently not a Nix package published, but for now you can use +`appimage-run` to execute the appimage. + +TODO: Add vulkan instructions. + +```nix +{...}: { + security.rtkit.enable = true; + + services.pipewire = { + enable = true; + alsa.enable = false; + pulse.enable = true; + wireplumber.enable = true; + }; + + environment.systemPackages = with pkgs; [ + appimage-run + ]; +} +``` + +#### Arch + +TODO: Add vulkan instructions. + +```sh +sudo pacman -S --needed wireplumber pipewire pipewire-pulse pipewire-alsa fuse3 +systemctl --user --now enable pipewire pipewire-pulse wireplumber +``` + +#### Ubuntu + +TODO: Add vulkan instructions. + +```sh +sudo apt update +sudo apt install wireplumber pipewire pipewire-pulse pipewire-alsa fuse3 +systemctl --user --now enable pipewire pipewire-pulse wireplumber +``` + +## Development + +[Nix](https://nixos.org/download/#download-nix) is required for development, +unless you want to install all dependencies manually. See `flake.nix` if you'd +like to do so. ```sh # Build diff --git a/build.zig b/build.zig index efbc231..25d92d9 100644 --- a/build.zig +++ b/build.zig @@ -65,20 +65,59 @@ fn addSharedDependencies( const zigrc = b.dependency("zigrc", .{}); exe.root_module.addImport("zigrc", zigrc.module("zigrc")); - // ffmpeg - // Add ffmpeg headers here. They can be shared cross platform. Libs - // are added separately because they are platform specific. + // ffmpeg headers are shared across platforms; libs are platform-specific. const ffmpeg = b.dependency("ffmpeg", .{}); - const ffmpeg_path = ffmpeg.path("").getPath3(b, null).root_dir.path.?; exe.addIncludePath(ffmpeg.path("")); +} + +const FfmpegBuild = struct { + step: *std.Build.Step, + /// Directory will contain the static libraries. + lib_dir: std.Build.LazyPath, +}; + +// Build ffmpeg static libraries. +// NOTE: If any new ffmpeg functionality is added, then features will +// need to be enabled here. +fn build_ffmpeg(b: *std.Build) FfmpegBuild { + const ffmpeg = b.dependency("ffmpeg", .{}); + const build_ffmpeg_step = b.addSystemCommand(&.{ + "bash", + "-lc", + \\set -euo pipefail + \\./configure \ + \\ --prefix="$1" \ + \\ --disable-all \ + \\ --disable-debug \ + \\ --disable-autodetect \ + \\ --disable-doc \ + \\ --disable-network \ + \\ --disable-programs \ + \\ --disable-shared \ + \\ --enable-avutil \ + \\ --enable-avcodec \ + \\ --enable-avformat \ + \\ --enable-avdevice \ + \\ --enable-avfilter \ + \\ --enable-swresample \ + \\ --enable-swscale \ + \\ --enable-small \ + \\ --disable-runtime-cpudetect \ + \\ --enable-protocol=file \ + \\ --enable-muxer=mov,mp4,wav \ + \\ --enable-encoder=aac,pcm_f32le + \\make -j + \\make install + , + "ffmpeg-build", + }); + build_ffmpeg_step.setCwd(ffmpeg.path("")); + const ffmpeg_install_prefix = build_ffmpeg_step.addOutputDirectoryArg("ffmpeg-install"); + build_ffmpeg_step.expectExitCode(0); - // TODO: Make sure this only runs once. Currently during fresh install - // it runs 3 times - one for each type of build. - (try std.fs.openDirAbsolute(ffmpeg_path, .{})).access("libavutil/avconfig.h", .{}) catch { - std.debug.print("configuring ffmpeg... this may take a minute\n", .{}); - const ffmpeg_configure_step = b.addSystemCommand(&.{"./configure"}); - ffmpeg_configure_step.setCwd(ffmpeg.path("")); - exe.step.dependOn(&ffmpeg_configure_step.step); + return .{ + .step = &build_ffmpeg_step.step, + .lib_dir = ffmpeg_install_prefix.path(b, "lib"), }; } @@ -88,12 +127,9 @@ fn addLinuxDependencies( exe: *std.Build.Step.Compile, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, + ffmpeg_build: FfmpegBuild, ) !void { - // xkbcommon - // NOTE: may not be used actually? - // exe.addLibraryPath(.{ .cwd_relative = std.posix.getenv("LIBXKBCOMMON").? }); - // try installAndLinkSystemLibrary(allocator, b, exe, std.posix.getenv("LIBXKBCOMMON").?, "xkbcommon", .linux); - + _ = allocator; const pipewire = b.dependency("pipewire", .{ .optimize = optimize, .target = target, @@ -107,24 +143,24 @@ fn addLinuxDependencies( exe.root_module.addImport("gio", gobject.module("gio2")); exe.root_module.addImport("gobject", gobject.module("gobject2")); - // libportal - try installAndLinkSystemLibrary(allocator, b, exe, std.posix.getenv("LIBPORTAL").?, "portal", .linux, "libportal.so"); - - // vulkan - exe.addLibraryPath(.{ .cwd_relative = std.posix.getenv("VULKAN_SDK_PATH").? }); - try installAndLinkSystemLibrary(allocator, b, exe, std.posix.getenv("VULKAN_SDK_PATH").?, "vulkan", .linux, "libvulkan.so.1"); - - // ffmpeg - const ffmpeg_linux = b.dependency("ffmpeg_linux", .{}); - exe.addLibraryPath(ffmpeg_linux.path("lib")); - const ffmpeg_path = ffmpeg_linux.path("lib").getPath(b); - - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avformat", .linux, "libavformat.so.61"); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avcodec", .linux, "libavcodec.so.61"); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avdevice", .linux, "libavdevice.so.61"); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avfilter", .linux, "libavfilter.so.10"); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avutil", .linux, "libavutil.so.59"); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "swresample", .linux, "libswresample.so.5"); + exe.root_module.linkSystemLibrary("glib-2.0", .{}); + exe.root_module.linkSystemLibrary("gio-2.0", .{}); + exe.root_module.linkSystemLibrary("gobject-2.0", .{}); + exe.root_module.linkSystemLibrary("portal", .{}); + + // Vulkan is linked directly, because it is required that the + // system has the libs installed. + exe.root_module.linkSystemLibrary("vulkan", .{}); + + exe.step.dependOn(ffmpeg_build.step); + exe.addLibraryPath(ffmpeg_build.lib_dir); + exe.root_module.linkSystemLibrary("avformat", .{ .preferred_link_mode = .static }); + exe.root_module.linkSystemLibrary("avcodec", .{ .preferred_link_mode = .static }); + exe.root_module.linkSystemLibrary("avutil", .{ .preferred_link_mode = .static }); + exe.root_module.linkSystemLibrary("swresample", .{ .preferred_link_mode = .static }); + exe.root_module.linkSystemLibrary("avdevice", .{ .preferred_link_mode = .static }); + exe.root_module.linkSystemLibrary("avfilter", .{ .preferred_link_mode = .static }); + exe.root_module.linkSystemLibrary("swscale", .{ .preferred_link_mode = .static }); } /// Install a dynamic library in the /lib directory @@ -132,40 +168,41 @@ fn addLinuxDependencies( /// /// Lib name should be the name of the lib without extensions /// e.g. avformat NOT libavformat.so -fn installAndLinkSystemLibrary( +fn installAndLinkSystemLibrary(args: struct { allocator: std.mem.Allocator, b: *std.Build, exe: *std.Build.Step.Compile, source_dir: []const u8, lib_name: []const u8, target: enum { linux, windows }, - file_name_override: ?[]const u8, -) !void { - const file_name = file_name_override orelse switch (target) { - .linux => try std.fmt.allocPrint(allocator, "lib{s}.so", .{lib_name}), - .windows => try std.fmt.allocPrint(allocator, "{s}.dll", .{lib_name}), + file_name_override: ?[]const u8 = null, + link_options: std.Build.Module.LinkSystemLibraryOptions = .{}, +}) !void { + const file_name = args.file_name_override orelse switch (args.target) { + .linux => try std.fmt.allocPrint(args.allocator, "lib{s}.so", .{args.lib_name}), + .windows => try std.fmt.allocPrint(args.allocator, "{s}.dll", .{args.lib_name}), }; defer { - if (file_name_override == null) { - allocator.free(file_name); + if (args.file_name_override == null) { + args.allocator.free(file_name); } } - const full_file_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ source_dir, file_name }); - defer allocator.free(full_file_path); + const full_file_path = try std.fmt.allocPrint(args.allocator, "{s}/{s}", .{ args.source_dir, file_name }); + defer args.allocator.free(full_file_path); - const target_name = switch (target) { + const target_name = switch (args.target) { .linux => "linux/lib", .windows => "windows/lib", }; - const dest_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ target_name, file_name }); - defer allocator.free(dest_path); + const dest_path = try std.fmt.allocPrint(args.allocator, "{s}/{s}", .{ target_name, file_name }); + defer args.allocator.free(dest_path); - const step = b.addInstallFile(.{ .cwd_relative = full_file_path }, dest_path); - exe.step.dependOn(&step.step); + const step = args.b.addInstallFile(.{ .cwd_relative = full_file_path }, dest_path); + args.exe.step.dependOn(&step.step); - exe.linkSystemLibrary(lib_name); + args.exe.root_module.linkSystemLibrary(args.lib_name, args.link_options); } fn buildWindows( @@ -197,7 +234,14 @@ fn buildWindows( exe.addLibraryPath(.{ .cwd_relative = std.posix.getenv("VULKAN_SDK_PATH_WINDOWS").? }); - try installAndLinkSystemLibrary(allocator, b, exe, std.posix.getenv("VULKAN_SDK_PATH_WINDOWS").?, "vulkan-1", .windows, null); + try installAndLinkSystemLibrary(.{ + .allocator = allocator, + .b = b, + .exe = exe, + .source_dir = std.posix.getenv("VULKAN_SDK_PATH_WINDOWS").?, + .lib_name = "vulkan-1", + .target = .windows, + }); // All windows machines should be able to link to this by default exe.linkSystemLibrary("gdi32"); @@ -210,11 +254,46 @@ fn buildWindows( exe.addLibraryPath(ffmpeg_windows.path("bin")); const ffmpeg_path = ffmpeg_windows.path("bin").getPath(b); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avformat-61", .windows, null); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avcodec-61", .windows, null); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avdevice-61", .windows, null); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avfilter-10", .windows, null); - try installAndLinkSystemLibrary(allocator, b, exe, ffmpeg_path, "avutil-59", .windows, null); + try installAndLinkSystemLibrary(.{ + .allocator = allocator, + .b = b, + .exe = exe, + .source_dir = ffmpeg_path, + .lib_name = "avformat-61", + .target = .windows, + }); + try installAndLinkSystemLibrary(.{ + .allocator = allocator, + .b = b, + .exe = exe, + .source_dir = ffmpeg_path, + .lib_name = "avcodec-61", + .target = .windows, + }); + try installAndLinkSystemLibrary(.{ + .allocator = allocator, + .b = b, + .exe = exe, + .source_dir = ffmpeg_path, + .lib_name = "avdevice-61", + .target = .windows, + }); + try installAndLinkSystemLibrary(.{ + .allocator = allocator, + .b = b, + .exe = exe, + .source_dir = ffmpeg_path, + .lib_name = "avfilter-10", + .target = .windows, + }); + try installAndLinkSystemLibrary(.{ + .allocator = allocator, + .b = b, + .exe = exe, + .source_dir = ffmpeg_path, + .lib_name = "avutil-59", + .target = .windows, + }); const install_step = b.addInstallArtifact(exe, .{ .dest_dir = .{ .override = .{ .custom = "windows" } }, @@ -227,7 +306,9 @@ fn buildLinux( b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, -) !void { + nix: bool, + ffmpeg_build: FfmpegBuild, +) !*std.Build.Step { const module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, @@ -242,17 +323,26 @@ fn buildLinux( // with pipewire using the Zig backend. Just stick to LLVM for now... .use_llvm = true, }); - exe.addRPath(b.path("./lib")); + + if (!nix) { + // This prevents linker errors when building for generic Linux target on NixOS. + exe.linker_allow_shlib_undefined = true; + // NixOS can't run dynamically linked executables, so there + // is no need to change the rpath. + exe.root_module.addRPathSpecial("$ORIGIN/lib"); + } try addSharedDependencies(allocator, b, exe, target, optimize); - try addLinuxDependencies(allocator, b, exe, target, optimize); + try addLinuxDependencies(allocator, b, exe, target, optimize, ffmpeg_build); const install_step = b.addInstallArtifact(exe, .{ .dest_dir = .{ .override = .{ .custom = "linux" } }, }); b.getInstallStep().dependOn(&install_step.step); - const run_cmd = b.addRunArtifact(exe); + const run_cmd = b.addSystemCommand(&.{ + b.getInstallPath(.prefix, "linux/" ++ EXE_NAME), + }); run_cmd.step.dependOn(b.getInstallStep()); if (b.args) |args| { @@ -261,6 +351,31 @@ fn buildLinux( const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); + + return &install_step.step; +} + +fn buildLinuxAppImage( + b: *std.Build, + allocator: std.mem.Allocator, + linux_install_step: *std.Build.Step, +) *std.Build.Step { + const appimage_step = b.step("appimage", "Build Linux AppImage"); + + const file = std.fs.cwd().openFile("./build_app_image.sh", .{ .mode = .read_only }) catch unreachable; + defer file.close(); + const stat = file.stat() catch unreachable; + + var reader = file.reader(&.{}); + const buffer = reader.interface.readAlloc(allocator, stat.size) catch unreachable; + defer allocator.free(buffer); + + const cmd = b.addSystemCommand(&.{ "bash", "-lc", buffer }); + + cmd.step.dependOn(linux_install_step); + appimage_step.dependOn(&cmd.step); + + return appimage_step; } fn buildUnitTestsDefault( @@ -268,6 +383,7 @@ fn buildUnitTestsDefault( b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, + ffmpeg_build: FfmpegBuild, ) !void { const unit_test_files = [_][]const u8{ "./src/test.zig", @@ -291,7 +407,7 @@ fn buildUnitTestsDefault( exe.linkLibC(); try addSharedDependencies(allocator, b, exe, target, optimize); - try addLinuxDependencies(allocator, b, exe, target, optimize); + try addLinuxDependencies(allocator, b, exe, target, optimize, ffmpeg_build); const run_exe_unit_tests = b.addRunArtifact(exe); @@ -308,20 +424,43 @@ pub fn build(b: *std.Build) !void { defer _ = gpa.deinit(); const allocator = gpa.allocator(); - const nix = b.option(bool, "nix", "If on NixOS, use this flag to run"); + const nix = b.option(bool, "nix", "If on NixOS, use this flag to run") orelse false; + const appimage = b.option(bool, "appimage", "Build Linux AppImage after install") orelse false; + + if (appimage and nix == true) { + std.log.err("AppImage builds require generic linux target. Run without -Dnix.", .{}); + return error.InvalidBuildConfig; + } const optimize = b.standardOptimizeOption(.{}); + if (appimage and optimize == .Debug) { + std.log.err("AppImage builds require a release optimize mode. Use -Doptimize=ReleaseFast, -Doptimize=ReleaseSafe, or -Doptimize=ReleaseSmall.", .{}); + return error.InvalidBuildConfig; + } try buildWindows(allocator, b, optimize); - // TODO: Linux build is currently broken due to llvm linker errors. Check back - // when switched back to zig linker when it's fixed. - const target = if (nix == true) b.standardTargetOptions(.{}) else b.resolveTargetQuery(.{ + const ffmpeg_build = build_ffmpeg(b); + + const linux_target = if (nix == true) b.standardTargetOptions(.{}) else b.resolveTargetQuery(.{ .os_tag = .linux, .abi = .gnu, .cpu_arch = .x86_64, }); - try buildLinux(allocator, b, target, optimize); - try buildUnitTestsDefault(allocator, b, target, optimize); + const linux_install_step = try buildLinux( + allocator, + b, + linux_target, + optimize, + nix, + ffmpeg_build, + ); + const appimage_step = buildLinuxAppImage(b, allocator, linux_install_step); + + if (appimage) { + b.getInstallStep().dependOn(appimage_step); + } + + try buildUnitTestsDefault(allocator, b, linux_target, optimize, ffmpeg_build); } diff --git a/build.zig.zon b/build.zig.zon index 6fa8606..28d7bd6 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -10,7 +10,7 @@ // This is a [Semantic Version](https://semver.org/). // In a future version of Zig it will be used for package deduplication. - .version = "0.0.0", + .version = "0.1.0", // Together with name, this represents a globally unique package // identifier. This field is generated by the Zig toolchain when the @@ -74,24 +74,21 @@ .hash = "N-V-__8AAPJS7wFRVAIGhMZ7cis5e-y5LZfn2KfO5O2jiHGH", }, .vulkan_zig = .{ - .url = "https://github.com/Snektron/vulkan-zig/archive/zig-0.15-compat.tar.gz", - .hash = "vulkan-0.0.0-r7Ytx_VDAwAiMl0YSu2UOkVMIGJN7CeIQaxJR-hUSfD6", + // Pinned to 0.15.1 + .url = "https://github.com/Snektron/vulkan-zig/archive/af34c77ab52e8ce353faf2cd13c1ef13a99c2171.tar.gz", + .hash = "vulkan-0.0.0-r7YtxztIAwBc30xIMx4tzfUcEmc7goWiFJkR13yeLfi8", }, .imguiz = .{ .url = "git+https://github.com/mgerb/imguiz#7d6e1a4dfb2f31da24c9f76e0c4ba8285e89d289", .hash = "imguiz-0.0.0-sI63zx-_DwQbtJX5KbwrOK_VnEkxIft96REu9HVBZ3W8", }, .ffmpeg = .{ - .url = "https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.1.tar.gz", - .hash = "N-V-__8AAL-l8gSpW3fjjSbxZmbJt3OvF_ofxYzT8TsfzD9T", - }, - .ffmpeg_linux = .{ - .url = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n7.1-latest-linux64-gpl-shared-7.1.tar.xz", - .hash = "N-V-__8AAMVhFwyEn-loOh3RvLuNHFbK-_cUx6F781wmKELD", + .url = "https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n8.0.1.tar.gz", + .hash = "N-V-__8AAH4RHQXHEafp_hkUel3EMeK1wjHBfaIYYxYsKdiM", }, .ffmpeg_windows = .{ - .url = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n7.1-latest-win64-gpl-shared-7.1.zip", - .hash = "N-V-__8AAJk_Dgsw1bGPOwZaDiVZlSS6gFZfdqjg2ObF4vOu", + .url = "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-02-10-13-08/ffmpeg-n7.1.3-40-gcddd06f3b9-win64-gpl-shared-7.1.zip", + .hash = "N-V-__8AAJl9FQsCnNsH8YA5Qbdo9lCRXd8h-ZU9O0Uq5BH7", }, .libportal = .{ .url = "https://github.com/flatpak/libportal/archive/refs/tags/0.9.1.tar.gz", diff --git a/build_app_image.sh b/build_app_image.sh new file mode 100755 index 0000000..3a750f0 --- /dev/null +++ b/build_app_image.sh @@ -0,0 +1,20 @@ +# Requires appimagetool and linuxdeploy. + +set -euo pipefail + +# Get page-aligned errors on some dynamic libs without this. +export NO_STRIP=1 + +rm -rf AppDir +rm -f zig-out/linux/spacecap-linux-x86_64.AppImage + +# NOTE: Vulkan is excluded because system libraries should be used. +LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}" linuxdeploy \ + --appdir AppDir \ + --executable zig-out/linux/spacecap \ + --desktop-file packaging/linux/spacecap.desktop \ + --icon-file packaging/linux/spacecap.svg \ + --exclude-library libvulkan.so.1 + +env -u SOURCE_DATE_EPOCH APPIMAGE_EXTRACT_AND_RUN=1 ARCH=x86_64 appimagetool AppDir zig-out/linux/spacecap-linux-x86_64.AppImage +rm -rf AppDir diff --git a/flake.nix b/flake.nix index 368311f..e76098a 100644 --- a/flake.nix +++ b/flake.nix @@ -36,44 +36,99 @@ mv zls $out/bin/ ''; }; + linuxdeploy = pkgs.stdenv.mkDerivation { + pname = "linuxdeploy"; + version = "continuous"; + src = pkgs.fetchurl { + url = "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"; + sha256 = "sha256-nFCMLLcA+ExmAufWDpnxgGodn5Doomw8nrvHxiu5UFs="; + }; + dontUnpack = true; + dontFixup = true; + dontStrip = true; + installPhase = '' + mkdir -p $out/bin $out/libexec + cp "$src" "$out/libexec/linuxdeploy-x86_64.AppImage" + chmod +x "$out/libexec/linuxdeploy-x86_64.AppImage" + cat > "$out/bin/linuxdeploy" < "$out/bin/appimagetool" < + + + + + + + + + diff --git a/src/audio_encoder.zig b/src/audio_encoder.zig index 490c87e..59f40b6 100644 --- a/src/audio_encoder.zig +++ b/src/audio_encoder.zig @@ -68,7 +68,7 @@ pub const AudioEncoder = struct { if (chosen_fmt == ffmpeg.AV_SAMPLE_FMT_NONE) return error.UnsupportedAudioSampleFormat; audio_codec_ctx.*.sample_fmt = chosen_fmt; - audio_codec_ctx.*.profile = ffmpeg.FF_PROFILE_AAC_LOW; + audio_codec_ctx.*.profile = ffmpeg.AV_PROFILE_AAC_LOW; if (format_context.oformat.*.flags & ffmpeg.AVFMT_GLOBALHEADER != 0) { audio_codec_ctx.*.flags |= ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER; diff --git a/src/vulkan/vulkan.zig b/src/vulkan/vulkan.zig index 73ad757..1f12c91 100644 --- a/src/vulkan/vulkan.zig +++ b/src/vulkan/vulkan.zig @@ -20,8 +20,7 @@ pub const Device = vk.DeviceProxy; pub const CommandBuffer = vk.CommandBufferProxy; pub const API_VERSION = vk.API_VERSION_1_4; -// TODO: update before release -const DEBUG = true; +const DEBUG = @import("builtin").mode == .Debug; const INSTANCE_EXTENSIONS = [_][*:0]const u8{ vk.extensions.khr_get_physical_device_properties_2.name,