Xcode 10 framework dependency cycle with itself
Asked Answered
L

5

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.

Linnlinnaeus answered 9/10, 2018 at 15:24 Comment(24)
Which dependency management system are you using? Can you, please, give more information?Ramires
None, clarified in the questionLinnlinnaeus
Have you tried deleting derived data?Cottonwood
@BenKane as mentioned in the question, clean builds do work, but incremental builds fail. Doing a clean build every time is not a "solution"Linnlinnaeus
Sorry about that, I missed that detail.Cottonwood
Have you read through the "If the cycle is reported only during incremental builds" of this Xcode 10 help page?Cottonwood
@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)Linnlinnaeus
If you're able to create a small project that reproduces the issue, I'd be happy to take a lookCottonwood
Can you add the output you get after running defaults write com.apple.dt.XCBuild EnableDebugActivityLogs -bool YES in terminal then building again?Cottonwood
@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.Linnlinnaeus
Glad to see you were able to fix itCottonwood
@Linnlinnaeus Why did you delete your answer?Achievement
The problem appeared in other frameworks and my answer did not solve it.Linnlinnaeus
@BenKane I added the debug build log to my question. I'm still getting this issue. Any insight would be greatly appreciated.Linnlinnaeus
What is the point of a bounty on a question you’ve already answered?Achievement
@Achievement I thought my answer worked but it didn't. I forgot to delete itLinnlinnaeus
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.Achievement
@Linnlinnaeus If you're able to create a small project that reproduces the issue, I'd be happy to take a lookFreiman
@Freiman that's on my todo list. The code where the cycle appears is unfortunately large and proprietary, so it's nontrivialLinnlinnaeus
@Linnlinnaeus 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?Freiman
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 outLinnlinnaeus
This has again come up in Xcode 12.4...Bloxberg
@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?Linnlinnaeus
Which warning? Also, how to enable Wframework-include-private-from-public flag?Bloxberg
L
1

This problems seems to have resolved itself in Xcode 10.2

Linnlinnaeus answered 23/4, 2019 at 19:48 Comment(0)
R
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!

Ramires answered 12/10, 2018 at 14:18 Comment(0)
S
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.

Sunshine answered 16/3, 2019 at 16:40 Comment(1)
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.Linnlinnaeus
T
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

Telesthesia answered 18/3, 2019 at 13:20 Comment(1)
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.Linnlinnaeus
S
0

I was getting a similar cycling error, not with LoggingSharedFramework but with the iOS app I was building. Let me preface that I'm early on the learning curve for using Xcode. So there's a lot I don't yet understand about Xcode. I'm also not experienced enough to tell if my situation is relevant to the posted question other than saying we're both experiencing a cycling issue. My problem certainly differs from the original post in that cleaning the build never fixed the problem for me as Max randomly observed. Off the top on my head I would think that kind of behavior might be the caused by some ill-formed target dependency.

I eventually tracked the problem down. I went to the settings for the target I was building and selected the "Build Phases" tab. Under the "Compile Sources" section I had 3 items. One of those item was "iosApp.app" (the thing I was cycling on). I deleted it and that fixed the problem. I no longer had the cycle issue, and I was able to run the application.

Here's what I suspect: I copied the project from a Windows machine that I use Android Studio to build me a KMM project. I then used Xcode to try and build the iosApp contained in that project. That's when the problem occurred. Since this was just a basic getting started KMM project that I built, I decided to just build that project on the Mac side rather than using a copy of that project from the Windows computer. Low and behold, that new project ran just fine. From that I was able to compare the two projects and that's when I noticed the extra "Compile Sources" file. So there's some difference from building a KMM project on Windows and on MAC. By the way, both sides say they have the same version of the KMM plugin installed. It's possible that I built the project on the window's side with an earlier version of the KMM plugin, but I don't think there's any way I can tell since now both sides now say that they have the same version of the KMM plugin installed.

For those with more experience using Xcode, is there any way to tell which build phase the problem occurred in? That would be a big help and it wasn't obvious to me how to find that out in the Xcode Build Log. Even in the original post I can't tell what build phase is running when Max got his error.

Signature answered 1/4 at 21:48 Comment(1)
I think you would be best served by posting this as a separate question. The circumstances seem very different.Linnlinnaeus

© 2022 - 2024 — McMap. All rights reserved.