Running a headless Introjucer


#1

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!

 


#2

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?


#3

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

#4

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


#5

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!

 


#6

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); }

?


#7

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