spotify / XCRemoteCache

Other
827 stars 50 forks source link

Build failure on 2nd attempt due to Dependency cycle issue after running xcprepare integrate #101

Closed cezarsignori closed 2 years ago

cezarsignori commented 2 years ago

My integration setup

[X] Automatic integration using xcprepare integrate ...

Expected/desired behavior

The application build succeeds every time I hit build without making changes after a successful build.

Minimal reproduction of the problem with instructions

Step 1. Integrate XCRemoteCache with the application using the automatic integration method; Step 2. Run rm -rf ~/Library/Developer/Xcode/DerivedData and rm -rf ~/Library/Caches/XCRemoteCache; Step 3. Build the application (success) Step 4. Build the application (failure).

Consumer Logs

When using `log show --predicate 'sender BEGINSWITH "xc"' --style compact --info --debug -last 7m` after running `xcprepare integrate`, I see no errors but ``` E xcodebuild[1023:573394] objc[1023]: Class AMSupportURLSession is implemented in both /usr/lib/libamsupport.dylib (0x1f798b6c8) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x1084c0318). One of the two will be used. Which one is undefined. ``` When using `log show --predicate 'sender BEGINSWITH "xc"' --style compact --info --debug -last 1m` after building my application I see no errors but ``` Couldn't verify if should disable RC for {commit} ```

Environment

Post build stats

** 1st build **
hit_count: 79
miss_count: 0
local_cache_bytes: 50233344
indexing_hit_count: 12
indexing_miss_count: 1
  
** 2nd build **
 hit_count: 0
miss_count: 0
local_cache_bytes: 50233344
indexing_hit_count: 0
indexing_miss_count: 0
   

Others

I am on an Apple M1 Max with macOS Monterey 12.2.1 (21D62).

I am testing RC 0.3.7 on small application with 70 dependencies (across my own modules, git submodules and Pods committed to the repo).

Each xcodeproj has an associated manually created .rcinfo file with the following content:

primary_repo: {xcodeproj_repo}
cache_addresses:
- http://localhost:8080/cache
primary_branch: csignori/xcremotecache-proto
out_of_band_mappings: {
 'TOOLS_BUCK-OUT':'/Users/csignori/Projects/iphone/tools/config/buck-out',
 'CORE_MODULES_BUCK-OUT':'/Users/csignori/Projects/iphone/{root_project}/Modules/CoreModules/buck-out',
 'BDUX_BUCK-OUT':'/Users/csignori/Projects/iphone/{root_project}/Modules/Platform/Bdux/buck-out', 
 'MISC_BUCK-OUT':'/Users/csignori/Projects/iphone/{root_project}/Modules/Miscellaneous/buck-out', 
 'PODS_BUCK-OUT':'/Users/csignori/Projects/iphone/Pods/buck-out',
 'SUBMODULES_BUCK-OUT':'/Users/csignori/Projects/iphone/Submodules/buck-out',
 'ROOT_BUCK-OUT':'/Users/csignori/Projects/iphone/buck-out',
 'PODS':'/Users/csignori/Projects/iphone/Pods',
}

The first build succeeds, and then the second fails with the following error:

Showing All Errors Only
Cycle in dependencies between targets 'DMLProto_Showdown' and 'Networking'; building could produce unreliable results. This usually can be resolved by moving the target's Headers build phase before Compile Sources.
Cycle path: DMLProto_Showdown → Networking → DMLProto_Showdown
Cycle details:
→ Target 'DMLProto_Showdown' has copy command from '/Users/csignori/Projects/iphone/buck-out/xcode/xcframework-slices/DMLProto_Showdown_Binary-baed0d227d0182fee0f99035cde61e6d6bfb312f/OTPublishersHeadlessSDK-Slice/OTPublishersHeadlessSDK.framework' to '/Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Products/Debug-iphonesimulator/DMLProto_Showdown.app/Frameworks/OTPublishersHeadlessSDK.framework'
○ Target 'DMLProto_Showdown' has target dependency on Target 'Networking'
→ Target 'Networking' has compile command for Swift source files
○ That command depends on command in Target 'Networking': script phase “[RC] RemoteCache_prebuild”
○ Target 'Networking' has copy command from '/Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/Networking-Swift.h' to '/Users/csignori/Projects/iphone/{project}/Modules/CoreModules/buck-out/xcode/derived-sources/Networking-720ecdc4254a9a596d1698dc8a91303c9363b022/Networking-Swift.h'
○ Target 'Networking' has compile command for Swift source files

Raw dependency cycle trace:

target:  ->

node:  ->

command:  ->

node: /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Products/Debug-iphonesimulator/DMLProto_Showdown.app/Frameworks/OTPublishersHeadlessSDK.framework ->

command: target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1-:Debug:PBXCp /Users/csignori/Projects/iphone/buck-out/xcode/xcframework-slices/DMLProto_Showdown_Binary-baed0d227d0182fee0f99035cde61e6d6bfb312f/OTPublishersHeadlessSDK-Slice/OTPublishersHeadlessSDK.framework /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Products/Debug-iphonesimulator/DMLProto_Showdown.app/Frameworks/OTPublishersHeadlessSDK.framework ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--phase4-copy-bundle-resources ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--phase2-compile-sources ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--phase1--rc--remotecache-prebuild ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--phase0-slicing-xcframeworks ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--ModuleMapTaskProducer ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--HeadermapTaskProducer ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--GeneratedFilesTaskProducer ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--ProductStructureTaskProducer ->

node:  ->

command: Gate target-DMLProto_Showdown-111e58bab44a349562ba8c0c4233d65b6d925b55934b21c9ad78429ecb6851c1--begin-compiling ->

node:  ->

command: Gate target-Networking-f5133eebb2ffb8190194ce0ce8a1ee02a28b59ec223466b45be229c4860cfbd9--modules-ready ->

node: /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/JSONExtractable.o ->

CYCLE POINT ->

command: target-Networking-f5133eebb2ffb8190194ce0ce8a1ee02a28b59ec223466b45be229c4860cfbd9-:Debug:CompileSwiftSources normal arm64 com.apple.xcode.tools.swift.compiler ->

node: /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/Networking.SwiftFileList ->

command: target-Networking-f5133eebb2ffb8190194ce0ce8a1ee02a28b59ec223466b45be229c4860cfbd9-:Debug:WriteAuxiliaryFile /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/Networking.SwiftFileList ->

node:  ->

command: Gate target-Networking-f5133eebb2ffb8190194ce0ce8a1ee02a28b59ec223466b45be229c4860cfbd9--phase0--rc--remotecache-prebuild ->

node: /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/rc.enabled ->

command: target-Networking-f5133eebb2ffb8190194ce0ce8a1ee02a28b59ec223466b45be229c4860cfbd9-:Debug:PhaseScriptExecution [RC] RemoteCache_prebuild /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Script-AA5B8D6D56EF38FEF8F7FDC5.sh ->

node: /Users/csignori/Projects/iphone/{project}/Modules/CoreModules/buck-out/xcode/derived-sources/Networking-720ecdc4254a9a596d1698dc8a91303c9363b022/Networking-Swift.h ->

command: target-Networking-f5133eebb2ffb8190194ce0ce8a1ee02a28b59ec223466b45be229c4860cfbd9-:Debug:PBXCp /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/Networking-Swift.h /Users/csignori/Projects/iphone/{project}/Modules/CoreModules/buck-out/xcode/derived-sources/Networking-720ecdc4254a9a596d1698dc8a91303c9363b022/Networking-Swift.h ->

node: /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/Networking-Swift.h/ ->

directoryTreeSignature: � ->

directoryContents: /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/Networking-Swift.h ->

node: /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/Networking-Swift.h ->

command: target-Networking-f5133eebb2ffb8190194ce0ce8a1ee02a28b59ec223466b45be229c4860cfbd9-:Debug:CompileSwiftSources normal arm64 com.apple.xcode.tools.swift.compiler

DMLProto_Showdown is my application workspace and Networking is one of the dependencies.

Without integrating RC, Xcode always/consistently succeeds in building the same application.

DMLProto_Showdown does not depend on Networking (though some of the DMLProto_Showdown dependencies do) and Networking does not depend on DMLProto_Showdown. The cyclic dependency issue seems to be introduced by RC and is related to the build steps of the targets involved.

polac24 commented 2 years ago

Hi! I think I know why it is happening that let me just verify:

polac24 commented 2 years ago

I saw that it is caused becaused your Network-Swift.h is placed in ...buck-out/xcode/derived-sources. XCRemoteCache doesn't know that it is a file coming from DerivedData.

It is an interesting step to move -Swift.h PBXCp from DerivedSources to back-out, probably you need it for some other integration. To workaround, you could move xcpostbuild before that Copy step on a producer side. Most likely that will eliminate -Swift.h from the meta as XCRemoteCache will realize that this file is special - comes as a derived product of the target itself.

cezarsignori commented 2 years ago

Hi @polac24 !

Yes, Networking is a hybrid target where some ObjC files import its own Swift API via #import "Network-Swift.h" and the problem only happens on the consumer side.

I moved the xcpostbuild before the Copy step on the producer side for Networking and the result in the consumer side was the same.

Screenshot 2022-03-09 at 15 22 16

The Copy step (called Fake Swift Dependencies) is actually copying 3 static libraries and has destination set to Products Directory.

Just in case, I also moved the Copy step from the DMLProtoShowdown target that copies the OTPublishersHeadlessSDK.framework to Frameworks (listed in the error message) after the xcpostbuildstep. Again, the same error message showed up on the 2nd build of the consumer.

polac24 commented 2 years ago

If Fake Swift Dependencies moves only 3 static libraries and does nothing with Networking-Swift.h, then you need to find the step that is responsible for that:

command: target-Networking-f5133eebb2ffb8190194ce0ce8a1ee02a28b59ec223466b45be229c4860cfbd9-:Debug:PBXCp /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/Debug-iphonesimulator/Networking.build/Objects-normal/arm64/Networking-Swift.h /Users/csignori/Projects/iphone/{project}/Modules/CoreModules/buck-out/xcode/derived-sources/Networking-720ecdc4254a9a596d1698dc8a91303c9363b022/Networking-Swift.h ->

It looks like a very custom setup.

How to troubleshoot? On a producer side, after a successful build of the Networking target, open /Users/csignori/Library/Developer/Xcode/DerivedData/DMLProto_Showdown-fnyfbyrumovpfnfvglkwmudrshgr/Build/Intermediates.noindex/Networking.build/xccache/produced/{hash}.json (or similar) and see if there is Networking-Swift.h listed. It shouldn't be. If still have it, find the culprit of the cp step mentioned above and try deferring it a bit (reorder).

cezarsignori commented 2 years ago

Hi!

Yes, I find Networking-Swift.h listed in the json file as "$(CORE_MODULES_BUCK-OUT)\/xcode\/derived-sources\/Networking-720ecdc4254a9a596d1698dc8a91303c9363b022\/Networking-Swift.h".

However I cannot find any xcodeproj files with explicit PBXcp for that file. Nor manually looking at Build Phases nor for example via find . -type d -name '*.xcodeproj' -exec grep -r "Networking-Swift.h" {} +. It seems like Xcode is doing it automatically and dynamically.

Isn't it showing up in the json file because that's the header which makes the Swift API in the module available to Objective-C code?

disable_vfs_overlay: true does not eliminate the problem.

polac24 commented 2 years ago

Hello! There are many places in an Xcode project where the destination location can be customized. Without a project to review, it would be very difficult to guess. Nevertheless, let me try: does your Networking target have a custom build setting that overrides Xcode's default location? Here is a non exhaustive list of settings that could potentially cause that:

ahmednafei commented 2 years ago

Hi! From my investigation I found out that this integration issue between Buck <> XC-RC is because of the following:

When Buck generates the xcodeproj file, it does not populate the “Dependencies” build step with the target dependencies and relies on HEADER_SEARCH_PATHS instead (implicit dependencies).
For mixed modules, Buck sets DERIVED_FILES_DIR build setting to point to a location inside buck-out directory, so during the compilation step, the -Swift.h file is generated under this directory. During ./buckw generate <project> phase Buck creates symlinks to the source header files (and to -Swift.h file in case of mixed modules) under the buck-out/gen/_p/<module-dir>-pub folder. These symlinks point to the location of header files inside module source code (symlink of -Swift.h in case of mixed modules point to the location where the file would be generated during compilation step). So DERIVED_FILES_DIR build setting defines where the compiler generates and finds the -Swift.h for internal module references.

The dump issue that buck has is that it hard codes the symlink of module>-Swift.h during the project generation phase to the default location of where buck sets DERIVED_FILES_DIR value. So any try to change the value of DERIVED_FILES_DIR while make the generated link is invalid, and will make any module that depends on that mixed module fails to import that mixed module (because simply any module that depends on that mixed module would have buck-out/gen/_p/<mixed-module-dir>-pub added to HEADER_SEARCH_PATHS and it will only see an invalid symlink).

The Buck <> RC integration issue is mainly happening because buck sets DERIVED_FILES_DIR to a location inside 'buck-out' which is outside the DerivedFiles folder where RC expects any -Swift.h file should be generated in. Because of that RC is not aware that -Swift.h inside buck-out is an auto-generated file during compilation so it lists it in the meta file where it should not do.

The issue is solved on my side by overriding DERIVED_FILES_DIR value to a new location inside DerivedData folder, during project generation phase and at the same time adding build-step for each mixed module that has internal import to -Swift.h to update the symlink of <module>-Swift.h generated by buck, so any module depending on mixed module would be able to find the new location of <module>-Swift.h. Also RC now is aware that <module>-Swift.h is an auto-generated file so it does not include it into meta file.

polac24 commented 2 years ago

Thanks for a context, your workaround makes sense.

XCRemoteCache can be smarter and also inspect DERIVED_FILES_DIR when looking for intermediate files (right now it only classifies files in TARGET_TEMP_DIR): https://github.com/spotify/XCRemoteCache/blob/963e6858ee31d750b2a3cae65c37cf31f279d5d0/Sources/XCRemoteCache/Dependencies/DependencyProcessor.swift#L78-L79

The fix should be simple:

Let me know if you would like to contribute.

_In the future, we could make the classification configurable (which ENV directory points to the directory with given dependency kind) but for now, adding only DERIVED_FILES_DIR makes sense._