Xcode folder reference to an aggregate target's output folder breaks between Debug/Release builds. BUILT_PRODUCTS_DIR not relative to the aggregate

In my project I have an aggregate target that runs a script that generates a "web bundle" in a folder build/ with all the html/css/resources needed for the website. These resources are generated via a script. I use these resources in multiple targets so I have created an aggregate target to make this simple.

However, I have not found this simple. If I have the aggregate target output to $(DERIVED_FILE_DIR)/ then it outputs to a path that looks like this

DerivedData/BrowserApp-bla/Build/Intermediates.noindex/BrowserApp.build/Debug-iphonesimulator/WebBundle.build/DerivedSources/build/index.html

If I then drag that folder into Xcode and create a folder that references it. Then take that reference and make it relative to build products I get this

        A9DE6A502E41E397005EF4E0 /* build */ = {
			isa = PBXFileSystemSynchronizedRootGroup;
			name = build;
			path = "../../Intermediates.noindex/BrowserApp.build/Debug-iphonesimulator/WebBundle.build/DerivedSources/build";
			sourceTree = BUILT_PRODUCTS_DIR;
		};

This is because to the main BrowserApp target the BUILT_PRODUCTS_DIR is DerivedData/BrowserApp-bla/Build/Products/Debug-iphonesimulator.

You can see that the reference essentially has to maneuver out of the built products folder for the main BrowserApp target and into the built products directory for WebBundle.

This is ok for a debug build I suppose you can imagine that this is going to break down the moment you have a release build.

Now if we ignore that problem then comes the problem of getting it in Copy Bundle Resources. Xcode 16s PBXFileSystemSynchronizedRootGroup is a great improvement but does not help here. The only way I have found to get the folder copied is to hop into an earlier Xcode or make the following modifications.

Change the PBXFileSystemSynchronizedRootGroup into a PBXBuildFile

(In PBXFileReference section)

A9DE6A502E41E397005EF4E0 /* ../../Intermediates.noindex/client.build/Debug/WebBundle.build/DerivedSources/build */ = {isa = PBXFileReference; lastKnownFileType = text; path = ../../Intermediates.noindex/BrowserApp.build/Debug-iphonesimulator/WebBundle.build/DerivedSources/build; sourceTree = BUILT_PRODUCTS_DIR; };

(In the mainGroup PBXGroupSection)

A9DE6A502E41E397005EF4E0 /* ../../Intermediates.noindex/client.build/Debug/WebBundle.build/DerivedSources/build */,

Then remove any references for A9DE6A502E41E397005EF4E0 as a synchronized group

Create a PBXBuildFile using the previous PBXBuildFile as its fileRef then add it to the copy resources stage

(In PBXBuildFile section)

96516AC32BF928DD00576562 /* ../../Intermediates.noindex/BrowserApp.build/Debug-iphonesimulator/WebBundle.build/DerivedSources/build in Resources */ = {isa = PBXBuildFile; fileRef = A9DE6A502E41E397005EF4E0 /* ../../Intermediates.noindex/BrowserApp.build/Debug-iphonesimulator/WebBundle.build/DerivedSources/build */; };

(In the PBXBuildResources phase for the resources section of the main target)

96516AC32BF928DD00576562 /* ../../Intermediates.noindex/BrowserApp.build/Debug-iphonesimulator/WebBundle.build/

I do not know of a way to do this with a PBXFileSystemSynchronizedRootGroup so these manual changes seem necessary. After these changes I have the following:

And you can confirm the entire folder is accessible to the app. However if you try to build for release/Archive you get

lstat(/Users/calebkierum/Library/Developer/Xcode/DerivedData/BrowserApp-bla/Build/Intermediates.noindex/ArchiveIntermediates/BrowserApp/Intermediates.noindex/BrowserApp.build/Debug-iphonesimulator/WebBundle.build/DerivedSources/build): No such file or directory (2)

Which makes sense. The file reference has a relative path that explicitly mentions "debug" however because this path is based on an aggregate target I am not sure how you really update this reference for release.

Answered by Caleb K in 852288022

Also @DTS Engineer a bit of an AHA moment. And thank you that was a great idea haha.

Using BUILT_PRODUCTS_DIR as the output destination for the script rather than DERIVED_FILE_DIR is the solution to this problem.

The core issue of this is how to link the folder that is output from the aggregate build target. This is difficult because:

  1. Since Xcode 16 Xcode wants to work with PBXFileSystemSynchronizedRootGroup's not folders. This makes it (I think) impossible through the UI to link the folder that is output. I had to manually modify my project file.
  2. If you use DERIVED_FILE_DIR it makes a separate directory for each target. Making it difficult for your folder reference to stay the same between debug and release builds.

Anywho that solves this portion of the problem.

Other little bit. Xcode complained about multiple things writing to the same location with $(BUILT_PRODUCTS_DIR)/ in the output file list (that works just fine for DERIVED_FILE_DIR so I can only assume the entire BUILT_PRODUCTS_DIR is reserved. I just had to change it to $(BUILT_PRODUCTS_DIR)/build/

Let's join this with your other thread. I made a suggestion over there for BUILT_PRODUCTS_DIR that it appears you tried here (I see the other thread first), but I have other suggestions for you in that other thread.

— Ed Ford,  DTS Engineer

Yeah sounds good. It's possible these have different answers. The other question is somewhat poorly scoped and this was one isolated portion of it I thought could help crack it thats less specific to my use case. This makes sense let's go that route. Thanks for your help.

Accepted Answer

Also @DTS Engineer a bit of an AHA moment. And thank you that was a great idea haha.

Using BUILT_PRODUCTS_DIR as the output destination for the script rather than DERIVED_FILE_DIR is the solution to this problem.

The core issue of this is how to link the folder that is output from the aggregate build target. This is difficult because:

  1. Since Xcode 16 Xcode wants to work with PBXFileSystemSynchronizedRootGroup's not folders. This makes it (I think) impossible through the UI to link the folder that is output. I had to manually modify my project file.
  2. If you use DERIVED_FILE_DIR it makes a separate directory for each target. Making it difficult for your folder reference to stay the same between debug and release builds.

Anywho that solves this portion of the problem.

Other little bit. Xcode complained about multiple things writing to the same location with $(BUILT_PRODUCTS_DIR)/ in the output file list (that works just fine for DERIVED_FILE_DIR so I can only assume the entire BUILT_PRODUCTS_DIR is reserved. I just had to change it to $(BUILT_PRODUCTS_DIR)/build/

Xcode folder reference to an aggregate target's output folder breaks between Debug/Release builds. BUILT_PRODUCTS_DIR not relative to the aggregate
 
 
Q