I have problems with embedding a custom framework in a JUCE-Project on MacOS. I want to to build AU-, VST3-Plugin and also a standalone application. I am using MacOS 14.5, Xcode 15.4, JUCE 8.01, using Projucer to configure the project.
I have bundled the custom.framework in a custom.xcframework (containing MacOS, iOS, iOS-Simulator variants) which already worked in a different application. I put the path to the custom.xcframework in the field for “Embedded Frameworks” in the Projucer.
When I try to build the generated Xcode project it does not work out of the box for any target. The error message shows:
“Multiple commands produce ‘/Users/…custom.framework’” (Coming from the Command: ProcessXCFramework)
This can be solved by selecting “copy only when installing” in the Build Phase “Embed Frameworks” of the Shared Code target. Alternatively the Embed Framework step can also be removed and it works. (Since the step embed framework is in all targets.)
Now I can build the AU and Standalone target but VST3 still fails.
Same option mentioned before can be checked in the VST3 Manifest Helper target now also VST3 builds successfully.
I tried pointing the Embedded Frameworks directly to the custom.xcframework/macos-arm64_x86_64/custom.framework. This let me build AU and standalone but VST3 still fails because of the VST3-Manifest Helper (with basically the same error, but coming from a copy command and can be solved the same way by selecting copy only on install).
Am I missing something in the projucer? Is there a way to configure the option “copy only when installing” in Projucer? since always having to change this setting is not really feasible.
Are there alternative ways to configure the project without the necessity of having e.g. an additional Post-Build Shell Script?
You say that the AU and Standalone builds succeed, but do they run? Can you load the resulting AU in a DAW, or launch the Standalone? If the AU and Standalone don’t work, this might indicate that the framework dependency isn’t being resolved correctly. The VST3 build failure would be caused by the juce_vst3_helper failing to load the plugin bundle.
Framework dependencies are discussed here:
You could try adding the rpath flag to the extra linker flags, as mentioned in the final post. This might allow the system to find the framework dependency.
I’m having this exact same problem when trying to embed frameworks. I’m on JUCE 8.0.3.
I tried to figure out a way around it separate to this post and came to the exact same solution - by removing the embedded frameworks stage completely for VST3 Manifest Helper.
This is fine for doing one-off releases but a nightmare for CI/CD as it means I can’t use Projucer in the command line as I need to manually edit the XCode project to get it working.
I would similarly need a way of toggling “copy only on installing” or removing the embedded frameworks stage for that target.
If anyone has a workaround for this such as using a post-build script I would be very appreciative!
This gives an option for enabling the “only copy when installing” step.
I would imagine it would be pretty trivial to add this to the XCode project generator in JUCE but I’m not sure whether VST3 Manifest Helper should always have this enabled or not?
It’s not ideal but there is a step in the Projucer that allows a script to run post-export. From there you should be able to add a script that removes the embedded framework for the manifest helper.
I’ve recently been working on some changes surrounding the manifest helper so when I’m back in the new year I may take a look at this but in the meantime the post-export script should allow you to keep moving.
Thanks! Do you have any examples of modifying a target via the post-export script? I’m sure I can figure it out but if you have pointers that would be super helpful
#!/bin/bash
if [[ "$(uname)" != "Darwin" ]]; then
echo "Not macos"
exit 0
fi
# Path to the project.pbxproj file
PBXPROJ_FILE="$1"
PROJECT_NAME="$2"
if [[ ! -f "$PBXPROJ_FILE" ]]; then
echo "Error: File '$PBXPROJ_FILE' not found!"
exit 1
fi
# Temporary file for storing embed framework blocks and the final output
TEMP_EMBED_FILE="$(mktemp)"
TEMP_FILE="$(mktemp)"
# Step 1: Find all 'Embed Frameworks' blocks and their IDs
echo "Debug: Extracting Embed Frameworks blocks and IDs..."
awk '
BEGIN { in_embed_block = 0; current_id = ""; embed_block = ""; }
{
# Look for the start of an Embed Frameworks block
if ($0 ~ /^[[:space:]]*[A-Z0-9]+[[:space:]]*\/\* Embed Frameworks \*\/[[:space:]]*=/) {
in_embed_block = 1;
split($0, parts, " ");
current_id = parts[1];
embed_block = $0 "\n"; # Start of the block
}
# Add lines to the block if we are inside it
if (in_embed_block) {
embed_block = embed_block $0 "\n";
}
# Look for the end of the Embed Frameworks block
if (in_embed_block && $0 ~ /^[[:space:]]*};/) {
in_embed_block = 0;
# Store the embed block and ID
print current_id "\t" embed_block;
embed_block = ""; # Reset the block for the next one
current_id = "";
}
}
' "$PBXPROJ_FILE" > "$TEMP_EMBED_FILE"
# Step 2: Find the ID of the 'VST3 Manifest Helper' target
TARGET_ID=$(grep -E "^\s*[A-Z0-9]+ \/\* $PROJECT_NAME - VST3 Manifest Helper \*\/ = {" "$PBXPROJ_FILE" | grep -oE '^\s*[A-Z0-9]+' | tr -d '[:space:]')
if [[ -z "$TARGET_ID" ]]; then
echo "Error: Could not find the 'VST3 Manifest Helper' target!"
exit 1
fi
echo "Debug: Found target ID: $TARGET_ID"
# Step 3: Extract the buildPhases for the 'VST3 Manifest Helper' target
echo "Debug: Extracting buildPhases for the 'VST3 Manifest Helper' target..."
BUILD_PHASES=$(awk -v target_id="$TARGET_ID" -v project_name="$PROJECT_NAME" '
BEGIN { found_target = 0; in_build_phases = 0; }
{
# Look for the target block
if ($0 ~ target_id " /\\* " project_name " - VST3 Manifest Helper \\*/ = \\{") { found_target = 1; }
# Capture the buildPhases section
if (found_target && $0 ~ /buildPhases = \(/) {
in_build_phases = 1;
}
if (in_build_phases) {
print $0;
}
# Stop capturing buildPhases after the closing parenthesis
if (in_build_phases && $0 ~ /^[[:space:]]*\);/) {
in_build_phases = 0;
}
# End target block
if (found_target && $0 ~ /^[[:space:]]*\};/) {
found_target = 0;
}
}
' "$PBXPROJ_FILE" | grep -oE '^\s*[A-Z0-9]+' | tr -d "[:blank:]")
# Debug: Show the captured buildPhases
echo "Debug: Build Phases for 'VST3 Manifest Helper':"
echo "$BUILD_PHASES"
# Step 4: Extract the Embed Framework ID that matches the build phase ID
echo "Debug: Matching Embed Frameworks with Build Phases..."
EMBED_ID=""
while read -r build_phase_id; do
# For each build phase ID, check if it exists in TEMP_EMBED_FILE
# Extract the matching Embed Framework ID from TEMP_EMBED_FILE
EMBED_ID=$(grep "$build_phase_id" "$TEMP_EMBED_FILE")
# If a matching Embed ID is found, exit the loop
if [[ -n "$EMBED_ID" ]]; then
EMBED_ID=$build_phase_id
echo "Debug: Found matching Embed ID: $EMBED_ID"
break
fi
done <<< "$BUILD_PHASES"
if [[ -z "$EMBED_ID" ]]; then
echo "Error: Could not find a matching Embed Frameworks ID in the build phases!"
exit 1
fi
# Step 5: Remove the 'Embed Frameworks' block and references to it
echo "Debug: Removing the Embed Frameworks block with ID $EMBED_ID..."
awk -v embed_id="$EMBED_ID" '
{
# Skip the Embed Frameworks block
if ($0 ~ embed_id " \\*/ = {") {in_embed_block=1}
if (in_embed_block && $0 ~ "^[[:space:]]*};") {in_embed_block=0; next}
if (in_embed_block) next
# Remove references to the Embed Frameworks block in the build phases
if ($0 ~ "buildPhases = \\(") {in_build_phases=1}
if (in_build_phases && $0 ~ embed_id ",") {next}
if (in_build_phases && $0 ~ "^[[:space:]]*\\);") {in_build_phases=0}
# Print the remaining lines
print
}' "$PBXPROJ_FILE" > "$TEMP_FILE"
# Replace the original file with the modified file
mv "$TEMP_FILE" "$PBXPROJ_FILE"
echo "Successfully removed the Embed Frameworks block for 'VST3 Manifest Helper'."
There has been some remarkable progress on embedding frameworks (just adding the framework to the embedded frameworks field, and setting the rpath now gets is building in all cases expect VST3 and “all”).
Thats awesome, but in the case of VST3s I am still getting the error here simply because the VST3 manifest helper is trying to copy the framework.
Surely, since the VST3 helper doesn’t actually need the dylib, the juce team could just make it so that anything in the “embedded frameworks” field is just not actually added / linked in the case of the manifest helper?
I think the same applies to “all”, where I can just remove the dependency since it’s in all the products individually?