예제 #1
0
void WebCLValidatorAction::ExecuteAction()
{
    // We will get assertions if sema_ isn't wrapped here.
    llvm::OwningPtr<clang::Sema> sema(sema_);
    ParseAST(*sema.get());
    validatedSource_ = consumer_->getTransformedSource();
    kernels_ = consumer_->getKernels();
}
예제 #2
0
void CommentHandler::handleComment(Annotator &A, Generator& generator, clang::Sema &Sema,
                                   const char *bufferStart, int commentStart, int len,
                                   clang::SourceLocation searchLocBegin, clang::SourceLocation searchLocEnd,
                                   clang::SourceLocation commentLoc)
{
    llvm::StringRef rawString(bufferStart+commentStart, len);
    std::string attributes;
    std::string DeclRef;


    if ((rawString.ltrim().startswith("/**") && !rawString.ltrim().startswith("/***"))
            || rawString.ltrim().startswith("/*!") || rawString.ltrim().startswith("//!")
            || (rawString.ltrim().startswith("///") && !rawString.ltrim().startswith("////")))
#if CLANG_VERSION_MAJOR==3 && CLANG_VERSION_MINOR<=4
        if (rawString.find("deprecated") == rawString.npos) // workaround crash in comments::Sema::checkDeprecatedCommand
#endif
    {
        attributes = "class=\"doc\"";

        clang::Preprocessor &PP = Sema.getPreprocessor();
        clang::comments::CommandTraits traits(PP.getPreprocessorAllocator(), clang::CommentOptions());
#if CLANG_VERSION_MAJOR==3 && CLANG_VERSION_MINOR<=4
        traits.registerBlockCommand("deprecated"); // avoid typo correction leading to crash.
#endif
        clang::comments::Lexer lexer(PP.getPreprocessorAllocator(), PP.getDiagnostics(), traits,
                                     clang::SourceLocation::getFromRawEncoding(commentStart), bufferStart + commentStart, bufferStart + commentStart + len);
        clang::comments::Sema sema(PP.getPreprocessorAllocator(), PP.getSourceManager(), PP.getDiagnostics(), traits, &PP);
        clang::comments::Parser parser(lexer, sema, PP.getPreprocessorAllocator(), PP.getSourceManager(),
                                       PP.getDiagnostics(), traits);
        auto fullComment = parser.parseFullComment();
        CommentVisitor visitor{A, generator, traits, Sema};
        visitor.visit(fullComment);
        DeclRef = visitor.DeclRef;
    }

    if (!DeclRef.empty()) {
        docs.insert({std::move(DeclRef), { rawString.str() , commentLoc }});
        generator.addTag("i", attributes, commentStart, len);
        return;
    }

    // Try to find a matching declaration
    const auto &dof = decl_offsets;
    //is there one and one single decl in that range.
    auto it_before = dof.lower_bound(searchLocBegin);
    auto it_after = dof.upper_bound(searchLocEnd);
    if (it_before != dof.end() && it_after != dof.begin() && it_before == (--it_after)) {
        if (it_before->second.second) {
            docs.insert({it_before->second.first, { rawString.str() , commentLoc }});
        } else {
            attributes %= " data-doc=\"" % it_before->second.first % "\"";
        }
    }

    generator.addTag("i", attributes, commentStart, len);
}
예제 #3
0
파일: main.cpp 프로젝트: CCJY/coliru
    	bool Parse( Furrovine::String filename ) {
    		class ASTDeclConsumer : public clang::ASTConsumer {
    	public:
			ASTDeclConsumer( std::vector<clang::Decl*>& arg )
				: vec( arg ) {}
			std::vector<clang::Decl*>& vec;
			bool HandleTopLevelDecl( clang::DeclGroupRef arg ) {
				for ( auto && x : arg ) {
					vec.push_back( x );
				}
				return true;
			}
		};

		ASTDeclConsumer astconsumer( declarations );

		llvm::StringRef llvmfilename( reinterpret_cast<char*>( filename.data( ) ) );
		
		clang::CompilerInstance ci;
		clang::DiagnosticsEngine diagnosticsengine( diagnosticids, llvmdiagnosticsoptions.getPtr(), new clang::TextDiagnosticPrinter( errorstream, llvmdiagnosticsoptions.getPtr(), false ), true );
		clang::FileManager filemanager( filesystemoptions );
		clang::SourceManager sourcemanager( diagnosticsengine, filemanager );
		std::unique_ptr<clang::TargetInfo> targetinfo( clang::TargetInfo::CreateTargetInfo( diagnosticsengine, &targetoptions ) );
		targetinfo->setCXXABI( clang::TargetCXXABI::Microsoft );
		clang::HeaderSearchOptions& headersearchoptions = *llvmheadersearchoptions;
		clang::HeaderSearch hs( llvmheadersearchoptions, filemanager, diagnosticsengine, languageoptions, targetinfo.get( ) );
		clang::PreprocessorOptions& preprocessoroptions = *llvmpreprocessoroptions;
		clang::Preprocessor preprocessor( llvmpreprocessoroptions, diagnosticsengine, languageoptions, targetinfo.get( ), sourcemanager, hs, ci );
		clang::InitializePreprocessor( preprocessor, preprocessoroptions, headersearchoptions, frontendoptions );
		preprocessor.getBuiltinInfo( ).InitializeBuiltins( preprocessor.getIdentifierTable( ), languageoptions );
		clang::ASTContext astcontext( languageoptions, sourcemanager, targetinfo.get( ), preprocessor.getIdentifierTable( ), preprocessor.getSelectorTable( ), preprocessor.getBuiltinInfo( ), 1024 );
		clang::Sema sema( preprocessor, astcontext, astconsumer, clang::TU_Complete, null );

		const clang::DirectoryLookup* directlookup = nullptr;
		auto entry = hs.LookupFile( llvmfilename, true, nullptr, directlookup, nullptr, nullptr, nullptr, nullptr );
		if ( !entry )
			entry = filemanager.getFile( llvmfilename );
		if ( !entry )
			throw Furrovine::TException<std::exception>( "Could not find file " + filename );

		auto fileid = sourcemanager.createFileID( entry, clang::SourceLocation( ), clang::SrcMgr::CharacteristicKind::C_User );
		if ( fileid.isInvalid( ) )
			throw Furrovine::TException<std::exception>( "Error translating file " + filename );
		
		sourcemanager.setMainFileID( fileid );
		diagnosticsengine.getClient( )->BeginSourceFile( languageoptions, &preprocessor );
		clang::ParseAST( sema );
		diagnosticsengine.getClient( )->EndSourceFile( );

		return diagnosticsengine.getClient( )->getNumErrors() == 0;
    	}
예제 #4
0
    bool parse(Component& component, Module* existingMod, const std::string& filename, bool printAST) {
        // NOTE: seems to get deleted by Preprocessor
        HeaderSearch* Headers = new HeaderSearch(HSOpts, SM, Diags, LangOpts, pti);
        DummyLoader loader;

        std::shared_ptr<PreprocessorOptions> PPOpts(new PreprocessorOptions());
        Preprocessor PP(PPOpts, Diags, LangOpts, SM, *Headers, loader);

        ApplyHeaderSearchOptions(PP.getHeaderSearchInfo(), *HSOpts, LangOpts, pti->getTriple());
        PP.setPredefines(configs);
        PP.Initialize(*pti);

        // File stuff
        const FileEntry *pFile = FileMgr.getFile(filename);
        if (pFile == 0) {
            fprintf(stderr, "Error opening file: '%s'\n", filename.c_str());
            exit(-1);
        }
        FileID id = SM.createFileID(pFile, SourceLocation(), SrcMgr::C_User);
        PP.EnterSourceFile(id, nullptr, SourceLocation());

        // TEMP rewriter test
        //ast.fileID = id;
        // Manually set predefines (normally done in EnterMainSourceFile())
        std::unique_ptr<llvm::MemoryBuffer> SB = llvm::MemoryBuffer::getMemBufferCopy(configs, "<built-in>");
        assert(SB && "Cannot create predefined source buffer");
        FileID FID = SM.createFileID(std::move(SB));

        // NOTE: setPredefines() is normally private
        PP.setPredefinesFileID(FID);
        PP.EnterSourceFile(FID, nullptr, SourceLocation());

        Diags.getClient()->BeginSourceFile(LangOpts, 0);

        C2Sema sema(SM, Diags, PP, component, existingMod, filename);
        C2Parser parser(PP, sema, component.isExternal());
        bool ok = parser.Parse();
        if (printAST) sema.printAST();
#if 0
        PP.EndSourceFile();

        llvm::errs() << "\nSTATISTICS FOR '" << ast.getFileName() << "':\n";
        PP.PrintStats();
        PP.getIdentifierTable().PrintStats();
        llvm::errs() << "\n";
#endif
        return ok;
    }
예제 #5
0
int main(int argc, char *argv[])
{
    QSystemSemaphore sema("UniqueFishPro",1,QSystemSemaphore::Open);
    sema.acquire();
    QSharedMemory mem("SystemObject");
    if (!mem.create(1))
    {
        sema.release();
        QApplication::activeWindow();
        return 0;
    }
    sema.release();

    QApplication a(argc, argv);
    a.setOrganizationName("YanboOrg");
    a.setApplicationName("FishSoftware");

    initPath();

    QApplication::setStyle(QStyleFactory::create("Fusion"));

    Widget w;
    w.setWindowFlags(w.windowFlags()& ~Qt::WindowMaximizeButtonHint);
    //! [0]
    //Yanbo Tcp class Define and handle:TCP连接类处理方法
    ////1 槽
    //connect(/*你定义的TCP连接类对象*/,SIGNAL(/*对应的信号*/),&w,SLOT(finishedConnectSlot(int,bool)));//连接成功返回对应组号连接状态
    //connect(/*你定义的TCP连接类对象*/,SIGNAL(/*对应的信号*/),&w,SLOT(finishedPutWeigh(int,send_head)));//称量成功后返回实际称量对应槽处理函数
    ////1 信号处理
    ////    void sendConnectSocket(QMap<int,QString>);//点击“发送”按钮,触发信号,发送组号及对应IP
    ////    void sendGroupPutWeighInfo(int,send_head);//发送称量信息-以得到返回称量并进行UI交互
    ////    void sendGroupBatchInfo(int,send_head);//发送投料信息-以进行最后实际处理-TCP接收站信号后发送数据到硬件进行处理
    ////    void closeConnect();//断开连接信号
    //connect(&w,SIGNAL(sendConnectSocket(QMap<int,QString>)),/*你定义的TCP连接类对象*/,SIGNAL/SLOT(/*对应的信号 或 槽处理函数*/));
    //connect(&w,SIGNAL(sendGroupPutWeighInfo(int,send_head)),/*你定义的TCP连接类对象*/,SIGNAL/SLOT(/*对应的信号 或 槽处理函数*/));
    //connect(&w,SIGNAL(sendGroupBatchInfo(int,send_head)),/*你定义的TCP连接类对象*/,SIGNAL/SLOT(/*对应的信号 或 槽处理函数*/));
    //connect(&w,SIGNAL(closeConnect()),/*你定义的TCP连接类对象*/,SIGNAL/SLOT(/*对应的信号 或 槽处理函数*/));
    //! [0]-end!
    w.show();
    return a.exec();
}
예제 #6
0
int main()
{
#ifndef _WIN32
	Mutex mutex;
	Semaphore sema(1);
	struct timeval start;
	struct timeval tt;
	double d;

	Utility::GetTime(&start);
	for (int i = 0; i < 100000; i++)
		lock(mutex, i);
	Utility::GetTime(&tt);
	d = Diff(start, tt);
	printf("%.4f sec\n", d);

	Utility::GetTime(&start);
	for (int i = 0; i < 100000; i++)
		lock(sema, i);
	Utility::GetTime(&tt);
	d = Diff(start, tt);
	printf("%.4f sec\n", d);
#endif
}
예제 #7
0
int main()
{
    clang::DiagnosticOptions diagnosticOptions;
    clang::TextDiagnosticPrinter *pTextDiagnosticPrinter =
        new clang::TextDiagnosticPrinter(
            llvm::outs(),
            diagnosticOptions);
    llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> pDiagIDs;
    clang::DiagnosticsEngine *pDiagnosticsEngine =
        new clang::DiagnosticsEngine(pDiagIDs, pTextDiagnosticPrinter);

    clang::LangOptions languageOptions;
    clang::FileSystemOptions fileSystemOptions;
    clang::FileManager fileManager(fileSystemOptions);

    clang::SourceManager sourceManager(
        *pDiagnosticsEngine,
        fileManager);

    clang::HeaderSearchOptions headerSearchOptions;
    // <Warning!!> -- Platform Specific Code lives here
    // This depends on A) that you're running linux and
    // B) that you have the same GCC LIBs installed that
    // I do. 
    // Search through Clang itself for something like this,
    // go on, you won't find it. The reason why is Clang
    // has its own versions of std* which are installed under 
    // /usr/local/lib/clang/<version>/include/
    // See somewhere around Driver.cpp:77 to see Clang adding
    // its version of the headers to its include path.
    headerSearchOptions.AddPath("/usr/include/linux",
            clang::frontend::Angled,
            false,
            false,
            false);
    headerSearchOptions.AddPath("/usr/include/c++/4.4/tr1",
            clang::frontend::Angled,
            false,
            false,
            false);
    headerSearchOptions.AddPath("/usr/include/c++/4.4",
            clang::frontend::Angled,
            false,
            false,
            false);
    // </Warning!!> -- End of Platform Specific Code

    clang::TargetOptions targetOptions;
    targetOptions.Triple = llvm::sys::getDefaultTargetTriple();

    clang::TargetInfo *pTargetInfo = 
        clang::TargetInfo::CreateTargetInfo(
            *pDiagnosticsEngine,
            targetOptions);

    clang::HeaderSearch headerSearch(fileManager, 
                                     *pDiagnosticsEngine,
                                     languageOptions,
                                     pTargetInfo);
    clang::CompilerInstance compInst;

    clang::Preprocessor preprocessor(
        *pDiagnosticsEngine,
        languageOptions,
        pTargetInfo,
        sourceManager,
        headerSearch,
        compInst);

    clang::PreprocessorOptions preprocessorOptions;
    clang::FrontendOptions frontendOptions;
    clang::InitializePreprocessor(
        preprocessor,
        preprocessorOptions,
        headerSearchOptions,
        frontendOptions);
        
    const clang::FileEntry *pFile = fileManager.getFile(
        "input04.c");
    sourceManager.createMainFileID(pFile);

    const clang::TargetInfo &targetInfo = *pTargetInfo;

    clang::IdentifierTable identifierTable(languageOptions);
    clang::SelectorTable selectorTable;

    clang::Builtin::Context builtinContext;
    builtinContext.InitializeTarget(targetInfo);
    clang::ASTContext astContext(
        languageOptions,
        sourceManager,
        pTargetInfo,
        identifierTable,
        selectorTable,
        builtinContext,
        0 /* size_reserve*/);
   MyASTConsumer astConsumer;

    clang::Sema sema(
        preprocessor,
        astContext,
        astConsumer);

    pTextDiagnosticPrinter->BeginSourceFile(languageOptions, &preprocessor);
    clang::ParseAST(preprocessor, &astConsumer, astContext); 
    pTextDiagnosticPrinter->EndSourceFile();
    return 0;
}
예제 #8
0
int main(int argc, const char **argv, char * const *envp)
{
    // Path
    void *MainAddr = (void*) (intptr_t) GetExecutablePath;
    llvm::sys::Path Path = GetExecutablePath(argv[0]);

    // DiagnosticOptions
    DiagnosticOptions diagnosticOptions;

    // DiagnosticClient
    TextDiagnosticPrinter *DiagClient = new TextDiagnosticPrinter(llvm::outs(), diagnosticOptions);

    // Diagnostic
    llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
    Diagnostic diagnostic(DiagID, DiagClient);


    //DiagnosticOptions DiagOpts;
    //llvm::IntrusiveRefCntPtr<Diagnostic> diagnostic = CompilerInstance::createDiagnostics(DiagOpts, argc, argv);

    // LangOptions
    LangOptions langOptions;
    langOptions.CPlusPlus = 1;
    langOptions.CPlusPlus0x = 1;
    langOptions.Microsoft = 1;
    langOptions.Bool = 1;
    langOptions.Exceptions = 1;
    langOptions.CXXExceptions = 1;
    langOptions.EmitAllDecls = 1;

    // FileManager
    FileSystemOptions fileSystemOptions;
    FileManager fileManager(fileSystemOptions);

    // SourceManager
    SourceManager sourceManager(diagnostic, fileManager);

    // HeadderSearch
    HeaderSearch headerSearch(fileManager);

    // TargetOptions
    TargetOptions targetOptions;
    targetOptions.Triple = llvm::sys::getHostTriple();

    // TargetInfo
    TargetInfo *pTargetInfo =
        TargetInfo::CreateTargetInfo(
            diagnostic,
            targetOptions);

    // HeaderSearchOptions
    HeaderSearchOptions headerSearchOptions;
    ApplyHeaderSearchOptions(
        headerSearch,
        headerSearchOptions,
        langOptions,
        pTargetInfo->getTriple());

    // Preprocessor
    Preprocessor preprocessor(
        diagnostic,
        langOptions,
        *pTargetInfo,
        sourceManager,
        headerSearch);

    // PreprocessorOptions
    PreprocessorOptions preprocessorOptions;
    preprocessorOptions.DetailedRecord = true;
    preprocessor.createPreprocessingRecord();

    // FrontendOptions
    FrontendOptions frontendOptions;


    // InitializePreprocessor
    InitializePreprocessor(
        preprocessor,
        preprocessorOptions,
        headerSearchOptions,
        frontendOptions);

    //preprocessor.SetCommentRetentionState(true, true);

    // Tutorial
    const FileEntry *pFile = fileManager.getFile(
                                 "test.cpp",
                                 true);
    sourceManager.createMainFileID(pFile);


    /*preprocessor.EnterMainSourceFile();
    Token Tok;
    do {
    	preprocessor.Lex(Tok);  // read one token
    	//if (context.diags.hasErrorOccurred())  // stop lexing/pp on error
    	//	break;
    	preprocessor.DumpToken(Tok);  // outputs to cerr
    	std::cerr << std::endl;
    } while (Tok.isNot(tok::eof));*/


    const TargetInfo &targetInfo = *pTargetInfo;

    IdentifierTable identifierTable(langOptions);
    SelectorTable selectorTable;

    Builtin::Context builtinContext(targetInfo);
    ASTContext astContext(
        langOptions,
        sourceManager,
        targetInfo,
        identifierTable,
        selectorTable,
        builtinContext,
        0 /* size_reserve*/);
    // ASTConsumer astConsumer;
    MyASTConsumer astConsumer;
    astConsumer.sourceManager = &sourceManager;
    astConsumer.html = new ClangDocHTML;
    astConsumer.html->astConsumer = &astConsumer;
    preprocessor.addPPCallbacks(astConsumer.html);
    preprocessor.AddCommentHandler(astConsumer.html);

    Sema sema(
        preprocessor,
        astContext,
        astConsumer);
    sema.Initialize();

    //MySemanticAnalisys mySema( preprocessor, astContext, astConsumer);

    //Parser parser( preprocessor, sema);
    //parser.ParseTranslationUnit();
    astConsumer.preprocessor = &sema.PP;
    ParseAST(preprocessor, &astConsumer, astContext);
    return 0;
}
예제 #9
0
int main(int argc, char * argv[])
{
    /*
     * Before starting main application, need to set 'QT_X11_NO_MITSHM=1'
     * to make the runtime work with IBM PPC machine.
     */
#if defined (Q_OS_LINUX)
    QByteArray val("1");
    qputenv("QT_X11_NO_MITSHM", val);
#endif

    // Create the QT application
    QApplication app(argc, argv);
    app.setQuitOnLastWindowClosed(false);

    // Setup the settings management
    QCoreApplication::setOrganizationName("pgadmin");
    QCoreApplication::setOrganizationDomain("pgadmin.org");
    QCoreApplication::setApplicationName(PGA_APP_NAME.toLower().replace(" ", ""));

#if QT_VERSION >= 0x050000
    // Set high DPI pixmap to display icons clear on Qt widget.
    QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif

    // Create a hash of the executable path so we can run copies side-by-side
    QString homeDir = QDir::homePath();
    unsigned long exeHash = sdbm((unsigned char *)argv[0]);

    // Create the address file, that will be used to store the appserver URL for this instance
    addrFileName = homeDir + (QString("/.%1.%2.addr").arg(PGA_APP_NAME).arg(exeHash)).remove(" ");
    QFile addrFile(addrFileName);

    // Create a system-wide semaphore keyed by app name, exe hash and the username
    // to ensure instances are unique to the user and path
    QString userName = qgetenv("USER"); // *nix
    if (userName.isEmpty())
        userName = qgetenv("USERNAME"); // Windows

    QString semaName = QString("%1-%2-%3-sema").arg(PGA_APP_NAME).arg(userName).arg(exeHash);
    QString shmemName = QString("%1-%2-%3-shmem").arg(PGA_APP_NAME).arg(userName).arg(exeHash);

    QSystemSemaphore sema(semaName, 1);
    sema.acquire();

#ifndef Q_OS_WIN32
    // We may need to clean up stale shmem segments on *nix. Attaching and detaching
    // should remove the segment if it is orphaned.
    QSharedMemory stale_shmem(shmemName);
    if (stale_shmem.attach())
        stale_shmem.detach();
#endif

    QSharedMemory shmem(shmemName);
    bool is_running;
    if (shmem.attach())
    {
        is_running = true;
    }
    else
    {
        shmem.create(1);
        is_running = false;
    }
    sema.release();

    QSettings settings;

    if (is_running){
        addrFile.open(QIODevice::ReadOnly | QIODevice::Text);
        QTextStream in(&addrFile);
        QString addr = in.readLine();

        QString cmd = settings.value("BrowserCommand").toString();

        if (!cmd.isEmpty())
        {
            cmd.replace("%URL%", addr);
            QProcess::startDetached(cmd);
        }
        else
        {
            if (!QDesktopServices::openUrl(addr))
            {
                QString error(QWidget::tr("Failed to open the system default web browser. Is one installed?."));
                QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);

                exit(1);
            }
        }

        return 0;
    }

    atexit(cleanup);

    // In windows and linux, it is required to set application level proxy
    // because socket bind logic to find free port gives socket creation error
    // when system proxy is configured. We are also setting
    // "setUseSystemConfiguration"=true to use the system proxy which will
    // override this application level proxy. As this bug is fixed in Qt 5.9 so
    // need to set application proxy for Qt version < 5.9.
    //
#if defined (Q_OS_WIN) && QT_VERSION <= 0x050800
    // Give dummy URL required to find proxy server configured in windows.
    QNetworkProxyQuery proxyQuery(QUrl("https://www.pgadmin.org"));
    QNetworkProxy l_proxy;
    QList<QNetworkProxy> listOfProxies = QNetworkProxyFactory::systemProxyForQuery(proxyQuery);

    if (listOfProxies.size())
    {
        l_proxy = listOfProxies[0];

        // If host name is not empty means proxy server is configured.
        if (!l_proxy.hostName().isEmpty()) {
            QNetworkProxy::setApplicationProxy(QNetworkProxy());
        }
    }
#endif

#if defined (Q_OS_LINUX) && QT_VERSION <= 0x050800
    QByteArray proxy_env;
    proxy_env = qgetenv("http_proxy");
    // If http_proxy environment is defined in linux then proxy server is configured.
    if (!proxy_env.isEmpty()) {
        QNetworkProxy::setApplicationProxy(QNetworkProxy());
    }
#endif

    // Display the spash screen
    QSplashScreen *splash = new QSplashScreen();
    splash->setPixmap(QPixmap(":/splash.png"));
    splash->show();
    app.processEvents(QEventLoop::AllEvents);

    quint16 port = 0L;

    // Find an unused port number. Essentially, we're just reserving one
    // here that Flask will use when we start up the server.
    // In order to use the socket, we need to free this socket ASAP.
    // Hence - putting this code in a code block so the scope of the socket
    // variable vanishes to make that socket available.
    {
#if QT_VERSION >= 0x050000
        QTcpSocket socket;

        #if QT_VERSION >= 0x050900
        socket.setProxy(QNetworkProxy::NoProxy);
        #endif

        socket.bind(0, QTcpSocket::ShareAddress);
#else
        QUdpSocket socket;
        socket.bind(0, QUdpSocket::ShareAddress);
#endif
        port = socket.localPort();
    }

    // Generate a random key to authenticate the client to the server
    QString key = QUuid::createUuid().toString();
    key = key.mid(1, key.length() - 2);

    // Generate the filename for the log
    logFileName = homeDir + (QString("/.%1.%2.log").arg(PGA_APP_NAME).arg(exeHash)).remove(" ");

    // Start the tray service
    TrayIcon *trayicon = new TrayIcon(logFileName);

    if (!trayicon->Init())
    {
        QString error = QString(QWidget::tr("An error occurred initialising the tray icon"));
        QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);

        exit(1);
    }

    // Fire up the webserver
    Server *server;

    bool done = false;

    while (done != true)
    {
        server = new Server(port, key, logFileName);

        if (!server->Init())
        {
            splash->finish(NULL);

            qDebug() << server->getError();

            QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError());
            QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);

            exit(1);
        }

        server->start();

        // This is a hack to give the server a chance to start and potentially fail. As
        // the Python interpreter is a synchronous call, we can't check for proper startup
        // easily in a more robust way - we have to rely on a clean startup not returning.
        // It should always fail pretty quickly, and take longer to start if it succeeds, so
        // we don't really get a visible delay here.
        delay(1000);

        // Any errors?
        if (server->isFinished() || server->getError().length() > 0)
        {
            splash->finish(NULL);

            qDebug() << server->getError();

            QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError());
            QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);

            // Allow the user to tweak the Python Path if needed
            bool ok;

            ConfigWindow *dlg = new ConfigWindow();
            dlg->setWindowTitle(QWidget::tr("Configuration"));
            dlg->setBrowserCommand(settings.value("BrowserCommand").toString());
            dlg->setPythonPath(settings.value("PythonPath").toString());
            dlg->setApplicationPath(settings.value("ApplicationPath").toString());
            dlg->setModal(true);
            ok = dlg->exec();

            QString browsercommand = dlg->getBrowserCommand();
            QString pythonpath = dlg->getPythonPath();
            QString applicationpath = dlg->getApplicationPath();

            if (ok)
            {
                settings.setValue("BrowserCommand", browsercommand);
                settings.setValue("PythonPath", pythonpath);
                settings.setValue("ApplicationPath", applicationpath);
                settings.sync();
            }
            else
            {
                exit(1);
            }

            delete server;
        }
        else
            done = true;
    }

    // Ensure the server gets cleaned up later
    QObject::connect(server, SIGNAL(finished()), server, SLOT(deleteLater()));

    // Generate the app server URL
    QString appServerUrl = QString("http://127.0.0.1:%1/?key=%2").arg(port).arg(key);

    // Read the server connection timeout from the registry or set the default timeout.
    int timeout = settings.value("ConnectionTimeout", 30).toInt();

    // Now the server should be up, we'll attempt to connect and get a response.
    // We'll retry in a loop a few time before aborting if necessary.

    QTime endTime = QTime::currentTime().addSecs(timeout);
    bool alive = false;

    while(QTime::currentTime() <= endTime)
    {
        alive = PingServer(QUrl(appServerUrl));

        if (alive)
        {
            break;
        }

        delay(200);
    }

    // Attempt to connect one more time in case of a long network timeout while looping
    if (!alive && !PingServer(QUrl(appServerUrl)))
    {
        splash->finish(NULL);
        QString error(QWidget::tr("The application server could not be contacted."));
        QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);

        exit(1);
    }

    // Stash the URL for any duplicate processes to open
    if (addrFile.open(QIODevice::WriteOnly))
    {
        addrFile.setPermissions(QFile::ReadOwner|QFile::WriteOwner);
        QTextStream out(&addrFile);
        out << appServerUrl << endl;
    }

    // Go!
    trayicon->setAppServerUrl(appServerUrl);
    // Enable the shutdown server menu as server started successfully.
    trayicon->enableShutdownMenu();

    QString cmd = settings.value("BrowserCommand").toString();

    if (!cmd.isEmpty())
    {
        cmd.replace("%URL%", appServerUrl);
        QProcess::startDetached(cmd);
    }
    else
    {
        if (!QDesktopServices::openUrl(appServerUrl))
        {
            QString error(QWidget::tr("Failed to open the system default web browser. Is one installed?."));
            QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);

            exit(1);
        }
    }

    QObject::connect(trayicon, SIGNAL(shutdownSignal(QUrl)), server, SLOT(shutdown(QUrl)));

    splash->finish(NULL);

    return app.exec();
}