Exemplo n.º 1
0
    def getProject(self):
        """
            Returns a dict with plenty of information about a certain
            GitLab repository, given in the form
        """
        result = self._project.attributes
        Any.requireIsDictNonEmpty(result)

        return result
Exemplo n.º 2
0
def _switchEnv_linuxIntelToARM(targetPlatform):
    from ToolBOSCore.Settings import ProcessEnv
    from ToolBOSCore.Settings import ToolBOSSettings

    Any.requireIsTextNonEmpty(targetPlatform)

    # source cross-compiler package if not already done

    bspMap = ToolBOSSettings.getConfigOption('BST_crossCompileBSPs')
    Any.requireIsDictNonEmpty(bspMap)

    neededBSP = bspMap[targetPlatform]
    Any.requireIsTextNonEmpty(neededBSP)
    ProcessEnv.source(neededBSP)

    # setup arguments which will be passed to CMake

    if targetPlatform is 'peakcan':

        fileName = os.path.join(FastScript.getEnv('TOOLBOSCORE_ROOT'),
                                'include/CMake/Peakcan-cross.cmake')

        Any.requireIsFileNonEmpty(fileName)

        FastScript.setEnv('TARGETOS', 'peakcan')
        FastScript.setEnv('TARGETARCH', 'peakcan')
        FastScript.setEnv('COMPILER', 'gcc')
        FastScript.setEnv('BST_CMAKE_OPTIONS',
                          '-DCMAKE_TOOLCHAIN_FILE=%s' % fileName)

    elif targetPlatform is 'phyboardwega':

        fileName = os.path.join(FastScript.getEnv('TOOLBOSCORE_ROOT'),
                                'include/CMake/phyBOARD-WEGA-cross.cmake')

        Any.requireIsFileNonEmpty(fileName)

        FastScript.setEnv('TARGETOS', 'phyboardwega')
        FastScript.setEnv('TARGETARCH', 'phyboardwega')
        FastScript.setEnv('COMPILER', 'gcc')
        FastScript.setEnv('BST_CMAKE_OPTIONS',
                          '-DCMAKE_TOOLCHAIN_FILE=%s' % fileName)

    else:

        fileName = os.path.join(FastScript.getEnv('TOOLBOSCORE_ROOT'),
                                'include/CMake/Linux-ARMv7-cross.cmake')

        Any.requireIsFileNonEmpty(fileName)

        FastScript.setEnv('TARGETOS', 'linux')
        FastScript.setEnv('TARGETARCH', 'armv7')
        FastScript.setEnv('COMPILER', 'gcc')
        FastScript.setEnv('BST_CMAKE_OPTIONS',
                          '-DCMAKE_TOOLCHAIN_FILE=%s' % fileName)
Exemplo n.º 3
0
def sourceWindowsBSP(msvc):
    """
        Loads the necessary ToolBOSPluginWindows so that Wine and MSVC
        will be found in PATH etc.
    """
    allBSPs = ToolBOSSettings.getConfigOption('BST_crossCompileBSPs')
    Any.requireIsDictNonEmpty(allBSPs)

    if msvc in (2010, 2012):
        canonicalPath = allBSPs['windows-amd64-vs2012']
    elif msvc == 2017:
        canonicalPath = allBSPs['windows-amd64-vs2017']
    else:
        raise RuntimeError('sourceWindowsBSP: unsupported MSVC version: %s' %
                           msvc)

    ProjectProperties.requireIsCanonicalPath(canonicalPath)

    ProcessEnv.source(canonicalPath)
Exemplo n.º 4
0
    def _showSummaryTable(self):
        Any.requireIsDictNonEmpty(self.results)
        Any.requireIsListNonEmpty(self.rulesOrdered)

        for ruleID in self.rulesOrdered:
            if ruleID not in self.rulesImplemented:
                continue

            if ruleID not in self.rulesToRun:
                continue

            (status, passed, failed, shortText) = self.results[ruleID]

            successRate = self._computeSuccessRate(ruleID)

            logging.info('%8s | %14s | %4s | %s', ruleID.ljust(6),
                         status.ljust(14), successRate, shortText)

        logging.info('')
Exemplo n.º 5
0
    def _readFiles(self):
        """
            Reads the config files into memory.

            The master-list of available config options is defined in
            the fallback list shipped with the application.
        """
        self._allSettings = {}

        # do in reverse order (starting from default), and update step-by-step
        # with the higher-priority settings
        order = self._getEvalOrder()
        order.reverse()

        for filePath in order:
            fileSettings = self._readFile(filePath)
            Any.requireIsDict(fileSettings)

            # merge settings into overall dict
            self._allSettings.update(fileSettings)

            if filePath is self._cwdFile:
                self._cwdSettings = fileSettings

            elif filePath is self._userFile:
                self._userSettings = fileSettings

            elif filePath is self._machineFile:
                self._machineSettings = fileSettings

            elif filePath is self._defaultFile:
                self._defaultSettings = fileSettings

            elif filePath in self._addFiles:
                logging.debug('merging default settings from: %s', filePath)
                self._defaultSettings.update(fileSettings)

            else:
                logging.warning('unexpected config file: %s', filePath)

        Any.requireIsDictNonEmpty(self._allSettings)
Exemplo n.º 6
0
def _switchEnv_linuxToWindows(targetPlatform):
    import logging

    from ToolBOSCore.Settings import ProcessEnv
    from ToolBOSCore.Settings import UserSetup
    from ToolBOSCore.Settings import ToolBOSSettings

    Any.requireIsTextNonEmpty(targetPlatform)

    def _set_msvc_2017_conf():
        UserSetup.ensureMSVCSetup(sdk, postfix='.' + targetPlatform)
        Any.requireMsg(FastScript.getEnv('WINEPREFIX'), '$WINEPREFIX not set')

        msvcBasePath = r'c:\BuildTools\VC'
        msvcToolsBasePath = r'{}\Tools\MSVC\14.13.26128'.format(msvcBasePath)
        msvcAuxiliaryBasePath = r'{}\Auxiliary\VS'.format(msvcBasePath)
        wkitBasePath = r'c:\Program Files\Windows Kits\10'
        wsdkBasePath = r'c:\Program Files\Microsoft SDKs\Windows\v10.0A'
        wkitVersion = '10.0.16299.0'
        cpu = 'x64' if targetArch == 'amd64' else 'x86'

        FastScript.setEnv('TARGETOS', 'windows')
        FastScript.setEnv('TARGETARCH', targetArch)
        FastScript.setEnv('COMPILER', 'vs2017')
        FastScript.setEnv('INCLUDE', r'{}\include'.format(msvcBasePath))
        FastScript.setEnv(
            'LIB',
            r'{0}\lib\{3};{0}\lib\onecore\{3};{1}\Lib\{4}\um\{3};{1}\Lib\{4}\ucrt\{3};{2}\Lib'
            .format(msvcToolsBasePath, wkitBasePath, wsdkBasePath, cpu,
                    wkitVersion))
        FastScript.setEnv(
            'CL',
            r'/I"{0}\UnitTest\include" /I"{0}\include" /I"{1}\atlmfc\include" /I"{1}\include" /I"{2}\Include\{3}\ucrt" /I"{2}\Include\{3}\um" /I"{2}\Include\{3}\shared"'
            .format(msvcAuxiliaryBasePath, msvcToolsBasePath, wkitBasePath,
                    wkitVersion))
        FastScript.setEnv(
            'CL_CMD',
            r'{0}\bin\Host{1}\{1}\cl.exe'.format(msvcToolsBasePath, cpu))
        FastScript.setEnv(
            'LINK_CMD',
            r'{0}\bin\Host{1}\{1}\link.exe'.format(msvcToolsBasePath, cpu))
        FastScript.setEnv('RC_CMD',
                          r'{0}\bin\{1}\rc.Exe'.format(wkitBasePath, cpu))
        FastScript.setEnv('MT_CMD',
                          r'{0}\bin\{1}\mt.Exe'.format(wkitBasePath, cpu))
        FastScript.setEnv(
            'DUMPBIN_CMD',
            r'{0}\bin\Host{1}\{1}\dumpbin.exe'.format(msvcToolsBasePath, cpu))
        FastScript.setEnv(
            'WindowsLibPath',
            r'{0}\UnionMetadata\{1};{0}\References\{1}'.format(
                wkitBasePath, wkitVersion))
        FastScript.setEnv(
            'LIBPATH',
            r'{0}\atlmfc\lib\{2};{0}\lib\{2};{0}\lib\{2}\store\references;{1}\UnionMetadata\{3};{1}\References\{3}'
            .format(msvcToolsBasePath, wkitBasePath, cpu, wkitVersion))

        if cpu == 'x86':
            compilerBasePath = r'{0}\bin\Hostx86\x86'.format(msvcToolsBasePath)
            compilerBasePath += r';{0}\bin\Hostx86\x86;{0}\bin\Hostx86\x64\1033;{0}\bin\Hostx86\x86\1033'.format(
                msvcToolsBasePath)
        elif cpu == 'x64':
            compilerBasePath = r'{0}\bin\Hostx64\x64'.format(msvcToolsBasePath)
            compilerBasePath += r';{0}\bin\Hostx64\x64\1033'.format(
                msvcToolsBasePath)

        FastScript.setEnv('Path', compilerBasePath)

    def _set_msvc_legacy_conf():
        if sdk == 2008:
            compilerSuite = 'msvc'
            pdkVersion = 'v6.1'
        else:
            compilerSuite = 'vs%d' % sdk
            pdkVersion = 'v7.1'

        UserSetup.ensureMSVCSetup(sdk, postfix='.' + targetPlatform)
        Any.requireMsg(FastScript.getEnv('WINEPREFIX'), '$WINEPREFIX not set')

        basePath = '''c:\\msvc-sdk\\''' + compilerSuite + '''\\'''
        compilerBasePath = basePath + '''VC\\'''
        pdkBasePath = '''c:\\msvc-sdk\\Windows\\''' + pdkVersion + '''\\'''
        compilerCrossPath = ''
        x64 = ''
        amd64 = ''

        if targetArch == 'amd64':
            compilerCrossPath = '''\\x86_amd64'''
            x64 = '''\\x64'''
            amd64 = '''\\amd64'''

        FastScript.setEnv('TARGETOS', 'windows')
        FastScript.setEnv('TARGETARCH', targetArch)
        FastScript.setEnv('COMPILER', compilerSuite)
        FastScript.setEnv(
            'Include',
            pdkBasePath + '''Include;''' + compilerBasePath + '''include''')
        FastScript.setEnv(
            'Lib', compilerBasePath + '''lib''' + amd64 + ''';''' +
            pdkBasePath + '''Lib''' + x64)
        FastScript.setEnv(
            'Path', compilerBasePath + '''bin''' + compilerCrossPath +
            ''';''' + compilerBasePath + '''bin''' + compilerCrossPath +
            '''\\1033;''' + compilerBasePath + '''bin;''' + basePath +
            '''Common7\\IDE;''' + pdkBasePath + '''Bin''')
        FastScript.setEnv(
            'CL_CMD',
            compilerBasePath + '''bin''' + compilerCrossPath + '''\\cl.exe''')
        FastScript.setEnv(
            'LINK_CMD', compilerBasePath + '''bin''' + compilerCrossPath +
            '''\\link.exe''')
        FastScript.setEnv('RC_CMD', pdkBasePath + '''Bin\\RC.Exe''')
        FastScript.setEnv('MT_CMD', pdkBasePath + '''Bin\\mt.exe''')
        FastScript.setEnv('DUMPBIN_CMD',
                          compilerBasePath + '''Bin\\dumpbin.exe''')

    tmp = re.match("^(\S+)-(\S+)-vs(\d+)$", targetPlatform)
    targetArch = tmp.group(2)
    sdk = int(tmp.group(3))

    Any.requireIsTextNonEmpty(targetArch)
    Any.requireIsIntNotZero(sdk)

    # source "ToolBOSPluginWindows" if not already done

    bspMap = ToolBOSSettings.getConfigOption('BST_crossCompileBSPs')
    Any.requireIsDictNonEmpty(bspMap)

    neededBSP = bspMap[targetPlatform]
    Any.requireIsTextNonEmpty(neededBSP)
    ProcessEnv.source(neededBSP)

    logging.debug('using wine from: %s', ProcessEnv.which('wine'))

    # setup Wine

    if not FastScript.getEnv('WINEDEBUG'):
        FastScript.setEnv('WINEDEBUG', '-all')

    if sdk == 2017:
        _set_msvc_2017_conf()
    else:
        _set_msvc_legacy_conf()

    # setup arguments which will be passed to CMake

    fileName = os.path.join(FastScript.getEnv('TOOLBOSCORE_ROOT'),
                            'include/CMake/Windows-WineMSVC.cmake')
    Any.requireIsFileNonEmpty(fileName)

    oldOptions = FastScript.getEnv('BST_CMAKE_OPTIONS')

    if oldOptions:
        newOptions = '-DCMAKE_TOOLCHAIN_FILE=%s %s' % (fileName, oldOptions)
    else:
        newOptions = '-DCMAKE_TOOLCHAIN_FILE=%s' % fileName
    FastScript.setEnv('BST_CMAKE_OPTIONS', newOptions)

    FastScript.unsetEnv('GLIBC_ALIAS')
    FastScript.unsetEnv('GLIBC_VERSION')
    def __init__(self, appName=None, parent=None):
        super(PreferencesDialog, self).__init__(parent)

        self._conf = ToolBOSConf()
        self._allData = self._conf.getConfigOptions()
        self._appName = appName
        self._userData = self._conf.getUserConfigOptions()
        self._labels = {}
        self._fields = {}
        self._revert = {}
        i = 0
        table = QWidget()
        layout = QGridLayout()

        Any.requireIsDictNonEmpty(self._allData)
        Any.requireIsDict(self._userData)

        for name, value in sorted(self._allData.items()):

            if appName is not None and not name.startswith(appName):
                continue

            if appName:
                label = QLabel(name.replace(appName + "_", ''))
            else:
                label = QLabel(name)

            field = QLineEdit(repr(value))
            revert = QPushButton()

            # by default long lines would be scrolled to see the end
            # of the text, however we want to see the beginning of
            # long settings
            field.setCursorPosition(0)

            # highlight user-modified entries
            changed = name in self._userData

            if changed:
                style = self._styleChanged
            else:
                style = self._styleUnchanged

            label.setStyleSheet(style)
            field.setStyleSheet(style)

            field.textChanged.connect(functools.partial(self._onChange, name))

            revert.setIcon(IconProvider.getIcon('edit-undo'))
            revert.setMaximumHeight(field.sizeHint().height())
            revert.pressed.connect(functools.partial(self._onRevert, name))
            revert.setToolTip('revert to default')
            revert.setEnabled(changed)

            layout.addWidget(label, i, 0)
            layout.addWidget(field, i, 1)
            layout.addWidget(revert, i, 2)

            self._labels[name] = label
            self._fields[name] = field
            self._revert[name] = revert

            i += 1

        table.setLayout(layout)

        scrollArea = QScrollArea()
        scrollArea.setWidget(table)
        scrollArea.setWidgetResizable(True)

        self._saveButton = QPushButton('&Save')
        self._quitButton = QPushButton('&Quit')

        self._submitLayout = QHBoxLayout()
        self._submitLayout.setContentsMargins(0, 0, 0, 0)
        self._submitLayout.addStretch(1)
        self._submitLayout.addWidget(self._saveButton)
        self._submitLayout.addWidget(self._quitButton)

        self._submitWidget = QWidget()
        self._submitWidget.setLayout(self._submitLayout)

        self._dialogLayout = QVBoxLayout()
        self._dialogLayout.addWidget(scrollArea)
        self._dialogLayout.addWidget(self._submitWidget)

        self.setLayout(self._dialogLayout)
        self.setWindowIcon(IconProvider.getIcon('ToolBOS'))
        self.setModal(True)

        if appName:
            self.setWindowTitle('%s Preferences' % appName)
        else:
            self.setWindowTitle('ToolBOS SDK Preferences')

        # hack: no appname means show full list --> resize geometry,
        #       with name we assume to get a small widget only --> auto-geometry
        if appName is None:
            screen = QApplication.desktop().screenGeometry()
            dialogWidth = screen.width() / 5 * 3
            dialogHeight = screen.height() / 5 * 3

            self.resize(dialogWidth, dialogHeight)
            self.move(screen.center() - self.rect().center())  # center

        self._saveButton.pressed.connect(self._onSave)
        self._quitButton.pressed.connect(self.close)
Exemplo n.º 8
0
    def __init__(self,
                 filepath,
                 isCPlusPlus,
                 langStd,
                 verbose=False,
                 includepaths=None,
                 defines=None,
                 args=None):
        """
        Creates a new CParser instance.
        The file is indexed at constructor time.
        :param filepath: the file to parse
        :param verbose: whether or not to print diagnostic information during parsing.
        :param includepaths: a list of include paths. All the basic include
                             paths must be specified, including at least:
                             - the most basic include path, on unix usually /usr/include
                             - the clang standard library include path. Distribution and clang version dependent.
                               On Ubuntu Precise 64 with clang 3.4 this is /usr/lib/clang/3.4/include
                             For C++, the C++ include paths are also required.
                             On Precise 64 these are:
                             - /usr/include/c++/4.6/
                             - /usr/include/c++/4.6/x86_64-linux-gnu/
        :param args: extra compiler flags
        """

        Any.requireIsFileNonEmpty(filepath)
        includepaths = includepaths or []
        systemIncludePaths = self._getSystemIncludePaths()
        defines = defines or []

        self.langStd = langStd
        self.isCPlusPlus = isCPlusPlus
        self.verbose = verbose

        try:
            self._index = cidx.Index.create()
        except cidx.LibclangError:

            hostPlatform = Platforms.getHostPlatform()
            Any.requireIsTextNonEmpty(hostPlatform)

            libs = ToolBOSConf.getConfigOption('clang_lib')
            Any.requireIsDictNonEmpty(libs)

            try:
                libPath = libs[hostPlatform]
            except KeyError:
                logging.error('unsupported platform: %s', hostPlatform)
                return

            Any.requireIsFileNonEmpty(libPath)
            cidx.Config.set_library_file(libPath)

            self._index = cidx.Index.create()

        self.filepath = filepath

        # build a list of include directory compile flags to pass to the parse method of the index.
        argsIncludeDirs = ["-I{}".format(d) for d in systemIncludePaths]
        completeArgs = argsIncludeDirs + (args or [])
        self.args = completeArgs

        translationUnit = self._index.parse(
            filepath,
            options=cidx.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD,
            args=completeArgs)

        self.translationUnit = translationUnit

        # call super with the translation unit cursor
        super(CParser, self).__init__(translationUnit.cursor)

        # macros are extracted separately as the clang C bindings - and thus
        # the python ones - do not provide helpers to extract the info we need.
        self._populateMacros(filepath,
                             list(systemIncludePaths) + list(includepaths),
                             defines, langStd)
Exemplo n.º 9
0
    def _onDepDetectorFinished(self):
        logging.debug(
            'dependency detection in progress (helper-process finished)')

        self._depDetectorData.flush()

        base64payload = self._depDetectorData.getvalue()
        base64payloadSize = len(base64payload)
        base64payloadType = type(base64payload)

        logging.debug('base64payload type: %s', base64payloadType)
        logging.debug('base64payload size: %d', base64payloadSize)

        if base64payloadSize == 0:
            logging.debug('no dependency data received')
            return

        if six.PY2:
            if not Any.isInstance(base64payload, unicode):
                logging.debug('received dependency data of unexpected type')
                logging.debug(
                    '(this could come from a ~/.bashrc which prints text)')

                return

        else:
            if not Any.isInstance(base64payload, bytes):
                logging.debug('received dependency data of unexpected type')
                logging.debug(
                    '(this could come from a ~/.bashrc which prints text)')

                return

        dillPayload = base64.b64decode(base64payload)
        dillPayloadSize = len(dillPayload)
        dillPayloadType = type(dillPayload)

        logging.debug('dillPayload type: %s', dillPayloadType)
        logging.debug('dillPayload size: %d', dillPayloadSize)

        data = dill.loads(dillPayload)
        Any.requireIsDictNonEmpty(data)

        Any.requireIsInstance(data['bstpkg_src'], BSTPackage.BSTSourcePackage)
        Any.requireIsInstance(data['bstpkg_global'],
                              BSTPackage.BSTGloballyInstalledPackage)
        Any.requireIsDict(data['installStatus'])
        Any.requireIsDict(data['installStatusLocal'])
        Any.requireIsDict(data['installStatusProxy'])
        Any.requireIsDict(data['installStatusGlobal'])

        self._bstpkg_src.depSet = data['bstpkg_src'].depSet
        self._bstpkg_src.depTree = data['bstpkg_src'].depTree
        self._bstpkg_global = data['bstpkg_global']

        try:
            self._bstpkg_global.open(self.getCanonicalPath())
        except AssertionError as details:
            logging.debug(details)

        self._installStatus = data['installStatus']
        self._installStatusLocal = data['installStatusLocal']
        self._installStatusProxy = data['installStatusProxy']
        self._installStatusGlobal = data['installStatusGlobal']

        logging.debug('depSet:     %s', self._bstpkg_src.depSet)
        logging.debug('depTree:    %s', self._bstpkg_src.depTree)
        logging.debug('revDepSet:  %s', self._bstpkg_global.revDepSet)
        logging.debug('revDepTree: %s', self._bstpkg_global.revDepTree)

        self.depsDetected.emit(True)

        # retrieving direct dependencies should work, consider an error if not

        try:
            Any.requireIsSet(self._bstpkg_src.depSet)
            Any.requireIsList(self._bstpkg_src.depTree)
        except AssertionError:
            self.depsDetected.emit(False)
            logging.error('unable to retrieve dependencies')

        # while for reverse dependencies it is significant if the package is
        # installed, yet

        if self._bstpkg_global.isInstalled():
            try:
                Any.requireIsSet(self._bstpkg_global.revDepSet)
                Any.requireIsList(self._bstpkg_global.revDepTree)
            except AssertionError:
                logging.error('unable to retrieve reverse dependencies')
        else:
            logging.debug('not globally installed --> no reverse dependencies')

        logging.debug('dependency detection finished')
Exemplo n.º 10
0
    def main( self ):
        self.app                    = QApplication( [] )

        self.app.setStyle( 'fusion' )

        self.window                 = QMainWindow()
        self.menuBar                = MenuBar.MenuBar( self.window )
        self.mainLayout             = QGridLayout()
        self.mainWidget             = QWidget()
        self.multiTermWidget        = TerminalWidget.MultiTermWidget()
        self.optionsWidget          = BuildOptionsWidget.BuildOptionsWidget()
        self.controlsLayout         = QHBoxLayout()
        self.controlsWidget         = QWidget()
        self.metaInfo               = MetaInfoWidget.MetaInfoWidget( self.model, self.controlsWidget )
        self.console                = ConsoleWidget.ConsoleWidget( self.controlsWidget )
        self.taskButtons            = TaskButtonsWidget.TaskButtonsWidget()
        self.platformCBs_natLayout  = QVBoxLayout()
        self.platformCBs_natWidget  = QGroupBox( 'build natively on' )
        self.platformCBs_xcmpLayout = QVBoxLayout()
        self.platformCBs_xcmpWidget = QGroupBox( 'cross-compile for' )
        self.rightPaneLayout        = QVBoxLayout()
        self.rightPaneWidget        = QWidget()
        self.runningProcesses       = 0


        self._seqTasksRun.connect( self._onSeqTasksRun )
        self._seqTasksFinished.connect( self._onSeqTasksFinished )

        # create an always existing terminal for localhost commands
        terminal = TerminalWidget.TerminalWidget( True, parent=self.multiTermWidget )
        terminal.setToolTip( 'localhost' )
        terminal.setWindowTitle('localhost')

        self.terminals[ 'localhost' ] = terminal
        self.multiTermWidget.addTerminal( terminal )

        BST_localPaths     = tuple( ToolBOSConf.getConfigOption( 'BST_localPaths' ) )
        localHostname      = socket.gethostname()
        sshPossible        = SSH.guessRemoteAccessIsPossible()
        sshToolTip         = 'Remote compilation not possible as SSH authorized keys are not configured'
        onLocaldiskToolTip = 'Remote compilation not possible as project is on local disc'
        projectOnLocaldisk = self.projectRoot.startswith( BST_localPaths )
        remoteCompilation  = sshPossible and not projectOnLocaldisk
        xcmpPlatforms      = []
        crossCompileHosts  = self._toolBOSConf.getConfigOption( 'BST_crossCompileHosts' )


        Any.requireIsDictNonEmpty( crossCompileHosts )

        for platformName, compileHost in crossCompileHosts.items():
            if compileHost:
                xcmpPlatforms.append( platformName )

        xcmpPlatforms.sort()

        # platform selection in right pane,
        # create terminals for all other platforms (hide disabled ones)
        nativePlatforms  = Platforms.getPlatformNames()
        defaultNative    = CrossCompilation.getNativeCompilationList()
        defaultXcmp      = CrossCompilation.getCrossCompilationList()

        for platform in nativePlatforms:
            checkbox = QCheckBox( platform )
            checkbox.setChecked( platform in defaultNative )
            checkbox.stateChanged.connect( self._onPlatformSelectionChange )
            compileHost = CrossCompilation.getNativeCompileHost( platform )
            natHost = 'Natively compile for "%s" on "%s"' % (platform, compileHost)
            checkbox.setToolTip( natHost )

            self.platformCBs_nat[ platform ] = checkbox
            self.platformCBs_natLayout.addWidget( checkbox )

            if remoteCompilation or compileHost == localHostname:
                checkbox.setEnabled( True )

            else:
                checkbox.setEnabled( False )
                checkbox.setChecked( False )
                if projectOnLocaldisk:
                    checkbox.setToolTip( onLocaldiskToolTip )

                elif not sshPossible:
                    checkbox.setToolTip( sshToolTip )


            try:
                compileHost = CrossCompilation.getNativeCompileHost( platform )

                if compileHost:
                    logging.debug( 'native compile-host for platform=%s: %s',
                                   platform, compileHost )

                    fullPlatformString = Platforms.getFullPlatformString( platform )

                    infoText = 'Console output for %s (%s)' % ( platform, fullPlatformString )

                    terminal = TerminalWidget.TerminalWidget( False, parent=self.multiTermWidget )
                    terminal.setHostname( compileHost )
                    terminal.setToolTip( infoText )
                    terminal.setWindowTitle( infoText )

                    terminal.isNative = True

                    terminal.hostChanged.connect( functools.partial(
                                                        self._onHostChange,
                                                        terminal ) )

                    terminal.closeRequest.connect( functools.partial(
                                                        self._closeTerminal,
                                                        terminal,
                                                        checkbox ) )

                    self.terminals[ 'nativ_' + platform ] = terminal

                else:
                    logging.debug( 'no native compile-host for platform=%s',
                                   platform )
                    checkbox.setEnabled( False )
                    checkbox.hide()         # skip non-working platforms

            except KeyError:
                logging.error( "No native compile-host for platform=%s",
                               platform )
                return False


        for platform in xcmpPlatforms:
            checkbox = QCheckBox( platform )
            compileHost = CrossCompilation.getCrossCompileHost(platform)
            xcmp = 'Cross-compile for "%s" on "%s"' % ( platform, compileHost )
            checkbox.setToolTip( xcmp )
            checkbox.setChecked( platform in defaultXcmp )
            checkbox.stateChanged.connect( self._onPlatformSelectionChange )

            self.platformCBs_xcmp[ platform ] = checkbox
            self.platformCBs_xcmpLayout.addWidget( checkbox )

            if remoteCompilation or compileHost == localHostname:
                checkbox.setEnabled( True )

            else:
                checkbox.setEnabled( False )
                checkbox.setChecked( False )
                if projectOnLocaldisk:
                    checkbox.setToolTip( onLocaldiskToolTip )

                elif not sshPossible:
                    checkbox.setToolTip( sshToolTip )

            try:
                compileHost = CrossCompilation.getCrossCompileHost( platform )

                if compileHost:
                    logging.debug( 'cross-compile host for platform=%s: %s',
                                   platform, compileHost )

                    fullPlatformString = Platforms.getFullPlatformString(platform)
                    infoText = 'Console output for %s (%s)' % ( platform, fullPlatformString )
                    terminal = TerminalWidget.TerminalWidget( False, parent=self.multiTermWidget )
                    terminal.setHostname( compileHost )
                    terminal.setToolTip( infoText )
                    terminal.setWindowTitle(infoText)

                    terminal.isNative = False

                    terminal.hostChanged.connect( functools.partial(
                                                        self._onHostChange,
                                                        terminal ) )

                    terminal.closeRequest.connect( functools.partial(
                                                        self._closeTerminal,
                                                        terminal,
                                                        checkbox ) )

                    self.terminals[ 'xcomp_' + platform ] = terminal

                else:
                    logging.debug( 'no cross-compile host for platform=%s',
                                   platform )
                    checkbox.setEnabled( False )
                    checkbox.hide()         # skip non-working platforms

            except KeyError:
                logging.error( "No cross-compile host for platform=%s",
                               platform )
                return False

            self.terminals[ 'xcomp_' + platform ] = terminal


        # do this once to get initial grid configuration right
        # (localhost + platform terminals)

        self._onPlatformSelectionChange()

        self.externalTools = ExternalToolsWidget.ExternalToolsWidget( self.model, self.window )

        self.model.updatesAvailable.connect( self.externalTools.showUpdateIndicator )

        # build options in right pane
        self.rightPaneLayout.setContentsMargins( 0, 0, 0, 0 )
        self.platformCBs_natWidget.setLayout( self.platformCBs_natLayout )
        self.platformCBs_xcmpWidget.setLayout( self.platformCBs_xcmpLayout )
        self.rightPaneWidget.setLayout( self.rightPaneLayout )
        self.rightPaneLayout.addWidget( self.externalTools )
        self.rightPaneLayout.addWidget( self.platformCBs_natWidget )
        self.rightPaneLayout.addWidget( self.platformCBs_xcmpWidget )
        self.rightPaneLayout.addWidget( self.optionsWidget )


        self.console.localCommand.connect( self._onLocalShellInput )
        self.console.remoteCommand.connect( self._onRemoteShellInput )

        self.controlsLayout.addWidget( self.metaInfo )
        self.controlsLayout.addWidget( self.console )

        # self.terminalWidget.setLayout( self.terminalLayout )
        self.controlsWidget.setLayout( self.controlsLayout )
        self.controlsLayout.setContentsMargins( 0, 0, 0, 0 )

        self.taskButtons.clean.connect( self.clean )
        self.taskButtons.build.connect( self.build )
        self.taskButtons.proxyInstall.connect( self.proxyInstall )
        self.taskButtons.globalInstall.connect(self.globalInstall)
        self.taskButtons.test.connect( self.test )
        self.taskButtons.quit.connect( self.quit )

        self.menuBar.clean.connect( self.clean )
        self.menuBar.build.connect( self.build )
        self.menuBar.proxyInstall.connect( self.proxyInstall )
        self.menuBar.globalInstall.connect(self.globalInstall)
        self.menuBar.test.connect( self.test )


        try:
            Any.requireIsDir( self.projectRoot )
            FastScript.changeDirectory( self.projectRoot  )
            BuildSystemTools.requireTopLevelDir()
            self.openPackage( self.projectRoot )

        except ( AssertionError, OSError, RuntimeError ):
            self.projectRoot = None
            self.taskButtons.setEnabled( False )


        # main window configuration
        self.mainLayout.setColumnStretch( 0, 5 )
        self.mainLayout.addWidget( self.multiTermWidget,  0, 0 )
        self.mainLayout.addWidget( self.rightPaneWidget, 0, 1 )
        self.mainLayout.addWidget( self.controlsWidget,  1, 0 )
        self.mainLayout.addWidget( self.taskButtons,   1, 1 )
        self.mainWidget.setLayout( self.mainLayout )

        screen = QDesktopWidget().availableGeometry()

        self.window.setWindowIcon( IconProvider.getIcon( 'ToolBOS' ) )
        self.window.setWindowTitle( 'BST.py (zen build mode)' )
        self.window.setMenuBar( self.menuBar )
        self.window.setCentralWidget( self.mainWidget )
        self.window.resize( screen.width() / 5 * 4, screen.height() / 5 * 4 )
        self.window.move( screen.center() - self.window.rect().center() )
        self.window.show()

        self.menuBar.openPackage.connect( self.openPackage )
        self.menuBar.quit.connect( self.quit )

        if not self.projectRoot:
            self.menuBar.fileOpen()

        self.app.aboutToQuit.connect( self.quit )

        return self.app.exec_()