[{"data":1,"prerenderedAt":271},["ShallowReactive",2],{"article_list_ffmpeg_":3},[4],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"tags":11,"excerpt":10,"image":16,"publishDate":17,"body":18,"_type":262,"_id":263,"_source":264,"_file":265,"_stem":266,"_extension":267,"author":268},"/jbagley/2023-06-01/universal_ffmpeg_custom_builds","2023-06-01",false,"","Building Universal FFmpeg Custom Binaries","I am using a very pared down set of FFMpeg features for a macOS project that I\nbuild into a custom library. I had a script set up to configure the build which\nworked fine on my Intel based MacBook Pro. Then I upgraded to an Apple Silicon\nMacBookPro and wanted to run natively, or at least see what happened when I\ndid. To build, FFMpeg uses autoconf which produces a makefile that then handles\nthe build.",[12,13,14,15],"c","bash","ffmpeg","apple","/jbagley/2023-06-01/img/header.png","2024-04-01",{"type":19,"children":20,"toc":254},"root",[21,30,35,41,74,87,93,98,103,114,119,156,177,183,203,212,224,236,249],{"type":22,"tag":23,"props":24,"children":26},"element","h2",{"id":25},"motivation",[27],{"type":28,"value":29},"text","Motivation",{"type":22,"tag":31,"props":32,"children":33},"p",{},[34],{"type":28,"value":10},{"type":22,"tag":23,"props":36,"children":38},{"id":37},"the-simplest-thing-which-could-work-but-didnt",[39],{"type":28,"value":40},"The simplest thing which could work but didn't",{"type":22,"tag":31,"props":42,"children":43},{},[44,46,53,55,61,63,72],{"type":28,"value":45},"Autoconf supports passing flags for the compiler in the call to configure. I was\nalready passing ",{"type":22,"tag":47,"props":48,"children":50},"code",{"className":49},[],[51],{"type":28,"value":52},"-arch x86_64",{"type":28,"value":54}," as part of ",{"type":22,"tag":47,"props":56,"children":58},{"className":57},[],[59],{"type":28,"value":60},"--extra-cflags",{"type":28,"value":62},". Its\n",{"type":22,"tag":64,"props":65,"children":69},"a",{"href":66,"rel":67},"https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Multiple-Architectures.html",[68],"nofollow",[70],{"type":28,"value":71},"documentation",{"type":28,"value":73},"\nsays that I may provide more than one architecture on macOS. That page notes\nthat this doesn't always work.",{"type":22,"tag":31,"props":75,"children":76},{},[77,79,85],{"type":28,"value":78},"Based on that, I tried ",{"type":22,"tag":47,"props":80,"children":82},{"className":81},[],[83],{"type":28,"value":84},"-arch x86_64 -arch arm64",{"type":28,"value":86},"', and as foreshadowed it\nindeed did not work. Autoconf's compiler tests would fail when trying the x86_64\nbuild with clang. I went through some more permutations of arguments, but in the\nend I had to do separate builds for each architecture.",{"type":22,"tag":23,"props":88,"children":90},{"id":89},"building",[91],{"type":28,"value":92},"Building",{"type":22,"tag":31,"props":94,"children":95},{},[96],{"type":28,"value":97},"I use a build directory rather than calling configure in the top of my FFmpeg\nworking copy and dirtying it up. I am running this from that build dir.",{"type":22,"tag":31,"props":99,"children":100},{},[101],{"type":28,"value":102},"Here is bash psuedocode of my build script:",{"type":22,"tag":104,"props":105,"children":109},"pre",{"className":106,"code":108,"language":28},[107],"language-text","for arch in x86_64 arm64; do\n\n   rm -rf Makefile config.h ffbuild lib* tests \n\n    ../configure --cc=clang \\\n                 --extra-cflags=\"-arch $arch\" \\\n                 --extra-ldflags=\"-arch $arch\"  \\\n                 --build_suffix=\"$install_dir\"  \\\n                 --prefix=\"path/to/where/I/want/the/libs/to/be/copied/by/make/install\" \\\n                 # other flags for ffmpeg configuration, see '../configure -h' output\n    make -j\n    make install\ndone\n",[110],{"type":22,"tag":47,"props":111,"children":112},{"__ignoreMap":8},[113],{"type":28,"value":108},{"type":22,"tag":31,"props":115,"children":116},{},[117],{"type":28,"value":118},"Looping through the desired architectures, first it cleans the build directory\nto get rid of the previous architecture output, then configures, building and\ninstalling the library.",{"type":22,"tag":31,"props":120,"children":121},{},[122,124,130,132,138,140,146,148,154],{"type":28,"value":123},"In the end, you have two sets of library files installed into the path passed\nwith ",{"type":22,"tag":47,"props":125,"children":127},{"className":126},[],[128],{"type":28,"value":129},"--prefix",{"type":28,"value":131}," with the suffixes ",{"type":22,"tag":47,"props":133,"children":135},{"className":134},[],[136],{"type":28,"value":137},"_x86-64",{"type":28,"value":139}," and ",{"type":22,"tag":47,"props":141,"children":143},{"className":142},[],[144],{"type":28,"value":145},"_arm64",{"type":28,"value":147}," provided by the\n",{"type":22,"tag":47,"props":149,"children":151},{"className":150},[],[152],{"type":28,"value":153},"--build_suffix",{"type":28,"value":155}," argument.",{"type":22,"tag":31,"props":157,"children":158},{},[159,161,167,169,175],{"type":28,"value":160},"I needed to pass the architecture again with ",{"type":22,"tag":47,"props":162,"children":164},{"className":163},[],[165],{"type":28,"value":166},"--extra-ldflags",{"type":28,"value":168}," to avoid the\nautoconf compiler test failing. The most basic test for compiler support would\nfail when an x86_64 ",{"type":22,"tag":47,"props":170,"children":172},{"className":171},[],[173],{"type":28,"value":174},".o",{"type":28,"value":176}," file couldn't be linked to an arm64 exectuable.",{"type":22,"tag":23,"props":178,"children":180},{"id":179},"linking-the-universal-libraries",[181],{"type":28,"value":182},"Linking The Universal Libraries",{"type":22,"tag":31,"props":184,"children":185},{},[186,188,194,196,201],{"type":28,"value":187},"The call to ",{"type":22,"tag":47,"props":189,"children":191},{"className":190},[],[192],{"type":28,"value":193},"lipo",{"type":28,"value":195}," turned out to be simple. The trickier part for me was\nbuilding the array of names to pass to ",{"type":22,"tag":47,"props":197,"children":199},{"className":198},[],[200],{"type":28,"value":193},{"type":28,"value":202}," as input files. I don't remember\nthe last time I used these.",{"type":22,"tag":104,"props":204,"children":207},{"className":205,"code":206,"language":28},[107],"lib_output_dir=\u003Cthe same path I used as the --prefix argument to configure above>\ncd \"$lib_output_dir\"\nfor lib_name in \u003Clib names I produced in my build without the file extension, e.g. libavcodec>; do\n    input_names=()\n    for arch in x86_64 arm64; do\n        input_names+=(${lib_name}_{$arch}.a)\n    done\n    /usr/bin/lipo -create -output ${lib_name}.a ${input_names[*]}\n    rm ${input_names}[*]}\ndone\ncd -\n",[208],{"type":22,"tag":47,"props":209,"children":210},{"__ignoreMap":8},[211],{"type":28,"value":206},{"type":22,"tag":31,"props":213,"children":214},{},[215,217,222],{"type":28,"value":216},"It changes to the directory where the first section installed the libraries. Of\ncourse, ",{"type":22,"tag":47,"props":218,"children":220},{"className":219},[],[221],{"type":28,"value":193},{"type":28,"value":223}," could be run on the libs within the build folder, but my script\nwas already installing them, and I found this more convenient and simple.",{"type":22,"tag":31,"props":225,"children":226},{},[227,229,234],{"type":28,"value":228},"For each architecture, it builds the array of library filenames, including the\narchitecture suffix of each. That array gives the typical DRY advantages and\nkeeps the invocation of ",{"type":22,"tag":47,"props":230,"children":232},{"className":231},[],[233],{"type":28,"value":193},{"type":28,"value":235}," simple.",{"type":22,"tag":31,"props":237,"children":238},{},[239,241,247],{"type":28,"value":240},"You can confirm it worked using the ",{"type":22,"tag":47,"props":242,"children":244},{"className":243},[],[245],{"type":28,"value":246},"file",{"type":28,"value":248}," utility. You should see a line for each\narchitecture it its output.",{"type":22,"tag":31,"props":250,"children":251},{},[252],{"type":28,"value":253},"That's it. If you have a better way, or see something I can do better here, let\nme know.",{"title":8,"searchDepth":255,"depth":255,"links":256},3,[257,259,260,261],{"id":25,"depth":258,"text":29},2,{"id":37,"depth":258,"text":40},{"id":89,"depth":258,"text":92},{"id":179,"depth":258,"text":182},"markdown","content:jbagley:2023-06-01:Universal_FFMPEG_custom_builds.md","content","jbagley/2023-06-01/Universal_FFMPEG_custom_builds.md","jbagley/2023-06-01/Universal_FFMPEG_custom_builds","md",{"user":269,"name":270},"jbagley","Jason Bagley",1780330267141]