Moving to CMake & creating installers

Good morning. I’ve been on a journey moving my plugins from Projucer to CMake and it’s been interesting to say the least. Lots of bumps here and there, but I’ve received a lot of help from the AudioProgrammer Discord channel, especially from @benvining and @eyalamir.

First, I have to say that I’ve found the available documentation, examples and tutorials for CMake pretty obtuse, unclear and quite sparse. @benvining has a few YouTube videos that were helpful, but in many ways, it’s like learning a completely new programming language (which it is) from scratch. And with all that power, there are many ways to do anything and everyone seems to have their own way of doing things. Shocking, I know.

That being said, I’ve finally gotten to the point where my CMake workflow is working well using CLion on both Mac and Windows, and I’m able to produce proper release plugins and a standalone app on Mac (Windows is next). Now I’m moving on to creating installers and that’s really where the online help falls off a cliff. Creating installers is hard enough and unfortunately, CMake/CPack seem to be fairly lacking the full feature set needed to do the job. CMake-copy-plugin-after-build-clion-and-building-plugin-installers-with-cpack also confirms what I’ve found. Googling didn’t reveal much help out there either.

So it looks like I’ll be using some version of a my original shell script that creates the Mac installer, but to do that, I need to know where CMake created the release versions of my plugins and app without hardcoding them. Digging through the JUCE CMake files, it looks like these lines should point to those files:

get_target_property(aax_dest  "MyPlugin_AAX"        JUCE_PLUGIN_ARTEFACT_FILE)
get_target_property(vst_dest  "MyPlugin_VST"        JUCE_PLUGIN_ARTEFACT_FILE)
get_target_property(vst3_dest "MyPlugin_VST3"       JUCE_PLUGIN_ARTEFACT_FILE)
get_target_property(au_dest   "MyPlugin_AU"         JUCE_PLUGIN_ARTEFACT_FILE)
get_target_property(app_dest  "MyPlugin_Standalone" JUCE_PLUGIN_ARTEFACT_FILE)

But when I print those variables out, I get this:

AAX  = $<GENEX_EVAL:$<TARGET_PROPERTY:MyPlugin,LIBRARY_OUTPUT_DIRECTORY>>/AAX/$<TARGET_PROPERTY:MyPlugin,JUCE_PRODUCT_NAME>.aaxplugin
VST  = $<TARGET_BUNDLE_DIR:MyPlugin_VST>
VST3 = $<GENEX_EVAL:$<TARGET_PROPERTY:MyPlugin,LIBRARY_OUTPUT_DIRECTORY>>/VST3/$<TARGET_PROPERTY:MyPlugin,JUCE_PRODUCT_NAME>.vst3
AU   = $<TARGET_BUNDLE_DIR:MyPlugin_AU>
APP  = app_dest-NOTFOUND

I’m guessing that the difference here is that AAX and VST3 are actually folders. What I don’t understand is why there’s no standalone app? I can see the actual app in the folder, but that CMake line does not give me the path to the app. Any ideas, @reuk? Is there a better command to obtain the path to these products? Maybe JUCE could provide a CMake command to help out here.

If anyone has any ideas here, jump in, the water’s warm.

What’s the value of TARGET_BUILD_DIR and CMAKE_CURRENT_BINARY_DIR after compiling?

Rail

I didn’t get anything for TARGET_BUILD_DIR, but CMAKE_CURRENT_BINARY_DIR points to the top source level directory, cmake-build-release/HERE. It looks like Juce adds the target_artefacts and config folders below that.

I could mangle something together, but it would be handy, and probably more proper, if Juce gave us a higher level variable that points to the built targets.

use

“$<TARGET_FILE_DIR:${PROJECT_NAME}_AU>”

I’ve encountered the same problem, in the end I simply used one helper function to give me a list of paths, I think that it is easier to understand what this is doing than using three different types of generator-expressions for macOS-bundles, windows folders (for AAX and VST3) and windows files (for VST2):

function (get_plugin_binary_output_paths PLUGIN_TARGET)
    get_target_property (PLUGIN_FORMATS ${PLUGIN_TARGET} JUCE_PLUGIN_FORMATS) 
    foreach (FORMAT ${PLUGIN_FORMATS})
        set (BASE_PATH ${CMAKE_BINARY_DIR}/${PROJECT_NAME}_artefacts/${CMAKE_BUILD_TYPE}/${FORMAT})
        if (WIN32 AND ${FORMAT} STREQUAL "VST")
            list (APPEND OUTPUT_PATHS ${BASE_PATH}/${PROJECT_NAME}.dll)
        elseif (WIN32 AND ${FORMAT} STREQUAL "VST3")
            list (APPEND OUTPUT_PATHS ${BASE_PATH}/${PROJECT_NAME}.vst3/Contents/x86-64-win/${PROJECT_NAME}.vst3)
        elseif (WIN32 AND ${FORMAT} STREQUAL "AAX")
            list (APPEND OUTPUT_PATHS ${BASE_PATH}/${PROJECT_NAME}.aaxplugin/Contents/x64/${PROJECT_NAME}.aaxplugin)
        elseif (APPLE AND ${FORMAT} STREQUAL "AAX")
            list (APPEND OUTPUT_PATHS ${BASE_PATH}/${PROJECT_NAME}.aaxplugin)
        elseif (APPLE AND ${FORMAT} STREQUAL "VST")
            list (APPEND OUTPUT_PATHS ${BASE_PATH}/${PROJECT_NAME}.vst)
        elseif (APPLE AND ${FORMAT} STREQUAL "VST3")
            list (APPEND OUTPUT_PATHS ${BASE_PATH}/${PROJECT_NAME}.vst3)
        elseif (APPLE AND ${FORMAT} STREQUAL "AU")
            list (APPEND OUTPUT_PATHS ${BASE_PATH}/${PROJECT_NAME}.component)
        endif()
    endforeach()
    set (OUTPUT_PATHS ${OUTPUT_PATHS} PARENT_SCOPE)
endmacro()

That said- I spent quite some time on moving from InnoSetup/Packages to CPack, but we decided to keep our current approach. CPack simply did not reduce complexity in the installer creation, but it did in fact reduce the customization options. Also, since there are required steps before and after the installer creation (especially code signing), the whole process couldn’t be neatly written as one CPack command- and if you already have your python/bash scripts in place, it is way easier to execute the necessary steps there, instead of some cpack-pre-package-command.

I think that CPack definitely has its place for linux installer packages, but for commercial macOS and Windows software it is really cumbersome.

3 Likes

Thank you, but this didn’t work for me. It doesn’t seem right that YOU need to construct all these paths based on wherever Juce has created them. If Juce has created this output directory structure, then JUCE should provide variables or functions to retrieve them. After all, isn’t the whole point here to minimize hard coding paths? What if Juce decides to change where the output files are created?

I was hoping that @reuk or someone from the Juce team would respond here.

JUCE_PLUGIN_ARTEFACT_FILE is the correct way to discover the path to the built plugin bundle. This path is a generator expression because, for multi-configuration generators (VS, Xcode), the output path depends on the build configuration. The missing property for the standalone target is an oversight, I’ll add that shortly.

3 Likes

Any update here, @reuk?

Thanks.

That’s been added here:

4 Likes