13 Replies
      Latest reply on Jun 10, 2019 4:52 AM by eskimo
      MegaWatt Level 1 Level 1 (0 points)

        I used this script in the post-action of the archive in my scheme to make a FAT binary framework. One that will work on the simulator and actual device.

         

        `https://gist.github.com/gauravkeshre/eabb2a13ef6d673fadec84ca60b56b05`

         

        Does anyone know how to convert it to work with Xcode 10?

         

        Here is the script itself:

         

         

          exec > /tmp/${PROJECT_NAME}_archive.log 2>&1

         

         

          UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

         

         

          if [ "true" == ${ALREADYINVOKED:-false} ]

          then

          echo "RECURSION: Detected, stopping"

          else

          export ALREADYINVOKED="true"

         

         

          # make sure the output directory exists

          mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

         

         

          echo "Building for iPhoneSimulator"

          xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone XS' ONLY_ACTIVE_ARCH=NO ARCHS='i386 x86_64' BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" ENABLE_BITCODE=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE=bitcode clean build

         

         

          # Step 1. Copy the framework structure (from iphoneos build) to the universal folder

          echo "Copying to output folder"

          cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"

         

         

          # Step 2. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory

          SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule/."

          if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then

          cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule"

          fi

         

         

          # Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory

          echo "Combining executables"

          lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${EXECUTABLE_PATH}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${EXECUTABLE_PATH}"

         

         

          # Step 4. Create universal binaries for embedded frameworks

          # for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks" ); do

          # BINARY_NAME="${SUB_FRAMEWORK%.*}"

          # lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SUB_FRAMEWORK}/${BINARY_NAME}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}"

          # done

         

         

          # Step 5. Convenience step to copy the framework to the project's directory

          echo "Copying to project dir"

          yes | cp -Rf "${UNIVERSAL_OUTPUTFOLDER}/${FULL_PRODUCT_NAME}" "${PROJECT_DIR}"

         

         

          open "${PROJECT_DIR}"

         

         

          fi

        • Re: Fat Framework Script for Xcode 10?
          eskimo Apple Staff Apple Staff (11,465 points)

          One that will work on the simulator and actual device.

          There’s no supported way to do this.  The problem is that from Xcode’s perspective iOS and the iOS simulator are two different platforms [1], and all of the slices of a universal framework must be for the same platform.  If you do manage to create a framework with different platforms in its slices, you’re likely to encounter weird errors down the pike.

          It would be great if you could file an enhancement request for Xcode to support this properly.  We do actually have a bug on file about this already (r. 17611279), but a fresh bug report will allow you to express your needs in your own terms, and allow iOS engineering to gauge the level of demand.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

          [1] Consider this:

          $ ls -l /Applications/Xcode.app/Contents/Developer/Platforms/
          total 0
          …
          drwxr-xr-x  10 quinn  staff  320 11 Sep 23:43 iPhoneOS.platform
          drwxr-xr-x   7 quinn  staff  224 11 Sep 23:43 iPhoneSimulator.platform

          Also, imagine what would happen if Apple made an iOS device that used a 64-bit Intel CPU (and no, this isn’t a product announcement, it’s just a thought experiment!).  Now your framework would need three slices (64-bit Arm, 64-bit Intel for device, and 64-bit Intel for simulator) and there’d be no way to tell the last two apart.

            • Re: Fat Framework Script for Xcode 10?
              MegaWatt Level 1 Level 1 (0 points)

              The script I presented does work with Xcode 9. Why not in Xcode 10?

                • Re: Fat Framework Script for Xcode 10?
                  eskimo Apple Staff Apple Staff (11,465 points)

                  The script I presented does work with Xcode 9. Why not in Xcode 10?

                  To be clear, this technique has never been supported.  I’m not sure why it’s broken on Xcode 10, but that’s kinda the nature of unsupported things.  For supported things Apple tries to maintain compatibility from release to release — and where we fail that’s worth a bug report — but we make no such promises for unsupported things.

                  If you want to ship a pre-built framework that supports iOS and iOS Simulator, your only supported option right now is to ship two separate frameworks.  The iOS framework should contain all the architectures you support on iOS (typically 64-bit Arm) and the iOS Simulator framework should contain all the architectures you support on the iOS Simulator (typically 64-bit Intel).  Your clients will then have to link with the correct framework based on their build target.

                  And yes, I realise that this is a suboptimal experience, which is why I recommend that you file an enhancement request for Xcode to provide a better option.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"

                    • Re: Fat Framework Script for Xcode 10?
                      bilm Level 1 Level 1 (0 points)

                      "Your clients will then have to link with the correct framework based on their build target"

                       

                      There lies the rub.

                      If you can show me how to do that, then I wouldn't need a "fat" binary.

                       

                      Thanks in advance

                       

                      -bilm

                        • Re: Fat Framework Script for Xcode 10?
                          dardo Level 1 Level 1 (0 points)

                          Hello eskimo,

                           

                          we have similar issue. I am in the side of the client now, my SDK provider gave me an sdk for iOS devices and other iOS simulator. This adds extra complexity in my project for obvious reasons.

                           

                          Some SDKs (e.g. https://www.mapbox.com/help/ios-framework-size/#cpu-architectures) write the below, Is this true?

                           

                          "Our framework includes a “fat” (multi-architecture) binary that contains slices for armv7, arm64, i386, and x86_64 CPU architectures. ARM slices are used by physical iOS devices, while i386 and x86_64 are used by Simulator and are stripped from your app during the build and archive process. When a user downloads your app from the App Store, they receive only the architecture that their device requires."

                           

                          Furthermore as Bilm said (+1), would be great to have a suggestion on how to handle this without merging both (with lipo for instance)

                           

                          Thanks,

                           

                          Eduardo

                            • Re: Fat Framework Script for Xcode 10?
                              eskimo Apple Staff Apple Staff (11,465 points)

                              Some SDKs (…) write the below, Is this true?

                              Well, it’s true that they write that (-:  Seriously though, such a configuration is not supported by Apple.  As I mentioned back on 11 Oct, all slices of a framework must be for the same platform, and iOS and the iOS Simulator are different platforms.

                              I also want to stress that I completely agree that Apple should do a better job of supporting this.  I’m not aware of any good solution to this problem.  All of the available options have significant drawbacks.  So, once all is said and done, and you’ve decided on an approach that works for you, I encourage you to file an enhancement request describing the problems you encountered and requesting a better solution.

                              Furthermore as Bilm said (+1), would be great to have a suggestion on how to handle this without merging both

                              Agreed.  Unfortunately I don’t have a good answer for you on that front.  There’s three parts to this problem:

                              1. Compiling

                              2. Linking

                              3. Embedding

                              You can solve most of the compiling and linking problems via the Frameworks Search Paths (FRAMEWORK_SEARCH_PATHS) build setting.  The trick is to embed the platform name into the build setting value, for example, ThirdPartyFrameworks/$(PLATFORM_NAME) $(inherited).  This will cause it to search ThirdPartyFrameworks/iphoneos for device builds and ThirdPartyFrameworks/iphonesimulator for simulator builds.

                              One other linker gotcha relates to the Runpath Search Paths (LD_RUNPATH_SEARCH_PATHS) build setting.  This should be set to $(inherited) @executable_path/Frameworks.  Xcode sets this for you when you embed a framework that you’ve built from source, but you may need to set it yourself in this case.

                              The nasty gotcha relates to embedding.  The standard technique for embedding a framework, using a Copy Files build phase, triggers ‘magic’ when you give it a framework that comes from an SDK, and I haven’t found any way to replicate that magic for non-SDK frameworks.  After monkeying around with this for a while I eventually came up with a couple of solutions that work, but neither of them is clean enough that I’m happy to share them here.


                              At this point I’m going to have to admit defeat.  I don’t support tools for DTS (my focus is on low-level APIs, like networking), so I’m not up-to-date with all the latest developments on this front.  If you need help with this then I recommend that you open a DTS tech support incident and talk to one of our tools specialists.

                              Share and Enjoy

                              Quinn “The Eskimo!”
                              Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                              let myEmail = "eskimo" + "1" + "@apple.com"

                                • Re: Fat Framework Script for Xcode 10?
                                  NicoX Level 1 Level 1 (0 points)

                                  Just incase if people wants to use an all in one a build script that will create simulator and device frameworks based on the selected schema.

                                  Here it is:

                                  #  This is a modified script and it will ask the target to build separate libraries for simulator and device etc 
                                  # On project folder it will be found under Generated-Frameworks folder
                                  # As per the documentations for XCode 10 Apple does not allow building FAT librares
                                  # Go ahead and check https://forums.developer.apple.com/thread/66978 and https://forums.developer.apple.com/thread/109583
                                  # Enjoy NicoX :)
                                  
                                  set -e
                                  # If we're already inside this script then die
                                  if [ -n "$MULTIPLATFORM_BUILD_IN_PROGRESS" ]; then
                                    exit 0
                                  fi
                                    export MULTIPLATFORM_BUILD_IN_PROGRESS=1
                                  
                                  ############################################
                                  # Options
                                  ############################################
                                  REVEAL_ARCHIVE_IN_FINDER=true
                                  OUTPUT_DIR_NAME="Generated-Frameworks"
                                  FRAMEWORK_NAME="${PROJECT_NAME}"
                                  
                                  OUTPUT_DIR="${PROJECT_DIR}/${OUTPUT_DIR_NAME}/${FRAMEWORK_NAME}-${CONFIGURATION}-framework/"
                                  SIMULATOR_LIBRARY_OUT_DIR="${OUTPUT_DIR}/Simulator/"
                                  GENERATED_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal"
                                  SIMULATOR_FRAMEWORK_OUT_DIR="${GENERATED_LIBRARY_DIR}/Simulator"
                                  FRAMEWORK="${SIMULATOR_FRAMEWORK_OUT_DIR}/${FRAMEWORK_NAME}.framework"
                                  
                                  ########################################################################
                                  # Build Frameworks
                                  ########################################################################
                                  xcodebuild -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator OBJROOT="${OBJROOT}/DependantBuilds" 
                                  
                                  ########################################################################
                                  # Create directory for general
                                  ########################################################################
                                  rm -rf "${GENERATED_LIBRARY_DIR}"
                                  mkdir "${GENERATED_LIBRARY_DIR}"
                                  mkdir "${SIMULATOR_FRAMEWORK_OUT_DIR}"
                                  mkdir "${FRAMEWORK}"
                                  
                                  ########################################################################
                                  # Copy files Framework
                                  ########################################################################
                                  SIMULATOR_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${FRAMEWORK_NAME}.framework"
                                  
                                  ########################################################################
                                  # Make a binary for simulaotr ie. x86_64 ot i386 file system
                                  ########################################################################
                                  # For Swift framework, Swiftmodule needs to be copied in the universal framework 
                                  if [ -d "${SIMULATOR_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/" ]; then
                                    cp -f "${SIMULATOR_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/"* "${FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" | echo
                                  fi
                                  cp -r "${SIMULATOR_LIBRARY_PATH}/." "${FRAMEWORK}"
                                  
                                  ########################################################################
                                  # Copy simulator build library in output folder
                                  ########################################################################
                                  rm -rf "$OUTPUT_DIR"
                                  mkdir -p "$OUTPUT_DIR"
                                  mkdir -p ${SIMULATOR_LIBRARY_OUT_DIR}
                                  
                                  cp -r "${FRAMEWORK}" "$SIMULATOR_LIBRARY_OUT_DIR"
                                  #########################################################################
                                  
                                  echo "Simulator Lib Path ---->" "${SIMULATOR_LIBRARY_PATH}" "<----"
                                  
                                  if [ ${REVEAL_ARCHIVE_IN_FINDER} = true ]; then
                                    open "${SIMULATOR_LIBRARY_OUT_DIR}/"
                                  fi
                                  
                                  
                                  ##########################################################################
                                  # Now lets build for device; shall we?
                                  ##########################################################################
                                  DEVICE_LIBRARY_OUT_DIR="${OUTPUT_DIR}/Device/"
                                  DEVICE_FRAMEWORK_OUT_DIR="${GENERATED_LIBRARY_DIR}/Device"
                                  FRAMEWORK="${DEVICE_FRAMEWORK_OUT_DIR}/${FRAMEWORK_NAME}.framework"
                                  
                                  ########################################################################
                                  # Build Device Frameworks
                                  ########################################################################
                                  xcodebuild -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} -sdk iphoneos ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos OBJROOT="${OBJROOT}/DependantBuilds" 
                                  
                                  ########################################################################
                                  # Create directory for general
                                  ########################################################################
                                  rm -rf "${GENERATED_LIBRARY_DIR}"
                                  mkdir "${GENERATED_LIBRARY_DIR}"
                                  mkdir "${DEVICE_FRAMEWORK_OUT_DIR}"
                                  mkdir "${FRAMEWORK}"
                                  
                                  ########################################################################
                                  # Copy files Framework
                                  ########################################################################
                                  DEVICE_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphoneos/${FRAMEWORK_NAME}.framework"
                                  
                                  ########################################################################
                                  # Make a binary for device ie. arm7, arm7v, arm8 file system
                                  ########################################################################
                                  # For Swift framework, Swiftmodule needs to be copied in the universal framework 
                                  if [ -d "${DEVICE_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/" ]; then
                                    cp -f "${DEVICE_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/"* "${FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" | echo
                                  fi
                                  cp -r "${DEVICE_LIBRARY_PATH}/." "${FRAMEWORK}"
                                  ########################################################################
                                  # Copy device build library in output folder
                                  ########################################################################
                                  mkdir -p ${DEVICE_LIBRARY_OUT_DIR}
                                  cp -r "${FRAMEWORK}" "$DEVICE_LIBRARY_OUT_DIR"
                                  
                                  echo "Device Lib Path ---->" "${DEVICE_LIBRARY_PATH}" "<----"
                                  
                                  if [ ${REVEAL_ARCHIVE_IN_FINDER} = true ]; then
                                    open "${DEVICE_LIBRARY_OUT_DIR}/"
                                  fi
                                  
                                  

                                  let kudus = "Kudos to eskimo" +"1" + "@apple.com"

                                  log(messege: "\(kudus)"

                                   

                                  Please feel free to update and share!!

                                  • Re: Fat Framework Script for Xcode 10?
                                    strm Level 1 Level 1 (0 points)

                                    created  enhancement request 47111236. Thanks eskimo

                        • Re: Fat Framework Script for Xcode 10?
                          iousin Level 1 Level 1 (0 points)

                          Hi,

                          Can I ask that bitcode is generated when you build for simulator? It doesn't for me, I'm using the following command to verify:

                           

                          otool -arch x86_64 -l MyFramework.framework/MyFramework | grep LLVM
                          • Re: Fat Framework Script for Xcode 10?
                            arunks Level 1 Level 1 (0 points)

                            This is on a slightly related topic, i am trying to package and publish a Swift FAT framework and it does not seem to work. I get issues with Simulator architecture not being able to find header files during compilation, but the device architecture works fine.

                            • Re: Fat Framework Script for Xcode 10?
                              eskimo Apple Staff Apple Staff (11,465 points)

                              There’s some good news on this front.  Xcode 11 beta supports a new concept, XCFrameworks, that makes it possible to bundle a multi-platform binary framework into a single distributable product.  See WWDC 2019 Session 416 Binary Frameworks in Swift for the details.

                              Share and Enjoy

                              Quinn “The Eskimo!”
                              Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                              let myEmail = "eskimo" + "1" + "@apple.com"