JUCE icon indicating copy to clipboard operation
JUCE copied to clipboard

[Bug]: Bad quote handling for custom XCode flags in Projucer

Open Username256 opened this issue 4 months ago • 7 comments

Detailed steps on how to reproduce the bug

If XCode flags for the XCODE_MAC config are like this in Projucer...

"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; "DEVELOPMENT_TEAM[sdk=macosx*]" = Z5CQDP53W7

Then it outputs it like so in the .pbxproj file.... "CODE_SIGN_IDENTITY[sdk = "macosx*]" = "Developer ID Application"; "DEVELOPMENT_TEAM[sdk=macosx*]" = Z5CQDP53W7";

For some reason the part in brackets in the first line gets spaces and a quote around the "=" character. Why? This is an error and does not allow for assigning the correct values. On the XCode side it will not even load the project.

What is the expected behaviour?

It should put the text as is into the XCode project.

Operating systems

macOS

What versions of the operating systems?

Sonoma 14.5

Architectures

x86_64

Stacktrace

There is no stacktrace.

Plug-in formats (if applicable)

No response

Plug-in host applications (DAWs) (if applicable)

It happens for all plugin formats.

Testing on the develop branch

I have not tested against the develop branch

Code of Conduct

  • [x] I agree to follow the Code of Conduct

Username256 avatar Aug 21 '25 14:08 Username256

Thanks for the report. I assume you are trying to set CODE_SIGN_IDENTITY and DEVELOPMENT_TEAM from the "Custom Xcode Flags" field in the Projucer? If so have you tried using the "Code-Signing Identity" and "Development Team ID" settings provided by the Projucer?

Note that while the "Code-Signing Identity" setting can be found as part of each configuration, the "Development Team ID" setting is in the exporter settings page.

For these specific settings I would strongly encourage that you do use the relevant fields provided by the Projucer as this allows us to make other intelligent decisions based on if those fields are provided or not.

If that's not the only use case in which you need this behaviour could you please share some other examples.

Thanks.

Anthony-Nicholls avatar Aug 21 '25 15:08 Anthony-Nicholls

Thanks.I cannot use those fields provided by projucer.a) It sets automatic signing on.b) It changes the signing identity from "Development ID Application" to something else like "Mac Developer". XCode does this I believe because it sees Automatic signing.c) The team id does not show up in the field in XCode but that is probably because of Automatic signing being set by projucer. Also this should all be for AU, AAX,VST,VST3 but not for AUv3 which seems to require something else. So even if it was not for this bug I would still have to tweak AUv3.

Username256 avatar Aug 21 '25 16:08 Username256

Please note that while adding support for square brackets when addressing Xcode build settings might be a reasonable feature request, I'm reluctant to add it for something where we already have fields already in place. I think it would be much better for all JUCE users if we address the issue so that these fields are useful. If users become reliant on setting Xcode flags rather than using the provided fields it means we can't rely on the fields themselves inside the Projucer, this in turn is likely to lead to more bugs and issues, and generally a bad experience for users. Therefore my questions below are just to help me understand your situation in more detail so that I can offer a better solution for you and other users who will inevitably end up in the same situation.

It sets automatic signing on

I see, when you set the Team ID that does seem to enable automatic signing. I'll see if I can figure out why that is. I think it's likely something to do with the fact that Team IDs tends to only be set for iOS applications, I think it's required it that case (untested). What problem does it cause for you when automatic signing is enabled? Just to help me understand (not to question your decisions), what is the benefit of using a Team ID when signing your plugins?

It changes the signing identity from "Development ID Application" to something else like "Mac Developer"

This should only happen when Team ID is not empty and the code signing identity is empty. Are you saying this happens for you when the code signing identity is not empty?

Also this should all be for AU, AAX,VST,VST3 but not for AUv3 which seems to require something else.

Could you please help me understand what else an AUv3 needs? I'm not aware of anything from other users. That being said I think very few customers are choosing to release AUv3 plugins on macOS at the moment. I think this is likely due to the fact that it requires the user to install and launch an application in addition to the plugin in order for the host to see it. As well as there being little advantage to using an AUv3 on macOS at the moment.

Hopefully this is helpful but I thought it might be worth highlighting the most common setups we tend to see.

  • For macOS development builds that normally only need to run locally for development purposes, both the Team ID and the Code Sign Identity are left blank. As long as the copy plugin step is enabled the plugins will be signed to run locally so they can be debugged.

  • For macOS development builds that might need to run on other machines the code signing identity can be set to something like "Mac Developer" or "Apple Development" (it used to always be "Mac developer" but recently apple started switching to "Apple Development"). Each developer will have their own personal development certificate installed on their machine, they should all use the same certificate type whose name starts with the same name entered in the Projucer. As long as each developer has only one matching certificate, Xcode will find it and sign using that certificate (if they have more than one Xcode will throw an error).

  • For macOS deployment builds an additional argument will be passed to xcodebuild in order to override the code signing identity, in this case it would be set to "Development ID Application". That way distribution builds are made from CI only.

  • For iOS development builds, a Team ID and default code signing identity are provided. From memory the Team ID is needed (and possibly automatic signing?), I think for the provisioning profiles but it's been a while since I looked at this.

  • For iOS deployment builds, if I remember correctly, just like for macOS an additional argument will be passed to xcodebuild in order to override the code signing identity.

Note that in all the above examples the Projucer is setup for development, not distribution. The advantages of this are...

  • The developers have easier setups, preferably no certificates at all
  • If developers do have a certificate, if their machine is ever compromised you can safely revoke the certificate without fear of impacting customers
  • Your valuable distribution certificate is located on the fewest devices possible, improving security

Unfortunately such development certificates don't exist for macOS package installers so you really will have to have that on developers machines if they need to be able to create the pkg installers.

Sorry for the long response. Hopefully there is some useful information in there for you.

Anthony-Nicholls avatar Aug 22 '25 11:08 Anthony-Nicholls

When I set the signing identity to "Development Team Id" and turn automatic signing off I get this error in XCode. That is why I then select a team id as well.

"Signing for "MyPlugin- VST3" requires a development team. Select a development team in the Signing & Capabilities editor."

If I do not do the signing this way then when packaging the notarize and staple step fails. I tried now to set Automatic signing on and the identities change to "Development" and "Sign to run locally" and when I select "Development" it also requires a team.

All I need is some way to set just the identity and the team where projucer does no thinking in the scheme. What makes sense to me is some kind of direct copy of "customXCodeFlags" with absolutely no formatting provided by projucer. Just something that takes the text and puts it into the .pbxproj as is.

Or change the way it works so that signing identity and team id are set in a way that matches what I need it to be which is... "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; "DEVELOPMENT_TEAM[sdk=macosx*]" = Z5CQDP53W7

And that without turning on Automatic signing. I need it to be that exact code in the .pbxproj file and nothing else. I understand there is the other way to do it but I need it to do that. Its not such a big problem I can hack projucer but to me being able to control not only signing by any xcode attributes in the file without projucer in the way would be a bonus for projucer in general.

As for AUv3 I seem to have assumed that because it has a provisioning profile field in the signing section that this makes it different but it does not seem to need it and it builds ok with "Developer ID Application" and the correct team.

The crucial thing is that it must absolutely be that attribute name in quotes with the brackets otherwise it does not seem to function. If I open the pbxproj and edit out the quotes and the brackets it opens with Automatic signing turned on and the wrong identity showing and no team selected.

The way that it is specified as above with the brackets and quotes is how XCode saves it when I select Developer Id Application and the team.

Username256 avatar Aug 22 '25 11:08 Username256

I hacked juce using this code and it now works as expected for these settings. Now I can use EachPy JuceGen to generate the .jucer projects using Python code, open them in projucer and then export to Xcode and all the signing is as expected.

s = "\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Developer ID Application\",\n"
s += "\"DEVELOPMENT_TEAM[sdk=macosx*]\" = Z5CDDP54W7,\n"
s += "CODE_SIGN_STYLE=Manual\n"
config.attrs["customXcodeFlags"] = s

The code I used to hack projucer was just this on the end of the function that formats the customXcodeFlags value. s.set ("GCC_PREPROCESSOR_DEFINITIONS", indentParenthesisedList (defsList, 1));

        StringArray customFlags;
        customFlags.addTokens (config.getCustomXcodeFlagsString(), ",", "\"'");
        customFlags.removeEmptyStrings();

        for (auto flag : customFlags)
        {
        
          flag = flag.trim();

          if (flag.isEmpty())
              continue;

          const int eq = findTopLevelEquals(flag);
          if (eq < 0)
          {
              // No assignment found; ignore or log a warning
              DBG("Invalid customXcodeFlag (no '='): " << flag);
              continue;
          }

          auto rawKey   = flag.substring(0, eq).trim();
          auto rawValue = flag.substring(eq + 1).trim();

          // Remove optional surrounding quotes, then trim again
          auto key   = rawKey.unquoted().trim();
          auto value = rawValue.unquoted().trim();
          // Ensure the attribute name is quoted if it contains '=' (e.g. [sdk=macosx*])
          if (key.containsChar('=') && !(key.startsWithChar('"') && key.endsWithChar('"')))
              key = key.quoted();

          // Match previous behavior: always quote the value when storing
          s.set(key, value.quoted());
          //s.set (flag.upToFirstOccurrenceOf ("=", false, false).trim(),
            //       flag.fromFirstOccurrenceOf ("=", false, false).trim().quoted());
        }
      
        return s;
    }
    static int findTopLevelEquals(const juce::String& s) {
      bool inSingle = false, inDouble = false, escape = false; int bracketDepth = 0;

      for (int i = 0; i < s.length(); ++i)
      {
          juce_wchar c = s[i];

          if (escape) { escape = false; continue; }

          if (inSingle) { if (c == '\\') { escape = true; } else if (c == '\'') inSingle = false; continue; }
          if (inDouble) { if (c == '\\') { escape = true; } else if (c == '"')  inDouble = false; continue; }

          if (c == '\'') { inSingle = true; continue; }
          if (c == '"')  { inDouble = true; continue; }

          if (c == '[')  { ++bracketDepth; continue; }
          if (c == ']')  { if (bracketDepth > 0) --bracketDepth; continue; }

          if (c == '=' && bracketDepth == 0)
              return i;
      }
      return -1;
    }

Username256 avatar Aug 25 '25 11:08 Username256

I have found a workaround by setting the config customXcodeFlags DEVELOPMENT_TEAM= Z5CQDP53W7

But this is still a bug because with the other settings mentioned above I can control it more but with this I have to change the id on 2 different systems.

This bug is now less about code signing than it is about handling quoted attribute names correctly. Since Xcode has quoted attribute names it means that it is a bug if projucer cant handle it.

Username256 avatar Aug 27 '25 09:08 Username256

I got a workaround that does exactly the same thing as what I wanted to do.

config["extraXcodeFlags"] = "CODE_SIGN_STYLE=Manual" config["codeSigningIdentity"] = "Developer ID Application" exporter["iosDevelopmentTeamID"] = "GHTAYZN!ETC"

This sets exactly the settings I want and the automatic code signing checkbox is off.

Username256 avatar Aug 28 '25 13:08 Username256