13

On Xcode 10 I'm getting this build error with one of my frameworks when I do an incremental build (clean builds work):

Showing All Messages
:-1: Cycle inside LoggingSharedFramework; building could produce unreliable results.
Cycle details:
→ Target 'LoggingSharedFramework' has a command with output 'blablabla/Build/Products/Debug-iphonesimulator/LoggingSharedFramework.framework/LoggingSharedFramework'
○ Target 'LoggingSharedFramework' has link command with output 'blablabla/Build/Intermediates.noindex/blablablah/Debug-iphonesimulator/LoggingSharedFramework.build/Objects-normal/x86_64/LoggingSharedFramework'
  • The framework has no target dependencies
  • The Headers phase is before Compile Sources
  • I have gone through every file and made sure that there are no imports grabbing files outside of LoggingSharedFramework (other than Cocoa stuff)
  • I am not using any dependency management system (e.g. carthage) because there are no external dependencies. This framework is maintained within the project

This error makes no sense to me. What is the actual cause? How can I figure out what is introducing the cycle? How can I fix the cycle?

Here's the debug build log I get:

Build system information
error: target:  ->

node: <all> ->

command: <all> ->

node: .../DerivedData/MyApp/Build/Products/Debug-iphoneos/LoggingSharedFramework.framework/LoggingSharedFramework ->

command: 60cc809630:Debug:CreateUniversalBinary .../DerivedData/MyApp/Build/Products/Debug-iphoneos/LoggingSharedFramework.framework/LoggingSharedFramework normal armv7 arm64 ->

node: .../DerivedData/MyApp/Build/Intermediates.noindex/MyApp.build/Debug-iphoneos/LoggingSharedFramework.build/Objects-normal/armv7/LoggingSharedFramework ->

command: 60cc809630:Debug:Ld .../DerivedData/MyApp/Build/Intermediates.noindex/MyApp.build/Debug-iphoneos/LoggingSharedFramework.build/Objects-normal/armv7/LoggingSharedFramework normal armv7 ->

node: .../DerivedData/MyApp/Build/Products/Debug-iphoneos/LoggingSharedFramework.framework/LoggingSharedFramework

** BUILD FAILED **

I guess there is a cycle there, but I don't understand why it exists or how to fix it. It looks like Ld on some intermediate object depends on the compiled framework? That makes no sense to me.

I previously thought that I had fixed this by moving my headers build phase earlier, fixing umbrella header warnings, and cleaning my build. But it turns out that that was only a temporary fix. This problem seems to reappear randomly and once Xcode detects a cycle it won't go away until I clean again. Then it stays gone for a while into some unknown cause brings it back.

Max
  • 21,123
  • 5
  • 49
  • 71
  • Which dependency management system are you using? Can you, please, give more information? – fewlinesofcode Oct 11 '18 at 16:31
  • None, clarified in the question – Max Oct 11 '18 at 17:37
  • Have you tried deleting derived data? – Ben Kane Oct 11 '18 at 17:40
  • @BenKane as mentioned in the question, clean builds do work, but incremental builds fail. Doing a clean build every time is not a "solution" – Max Oct 11 '18 at 17:47
  • Sorry about that, I missed that detail. – Ben Kane Oct 11 '18 at 17:48
  • 2
    Have you read through the "If the cycle is reported only during incremental builds" of [this Xcode 10 help page](https://help.apple.com/xcode/mac/current/#/dev621201fb0)? – Ben Kane Oct 11 '18 at 17:56
  • @BenKane the steps there are 1. remove target dependencies (I have none) 2. remove import statements (I have searched by hand and not found any that could indicate a cycle) and 3. restructure your source code (I'm willing, but where is the cycle!? I don't know what to restructure) – Max Oct 11 '18 at 17:58
  • 2
    If you're able to create a small project that reproduces the issue, I'd be happy to take a look – Ben Kane Oct 11 '18 at 18:01
  • 1
    Can you add the output you get after running `defaults write com.apple.dt.XCBuild EnableDebugActivityLogs -bool YES` in terminal then building again? – Ben Kane Oct 11 '18 at 18:07
  • @BenKane I was going to try `EnableDebugActivityLogs` but first I figured I should clean up any irrelevant warnings in the framework so that the error would be more apparent. But fixing the umbrella header warnings lead me to the solution I posted below. Thanks for your suggestions. – Max Oct 12 '18 at 18:25
  • Glad to see you were able to fix it – Ben Kane Oct 12 '18 at 18:48
  • @Max Why did you delete your answer? – matt Oct 15 '18 at 14:22
  • The problem appeared in other frameworks and my answer did not solve it. – Max Oct 15 '18 at 20:30
  • @BenKane I added the debug build log to my question. I'm still getting this issue. Any insight would be greatly appreciated. – Max Mar 11 '19 at 14:51
  • What is the point of a bounty on a question you’ve already answered? – matt Mar 11 '19 at 15:33
  • @matt I thought my answer worked but it didn't. I forgot to delete it – Max Mar 11 '19 at 16:06
  • Okay but you might want to clarify by adding your “failed” solution to your _question_ so that people understand what you’ve tried and why it didn’t work. You are not really asking this question in a way that can produce useful answers. 500 rep is a big bounty; you owe it to yourself to be as clear and complete as possible, or you might just lose the rep without getting a good answer. – matt Mar 11 '19 at 17:16
  • @Max If you're able to create a small project that reproduces the issue, I'd be happy to take a look – staticVoidMan Mar 18 '19 at 13:47
  • @staticVoidMan that's on my todo list. The code where the cycle appears is unfortunately large and proprietary, so it's nontrivial – Max Mar 18 '19 at 14:43
  • @Max Ok, it's mostly a project configuration issue. But for starters, in `LoggingSharedFramework` have you checked `Xcode Project Settings > Build Phases > Compile Sources` for files that should not be there? – staticVoidMan Mar 19 '19 at 05:31
  • From the Xcode 10.2 release notes "Public headers in a framework might mistakenly #import or #include private headers, which causes layering violations and potential module cycles. There’s a new diagnostic that reports such violations. It’s OFF by default in clang and is controlled by the -Wframework-include-private-from-public flag." As soon as this problem occurs again I will test this out – Max Mar 29 '19 at 13:09
  • This has again come up in Xcode 12.4... – Parth Jun 08 '21 at 15:09
  • @ParthTamane I haven't had this issue in a while, even with Xcode 12.4. Have you tried checking that warning in my previous comment? – Max Jun 08 '21 at 17:55
  • Which warning? Also, how to enable `Wframework-include-private-from-public flag`? – Parth Jul 02 '21 at 19:32

4 Answers4

1

This problems seems to have resolved itself in Xcode 10.2

Max
  • 21,123
  • 5
  • 49
  • 71
0

Steps I would take in this situation would be the following:

If LoggingSharedFramework target is maintained within bigger project:

  1. Isolate it to separate project
  2. Remove all LoggingSharedFramework files and imports from Containing project
  3. Build fat framework and add it to the project as embedded framework
  4. Check if error still persists

If LoggingSharedFramework is already a separate project:

  1. Check every single file and remove all unnecessary imports (including Cocoa stuff)
  2. Check linking phase and related build scripts and remove all unnecessary stuff

It's alwo worth trying to Enable/Disable parallel builds

I think, you've already went through it all, but Good luck!

fewlinesofcode
  • 3,007
  • 1
  • 13
  • 30
0

I've encountered that error a couple of time but this is what worked for me.

1) I go to Xcode -> Preferences -> Locations, clear out all derived data and close Xcode.

2) If your using cocoa pods, delete the workspace i.e (.xcworkspace) file and the Podile/ directory

3) Navigate to your project and run pod install.

4) Open your project through Xcode, clean and build.

5) Run the project and everything should work fine.

GyroCocoa
  • 1,542
  • 16
  • 19
  • Yes, clean builds always work. But clean builds take _way_ longer than incremental builds and this fix is still only temporary because the cycles randomly appear later on. – Max Mar 18 '19 at 12:14
0

My guess would be that LoggingSharedFramework is not properly built as a fat framework with all the available architectures. Try this post script when building the framework.

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}"
#mkdir -p "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework" 

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}/" "${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}"

echo "Combining executables end"

# Step 4. Create universal binaries for embedded frameworks
for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks" ); do
BINARY_NAME="${SUB_FRAMEWORK%.*}"
echo "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}"
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

AlexM
  • 552
  • 4
  • 17
  • The cycle error appears when building the framework target on its own though. I haven't tried this script yet but my guess is it wouldn't even run because when I try to build it immediately detects a cycle inside the framework and refuses to do anything else. – Max Mar 18 '19 at 14:42