-
-
Save Spaceghost/16a2f60aa6cdfcec6ce7636fb2076ac6 to your computer and use it in GitHub Desktop.
static ffmpeg zig build
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const builtin = @import("builtin"); | |
pub fn build(b: *std.Build) !void { | |
const av = b.option(bool, "av", "Compile ffmpeg") orelse false; | |
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast }); | |
const dep_stb = b.dependency("stb", .{}); | |
const dep_ffmpeg = b.dependency("ffmpeg", .{}); | |
// Add a new step for compiling Ffmpeg | |
if (av) { | |
try compileFfmpegForTarget(b, dep_ffmpeg, b.standardTargetOptionsQueryOnly(.{})); | |
} else { | |
const target = b.standardTargetOptionsQueryOnly(.{}); | |
const ffmpeg_libs = try getFfmpegLibs(b, target); | |
try runZig(b, target, optimize, dep_stb, ffmpeg_libs, dep_ffmpeg); | |
} | |
} | |
fn compileFfmpegForTarget(b: *std.Build, dep_ffmpeg: *std.Build.Dependency, t: std.Target.Query) !void { | |
const cache_path = try getFfmpegCachePath(b, t); | |
const abs_path = cache_path[0]; | |
// Check if cached Ffmpeg libraries exist | |
if (!std.meta.isError(std.fs.accessAbsolute(abs_path, .{}))) { | |
// Ffmpeg is already compiled and cached for this target | |
std.debug.print("Ffmpeg is already compiled and cached for this target\n", .{}); | |
return; | |
} | |
const ffmpeg_path = dep_ffmpeg.path("").getPath(b); | |
const configure_path = dep_ffmpeg.path("configure").getPath(b); | |
// Compile x264 first | |
const dep_x264 = b.dependency("x264", .{}); | |
const x264_path = try std.fmt.allocPrint(b.allocator, "--prefix={s}", .{abs_path}); | |
const configure_x264_cmd = b.addSystemCommand(&.{ | |
"./configure", | |
x264_path, | |
"--enable-static", | |
"--disable-cli", | |
}); | |
configure_x264_cmd.cwd = dep_x264.path(""); | |
const make_x264_cmd = b.addSystemCommand(&.{ "make", "-j4" }); | |
make_x264_cmd.cwd = dep_x264.path(""); | |
make_x264_cmd.step.dependOn(&configure_x264_cmd.step); | |
const install_x264_cmd = b.addSystemCommand(&.{ "make", "install" }); | |
install_x264_cmd.cwd = dep_x264.path(""); | |
install_x264_cmd.step.dependOn(&make_x264_cmd.step); | |
const extra_cflags = try std.fmt.allocPrint(b.allocator, "--extra-cflags=-I{s}/include", .{abs_path}); | |
const extra_ldflags = try std.fmt.allocPrint(b.allocator, "--extra-ldflags=-L{s}/lib", .{abs_path}); | |
// Configure Ffmpeg | |
const configure_cmd = b.addSystemCommand(&.{ | |
configure_path, | |
"--disable-shared", | |
"--enable-static", | |
"--disable-programs", | |
"--disable-doc", | |
"--enable-small", | |
"--enable-libx264", | |
"--enable-gpl", | |
extra_cflags, | |
extra_ldflags, | |
}); | |
configure_cmd.setCwd(dep_ffmpeg.path("")); | |
configure_cmd.step.dependOn(&install_x264_cmd.step); | |
// Make Ffmpeg | |
const make_cmd = b.addSystemCommand(&.{ "make", "-j4" }); | |
make_cmd.setCwd(dep_ffmpeg.path("")); | |
make_cmd.step.dependOn(&configure_cmd.step); | |
// Create cache directory | |
const mkdir_cmd = b.addSystemCommand(&.{ "mkdir", "-p", abs_path }); | |
mkdir_cmd.step.dependOn(&make_cmd.step); | |
// Copy Ffmpeg libraries to cache | |
const libs = [_][]const u8{ "libavcodec", "libavformat", "libavutil", "libswscale", "libswresample" }; | |
var copy_steps = std.ArrayList(*std.Build.Step).init(b.allocator); | |
inline for (libs) |lib| { | |
const copy_cmd = b.addSystemCommand(&.{ | |
"cp", | |
b.pathJoin(&.{ ffmpeg_path, lib, lib ++ ".a" }), | |
b.pathJoin(&.{ abs_path, lib ++ ".a" }), | |
}); | |
copy_cmd.step.dependOn(&mkdir_cmd.step); | |
try copy_steps.append(©_cmd.step); | |
} | |
// Create a step that depends on all copy commands | |
// const copy_all = b.step("copy-ffmpeg-libs", "Copy all Ffmpeg libraries to cache"); | |
for (copy_steps.items) |s| { | |
b.getInstallStep().dependOn(s); | |
} | |
// return copy_all; | |
} | |
fn getFfmpegCachePath(b: *std.Build, target: std.Target.Query) ![2][]u8 { | |
const abs_cache_dir = try std.fs.path.join(b.allocator, &.{ b.cache_root.path.?, "ffmpeg" }); | |
const relative_cache_dir = try std.fs.path.relative(b.allocator, try std.fs.realpathAlloc(b.allocator, "."), abs_cache_dir); | |
const target_name = try target.zigTriple(b.allocator); | |
std.debug.print("Target name: {s}\n", .{target_name}); | |
const relative_path = try std.fs.path.join(b.allocator, &.{ relative_cache_dir, target_name }); | |
const absolute_path = try std.fs.path.join(b.allocator, &.{ abs_cache_dir, target_name }); | |
std.debug.print("Cache dir: {s}\n", .{relative_path}); | |
return .{ absolute_path, relative_path }; | |
} | |
fn getFfmpegLibs(b: *std.Build, target: std.Target.Query) ![]const *std.Build.Step.Compile { | |
const cache_path = try getFfmpegCachePath(b, target); | |
const rel_path = cache_path[1]; | |
const abs_path = cache_path[0]; | |
if (!std.meta.isError(std.fs.accessAbsolute(abs_path, .{}))) { | |
// Return cached libraries | |
return loadCachedFfmpegLibs(b, rel_path); | |
} else { | |
std.debug.print("Ffmpeg libraries not found for {s}. Please run 'zig build compile-ffmpeg' first.\n", .{try target.zigTriple(b.allocator)}); | |
return error.FfmpegLibrariesNotFound; | |
} | |
} | |
fn createCacheDir(cache_path: []const u8) !void { | |
const parent_dir = std.fs.path.dirname(cache_path) orelse return error.NoParentDir; | |
try std.fs.makeDirAbsolute(parent_dir); | |
const dir_name = std.fs.path.basename(cache_path); | |
var dir = try std.fs.openDirAbsolute(parent_dir, .{}); | |
defer dir.close(); | |
try dir.makeDir(dir_name); | |
} | |
fn loadCachedFfmpegLibs(b: *std.Build, cache_path: []const u8) ![]const *std.Build.Step.Compile { | |
var dir = try std.fs.cwd().openDir(cache_path, .{ .iterate = true }); | |
defer dir.close(); | |
var ffmpeg_libs = std.ArrayList(*std.Build.Step.Compile).init(b.allocator); | |
// libx264 | |
const lib_path = try std.fmt.allocPrint(b.allocator, "{s}/lib", .{cache_path}); | |
std.debug.print("lib_path: {s}\n", .{lib_path}); | |
var x264_dir = try std.fs.cwd().openDir(lib_path, .{ .iterate = true }); | |
var it = x264_dir.iterate(); | |
while (try it.next()) |entry| { | |
if (entry.kind == .file) { | |
const lib_name = std.fs.path.stem(entry.name); | |
const lib_step = b.addStaticLibrary(.{ | |
.name = lib_name, | |
.target = b.host, | |
.optimize = .ReleaseFast, | |
}); | |
const object_path = b.path(b.pathJoin(&.{ cache_path, "lib", entry.name })); | |
lib_step.addObjectFile(object_path); | |
try ffmpeg_libs.append(lib_step); | |
} | |
} | |
// ffmpeg | |
it = dir.iterate(); | |
while (try it.next()) |entry| { | |
if (entry.kind == .file) { | |
const lib_name = std.fs.path.stem(entry.name); | |
const lib_step = b.addStaticLibrary(.{ | |
.name = lib_name, | |
.target = b.host, | |
.optimize = .ReleaseFast, | |
}); | |
const object_path = b.path(b.pathJoin(&.{ cache_path, entry.name })); | |
lib_step.addObjectFile(object_path); | |
try ffmpeg_libs.append(lib_step); | |
} | |
} | |
return ffmpeg_libs.toOwnedSlice(); | |
} | |
fn setupExecutable( | |
b: *std.Build, | |
name: []const u8, | |
target: std.Target.Query, | |
optimize: std.builtin.OptimizeMode, | |
dep_stb: *std.Build.Dependency, | |
ffmpeg_libs: []const *std.Build.Step.Compile, | |
dep_ffmpeg: *std.Build.Dependency, | |
link_libc: bool, | |
) !*std.Build.Step.Compile { | |
const exe = b.addExecutable(.{ | |
.name = name, | |
.root_source_file = b.path("src/main.zig"), | |
.target = b.resolveTargetQuery(target), | |
.optimize = optimize, | |
.link_libc = link_libc, | |
}); | |
const clap = b.dependency("clap", .{}); | |
exe.root_module.addImport("clap", clap.module("clap")); | |
const cache_dir = std.fs.path.basename(b.cache_root.path.?); | |
linkFfmpeg(exe, ffmpeg_libs, dep_ffmpeg); | |
exe.addCSourceFile(.{ .file = b.path("stb/stb.c") }); | |
exe.addIncludePath(b.path(b.pathJoin(&.{ cache_dir, "ffmpeg", "native", "include" }))); | |
// exe.addObjectFile(b.path(b.pathJoin(&.{ cache_dir, "ffmpeg", "native", "" }))); | |
exe.addIncludePath(dep_stb.path("")); | |
return exe; | |
} | |
fn linkFfmpeg( | |
exe: *std.Build.Step.Compile, | |
ffmpeg_libs: []const *std.Build.Step.Compile, | |
dep_ffmpeg: *std.Build.Dependency, | |
) void { | |
for (ffmpeg_libs) |lib| { | |
exe.linkLibrary(lib); | |
} | |
exe.addIncludePath(dep_ffmpeg.path(".")); | |
exe.addIncludePath(dep_ffmpeg.path("libavcodec")); | |
exe.addIncludePath(dep_ffmpeg.path("libavformat")); | |
exe.addIncludePath(dep_ffmpeg.path("libavutil")); | |
exe.addIncludePath(dep_ffmpeg.path("libswscale")); | |
exe.addIncludePath(dep_ffmpeg.path("libswresample")); | |
// Add system-specific libraries and frameworks | |
switch (builtin.os.tag) { | |
.macos => { | |
// macOS frameworks | |
exe.linkFramework("CoreFoundation"); | |
exe.linkFramework("CoreVideo"); | |
exe.linkFramework("CoreMedia"); | |
exe.linkFramework("VideoToolbox"); | |
exe.linkFramework("AudioToolbox"); | |
exe.linkFramework("Security"); | |
// Additional system libraries | |
exe.linkSystemLibrary("z"); | |
exe.linkSystemLibrary("bz2"); | |
exe.linkSystemLibrary("iconv"); | |
}, | |
.linux => { | |
// Linux-specific libraries | |
exe.linkSystemLibrary("z"); | |
exe.linkSystemLibrary("bz2"); | |
}, | |
.windows => { | |
// Windows-specific libraries | |
exe.linkSystemLibrary("bcrypt"); | |
exe.linkSystemLibrary("advapi32"); | |
exe.linkSystemLibrary("user32"); | |
}, | |
else => {}, | |
} | |
} | |
fn runZig( | |
b: *std.Build, | |
target: std.Target.Query, | |
optimize: std.builtin.OptimizeMode, | |
dep_stb: *std.Build.Dependency, | |
ffmpeg_libs: []const *std.Build.Step.Compile, | |
dep_ffmpeg: *std.Build.Dependency, | |
) !void { | |
const exe = try setupExecutable( | |
b, | |
"asciigen", | |
target, | |
optimize, | |
dep_stb, | |
ffmpeg_libs, | |
dep_ffmpeg, | |
true, | |
); | |
const exe_check = try setupExecutable( | |
b, | |
"asciigen-check", | |
target, | |
optimize, | |
dep_stb, | |
ffmpeg_libs, | |
dep_ffmpeg, | |
false, | |
); | |
const check_step = b.step("check", "Run the check"); | |
check_step.dependOn(&exe_check.step); | |
b.installArtifact(exe); | |
const run_cmd = b.addRunArtifact(exe); | |
run_cmd.step.dependOn(b.getInstallStep()); | |
if (b.args) |args| run_cmd.addArgs(args); | |
const run_step = b.step("run", "Run the app"); | |
run_step.dependOn(&run_cmd.step); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment