Skip to content

Commit

Permalink
Build libav and libx264 on GitHub Actions
Browse files Browse the repository at this point in the history
On all platforms, allowing exporting animations as MP4 video. Lots of
build system hacks involved, mostly on Windows because of course
building a dependency there is a nightmare as usual.

Partially implements #1320.
  • Loading branch information
askmeaboutlo0m committed Jul 30, 2024
1 parent 0b36312 commit 9fd0242
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 23 deletions.
34 changes: 33 additions & 1 deletion .github/actions/build-deps/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ inputs:
description: Additional arguments to the Qt build script
type: string

ffmpeg_pre_build:
description: Command to run before building ffmpeg dependencies
type: string
ffmpeg_args:
description: Additional arguments to the ffmpeg dependencies build script
type: string

other_pre_build:
description: Command to run before building other dependencies
type: string
Expand All @@ -45,6 +52,14 @@ inputs:
description: Version of Qt to build
required: true
type: string
libx264:
description: Commit of libx264 to build
default: '31e19f92f00c7003fa115047ce50978bc98c3a0d'
type: string
ffmpeg:
description: Version of ffmpeg to build
default: '7.0.1'
type: string
libmicrohttpd:
description: Version of libmicrohttpd to build
default: 1.0.1
Expand Down Expand Up @@ -79,7 +94,7 @@ outputs:
description: >
A semicolon-separated list of all generated dependency trees suitable for
use with `CMAKE_PREFIX_PATH`
value: ${{ github.workspace }}/${{ inputs.path }}/qt;${{ github.workspace }}/${{ inputs.path }}/other
value: ${{ github.workspace }}/${{ inputs.path }}/qt;${{ github.workspace }}/${{ inputs.path }}/ffmpeg;${{ github.workspace }}/${{ inputs.path }}/other

runs:
using: composite
Expand All @@ -99,6 +114,23 @@ runs:
${{ inputs.qt_args }}
-P .github/scripts/build-qt.cmake
- uses: ./.github/actions/build-and-cache
with:
name: ffmpeg dependencies
cache_key: ffmpeg-${{ inputs.cache_key }}-${{ inputs.libx264 }}-${{ inputs.ffmpeg }}
path: ${{ inputs.path }}/ffmpeg
pre_build: ${{ inputs.ffmpeg_pre_build }}
build: >
cmake
-DBUILD_TYPE=${{ inputs.build_type }}
"-DLIBX264=${{ inputs.libx264 }}"
"-DFFMPEG=${{ inputs.ffmpeg }}"
"-DCMAKE_PREFIX_PATH=${{ github.workspace }}/${{ inputs.path }}/qt"
"-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/${{ inputs.path }}/ffmpeg"
"-DTARGET_ARCH=${{ inputs.target_arch }}"
${{ inputs.ffmpeg_args }}
-P .github/scripts/build-ffmpeg.cmake
- uses: ./.github/actions/build-and-cache
with:
name: other dependencies
Expand Down
141 changes: 141 additions & 0 deletions .github/scripts/build-ffmpeg.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.19)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.11 CACHE STRING "macOS deployment target")
list(APPEND CMAKE_MODULE_PATH
${CMAKE_CURRENT_LIST_DIR}/cmake
${CMAKE_CURRENT_LIST_DIR}/../../cmake
)

set(LIBX264 "31e19f92f00c7003fa115047ce50978bc98c3a0d" CACHE STRING
"The commit of libx264 to build")
set(FFMPEG "7.0.1" CACHE STRING
"The version of ffmpeg to build")
option(KEEP_ARCHIVES "Keep downloaded archives instead of deleting them" OFF)
option(KEEP_SOURCE_DIRS "Keep source directories instead of deleting them" OFF)
option(KEEP_BINARY_DIRS "Keep build directories instead of deleting them" OFF)
set(TARGET_ARCH "x86_64" CACHE STRING
"Target architecture (x86, x86_64, arm32, arm64)")

include(BuildDependency)

if(LIBX264)
build_dependency(x264 ${LIBX264} ${BUILD_TYPE}
URL https://code.videolan.org/videolan/x264/-/archive/@version@/x264-@version@.tar.gz
TARGET_ARCH "${TARGET_ARCH}"
VERSIONS
31e19f92f00c7003fa115047ce50978bc98c3a0d
SHA384=bed835fcf11b4befa8341661b996c4f51842dfee6f7f87c9c2e767cebca0b7871a7f59435b4e92d89c2b13a659d1d737
ALL_PLATFORMS
AUTOMAKE
ASSIGN_HOST ASSIGN_PREFIX WIN32_CC_CL
ALL
--enable-static
--enable-pic
--disable-lavf
--disable-swscale
--disable-avs
--disable-ffms
--disable-gpac
--disable-lsmash
--disable-bashcompletion
--disable-cli
--enable-strip
)
endif()

if(FFMPEG)
set(ffmpeg_configure_args
--enable-gpl
--enable-version3
--disable-doc
--disable-autodetect
--disable-programs
--disable-avdevice
--disable-avfilter
--disable-swresample
--disable-postproc
--disable-alsa
--disable-appkit
--disable-avfoundation
--disable-bzlib
--disable-coreimage
--disable-iconv
--disable-lzma
--disable-metal
--disable-sndio
--disable-schannel
--disable-sdl2
--disable-securetransport
--disable-xlib
--disable-zlib
--enable-libx264
--disable-encoders
--disable-decoders
--disable-muxers
--disable-demuxers
--disable-parsers
--disable-bsfs
--disable-protocols
--disable-indevs
--disable-outdevs
--disable-devices
--disable-filters
--enable-encoder=libx264
--enable-muxer=mp4
)

if(ANDROID)
if(TARGET_ARCH STREQUAL "arm32")
list(PREPEND ffmpeg_configure_args
--arch=armv7-a --cpu=armv7-a --disable-neon)
elseif(TARGET_ARCH STREQUAL "arm64")
list(PREPEND ffmpeg_configure_args
--arch=aarch64 --cpu=armv8-a --enable-neon)
else()
message(FATAL_ERROR "Unhandled TARGET_ARCH '${TARGET_ARCH}'")
endif()
list(PREPEND ffmpeg_configure_args
--enable-cross-compile
--target-os=android
--enable-asm
--enable-inline-asm
)
elseif(WIN32)
if(TARGET_ARCH STREQUAL "x86")
list(PREPEND ffmpeg_configure_args --arch=x86_32)
elseif(TARGET_ARCH STREQUAL "x86_64")
list(PREPEND ffmpeg_configure_args --arch=x86_64)
else()
message(FATAL_ERROR "Unhandled TARGET_ARCH '${TARGET_ARCH}'")
endif()
list(PREPEND ffmpeg_configure_args --toolchain=msvc)
endif()

build_dependency(ffmpeg ${FFMPEG} ${BUILD_TYPE}
URL https://ffmpeg.org/releases/ffmpeg-@version@.tar.xz
TARGET_ARCH "${TARGET_ARCH}"
VERSIONS
7.0.1
SHA384=25650331f409bf7efc09f0d859ce9a1a8e16fe429e4f9b2593743eb68e723b186559739e8b02aac83c6e5c96137fec7e
ALL_PLATFORMS
AUTOMAKE
ASSIGN_PREFIX FFMPEG_QUIRKS
PKG_CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig"
ALL ${ffmpeg_configure_args}
PATCHES
ALL
patches/ffmpeg_configure.diff
)

# The pkg-config files generated on Windows contain garbage we have to fix.
if(WIN32)
execute_process(
COMMAND
perl
"${CMAKE_CURRENT_LIST_DIR}/fix-win32-ffmpeg-pkg-config-files.pl"
"${CMAKE_INSTALL_PREFIX}/lib/pkgconfig"
COMMAND_ECHO STDOUT
COMMAND_ERROR_IS_FATAL ANY
)
endif()
endif()
2 changes: 2 additions & 0 deletions .github/scripts/build-qt.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ if(OPENSSL)
CONFIGURATOR "Configure"
ASSIGN_PREFIX BROKEN_INSTALL NEEDS_VC_WIN_TARGET
MAKE_FLAGS ${OPENSSL_MAKE_FLAGS}
WIN32_CONFIGURE_COMMAND perl
WIN32_MAKE_COMMAND nmake
INSTALL_TARGET install_sw
ENV ${OPENSSL_ENV}
ALL shared no-tests ${OPENSSL_FLAGS}
Expand Down
69 changes: 59 additions & 10 deletions .github/scripts/cmake/BuildDependency.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ endfunction()

function(_build_automake build_type target_bits source_dir)
set(configure "${source_dir}/configure")
cmake_parse_arguments(PARSE_ARGV 2 ARG "ASSIGN_PREFIX;BROKEN_INSTALL;NEEDS_VC_WIN_TARGET" "INSTALL_TARGET" "MAKE_FLAGS")
cmake_parse_arguments(
PARSE_ARGV 2 ARG "ASSIGN_HOST;ASSIGN_PREFIX;BROKEN_INSTALL;FFMPEG_QUIRKS;NEEDS_VC_WIN_TARGET;WIN32_CC_CL"
"INSTALL_TARGET;PKG_CONFIG_PATH;WIN32_CONFIGURE_COMMAND;WIN32_MAKE_COMMAND" "MAKE_FLAGS")
_parse_flags("${build_type}" "${source_dir}" configure configure_flags env ${ARG_UNPARSED_ARGUMENTS})

if(NPROCS EQUAL 0 OR WIN32)
Expand All @@ -156,18 +158,40 @@ function(_build_automake build_type target_bits source_dir)

# https://developer.android.com/ndk/guides/other_build_systems#autoconf
if(CMAKE_ANDROID_NDK)
get_android_env(android_env abi "${CMAKE_ANDROID_NDK}" "${CMAKE_ANDROID_ARCH_ABI}" "${ANDROID_PLATFORM}")
get_android_env(
android_env android_ffmpeg_flags abi "${CMAKE_ANDROID_NDK}"
"${CMAKE_ANDROID_ARCH_ABI}" "${ANDROID_PLATFORM}")
list(APPEND env ${android_env})
list(APPEND configure_flags --host "${abi}")
# ffmpeg's build system looks like autoconf, but isn't actually
if(ARG_FFMPEG_QUIRKS)
list(APPEND configure_flags ${android_ffmpeg_flags})
list(APPEND env "AS_FLAGS=--target=${abi}")
else()
if(ARG_ASSIGN_HOST)
list(APPEND configure_flags "--host=${abi}")
else()
list(APPEND configure_flags --host "${abi}")
endif()
endif()
endif()

if(ARG_PKG_CONFIG_PATH)
# pkg-config chokes on backslashes in PKG_CONFIG_PATH, so convert those
# to forward slashes. TO_CMAKE_PATH does that, despite its odd name.
file(TO_CMAKE_PATH "${ARG_PKG_CONFIG_PATH}" pkg_config_path)
list(APPEND env "PKG_CONFIG_PATH=${pkg_config_path}")
endif()

# pkg-config also chokes on backslashes in the prefix.
file(TO_CMAKE_PATH "${CMAKE_INSTALL_PREFIX}" prefix)

# OpenSSL has a terrible configurator that only accepts `--prefix=foo` and
# does not bail out when it receives a bogus argument, so if you send
# `--prefix foo` it will just install to its default prefix!
if(ARG_ASSIGN_PREFIX)
set(prefix "--prefix=${CMAKE_INSTALL_PREFIX}")
set(prefix "--prefix=${prefix}")
else()
set(prefix "--prefix" "${CMAKE_INSTALL_PREFIX}")
set(prefix "--prefix" "${prefix}")
endif()

# OpenSSL somehow manages to find the 64bit compiler even when everything is
Expand All @@ -189,13 +213,26 @@ function(_build_automake build_type target_bits source_dir)
set(install install)
endif()

# On Windows, the only usable thing here is OpenSSL's pseudo-automake Perl
# script. Shebangs don't work on Windows, so we have to run it explicitly.
# Windows needs special care because shebangs don't work there and it has
# both GNU Make, which regular autoconf uses, and Microsoft's nmake, which
# OpenSSL uses. They are mutually incompatible of course.
if(WIN32)
set(make "nmake")
if(ARG_WIN32_CC_CL)
list(APPEND env "CC=cl")
endif()
if(ARG_WIN32_CONFIGURE_COMMAND)
set(winconfigure "${ARG_WIN32_CONFIGURE_COMMAND}")
else()
set(winconfigure "bash")
endif()
if(ARG_WIN32_MAKE_COMMAND)
set(make "${ARG_WIN32_MAKE_COMMAND}")
else()
set(make "make")
endif()
execute_process(
COMMAND "${CMAKE_COMMAND}" -E env ${env}
perl "${configure}" ${prefix} ${configure_flags}
"${winconfigure}" "${configure}" ${prefix} ${configure_flags}
COMMAND_ECHO STDOUT
WORKING_DIRECTORY "${source_dir}"
COMMAND_ERROR_IS_FATAL ANY
Expand Down Expand Up @@ -333,7 +370,7 @@ function(_build_cmake build_type target_bits source_dir)
endif()
endfunction()

function(get_android_env _out_env _out_triplet ndk abi platform)
function(get_android_env _out_env _out_ffmpeg_flags _out_triplet ndk abi platform)
if(abi STREQUAL "armeabi-v7a")
set(triplet armv7a-linux-androideabi)
elseif(abi STREQUAL "arm64-v8a")
Expand Down Expand Up @@ -368,6 +405,18 @@ function(get_android_env _out_env _out_triplet ndk abi platform)
"STRIP=${toolchain}/bin/llvm-strip"
PARENT_SCOPE
)
set(${_out_ffmpeg_flags}
"--ar=${toolchain}/bin/llvm-ar"
"--cc=${cc}"
"--cxx=${cc}++"
"--nm=${toolchain}/bin/llvm-nm"
"--ranlib=${toolchain}/bin/llvm-ranlib"
"--strip=${toolchain}/bin/llvm-strip"
"--extra-cflags=-DANDROID_NDK -fPIC -DANDROID -D__ANDROID__"
"--extra-ldflags=-lc -lm -ldl -llog -landroid -Wl,--hash-style=both -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libunwind.a"
"--sysroot=${CMAKE_SYSROOT}"
PARENT_SCOPE
)
set(${_out_triplet} ${triplet} PARENT_SCOPE)
endfunction()

Expand Down
40 changes: 40 additions & 0 deletions .github/scripts/fix-win32-ffmpeg-pkg-config-files.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env perl
# SPDX-License-Identifier: MIT
use strict;
use warnings;
use File::Find;
use Encode qw(encode decode);

sub slurp {
my ($path) = @_;
open my $fh, '<', $path or die "Can't open '$path': $!\n";
my $content = do { local $/; <$fh> };
close $fh or die "Can't close '$path': $!\n";
return decode('UTF-8', $content);
}

sub spew {
my ($path, $content) = @_;
open my $fh, '>', $path or die "Can't open '$path': $!\n";
print {$fh} encode('UTF-8', $content);
close $fh or die "Can't close '$path': $!\n";
}

sub fix_libs {
my ($libs) = @_;
print "Got libs: '$libs'\n";
my $result = join ' ', map { s/\A([^\-].*)\.lib\z/-l$1/r } grep { !/\A-libpath:/ } split ' ', $libs;
print "Fixed libs: '$result'\n";
return "Libs: $result";
}

print 'Collecting .pc files in ', join(', ', @ARGV), "\n";
my @paths;
find({wanted => sub { push @paths, $_ if -f && /\.pc\z/ }, no_chdir => 1}, @ARGV);

for my $path (@paths) {
print "Fixing $path\n";
my $content = slurp($path);
$content =~ s/^Libs:\s*(.+?)\s*$/fix_libs($1)/gme;
spew($path, $content);
}
16 changes: 16 additions & 0 deletions .github/scripts/patches/ffmpeg_configure.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Description: Trying to build ffmpeg on Windows dies with some weird cmd syntax
error. I've tried to make it use bash instead, since that's apparently what
it wants to use, but no luck. This is a random workaround I found at
https://trac.ffmpeg.org/ticket/9360 that seems to work okay.

--- a/configure
+++ b/configure
@@ -5018,7 +5018,7 @@
else
_ident=$($_cc --version 2>/dev/null | head -n1 | tr -d '\r')
fi
- _DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< 2>&1 | awk '\''/including/ { sub(/^.*file: */, ""); gsub(/\\/, "/"); if (!match($$0, / /)) print "$@:", $$0 }'\'' > $(@:.o=.d)'
+ _DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< 2>&1 | grep "^Note:.*file:" | sed -e "s^.*file: *^$@: ^" | tr \\\\ / > $(@:.o=.d)'
_DEPFLAGS='$(CPPFLAGS) $(CFLAGS) -showIncludes -Zs'
_cflags_speed="-O2"
_cflags_size="-O1"
Loading

0 comments on commit 9fd0242

Please sign in to comment.