[third_party/ffmpeg] add roll scripts for custom profiles

This CL adds python scripts for an updated roll process that supports
custom profiles. The new roll process is driven by files that
describe and enumerate the profiles. Those files will be generated
in the fuchsia main repo using GN.

This CL does not include a roll. That will happen in a subsequent CL.

Bug: 87666

Test: build change

Change-Id: I0442747b439107860605c0ba37d14015ee26ca53
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/ffmpeg/+/781083
Reviewed-by: Petr Hosek <[email protected]>
diff --git a/.gitignore b/.gitignore
index cb6f67a..204255c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
 /src
 /build.x64
 /build.arm64
+/roll
 
diff --git a/README.fuchsia b/README.fuchsia
index 98c1519..1c8edd6 100644
--- a/README.fuchsia
+++ b/README.fuchsia
@@ -60,20 +60,26 @@
 
 Make sure that sysroot is available at //prebuilt/third_party/sysroot/linux.
 
+Make sure the target //src/media/lib/ffmpeg/profiles is among your package
+labels, for example:
+
+  fx set workstation_eng.chromebook-x64 --release \
+      --with //src/media/lib/ffmpeg/profiles
+
 Perform the following steps in //build/secondary/third_party/ffmpeg:
 
-  # Build the ffmpeg for all profiles and architectures
-  ./scripts/build_ffmpeg.py x64
-  ./scripts/build_ffmpeg.py arm64
+  rm -rf config
+  rm -rf roll
+  ./scripts/roll \
+      <out>/gen/src/media/lib/ffmpeg/profiles/profiles_ffmpeg_profile_set
 
-  # Update Fuchsia GN and config files based on the results of the builds
-  ./scripts/copy_config.sh
-  ./scripts/generate_gn.py
+where <out> is your Fuchsia out directory (e.g. ~/fuchsia/out/default).
 
 Build and test for regressions.
 
-Prepare and submit a CL for the fuchsia.git changes. The scripts leave two
-build.* directories in third_party/ffmpeg. Those should not be checked in.
+Prepare and submit a CL for the ffmpeg repo changes. The scripts leave a
+roll/ directory in //build/secondary/third_party/ffmpeg. That should not
+be checked in.
 
 Once your changes are integrated, make sure the prebuilder is still working
 properly.
diff --git a/ffmpeg_options.gni b/ffmpeg_options.gni
index 5112be9..20cbc00 100644
--- a/ffmpeg_options.gni
+++ b/ffmpeg_options.gni
@@ -3,9 +3,8 @@
 # found in the LICENSE file.
 
 declare_args() {
-  # Controls what formats are supported. Legal values are [default|max].
+  # Controls what formats are supported.
   ffmpeg_profile = "max"
 }
 
-assert(ffmpeg_profile == "default" || ffmpeg_profile == "max")
 assert(current_cpu == "x64" || current_cpu == "arm64")
diff --git a/scripts/credits_updater.py b/scripts/credits_updater.py
index f6e87ff..2a5d6c9 100644
--- a/scripts/credits_updater.py
+++ b/scripts/credits_updater.py
@@ -303,9 +303,9 @@
     for license_bucket in self.known_credits:
       num_known_credits += len(self.known_credits[license_bucket])
     print('CreditsUpdater stats:')
-    print(f'\t{num_known_credits} files w/ known_credits')
-    print(f'\t{len(self.generated_credits.keys())} generated_credits')
-    print(f'\t{len(self.difficult_files)} difficult_files files')
+    print(f'\t{num_known_credits} files with known credits')
+    print(f'\t{len(self.generated_credits.keys())} generated credits')
+    print(f'\t{len(self.difficult_files)} difficult files')
 
   def WriteCredits(self, dest_dir):
     if self.difficult_files:
diff --git a/scripts/roll.py b/scripts/roll.py
new file mode 100755
index 0000000..e229387
--- /dev/null
+++ b/scripts/roll.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script performs most of the steps required to complete a roll of the
+# Fuchsia ffmpeg repository. It reads a 'profile set' file containing a list
+# of absolute paths to 'profile' files. A profile file contains a list of
+# command line arguments for the ffmpeg 'configure' script. Those command line
+# arguments determine which features are enabled for the profile in question.
+# The base name of a profile file must be <profile>_args, where <profile> is the
+# name of the profile.
+#
+# This script performs two make-based builds of ffmpeg for each profile, one
+# build for x64 and one for arm64. The resulting artifacts are deposited in a
+# temporary directory (the '--roll-dir' option, './roll' by default). It then
+# generates ffmpeg_generated.gni, a config/ directory, and CREDITS.fuchsia.
+#
+# A complete roll consists of the following steps:
+#
+# 1) Ensure that the most recent commit of the Fuchsia ffmpeg repository is
+#    present at //build/secondary/third_party/ffmpeg.
+# 2) Ensure that the most recent commit of the Chromium ffmpeg repository is
+#    present at //third_party/ffmpeg/src.
+# 3) Delete //build/secondary/third_party/ffmpeg/config and ensure the roll
+#    directory (usually //build/secondary/third_party/ffmpeg/roll) is absent
+#    or empty.
+# 4) Run this script.
+# 5) Update README.fuchsia to reflect the new Chromium ffmpeg repo commit.
+# 6) Submit the changes to the Fuchsia ffmpeg repo.
+
+import argparse
+import os
+import subprocess
+import sys
+
+
+def PrintAndCheckCall(argv, *args, **kwargs):
+    print("")
+    print("Running %s" % "\n ".join(argv))
+    print("")
+    subprocess.check_call(argv, *args, **kwargs)
+
+
+def main(argv):
+    parser = argparse.ArgumentParser(
+        epilog="Option defaults assume cwd is build/secondary/third_party/ffmpeg"
+    )
+
+    parser.add_argument(
+        "profile_set_path",
+        metavar="<profile set>",
+        help="file containing profile set paths",
+    )
+
+    parser.add_argument(
+        "--roll-dir",
+        default="./roll",
+        metavar="<path>",
+        help="directory for build output (default: ./roll)",
+    )
+
+    parser.add_argument(
+        "--fuchsia-src-dir",
+        metavar="<path>",
+        help="directory containing Fuchsia source code",
+    )
+
+    parser.add_argument(
+        "--ffmpeg-src-dir",
+        metavar="<path>",
+        help="directory containing ffmpeg source code",
+    )
+
+    parser.add_argument(
+        "--ffmpeg-dir",
+        default=".",
+        metavar="<path>",
+        help="directory containing config/ and CREDITS.fuchsia",
+    )
+
+    args = parser.parse_args(argv)
+
+    # Read the profile set from the provided path.
+    profile_set = []
+    with open(args.profile_set_path, "r") as f:
+        profile_set = f.read().splitlines()
+
+    # Pass on relevant options to roll_build.py.
+    common_build_args = []
+
+    if args.fuchsia_src_dir:
+        common_build_args.append("--fuchsia-src-dir=" + args.fuchsia_src_dir)
+
+    if args.ffmpeg_src_dir:
+        common_build_args.append("--ffmpeg-src-dir=" + args.ffmpeg_src_dir)
+
+    # Run the 'make' build for each profile, both architectures.
+    for profile_path in profile_set:
+        py = os.path.join(args.ffmpeg_dir, "scripts", "roll_build.py")
+        x64_cmd = [
+            py,
+            args.roll_dir,
+            profile_path,
+            "x64",
+        ]
+        arm64_cmd = [
+            py,
+            args.roll_dir,
+            profile_path,
+            "arm64",
+        ]
+
+        PrintAndCheckCall(x64_cmd + common_build_args)
+        PrintAndCheckCall(arm64_cmd + common_build_args)
+
+    # Pass on relevant options to roll_gen.py.
+    gen_args = []
+
+    if args.ffmpeg_dir:
+        gen_args.append("--ffmpeg-dir=" + args.ffmpeg_dir)
+
+    if args.ffmpeg_src_dir:
+        gen_args.append("--ffmpeg-src-dir=" + args.ffmpeg_src_dir)
+
+    # Generate ffmpeg_generated.gni, config/, and CREDITS.fuchsia from the 'make' build artifacts.
+    PrintAndCheckCall(
+        [os.path.join(args.ffmpeg_dir, "scripts", "roll_gen.py")]
+        + [args.roll_dir]
+        + gen_args
+    )
+
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv[1:]))
diff --git a/scripts/roll_build.py b/scripts/roll_build.py
index c6bcac1..9c45e21 100755
--- a/scripts/roll_build.py
+++ b/scripts/roll_build.py
@@ -1,46 +1,23 @@
 #!/usr/bin/env python3
 #
-# Copyright 2017 The Fuchsia Authors. All rights reserved.
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from __future__ import print_function
+# This script performs a single 'make' build for a given profile and
+# architecture. The included feature set is specified using 'configure'
+# script command line options, which are provided in the profile file.
+# Artifacts generated by the build are deposited in a temporary directory
+# by profile name and architecture.
 
-import collections
+import argparse
 import multiprocessing
-import optparse
 import os
-import platform
 import re
 import shutil
 import subprocess
 import sys
 
-SCRIPTS_DIR = os.path.abspath(os.path.dirname(__file__))
-FUCHSIA_DIR = os.path.abspath(os.path.join(SCRIPTS_DIR, "..", "..", "..", "..", ".."))
-FFMPEG_SRC_DIR = os.path.abspath(
-    os.path.join(FUCHSIA_DIR, "third_party", "ffmpeg", "src")
-)
-
-
-# These profiles select different levels of format support. The 'default'
-# profile includes only a few unencumbered formats, essentially those supported
-# by Chromium. The 'max' profile includes most of the formats that ffmpeg
-# supports. Some formats (e.g. Opus) aren't included, because more development
-# is required to support them.
-PROFILES = [
-    "default",
-    "max",
-]
-
-
-USAGE = """Usage: %prog [x64|arm64] [options] -- [configure_args]
-
-Resulting binaries will be placed in:
-  build.[x64|arm64]/default/out/
-  build.[x64|arm64]/max/out/
-  """
-
 
 def PrintAndCheckCall(argv, *args, **kwargs):
     print("Running %s" % "\n ".join(argv))
@@ -58,13 +35,21 @@
         f.write(re.sub(search, replace, contents))
 
 
-def BuildFFmpeg(target_arch, parallel_jobs, config_only, profile, configure_flags):
-    config_dir = "build.%s/%s" % (target_arch, profile)
+def BuildFFmpeg(
+    target_arch,
+    profile,
+    parallel_jobs,
+    config_only,
+    roll_dir,
+    ffmpeg_src_dir,
+    configure_flags,
+):
+    config_dir = os.path.join(roll_dir, profile, target_arch)
     shutil.rmtree(config_dir, ignore_errors=True)
     os.makedirs(os.path.join(config_dir, "out"))
 
     PrintAndCheckCall(
-        [os.path.join(FFMPEG_SRC_DIR, "configure")] + configure_flags, cwd=config_dir
+        [os.path.join(ffmpeg_src_dir, "configure")] + configure_flags, cwd=config_dir
     )
 
     RewriteFile(
@@ -111,38 +96,75 @@
         )
 
 
+def ProfileFromPath(path):
+    if not path.endswith("_args"):
+        exit(f"ERROR: Profile path {path} must be of the form <profile>_args.")
+    return os.path.basename(path)[: -len("_args")]
+
+
 def main(argv):
-    parser = optparse.OptionParser(usage=USAGE)
-    parser.add_option(
-        "--profile",
-        action="append",
-        dest="profiles",
-        choices=PROFILES,
-        help="Profile to build; determines e.g. supported codecs",
+    parser = argparse.ArgumentParser(
+        epilog="Resulting binaries will be placed in: <roll_dir>/<profile>/<x64|arm64>/out/"
     )
-    parser.add_option("--config-only", action="store_true", help="Skip the build step.")
-    options, args = parser.parse_args(argv)
 
-    if len(args) == 0:
-        parser.print_help()
-        return 1
+    parser.add_argument(
+        "roll_dir",
+        metavar="<roll dir>",
+        help="directory for build output",
+    )
 
-    target_arch = args[0]
-    configure_args = args[1:]
+    parser.add_argument(
+        "profile_file",
+        metavar="<profile file>",
+        help="profile file containing configure args",
+    )
+
+    parser.add_argument(
+        "target_arch",
+        metavar="<x64|arm64>",
+        help="target architecture",
+    )
+
+    parser.add_argument(
+        "--fuchsia-src-dir",
+        default="../../../..",
+        metavar="<path>",
+        help="directory containing Fuchsia source code",
+    )
+
+    parser.add_argument(
+        "--ffmpeg-src-dir",
+        default="../../../../third_party/ffmpeg/src",
+        metavar="<path>",
+        help="directory containing ffmpeg source code",
+    )
+
+    parser.add_argument(
+        "--config-only", action="store_true", help="skip the build step"
+    )
+
+    args = parser.parse_args(argv)
+
+    profile_name = ProfileFromPath(args.profile_file)
+
+    configure_file_args = []
+    with open(args.profile_file, "r") as f:
+        configure_file_args = f.read().splitlines()
 
     parallel_jobs = multiprocessing.cpu_count()
 
     print(
         "System information:\n"
+        "Profile       : %s\n"
         "Target arch   : %s\n"
-        "Parallel jobs : %d\n" % (target_arch, parallel_jobs)
+        "Parallel jobs : %d\n" % (profile_name, args.target_arch, parallel_jobs)
     )
 
-    configure_flags = collections.defaultdict(list)
+    configure_flags = []
 
-    # Common configuration.  Note: --disable-everything does not in fact disable
+    # Note: --disable-everything does not in fact disable
     # everything, just non-library components such as decoders and demuxers.
-    configure_flags["Common"].extend(
+    configure_flags.extend(
         [
             "--disable-everything",
             "--disable-all",
@@ -177,16 +199,11 @@
             # audit new [autodetect] configure options and add any desired options to
             # this file.
             "--disable-autodetect",
-            # Common codecs.
-            "--enable-decoder=vorbis,libopus,flac",
-            "--enable-decoder=pcm_u8,pcm_s16le,pcm_s24le,pcm_s32le,pcm_f32le,mp3",
-            "--enable-decoder=pcm_s16be,pcm_s24be,pcm_mulaw,pcm_alaw",
-            "--enable-decoder=theora,vp8,sbc,aptx",
-            "--enable-demuxer=ogg,matroska,wav,flac,mp3,mov",
-            "--enable-parser=opus,vorbis,flac,mpegaudio",
             # Setup include path so Fuchsia's libopus can be used.
-            "--extra-cflags=-I" + os.path.join(FUCHSIA_DIR, "third_party/opus/include"),
-            "--enable-parser=vp3,vp8",
+            "--extra-cflags=-I"
+            + os.path.abspath(
+                os.path.join(args.fuchsia_src_dir, "third_party/opus/include")
+            ),
             '--optflags="-O2"',
             # Force usage of yasm
             "--x86asmexe=yasm",
@@ -199,10 +216,9 @@
     )
 
     # TODO(dalesat): determine if we can use --enable-lto in x64
-    # TODO(dalesat): enable vp9 on arm64
 
-    if target_arch == "x64":
-        configure_flags["Common"].extend(
+    if args.target_arch == "x64":
+        configure_flags.extend(
             [
                 "--enable-cross-compile",
                 "--cross-prefix=/usr/bin/x86_64-linux-gnu-",
@@ -211,13 +227,19 @@
                 "--enable-decoder=vp9",
                 "--enable-parser=vp9",
                 "--sysroot="
-                + os.path.join(
-                    FUCHSIA_DIR, "prebuilt", "third_party", "sysroot", "linux"
+                + os.path.abspath(
+                    os.path.join(
+                        args.fuchsia_src_dir,
+                        "prebuilt",
+                        "third_party",
+                        "sysroot",
+                        "linux",
+                    )
                 ),
             ]
         )
-    elif target_arch == "arm64":
-        configure_flags["Common"].extend(
+    elif args.target_arch == "arm64":
+        configure_flags.extend(
             [
                 "--enable-cross-compile",
                 "--cross-prefix=/usr/bin/x86_64-linux-gnu-",
@@ -226,8 +248,14 @@
                 "--enable-armv8",
                 "--extra-cflags=-march=armv8-a",
                 "--sysroot="
-                + os.path.join(
-                    FUCHSIA_DIR, "prebuilt", "third_party", "sysroot", "linux"
+                + os.path.abspath(
+                    os.path.join(
+                        args.fuchsia_src_dir,
+                        "prebuilt",
+                        "third_party",
+                        "sysroot",
+                        "linux",
+                    )
                 ),
                 "--extra-cflags=--target=aarch64-linux-gnu",
                 "--extra-ldflags=--target=aarch64-linux-gnu",
@@ -235,57 +263,20 @@
             ]
         )
     else:
-        print("Error: Unknown target arch %r!" % (target_arch), file=sys.stderr)
+        print("Error: Unknown target arch %r!" % (args.target_arch), file=sys.stderr)
         return 1
 
-    configure_flags["default"].extend(
-        [
-            # max enables MPEG4 which requires error resilience :(
-            "--disable-error-resilience",
-        ]
+    BuildFFmpeg(
+        args.target_arch,
+        profile_name,
+        parallel_jobs,
+        args.config_only,
+        args.roll_dir,
+        os.path.abspath(args.ffmpeg_src_dir),
+        configure_flags + configure_file_args,
     )
 
-    configure_flags["max"].extend(
-        [
-            "--enable-decoder=aac,aac_latm,h264,mp3",
-            "--enable-demuxer=aac,mp3,mov",
-            "--enable-parser=aac,aac_latm,h264,mpegaudio",
-            # Enable playing avi files.
-            "--enable-decoder=mpeg4",
-            "--enable-parser=h263,mpeg4video",
-            "--enable-demuxer=avi",
-            # Enable playing Android 3gp files.
-            "--enable-demuxer=amr",
-            "--enable-decoder=amrnb,amrwb",
-            # Wav files for playing phone messages.
-            "--enable-decoder=gsm_ms",
-            "--enable-demuxer=gsm",
-            "--enable-parser=gsm",
-        ]
-    )
-
-    def do_build_ffmpeg(profile, configure_flags):
-        if options.profiles and profile not in options.profiles:
-            print("%s skipped" % profile)
-            return
-
-        print("%s configure/build:" % profile)
-        BuildFFmpeg(
-            target_arch, parallel_jobs, options.config_only, profile, configure_flags
-        )
-
-    do_build_ffmpeg(
-        "default",
-        configure_flags["Common"] + configure_flags["default"] + configure_args,
-    )
-    do_build_ffmpeg(
-        "max", configure_flags["Common"] + configure_flags["max"] + configure_args
-    )
-
-    print(
-        "Done. If desired you may copy config.h/config.asm into the "
-        "source/config tree using copy_config.sh."
-    )
+    print("Build completed successfully.")
     return 0
 
 
diff --git a/scripts/roll_gen.py b/scripts/roll_gen.py
index 104786e..b34c925 100755
--- a/scripts/roll_gen.py
+++ b/scripts/roll_gen.py
@@ -1,9 +1,12 @@
 #!/usr/bin/env python3
 #
-# Copyright (c) 2017 The Fuchsia Authors. All rights reserved.
+# Copyright (c) 2022 The Fuchsia Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# This script reads a temporary directory of ffmpeg 'make' build artifacts
+# and generates ffmpeg_generated.gni, the config/ directory, and CREDITS.fuchsia.
+
 """Creates a GN include file for building FFmpeg from source.
 
 The way this works is a bit silly but it's easier than reverse engineering
@@ -18,20 +21,17 @@
 Once you've built all architectures you may run this script.
 """
 
+import argparse
 import collections
 import copy
 import credits_updater
 import datetime
-import fnmatch
 import functools
 import hashlib
 import itertools
-import optparse
 import os
 import re
 import shutil
-import subprocess
-import sys
 
 COPYRIGHT = """# Copyright %d The Fuchsia Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
@@ -73,7 +73,7 @@
 Attr = collections.namedtuple("Attr", _Attrs)(*_Attrs)
 SUPPORT_MATRIX = {
     Attr.ARCHITECTURE: set(["x64", "arm64"]),
-    Attr.PROFILE: set(["default", "max"]),
+    Attr.PROFILE: set([]),
 }
 
 
@@ -525,55 +525,54 @@
         source_set.conditions.add(reduction.condition)
 
 
-def ParseOptions():
-    """Parses the options and terminates program if they are not sane.
+def ParseArgs():
+    """Parses the arguments and terminates program if they are not sane.
 
     Returns:
-      The pair (optparse.OptionValues, [string]), that is the output of
-      a successful call to parser.parse_args().
+      The output of a successful call to parser.parse_args().
     """
-    parser = optparse.OptionParser(usage="usage: %prog [options] DIR")
+    parser = argparse.ArgumentParser()
 
-    parser.add_option(
-        "-s",
-        "--source_dir",
-        dest="source_dir",
-        default="../../../../third_party/ffmpeg/src",
-        metavar="DIR",
-        help="FFmpeg source directory.",
+    parser.add_argument(
+        "roll_dir",
+        metavar="<roll dir>",
+        help="directory for build output",
     )
 
-    parser.add_option(
-        "-b",
-        "--build_dir",
-        dest="build_dir",
+    parser.add_argument(
+        "--ffmpeg-dir",
         default=".",
-        metavar="DIR",
-        help="Build root containing build.x64.linux, etc...",
+        metavar="<path>",
+        help="directory containing config/ and CREDITS.fuchsia",
     )
 
-    parser.add_option(
-        "-p",
+    parser.add_argument(
+        "--ffmpeg-src-dir",
+        default="../../../../third_party/ffmpeg/src",
+        metavar="<ffmpeg root directory>",
+        help="directory containing ffmpeg source code",
+    )
+
+    parser.add_argument(
         "--print_licenses",
         dest="print_licenses",
         default=False,
         action="store_true",
-        help="Print all licenses to console.",
+        help="print all licenses to console",
     )
 
-    options, args = parser.parse_args()
+    args = parser.parse_args()
 
-    if not options.source_dir:
-        parser.error("No FFmpeg source directory specified")
-    elif not os.path.exists(options.source_dir):
-        parser.error("FFmpeg source directory does not exist")
+    if not os.path.exists(args.roll_dir):
+        parser.error(f"Roll build output directory {args.roll_dir} does not exist")
 
-    if not options.build_dir:
-        parser.error("No build root directory specified")
-    elif not os.path.exists(options.build_dir):
-        parser.error("FFmpeg build directory does not exist")
+    if not os.path.exists(args.ffmpeg_dir):
+        parser.error(f"Fuchsia ffmpeg directory {args.ffmpeg_dir} does not exist")
 
-    return options, args
+    if not os.path.exists(args.ffmpeg_src_dir):
+        parser.error(f"FFmpeg source directory {args.ffmpeg_src_dir} does not exist")
+
+    return args
 
 
 def SourceSetCompare(x, y):
@@ -649,7 +648,7 @@
     files (including the seed file) in include_set.
 
     Pass in the set returned from previous calls to avoid re-walking parts of the
-    tree. Given file_path may be relative (to options.src_dir) or absolute.
+    tree. Given file_path may be relative (to args.ffmpeg_src_dir) or absolute.
 
     NOTE: This algorithm is greedy. It does not know which includes may be
     excluded due to compile-time defines, so it considers any mentioned include.
@@ -663,9 +662,7 @@
     reasons at definition for IGNORED_INCLUDE_FILES.
     """
 
-    print(f"GetIncludedSources: {file_path}, {source_dir}, ...")
-
-    # Use options.source_dir to correctly resolve relative file path. Use only
+    # Use args.ffmpeg_src_dir to correctly resolve relative file path. Use only
     # absolute paths in the set to avoid same-name-errors.
     if not os.path.isabs(file_path):
         file_path = os.path.abspath(os.path.join(source_dir, file_path))
@@ -740,7 +737,7 @@
 
 
 def UpdateCredits(sources_to_check, source_dir, build_dir):
-    print("Updating ffmpeg credits...")
+    print("Updating ffmpeg credits")
     updater = credits_updater.CreditsUpdater(source_dir)
     for source_name in sources_to_check:
         updater.ProcessFile(source_name)
@@ -748,24 +745,63 @@
     updater.WriteCredits(build_dir)
 
 
+def CopyConfig(source_dir, dest_dir):
+    files = [
+        "config.h",
+        "config_components.h",
+        "libavutil/avconfig.h",
+        "libavutil/ffversion.h",
+        "libavcodec/bsf_list.c",
+        "libavcodec/codec_list.c",
+        "libavcodec/parser_list.c",
+        "libavformat/demuxer_list.c",
+        "libavformat/muxer_list.c",
+        "libavformat/protocol_list.c",
+    ]
+
+    if source_dir.endswith("x64"):
+        files.append("config.asm")
+
+    for file in files:
+        from_path = os.path.join(source_dir, file)
+        if not os.path.exists(from_path):
+            exit(f"Error copying {from_path}, file does not exist")
+        else:
+            to_path = os.path.join(dest_dir, file)
+            if not os.path.exists(os.path.dirname(to_path)):
+                os.makedirs(os.path.dirname(to_path))
+            shutil.copy2(from_path, to_path)
+
+
 def main():
-    options, _ = ParseOptions()
+    args = ParseArgs()
 
     # Generate map of FFmpeg source files.
-    source_dir = options.source_dir
-    source_files = GetSourceFiles(source_dir)
+    roll_dir = args.roll_dir
+    ffmpeg_dir = args.ffmpeg_dir
+    ffmpeg_src_dir = args.ffmpeg_src_dir
+
+    source_files = GetSourceFiles(ffmpeg_src_dir)
     object_to_sources = GetObjectToSourceMapping(source_files)
 
+    SUPPORT_MATRIX[Attr.PROFILE] = os.listdir(roll_dir)
+    if not SUPPORT_MATRIX[Attr.PROFILE]:
+        exit(
+            "ERROR: found no build output in roll-dir. "
+            + "Is roll-dir (%s) option correct?" % roll_dir
+        )
+
+    print(f"Profiles found {SUPPORT_MATRIX[Attr.PROFILE]}")
+
     sets = []
 
     for arch in SUPPORT_MATRIX[Attr.ARCHITECTURE]:
         for profile in SUPPORT_MATRIX[Attr.PROFILE]:
-            # Assume build directory is of the form build.$arch/$profile.
-            name = "".join(["build.", arch])
-            build_dir = os.path.join(options.build_dir, name, profile)
+            # Build directories are of the form <roll_dir>/<profile>/<arch>.
+            build_dir = os.path.join(args.roll_dir, profile, arch)
             if not os.path.exists(build_dir):
                 continue
-            print(f"Processing build directory: {name}")
+            print(f"Processing build directory {build_dir}")
 
             object_files = GetObjectFiles(build_dir)
 
@@ -773,6 +809,8 @@
             s = GetSourceFileSet(object_to_sources, object_files)
             sets.append(SourceSet(s, set([SourceListCondition(arch, profile)])))
 
+            CopyConfig(build_dir, os.path.join(ffmpeg_dir, "config", profile, arch))
+
     sets = CreatePairwiseDisjointSets(sets)
 
     for source_set in sets:
@@ -781,8 +819,8 @@
     if not sets:
         exit(
             "ERROR: failed to find any source sets. "
-            + "Are build_dir (%s) and/or source_dir (%s) options correct?"
-            % (options.build_dir, options.source_dir)
+            + "Are output directory (%s) and/or ffmpeg-src-dir (%s) arguments correct?"
+            % (roll_dir, args.ffmpeg_src_dir)
         )
 
     # Sort sets prior to further processing and printing to make order
@@ -793,9 +831,9 @@
     sources_to_check = set()
     for source_set in sets:
         for source in source_set.sources:
-            GetIncludedSources(source, source_dir, sources_to_check)
+            GetIncludedSources(source, ffmpeg_src_dir, sources_to_check)
 
-    UpdateCredits(sources_to_check, source_dir, options.build_dir)
+    UpdateCredits(sources_to_check, ffmpeg_src_dir, ffmpeg_dir)
 
     gn_file_name = os.path.join(".", "ffmpeg_generated.gni")
     print(f"Writing: {gn_file_name}")
OSZAR »