[arm, cross-compiling] libopus not found by pkg-config?

I’m trying to build ffmpeg 3.4 with a reduced feature set [1] for aarch64 on my x86_64 desktop. During the build, configure fails with the message…

ERROR: opus not found using pkg-config

However, if I load my derivation into a nix-shell, I can step through the build phases manually, and before each one, pkg-config --cflags --libs opus shows libopus in the build environment.

[nix-shell:~/files/frc2020/toolchains/build/ffmpeg-3.4.7]$ pkg-config --cflags --libs opus
-I/nix/store/ynyz90bkjmnkzzb26cgk4xnw0sw7hvd8-libopus-1.3.1-aarch64-unknown-linux-gnu-dev/include/opus -L/nix/store/5y9ad070pjnx6x607jacx6k1pdysjglq-libopus-1.3.1-aarch64-unknown-linux-gnu/lib -lopus

Anyone know why I can run pkg-config in a pure nix-shell and it finds libopus while configure fails with the same tool?



Steps to reproduce:

mkdir working && cd working
# Clone nixpkgs.
git clone https://github.com/NixOS/nixpkgs
# Copy ffmpeg to working folder.
cp -r nixpkgs/pkgs/development/libraries/ffmpeg ./
# Replace ffmpeg/generic.nix with my modified derivation.  File in spoiler below.
ffmpeg/generic.nix
{ stdenv, fetchurl, pkgconfig, perl, texinfo, yasm
, bzip2, gnutls, libiconv, lame, libogg
, libtheora, libvorbis, libvpx, lzma, libpulseaudio, soxr
, x264, x265, xvidcore, zlib, libopus, speex, nv-codec-headers, dav1d
, libaomSupport ? false, libaom ? null
# Build options
, runtimeCpuDetectBuild ? true # Detect CPU capabilities at runtime
, multithreadBuild ? true # Multithreading via pthreads/win32 threads
# Developer options
, debugDeveloper ? false
, optimizationsDeveloper ? true
, extraWarningsDeveloper ? false
# Inherit generics
, branch, sha256, version, patches ? [], ...
}:

/* Maintainer notes:
 *
 * THIS IS A MINIMAL BUILD OF FFMPEG, do not include dependencies unless
 * a build that depends on ffmpeg requires them to be compiled into ffmpeg,
 * see `ffmpeg-full' for an ffmpeg build with all features included.
 *
 * Need fixes to support Darwin:
 *   pulseaudio
 *
 * Known issues:
 * 0.6     - fails to compile (unresolved) (so far, only disabling a number of
 *           features works, but that is not a feasible solution)
 * 0.6.90  - mmx: compile errors (fix: disable for 0.6.90-rc0)
 * 1.1     - libsoxr: compile error (fix: disable for 1.1)
 *           Support was initially added in 1.1 before soxr api change, fix
 *           would probably be to add soxr-1.0
 * ALL     - Cross-compiling will disable features not present on host OS
 *           (e.g. dxva2 support [DirectX] will not be enabled unless natively
 *           compiled on Cygwin)
 *
 */

let
  inherit (stdenv) isDarwin isFreeBSD isLinux isAarch32;
  inherit (stdenv.lib) optional optionals optionalString enableFeature filter;

  cmpVer = builtins.compareVersions;
  reqMin = requiredVersion: (cmpVer requiredVersion branch != 1);
  reqMatch = requiredVersion: (cmpVer requiredVersion branch == 0);

  ifMinVer = minVer: flag: if reqMin minVer then flag else null;

  # Version specific fix
  verFix = withoutFix: fixVer: withFix: if reqMatch fixVer then withFix else withoutFix;

  # Disable dependency that needs fixes before it will work on Darwin or Arm
  disDarwinOrArmFix = origArg: minVer: fixArg: if ((isDarwin || isAarch32) && reqMin minVer) then fixArg else origArg;

  vpxSupport = reqMin "0.6" && !isAarch32 && false;
in

assert libaomSupport -> libaom != null;

stdenv.mkDerivation rec {

  pname = "ffmpeg";
  inherit version;

  src = fetchurl {
    url = "https://www.ffmpeg.org/releases/${pname}-${version}.tar.bz2";
    inherit sha256;
  };

  postPatch = ''patchShebangs .'';
  inherit patches;

  outputs = [ "bin" "dev" "out" "man" ]
    ++ optional (reqMin "1.0") "doc" ; # just dev-doc
  setOutputFlags = false; # doesn't accept all and stores configureFlags in libs!

  configurePlatforms = [];
  configureFlags = filter (v: v != null) ([
      "--arch=${stdenv.hostPlatform.parsed.cpu.name}"
      "--target_os=${stdenv.hostPlatform.parsed.kernel.name}"
    # License
      "--enable-gpl"
      "--enable-version3"
    # Build flags
      "--enable-shared"
      (ifMinVer "0.6" "--enable-pic")
      (enableFeature runtimeCpuDetectBuild "runtime-cpudetect")
      "--enable-hardcoded-tables"
    ] ++
      (if multithreadBuild then (
         if stdenv.isCygwin then
           ["--disable-pthreads" "--enable-w32threads"]
         else # Use POSIX threads by default
           ["--enable-pthreads" "--disable-w32threads"])
       else
         ["--disable-pthreads" "--disable-w32threads"])
    ++ [
      (ifMinVer "0.9" "--disable-os2threads") # We don't support OS/2
      "--enable-network"
      (ifMinVer "2.4" "--enable-pixelutils")
    # Executables
      "--enable-ffmpeg"
      "--disable-ffplay"
      (ifMinVer "0.6" "--enable-ffprobe")
      (if reqMin "4" then null else "--disable-ffserver")
    # Libraries
      (ifMinVer "0.6" "--enable-avcodec")
      (ifMinVer "0.6" "--enable-avdevice")
      "--enable-avfilter"
      (ifMinVer "0.6" "--enable-avformat")
      (ifMinVer "1.0" "--enable-avresample")
      (ifMinVer "1.1" "--enable-avutil")
      "--enable-postproc"
      (ifMinVer "0.9" "--enable-swresample")
      "--enable-swscale"
    # Docs
      # (ifMinVer "0.6" "--disable-doc")
      "--disable-doc"
    # External Libraries
      "--disable-libass"
      "--disable-opengl"
      "--disable-xlib"
      "--disable-fontconfig"
      "--disable-htmlpages"
      "--disable-vdpau"
      "--disable-sdl2"
      "--enable-bzlib"
      "--disable-gnutls"
      #"--enable-gnutls"
      "--enable-libmp3lame"
      (ifMinVer "1.2" "--enable-iconv")
      "--enable-libtheora"
      #(ifMinVer "2.1" "--enable-libssh")
      "--disable-libssh"
      "--enable-libvorbis"
      # (ifMinVer "0.6" (enableFeature vpxSupport "libvpx"))
      (ifMinVer "2.4" "--enable-lzma")
      (ifMinVer "4.2" (enableFeature libaomSupport "libaom"))
      # (disDarwinOrArmFix (ifMinVer "0.9" "--enable-libpulse") "0.9" "--disable-libpulse")
      "--disable-libpulse"
      (ifMinVer "1.2" "--enable-libsoxr")
      "--enable-libx264"
      "--enable-libxvid"
      "--enable-zlib"
      (ifMinVer "2.8" "--enable-libopus")
      "--enable-libspeex"
      (ifMinVer "2.8" "--enable-libx265")
      (ifMinVer "4.2" (enableFeature (dav1d != null) "libdav1d"))
    # Developer flags
      (enableFeature debugDeveloper "debug")
      (enableFeature optimizationsDeveloper "optimizations")
      (enableFeature extraWarningsDeveloper "extra-warnings")
      "--disable-stripping"
    # Disable mmx support for 0.6.90
      (verFix null "0.6.90" "--disable-mmx")
  ] ++ optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
      "--cross-prefix=${stdenv.cc.targetPrefix}"
      "--enable-cross-compile"
  ] ++ optional stdenv.cc.isClang "--cc=clang");

  nativeBuildInputs = [ perl pkgconfig texinfo yasm ];

  buildInputs = [
    bzip2 libiconv lame libogg libtheora #gnutls
    libvorbis lzma soxr x264 x265 xvidcore zlib libopus speex # nv-codec-headers
  ] 
    ++ optional vpxSupport libaom
    ++ optional vpxSupport libvpx
    # ++ optionals (!isDarwin && !isAarch32) [ libpulseaudio ] # Need to be fixed on Darwin and ARM
    ++ optional (reqMin "4.2") dav1d;

  enableParallelBuilding = true;

  doCheck = false; # fails

  # ffmpeg 3+ generates pkg-config (.pc) files that don't have the
  # form automatically handled by the multiple-outputs hooks.
  postFixup = ''
    moveToOutput bin "$bin"
    moveToOutput share/ffmpeg/examples "$doc"
    for pc in ''${!outputDev}/lib/pkgconfig/*.pc; do
      substituteInPlace $pc \
        --replace "includedir=$out" "includedir=''${!outputInclude}"
    done
  '';

  installFlags = [ "install-man" ];

  passthru = {};

  meta = with stdenv.lib; {
    description = "A complete, cross-platform solution to record, convert and stream audio and video";
    homepage = http://www.ffmpeg.org/;
    longDescription = ''
      FFmpeg is the leading multimedia framework, able to decode, encode, transcode,
      mux, demux, stream, filter and play pretty much anything that humans and machines
      have created. It supports the most obscure ancient formats up to the cutting edge.
      No matter if they were designed by some standards committee, the community or
      a corporation.
    '';
    license = licenses.gpl3;
    platforms = platforms.all;
    maintainers = with maintainers; [ codyopel ];
    inherit branch;
  };
}
# Start nix-shell with ffmpeg derivation for cross compilation.
# And patch missing /usr/bin/strings in x264 package.
nix-shell --pure -E 'with (import <nixpkgs> {}); \
  pkgsCross.aarch64-multiplatform.callPackage ffmpeg/3.4.nix { \
    x264 = pkgsCross.aarch64-multiplatform.pkgs.x264.overrideAttrs (oldAttrs: { \
      nativeBuildInputs = oldAttrs.nativeBuildInputs ++ [ binutils-unwrapped ]; \
    }); \
  }
# Run pkg-config --libs --cflags opus to verify libopus is found.
pkg-config --libs --cflags opus
# Run nix build sequence.
genericBuild
# Observe configure fails to find libopus with pkg-config.

[1]: Reduce feature set = no libs with X11 dependencies (headless), no docs, and anything else I won’t need when building opencv against ffmpeg later.

I’m not sure if this is the proper fix in this case, but ffmpeg’s configure script provides an option to select the pkg-config binary to use. After adding "--pkg-config=${pkgconfig}/bin/pkg-config" to the configureFlags of my ffmpeg/generic.nix, configure now finds the cross compiled libraries it needs.

I’m at a loss as to which pkg-config was being used by configure by default.

Possibly it is the same issue that was recently solved by ffmpeg: fix cross compilation by danielfullmer · Pull Request #76915 · NixOS/nixpkgs · GitHub.

1 Like

Great find! That also solves a subsequent problem I had with the build host’s gcc not found.