Prototype: Precompiled headers for Visual Studio

@t0m, @jules, @ed95

Guys, could we have some input here please?

This is something weā€™d like to support properly in the Projucer, but (without looking into it properly) it feels like the kind of thing that could have lots of subtle ramifications that we need to test first. Everyoneā€™s working full steam on the JUCE 6 release this month so it wonā€™t be done before then, but we will look into it after that. Like Reuben said though, it should be possible to add this to your builds by using the CMake support that is a part of the JUCE 6 release.

thanks for the update.

I donā€™t want to move to use CMake which is why Iā€™m asking about this. I donā€™t know CMake and itā€™s not something that I really want to learn. Projucer does everything I need generally.

Please look into this, weā€™ve been asking for it for many years, and in all fairness, weā€™ve been told many times before that this is going to be investigated but it never happens.

4 Likes

I havenā€™t looked at the JUCE 6 CMake support. Does the Projucer 6 support Cmake export, so I can convert my existing projects to CMake and then tweak some CMake settings? Or do I need to start from scratch, rebuilding my projects in CMake?

I also vote for Pre-compiled header support. My local builds are fine, but on GitHub Actions where you get a 2 core VM, they are painfully slow. 20+ minutes for one plugin.

Also, is there anyway to find what Feature Requests I spent my votes on? I want to move one to this feature, but I canā€™t find what I spent them on.

2 Likes

Thereā€™s no Projucer exporter for the new CMake support, but there are commented examples included in the repo under examples/CMake, along with an API reference in the docs folder. The Projucer, DemoRunner and friends also have CMake builds that can be used as examples.

2 Likes

@leehu, I was reluctant towards CMake myself but fwiw I recently tried out @McMartinā€™s FRUT project which has some really nice tooling/api allowing you to work with CMake in a way thatā€™s similar to the Projucerā€™s setup. It can even auto-convert your jucer file to a cmake file. Might be worth taking a look if you havenā€™t already.

2 Likes

Hi, thx. The issue is that Iā€™m knee deep in projects at the moment so I donā€™t really have time to look into this currently and donā€™t want to get into the situation where I move over, start having issues and then need to understand CMake in order to solve the problems.
Iā€™m happy with my workflow with Projucer so want to stick with that for now - I think I have some free time scheduled for December :wink:

2 Likes

Hi @ed95,

With Juce6 being out for a month or so now to settle, will you guys be looking into this now? thx

4 Likes

Weā€™ve not had a chance yet, but itā€™s on the to-do list.

1 Like

thx - upgrading to Juce 6 is contingent on this for me.

Weā€™ve added PCH support to the Projucer Xcode and Visual Studio exporters in d677fd6.

In the build configuration settings for these exporters there is now a ā€œUse Precompiled Headerā€ option (disabled by default) and immediately below is a ā€œPrecompiled Header Fileā€ file path setting where you can specify an input header file that will be used to generate a JucePrecompiledHeader_Debug/Release file which is used to generate the PCH file artefact in Xcode and VS. This generated file will be written on project save and placed in the target folder for the exporter (Builds/MacOSX or Builds/VisualStudio201x) so any relative #includes need to be relative to this. This file will be force included for all source files in your project by default, but you can disable this for specific files with the ā€œSkip PCHā€ option in the Projucer file explorer:

Screenshot 2020-08-25 at 12.45.52

Since this option is only available for the Xcode and VS exporters, you probably shouldnā€™t remove the #includes covered by the force-included PCH from your source files as they will then fail to compile on other platforms/IDEs, but the use of #pragma once should ensure that the compile time cost for multiple includes is minimal.

13 Likes

Out of interest, have you seen this give much or a reduction in build times?
How does it scale with project size?

awesome - will have chance to try it out on Fri! thanks!

Maybe Iā€™m doing something wrong but on VS2019 Iā€™m able to do the first compilation.
But any future build fails with:

Error	C2859	<path_here>\Builds\VisualStudio2019\x64\Debug\Shared Code\project_SharedCode.pdb is not the pdb file 
that was used when this precompiled header was created, 
recreate the precompiled header.	pajam_SharedCode	<path_here>	1	

Xcode works as expected from my testing.

1 Like

Are you able to reproducde the error with a blank JUCE project? Iā€™ve created a new blank GUI project in the Projucer, set the VS2019 exporter debug build config to use PCH, and set the file path to pch.h which just #includes <JuceHeader.h>. Iā€™m able to build and re-build the project in VS whilst making changes to the source files without seeing that error. Can you perhaps attach the misbehaving project file?

1 Like

Hi Reuben,

Iā€™m attempting to add a precomiled header to my JUCE/CMake project with target_precompiled_headers. Inside my pch.h, Iā€™ve got:

// included a bunch of other libraries, and then...
#include <JuceHeader.h>

Then in my CMakeLists.txt, Iā€™ve got:

juce_generate_juce_header(MyPlugin)
target_precompile_headers(MyPlugin PRIVATE pch.h)
target_link_libraries(MyPlugin PRIVATE BinaryData juce::juce_audio_utils)

When I try to build, I get the following error, for several of the JUCE modules:

[build] \path\to\JUCE\modules\juce_dsp\juce_dsp.cpp(32): fatal error C1189: #error:  "Incorrect use of JUCE cpp file" [\path\to\build\MyPlugin.vcxproj]

For reference, Iā€™m using CMake version 3.18, and JUCE version 6.0.0. Any assistance would be much appreciated!

The JUCE module sources themselves donā€™t support being built with PCH. To use PCH in JUCE/CMake projects, Iā€™ve been putting the JUCE modules into a standalone static lib (with no PCH) and then including the module headers in the PCHs for dependent targets.

To build correctly, itā€™s very important that the ā€˜JUCE staticlibā€™ target includes all of the JUCE modules that will be used by a particular target. Including a few modules in the staticlib and a few modules directly in the dependent target will result in inconsistent preprocessor definitions.

JUCE staticlib:

add_library(juce_plugin_modules STATIC)

target_link_libraries(juce_plugin_modules
    PRIVATE
        juce::juce_audio_utils
        juce::juce_dsp
    PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags)

target_compile_definitions(juce_plugin_modules
    PUBLIC
        JUCE_WEB_BROWSER=0
        JUCE_USE_CURL=0
        # etc.
    INTERFACE
        $<TARGET_PROPERTY:juce_plugin_modules,COMPILE_DEFINITIONS>)

target_include_directories(juce_plugin_modules
    INTERFACE
        $<TARGET_PROPERTY:juce_plugin_modules,INCLUDE_DIRECTORIES>)

set_target_properties(juce_plugin_modules PROPERTIES
    POSITION_INDEPENDENT_CODE TRUE
    VISIBILITY_INLINES_HIDDEN TRUE
    C_VISIBILITY_PRESET hidden
    CXX_VISIBILITY_PRESET hidden)

Dependent target:

target_sources(demo PRIVATE ${sources})

target_precompile_headers(demo PRIVATE demo_pch.hpp)

target_link_libraries(demo PUBLIC juce_plugin_modules)
1 Like

Hi @reuk ,

One problem I found with the approach suggested above is that the JucePlugin_Build_XXX, JucePlugin_WantsMidiInput and similar macros are only defined in the demo target (which I added as usual via juce_add_plugin) but not in the juce_plugin_modules target, therefore changing some behavior vs. when the module targets are directly linked to demo (and thus their sources compiled with this macros in place).

How do you recommend handling this?

Thanks,
Dan

1 Like

Youā€™re right - there are a handful of occurrences of JucePlugin_ macros outside of the juce_audio_plugin_client module. The safest option, ensuring the same preprocessor definitions in all TUs, would be to avoid using a staticlib. Instead, you would need to build the JUCE sources directly into the each pluginā€™s shared code target.

If I search for occurrences of JucePlugin_ outside of the juce_audio_plugin_client module, I only find a handful of instances. All of these appear to affect definitions of functions in .cpp files, rather than layouts of classes in headers, so I wouldnā€™t expect leaving the JucePlugin_ macros undefined in these locations to cause any issues. Again, this is not a ā€˜supportedā€™ use-case and is not the way that JUCE is designed to be used so I canā€™t recommend this approach. However, I would expect it to produce working binaries.

If the problem is elsewhere, i.e. you have your own custom modules which are written such that the presence/absence of JucePlugin_ macros will cause the same symbol to be defined differently in different TUs, then the solution is trickier. I think youā€™d need to either avoid using a JUCE staticlib completely, or rewrite the problematic code so that any symbols visible in multiple TUs always have the same definition.

2 Likes

If I search for occurrences of JucePlugin_ outside of the juce_audio_plugin_client module

What about that module though?

Again, this is not a ā€˜supportedā€™ use-case

I did notice some issue which led me to notice these missing defines (though itā€™s not very interesting to mention specifically).

In any case, Iā€™m looking for a supported use case.

Iā€™m not using custom modules so thatā€™s not an issue for me personally.

Instead, you would need to build the JUCE sources directly into the each pluginā€™s shared code target.

Is there a robust way to do that with CMake? I can try just globbing files, but Iā€™m concerned Iā€™m not considering all the implications of not using the modules as provided by JUCE, and that it would require some extra non-trivial work.