1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-11 23:54:18 +00:00
JUCE/extras/Projucer/Source/LiveBuildEngine/projucer_CompileEngineServer.cpp
stefan 8ec9443543 Projucer various fixes
* Use separate folder for disk cache in debug mode to avoid mixing debug/release-mode object files while testing
* Quote the server's file name as it may contain spaces etc.
* Fix saving source files during compilation on Windows
* Fix JuceDemo for live builds on Mac: long chains of recursive operator<< invocations caused compiler crash
* Move code for creating disabled Build tab to extra function and add names to improve readability
* Implement new "subscribe" behavior for createDisabledBuildTab
* Clean up trailing spaces
2016-09-15 17:13:41 +02:00

308 lines
8.4 KiB
C++

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#include "../jucer_Headers.h"
#include "../Utility/jucer_PresetIDs.h"
#include "../Utility/jucer_FileHelpers.h"
#include "../Application/jucer_AppearanceSettings.h"
#include "../Application/jucer_Application.h"
#include "../Utility/jucer_CodeHelpers.h"
#include "projucer_CompileEngineDLL.h"
#include "projucer_MessageIDs.h"
#include "projucer_CppHelpers.h"
#include "projucer_SourceCodeRange.h"
#include "projucer_ClassDatabase.h"
#include "projucer_DiagnosticMessage.h"
#include "projucer_ProjectBuildInfo.h"
#include "projucer_ClientServerMessages.h"
#if JUCE_LINUX
#include <sys/types.h>
#include <unistd.h>
#endif
#ifndef RUN_CLANG_IN_CHILD_PROCESS
#error
#endif
#if RUN_CLANG_IN_CHILD_PROCESS
static bool parentProcessHasExited();
#endif
#if JUCE_WINDOWS
static void setParentProcessID (int);
static int getCurrentProcessID();
#endif
//==============================================================================
/** Detects whether this process has hung, and kills it if so. */
struct ZombiePatrol : private Thread,
private AsyncUpdater,
private Timer
{
ZombiePatrol (MessageHandler& mh)
: Thread ("Ping"), owner (mh)
{
startThread (2);
startTimer (1000);
}
~ZombiePatrol()
{
stopThread (1000);
}
private:
MessageHandler& owner;
int failedPings = 0;
void run() override
{
while (! threadShouldExit())
{
#if RUN_CLANG_IN_CHILD_PROCESS
if (parentProcessHasExited())
{
killProcess();
break;
}
#endif
wait (1000);
}
}
void handleAsyncUpdate() override
{
DBG ("Server: quitting");
stopTimer();
ProjucerApplication::getApp().systemRequestedQuit();
}
void timerCallback() override
{
if (! MessageTypes::sendPing (owner))
{
if (++failedPings == 10)
{
killProcess();
return;
}
}
else
{
failedPings = 0;
}
}
void killProcess()
{
triggerAsyncUpdate(); // give the messagequeue a chance to do things cleanly.
static UnstoppableKillerThread* k = new UnstoppableKillerThread(); // (allowed to leak, but static so only one is created)
ignoreUnused (k);
}
struct UnstoppableKillerThread : public Thread
{
UnstoppableKillerThread() : Thread ("Killer") { startThread(); }
void run() override
{
wait (15000);
if (! threadShouldExit())
Process::terminate();
}
};
};
//==============================================================================
class ServerIPC : public InterprocessConnection,
public MessageHandler
{
public:
ServerIPC (const StringArray& info)
: InterprocessConnection (true), liveCodeBuilder (nullptr)
{
if (! createPipe (info[0], -1))
{
Logger::writeToLog ("*** Couldn't create pipe!");
ProjucerApplication::getApp().systemRequestedQuit();
return;
}
if (dll.isLoaded())
liveCodeBuilder = dll.projucer_createBuilder (sendMessageCallback, this, info[1].toRawUTF8(), info[2].toRawUTF8());
#if JUCE_WINDOWS
setParentProcessID (info[3].getHexValue32());
#endif
zombieKiller = new ZombiePatrol (*this);
}
~ServerIPC()
{
zombieKiller = nullptr;
if (dll.isLoaded())
dll.projucer_deleteBuilder (liveCodeBuilder);
dll.shutdown();
DBG ("Server: finished closing down");
}
void connectionMade() override
{
DBG ("Server: client connected");
}
void connectionLost() override
{
Logger::writeToLog ("Server: client lost");
JUCEApplication::quit();
}
void sendQuitMessageToIDE()
{
MessageTypes::sendShouldCloseIDE (*this);
}
bool sendMessage (const ValueTree& m) override
{
return InterprocessConnection::sendMessage (MessageHandler::convertMessage (m));
}
void messageReceived (const MemoryBlock& message) override
{
jassert (dll.isLoaded());
dll.projucer_sendMessage (liveCodeBuilder, message.getData(), message.getSize());
}
static bool sendMessageCallback (void* userInfo, const void* data, size_t dataSize)
{
return static_cast<InterprocessConnection*> (static_cast<ServerIPC*> (userInfo))
->sendMessage (MemoryBlock (data, dataSize));
}
CompileEngineDLL dll;
LiveCodeBuilder liveCodeBuilder;
ScopedPointer<ZombiePatrol> zombieKiller;
};
//==============================================================================
const char* commandPrefix = "--server:";
const char* commandTokenSeparator = "\x01";
String createCommandLineForLaunchingServer (const String& pipeName, const String& projectUID, const File& cacheLocation)
{
StringArray info;
info.add (pipeName);
info.add (projectUID);
info.add (cacheLocation.getFullPathName());
#if JUCE_WINDOWS
info.add (String::toHexString (getCurrentProcessID()));
#endif
const File exe (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
return "\"" + exe.getFullPathName() + "\" " + commandPrefix + info.joinIntoString (commandTokenSeparator);
}
static ServerIPC* currentServer = nullptr;
static void crashCallback (const char* message)
{
if (currentServer != nullptr)
{
#if RUN_CLANG_IN_CHILD_PROCESS
MessageTypes::sendCrash (*currentServer, message);
Logger::writeToLog ("*** Crashed! " + String (message));
#else
ignoreUnused (message);
jassertfalse;
#endif
currentServer->disconnect();
}
}
static void quitCallback()
{
ProjucerApplication::getApp().systemRequestedQuit();
}
void* createClangServer (const String& commandLine)
{
StringArray info;
info.addTokens (commandLine.fromFirstOccurrenceOf (commandPrefix, false, false), commandTokenSeparator, "");
ScopedPointer<ServerIPC> ipc = new ServerIPC (info);
if (ipc->dll.isLoaded())
{
ipc->dll.initialise (crashCallback, quitCallback, (bool) RUN_CLANG_IN_CHILD_PROCESS);
currentServer = ipc.release();
return currentServer;
}
return nullptr;
}
void destroyClangServer (void* server)
{
currentServer = nullptr;
delete static_cast<ServerIPC*> (server);
}
void sendQuitMessageToIDE (void* server)
{
static_cast<ServerIPC*> (server)->sendQuitMessageToIDE();
}
//==============================================================================
#if JUCE_WINDOWS
#define STRICT 1
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
static HANDLE parentProcessHandle = 0;
static void setParentProcessID (int pid) { parentProcessHandle = OpenProcess (SYNCHRONIZE, FALSE, (DWORD) pid); }
static int getCurrentProcessID() { return (int) GetCurrentProcessId(); }
#endif
#if RUN_CLANG_IN_CHILD_PROCESS
bool parentProcessHasExited()
{
#if JUCE_WINDOWS
return WaitForSingleObject (parentProcessHandle, 0) == WAIT_OBJECT_0;
#else
return getppid() == 1;
#endif
}
#endif