Running a headless Introjucer

As part of our workflow, we prefer not to commit any auto generated files from JUCE. This prevents engineers from accidentally making changes to auto-generated files, and also ensures that we don't clutter code reviews with code out of our control.

To get around this, we can just run:

./Introjucer --resave /path/to/project.jucer

This works fine for a machine with a GUI, but on our headless linux build servers, this is what happens:

./Introjucer --help
JUCE Assertion failure in juce_Desktop.cpp:336
Segmentation fault (core dumped)

It looks like there is a dependency on there actually being a GUI, which is a little strange when all we want to do is run a command line version.

Any thoughts on how we can get around this?

Cheers!

 

Well.. It's all just done as one executable for simplicity, but it should be able to survive that. I don't have a headless set-up to try, do you have stack traces for the assertion and the crash?

Stack trace for assertion:

* thread #1: tid = 27400, 0x00007fb5cacc6ea7 libc.so.6`kill + 7, name = 'Introjucer', stop reason = trace
  * frame #0: 0x00007fb5cacc6ea7 libc.so.6`kill + 7
    frame #1: 0x000000000070c268 Introjucer`juce::Desktop::Displays::init(this=0x00000000023d20c0, desktop=0x00000000023d1d50) + 108 at juce_Desktop.cpp:336
    frame #2: 0x000000000070bdda Introjucer`juce::Desktop::Displays::Displays(this=0x00000000023d20c0, desktop=0x00000000023d1d50) + 48 at juce_Desktop.cpp:261
    frame #3: 0x000000000070ac31 Introjucer`juce::Desktop::Desktop(this=0x00000000023d1d50) + 459 at juce_Desktop.cpp:33
    frame #4: 0x000000000070afa0 Introjucer`juce::Desktop::getInstance() + 40 at juce_Desktop.cpp:51
    frame #5: 0x0000000000740d97 Introjucer`juce::LookAndFeel::setDefaultLookAndFeel(newDefaultLookAndFeel=0x00000000023d16d0) + 17 at juce_LookAndFeel.cpp:91
    frame #6: 0x00000000004186eb Introjucer`IntrojucerApp::initialise(this=0x00000000023d16a0, commandLine=0x00007fffcee1e7d0) + 37 at jucer_Application.h:47
    frame #7: 0x00000000006211ec Introjucer`juce::JUCEApplicationBase::initialiseApp(this=0x00000000023d16a0) + 180 at juce_ApplicationBase.cpp:261
    frame #8: 0x00000000007956d6 Introjucer`juce::JUCEApplication::initialiseApp(this=0x00000000023d16a0) + 24 at juce_Application.cpp:88
    frame #9: 0x00000000006210a7 Introjucer`juce::JUCEApplicationBase::main() + 187 at juce_ApplicationBase.cpp:234
    frame #10: 0x0000000000620fea Introjucer`juce::JUCEApplicationBase::main(argc=3, argv=0x00007fffcee1e978) + 40 at juce_ApplicationBase.cpp:218
    frame #11: 0x0000000000417146 Introjucer`main(argc=3, argv=0x00007fffcee1e978) + 43 at jucer_Main.cpp:27
    frame #12: 0x00007fb5cacb1ec5 libc.so.6`__libc_start_main + 245

Stack trace for crash:

* thread #1: tid = 27400, 0x00007fb5cc37f309 libX11.so.6`XLockDisplay + 9, name = 'Introjucer', stop reason = invalid address (fault address: 0x968)
  * frame #0: 0x00007fb5cc37f309 libX11.so.6`XLockDisplay + 9
    frame #1: 0x000000000062505f Introjucer`juce::ScopedXLock::ScopedXLock(this=0x00007fffcee1dd30) + 27 at juce_linux_Messaging.cpp:40
    frame #2: 0x00000000007993ae Introjucer`juce::MouseCursor::createStandardMouseCursor(type=IBeamCursor) + 496 at juce_linux_Windowing.cpp:3377
    frame #3: 0x00000000007a3b35 Introjucer`juce::MouseCursor::SharedCursorHandle::SharedCursorHandle(this=0x00000000023d3970, type=IBeamCursor) + 25 at juce_MouseCursor.cpp:49
    frame #4: 0x00000000007a3d2c Introjucer`juce::MouseCursor::SharedCursorHandle::createStandard(type=IBeamCursor) + 144 at juce_MouseCursor.cpp:78
    frame #5: 0x000000000070dd9b Introjucer`juce::MouseCursor::MouseCursor(this=0x00007fffcee1de10, type=IBeamCursor) + 31 at juce_MouseCursor.cpp:137
    frame #6: 0x000000000081c5de Introjucer`juce::CodeEditorComponent::CodeEditorComponent(this=0x00007fffcee1e040, doc=0x00007fffcee1df60, tokeniser=0x00007fffcee1dec0) + 756 at juce_CodeEditorComponent.cpp:363
    frame #7: 0x0000000000408086 Introjucer`AppearanceSettings::AppearanceSettings(this=0x00000000023d2240, updateAppWhenChanged=true) + 552 at jucer_AppearanceSettings.cpp:68
    frame #8: 0x00000000004d2a8a Introjucer`StoredSettings::StoredSettings(this=0x00000000023d2200) + 62 at jucer_StoredSettings.cpp:42
    frame #9: 0x0000000000418700 Introjucer`IntrojucerApp::initialise(this=0x00000000023d16a0, commandLine=0x00007fffcee1e7d0) + 58 at jucer_Application.h:48
    frame #10: 0x00000000006211ec Introjucer`juce::JUCEApplicationBase::initialiseApp(this=0x00000000023d16a0) + 180 at juce_ApplicationBase.cpp:261
    frame #11: 0x00000000007956d6 Introjucer`juce::JUCEApplication::initialiseApp(this=0x00000000023d16a0) + 24 at juce_Application.cpp:88
    frame #12: 0x00000000006210a7 Introjucer`juce::JUCEApplicationBase::main() + 187 at juce_ApplicationBase.cpp:234
    frame #13: 0x0000000000620fea Introjucer`juce::JUCEApplicationBase::main(argc=3, argv=0x00007fffcee1e978) + 40 at juce_ApplicationBase.cpp:218
    frame #14: 0x0000000000417146 Introjucer`main(argc=3, argv=0x00007fff

Thanks - I've added what should be a workaround for that (although have no way of testing it)

Sorry for the delay, but I'm just getting back where I can test this fix.

Unfortunately that didn't do the trick. It looks like IntrojucerApp::isRunningCommandLine doesn't get set until after it has passed the check that was added to jucer_AppearanceSettings.cpp. I fixed that little part like so:

class IntrojucerApp   : public JUCEApplication
{
public:
    //==============================================================================
    IntrojucerApp() :  isRunningCommandLine (true) {}

    //==============================================================================
    void initialise (const String& commandLine) override
    {
        LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
        settings = new StoredSettings();

        if (commandLine.isNotEmpty())
        {
            const int appReturnCode = performCommandLine (commandLine);

            if (appReturnCode != commandLineNotPerformed)
            {
                setApplicationReturnValue (appReturnCode);
                quit();
                return;
            }
        }

        isRunningCommandLine = false;

But that only led to failure later on in juce::MouseCursor::showWaitCursor():

* thread #1: tid = 2551, 0x00007ff968ffb309 libX11.so.6`XLockDisplay + 9, name = 'Introjucer', stop reason = invalid address (fault address: 0x968)
  * frame #0: 0x00007ff968ffb309 libX11.so.6`XLockDisplay + 9
    frame #1: 0x0000000000634d17 Introjucer`juce::ScopedXLock::ScopedXLock(this=0x00007fff7e73a0f0) + 27 at juce_linux_Messaging.cpp:40
    frame #2: 0x00000000007a8fc0 Introjucer`juce::MouseCursor::createStandardMouseCursor(type=WaitCursor) + 496 at juce_linux_Windowing.cpp:3377
    frame #3: 0x00000000007b371f Introjucer`juce::MouseCursor::SharedCursorHandle::SharedCursorHandle(this=0x0000000002bc8dc0, type=WaitCursor) + 25 at juce_MouseCursor.cpp:49
    frame #4: 0x00000000007b3916 Introjucer`juce::MouseCursor::SharedCursorHandle::createStandard(type=WaitCursor) + 144 at juce_MouseCursor.cpp:78
    frame #5: 0x000000000071d80d Introjucer`juce::MouseCursor::MouseCursor(this=0x00007fff7e73a1c0, type=WaitCursor) + 31 at juce_MouseCursor.cpp:137
    frame #6: 0x000000000071dba4 Introjucer`juce::MouseCursor::showWaitCursor() + 26 at juce_MouseCursor.cpp:209
    frame #7: 0x0000000000828142 Introjucer`juce::FileBasedDocument::loadFrom(this=0x0000000002bc9d90, newFile=0x00007fff7e73a330, showMessageOnFailure=true) + 46 at juce_FileBasedDocument.cpp:70
    frame #8: 0x0000000000412b2e Introjucer`load(this=0x00007fff7e73a320, projectFile=0x00007fff7e73a330) + 308 at jucer_CommandLine.cpp:89
    frame #9: 0x0000000000412da8 Introjucer`resaveProject(args=0x00007fff7e73a3f0, justSaveResources=false) + 118 at jucer_CommandLine.cpp:132
    frame #10: 0x00000000004146bf Introjucer`performCommandLine(commandLine=0x00007fff7e73a4b0) + 270 at jucer_CommandLine.cpp:373
    frame #11: 0x000000000041ba27 Introjucer`IntrojucerApp::initialise(this=0x0000000002bc5720, commandLine=0x00007fff7e73a4b0) + 107 at jucer_Application.h:54
    frame #12: 0x0000000000630ea4 Introjucer`juce::JUCEApplicationBase::initialiseApp(this=0x0000000002bc5720) + 180 at juce_ApplicationBase.cpp:261
    frame #13: 0x00000000007a52e8 Introjucer`juce::JUCEApplication::initialiseApp(this=0x0000000002bc5720) + 24 at juce_Application.cpp:88
    frame #14: 0x0000000000630d5f Introjucer`juce::JUCEApplicationBase::main() + 187 at juce_ApplicationBase.cpp:234
    frame #15: 0x0000000000630ca2 Introjucer`juce::JUCEApplicationBase::main(argc=3, argv=0x00007fff7e73a658) + 40 at juce_ApplicationBase.cpp:218
    frame #16: 0x0000000000417ac6 Introjucer`main(argc=3, argv=0x00007fff7e73a658) + 43 at jucer_Main.cpp:27
    frame #17: 0x00007ff96792dec5 libc.so.6`__libc_start_main + 245

 

More generally, it seems that any call to Desktop::getInstance() is going to be problematic.

I am running ubuntu server inside of VirtualBox to test this, just in case that helps with debugging.

Many thanks!

 

Could this be worked around by just adding some checks to the ScopedXLock class, e.g.

ScopedXLock::ScopedXLock()   { if (display != 0) XLockDisplay (display); } 
ScopedXLock::~ScopedXLock()  { if (display != 0) XUnlockDisplay (display); }

?

Yeah, it appears that did the trick. Nice work!