Post-build script

With JUCE 4.1/4.2, when I build “PluginName (All)” in XCode, the “Post-build shell script” (that I’ve set in the Projucer) is now run after every single plugin format that is built.

I need an option to run a script only after all plugin formats are built (for then building and signing an installer). It worked before with pre-JUCE-4.1, so I wonder if there is still a way to do it?

Thank you for your answer. This other thread talks about the pre-build script and provides a workaround for it, but this does not fit for the post-build script I guess.

It is the exact same thing. if you check your target variable you’ll be able to decide whether the script should or shouldn’t run… (I’ve even tested it for you and it’s work exactly the same…)

For your specific question:

  • You can create a bash script and run projucer from command line (to rebuild the project)
  • Then run xcodebuild to build.
  • Then run your installer / signing script.

This is more automated and robust.

Also, you can check which is the last target built and run it only then or validate that all the expected binaries are already exists and only then run the script.

The targets (for example VST and AU) are built in parallel, so sometimes VST might be the first that finishes, sometimes AU.

Checking if all binaries are there is a great idea. The post script might still see the binaries from another build, but looking at their timestamps and checking that they’ve all built within the same minute might do the trick.

@Jakob would it not make more sense as ttg has suggested to have a script that resaves your project, builds using xcodebuild, then runs your installer and signing script? otherwise aren’t you creating a signed installer everytime you build?

Also if you make your script executable, include a hash bang, and remove the extension you should be able to double click it to generate your new installer rather than having to open up the IDE each time.

I’d prefer building from within XCode, as this displays any warnings more nicely.

That’s no problem - my script checks if I am archiving and immediately returns otherwise.

Fair enough, if clear warnings is the main thing then xcpretty is a useful tool should you feel the need to build from the command line in the future.

It seems however to properly achieve what you are trying to do it would require a post-build step on the ‘All’ target. It might even be true to say that having the pre and post build steps added here would be more like it was with the polymorphic builds. However it would mean if you ever built one of the other targets independently, the script wouldn’t run.

I was going to try and add the pre/post build phases to the ‘All’ target and see if we can differentiate between them using TARGETNAME, but every time I tried to add them Xcode crashed! However there may be a valid argument to add the pre/post scripts to this target too?

@JUCE team: Do you have any plans to add a hook to the Projucer that allows us to execute a script that only runs after everything is build and after your copy-script is executed?

I’d appreciate to get the option back, especially as it worked before.

On a side note: I am very happy with the improved build times of JUCE 4.2.

@JUCE team: So, do you have any plans to restore the previous behavior, to let us define a script in Projucer, which runs only once, and at the end when all targets are built in XCode (ideally after your copy-script is executed)?

This is now fixed on the latest develop branch. The post-build script is now also executed for the aggregate target.

Xcode conveniently passes the target name that is currently processing to the script via the TARGET_NAME environment variable. You can then have Xcode only do specific things for specific targets - for example, only when the aggregate target finishes.

TARGET_NAME unfortunately includes the project name as well, so the same build script won’t work between different projects. The project name can be excluded with a bit of sed magic:

TARGET=`echo $TARGET_NAME | sed 's/^.*(\(.*\)).*$/\1/g'`

The following screenshot shows a plug-in project where the script is only run at the very end (i.e. when the aggregate target has finished). The script will work regardless of the project name.

And here again so you can copy&paste:

TARGET=`echo $TARGET_NAME | sed 's/^.*(\(.*\)).*$/\1/g'`

if  [ $TARGET = App -o $TARGET = All ]; then
  echo "Do your stuff here!"
fi
1 Like

Perfect, thank you so much! I will test this within the next days.

Worked perfectly!

For anyone who prefers Python for scripts, see also this thread:

I need to do this for Windows (non-polymorphic) builds now, especially for AAX signing. Has anyone got a ready made solution for Windows for running different scripts for the different targets?

Here is my dirty but working current workflow* (we’ve yet to fully start building with this).

If you need more sophisticated things, read some BATCH hell.
remember CRLF counts for batch! (made me scratch my head for a while).
You can switch the logic to your needs. currently it works IF NOT Shared Code.
but it’s pretty much same logic for setting it to AAX/VST/VST3/RTAS targets.

And about signing. it doesn’t hurt to sign non-AAX. the only price if using PACE/AAX tool is build time…

my_post_build.bat "$(TargetDir)$(TargetFilename)"

 echo off
if [%1]==[/wrap] goto wrap
if [%1]==[/sign] goto sign_cmd
if not %1=="" goto sign_default
rem # PACE SIGN FOR AAX VALIDATION
:sign_default
set filename=%1
goto sign

:sign_cmd
set filename=%2
goto sign

:sign
echo.%filename%|findstr /C:"Shared Code" >nul 2>&1
if errorlevel 1 (
    echo Signing %filename% with PACE
    <add your PACE signing cmd here>
)
goto fin

rem # PACE ILOK PROTECTION
:wrap
set filename=%2
echo.%filename%|findstr /C:"Shared Code" >nul 2>&1
if errorlevel 1 (
    echo Wrapping %filename% with PACE
    <add your PACE wrapping cmd here>
)
goto fin

:fin
exit 0

Great, that’s really useful. Thanks.

Hey Folks. For those of you on Mac who are struggling with this, here is how to figure out all of your environment variables the easy way.

go into XCode for your project
Click on the project in the project navigator so you get the general settings for the project
click on Build Phases
Add a Post-Build Script
Expand “Post build script”
make sure “Shore environment variables in build log” is checked.
Build your project.
Now navigate to the Report Navigator
that’s this thing on the right:
Go to your most recent build. Make sure All Messages is selected in the report window, not All Issues and not Errors Only
Click on "run custom shell script ‘Post-build script’
You’ll see a hamburger button appear on the right that you can click on to expand the report.


Click the button, and all of your environment variables with their assigned values will be there for ya!

now just stick a $ in front of whatever variable you need to use in your script and that’s it.

In my case, I needed to copy my app to a shared folder so my testers could get the latest build every time I compile:
cp -a $BUILT_PRODUCTS_DIR /newLocation/

2 Likes

either I messed up something in my project, or this is not working with the master, I had to modify as follows:

TARGET=echo $TARGET_NAME | sed 's/^.*- \(.*\).*$/\1/g'

if [ “$TARGET” = “Shared Code” ]; then
BIN_PATH=$PROJECT_DIR/…/…/…/…/…/bin/Mac/${PRODUCT_NAME}
mkdir -p “${BIN_PATH}”
fi

That first line (TARGET =…) gives me an error. It says “MyPlugin: command not found”. If I change TARGET_NAME to "{TARGET_NAME}", it says “MyPlugin - VST3: command not found”. The sed portion seems to work fine when I simply enter it in a Terminal window (with “MyPlugin - VST3” as the string); it reports VST3 as the output. But when run this script code in my Post-Build Shell Script, it gives me an error. It’s as if it’s ignoring the assignment to TARGET, and trying to execute the string rather than pass it to sed. What am I doing wrong here? (Sorry, not a script expert at all.)

Oh, I see it. Your example was missing the ` around everything after TARGET=. Working now.