示例#1
0
 def OnHelp(self, event=None):  #@UnusedVariable
     if u'alpha' in GetTestList():
         HelpDialog(self).ShowModal()
     else:
         # FIXME: files should be re-cached when Debreate upgraded to new version
         # TODO: trim unneeded text
         cached = False
         manual_cache = ConcatPaths(PATH_cache, u'manual')
         manual_index = ConcatPaths(manual_cache, u'index.html')
         if not os.path.isdir(manual_cache):
             os.makedirs(manual_cache)
         elif os.path.isfile(manual_index):
             cached = True
         url_manual = u'https://debreate.wordpress.com/manual/'
         # NOTE: use urllib.request.urlopen for Python 3
         manual_data = urllib.urlopen(url_manual)
         url_state = manual_data.getcode()
         if url_state == 200:
             # cache files
             if not cached:
                 self.progress = ProgressDialog(
                     self,
                     message=GT(u'Caching manual files'),
                     style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE)
                 self.Disable()
                 self.timer.Start(100)
                 Thread(self.__cacheManualFiles, (
                     url_manual,
                     manual_cache,
                     manual_index,
                 )).Start()
                 self.progress.ShowModal()
             manual_dialog = wx.Dialog(self,
                                       title=u'Debreate Manual',
                                       size=(800, 500),
                                       style=wx.DEFAULT_DIALOG_STYLE
                                       | wx.RESIZE_BORDER)
             manual = wx.html.HtmlWindow(manual_dialog)
             wx.Yield()
             if manual.LoadFile(manual_index):
                 manual_dialog.CenterOnParent()
                 manual_dialog.ShowModal()
             else:
                 wx.Yield()
                 webbrowser.open(url_manual)
             manual_dialog.Destroy()
         else:
             # open local document
             wx.Yield()
             subprocess.call(
                 [u'xdg-open', u'{}/docs/usage.pdf'.format(PATH_app)])
示例#2
0
    def AddPaths(self, dirs, fileCount=None, showDialog=False):
        target = self.GetTarget()

        if fileCount == None:
            fileCount = 0
            for D in dirs:
                for F in dirs[D]:
                    fileCount += 1

        progress = None

        Logger.Debug(__name__, u'Adding {} files ...'.format(fileCount))

        if showDialog:
            progress = ProgressDialog(GetMainWindow(),
                                      GT(u'Adding Files'),
                                      maximum=fileCount,
                                      style=PD_DEFAULT_STYLE | wx.PD_CAN_ABORT)
            progress.Show()

        completed = 0
        for D in sorted(dirs):
            for F in sorted(dirs[D]):
                if progress and progress.WasCancelled():
                    progress.Destroy()
                    return False

                if progress:
                    wx.Yield()
                    progress.Update(completed, GT(u'Adding file {}').format(F))

                self.lst_files.AddFile(F, D, target)

                completed += 1

        if progress:
            wx.Yield()
            progress.Update(completed)

            progress.Destroy()

        return True
示例#3
0
class MainWindow(wx.Frame, ModuleAccessCtrl):
    ## Constructor
    #
    #  \param pos
    #	<b><i>Integer tuple</i></b> or <b><i>wx.Point</i></b> instance indicating the screen position of the window
    #  \param size
    #	<b><i>Integer tuple</i></b> or <b><i>wx.Size</i></b> instance indicating the dimensions of the window
    def __init__(self, pos, size):
        wx.Frame.__init__(self, None, wx.ID_ANY, default_title, pos, size)
        ModuleAccessCtrl.__init__(self, __name__)

        self.timer = DebreateTimer(self)
        # placeholder for progress dialog
        self.progress = None

        self.Bind(wx.EVT_TIMER, self.__onTimerEvent)
        self.Bind(EVT_TIMER_STOP, self.__onTimerStop)

        # Make sure that this frame is set as the top window
        if not wx.GetApp().GetTopWindow() == self:
            Logger.Debug(__name__,
                         GT(u'Setting MainWindow instance as top window'))

            wx.GetApp().SetTopWindow(self)

        if DebugEnabled():
            self.SetTitle(u'{} ({})'.format(default_title, GT(u'debugging')))

        self.SetMinSize(wx.Size(640, 400))

        # ----- Set Titlebar Icon
        self.SetIcon(Icon(LOGO))

        # *** Status Bar *** #
        StatusBar(self)

        # *** Menus *** #
        createMenuBar(self)

        self.Wizard = Wizard(self)

        # *** Current Project Status *** #

        self.LoadedProject = None
        self.ProjectDirty = False

        # *** Event Handling *** #

        wx.EVT_MENU(self, menuid.NEW, self.OnProjectNew)
        wx.EVT_MENU(self, menuid.OPEN, self.OnProjectOpen)
        wx.EVT_MENU(self, menuid.SAVE, self.OnProjectSave)
        wx.EVT_MENU(self, menuid.SAVEAS, self.OnProjectSave)
        wx.EVT_MENU(self, menuid.QBUILD, self.OnQuickBuild)
        wx.EVT_MENU(self, menuid.EXIT, self.OnQuit)

        wx.EVT_MENU(self, menuid.TOOLTIPS, self.OnToggleToolTips)
        wx.EVT_MENU(self, menuid.DIST, self.OnUpdateDistNamesCache)

        wx.EVT_MENU(self, menuid.UPDATE, self.OnCheckUpdate)
        wx.EVT_MENU(self, menuid.HELP, self.OnHelp)
        wx.EVT_MENU(self, menuid.ABOUT, self.OnAbout)

        self.Bind(EVT_CHANGE_PAGE, self.OnWizardBtnPage)

        # Custom close event shows a dialog box to confirm quit
        wx.EVT_CLOSE(self, self.OnQuit)

        # *** Layout *** #

        lyt_main = BoxSizer(wx.VERTICAL)
        lyt_main.Add(self.Wizard, 1, wx.EXPAND)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()

    ## Retrieves menu by ID
    def GetMenu(self, menuId):
        return self.GetMenuBar().GetMenuById(menuId)

    ## Retrieves the Wizard instance
    #
    #  \return
    #		wiz.wizard.Wizard
    def GetWizard(self):
        return self.Wizard

    ## Sets the pages in the wiz.wizard.Wizard instance
    def InitWizard(self):
        self.Wizard.AddPage(PageInit(self.Wizard))
        self.Wizard.SetModeBin(0)

    ## TODO: Doxygen
    def IsNewProject(self):
        title = self.GetTitle()
        if title == default_title:
            return True

        else:
            return False

    ## TODO: Doxygen
    def IsSaved(self):
        title = self.GetTitle()
        if title[-1] == u'*':
            return False

        else:
            return True

    ## Opens a dialog box with information about the program
    def OnAbout(self, event=None):  #@UnusedVariable
        about = AboutDialog(self)

        about.SetGraphic(LOGO)
        about.SetVersion(VERSION_string)
        about.SetDescription(GT(u'A package builder for Debian based systems'))
        about.SetAuthor(AUTHOR_name)

        about.SetWebsites((
            (GT(u'Homepage'), APP_homepage),
            (GT(u'GitHub Project'), APP_project_gh),
            (GT(u'Sourceforge Project'), APP_project_sf),
        ))

        about.AddJobs(AUTHOR_name, (
            GT(u'Head Developer'),
            GT(u'Packager'),
            u'{} (es, it)'.format(GT(u'Translation')),
        ), AUTHOR_email)

        about.AddJobs(u'Hugo Posnic', (
            GT(u'Code Contributor'),
            GT(u'Website Designer & Author'),
        ), u'*****@*****.**')

        about.AddJob(u'Lander Usategui San Juan', GT(u'General Contributor'),
                     u'*****@*****.**')

        about.AddTranslator(
            u'Karim Oulad Chalha',
            u'*****@*****.**',
            u'ar',
        )
        about.AddTranslator(u'Philippe Dalet',
                            u'*****@*****.**', u'fr')
        about.AddTranslator(u'Zhmurkov Sergey', u'*****@*****.**', u'ru')

        about.AddJob(u'Benji Park', GT(u'Button Base Image Designer'))

        about.SetChangelog()

        about.SetLicense()

        about.ShowModal()
        about.Destroy()

    ## Checks for new release availability
    def OnCheckUpdate(self, event=None):  #@UnusedVariable
        update_test = u'update-fail' in GetTestList()

        if UsingDevelopmentVersion() and not update_test:
            DetailedMessageDialog(
                self,
                GT(u'Update'),
                text=GT(u'Update checking is disabled in development versions'
                        )).ShowModal()
            return

        wx.SafeYield()

        if update_test:
            # Set a bad url to force error
            current = GetCurrentVersion(u'http://dummyurl.blah/')

        else:
            current = GetCurrentVersion()

        Logger.Debug(__name__, GT(u'URL request result: {}').format(current))

        error_remote = GT(
            u'An error occurred attempting to contact remote website')

        if isinstance(current, (URLError, HTTPError)):
            current = GS(current)
            ShowErrorDialog(error_remote, current)

        elif isinstance(current, tuple) and current > VERSION_tuple:
            current = u'{}.{}.{}'.format(current[0], current[1], current[2])
            l1 = GT(u'Version {} is available!').format(current)
            l2 = GT(u'Would you like to go to Debreate\'s website?')
            if ConfirmationDialog(self, GT(u'Update'),
                                  u'{}\n\n{}'.format(l1, l2)).Confirmed():
                wx.LaunchDefaultBrowser(APP_homepage)

        elif isinstance(current, (unicode, str)):
            ShowErrorDialog(error_remote, current)

        else:
            DetailedMessageDialog(
                self, GT(u'Debreate'),
                text=GT(u'Debreate is up to date!')).ShowModal()

    def __cacheManualFiles(self, args):
        url_manual = args[0]
        manual_cache = args[1]
        manual_index = args[2]
        main_dir = os.getcwd()
        os.chdir(manual_cache)

        try:
            subprocess.Popen([
                u'wget', u'-rkp', u'-nd', u'-np', u'-H', u'-D',
                u'debreate.wordpress.com,antumdeluge.github.io', url_manual
            ]).communicate()
            # FIXME: use Python commands
            subprocess.Popen([
                u'sed', u'-i', u'-e', u's|<a.*>||g', u'-e', u's|</a>||g',
                manual_index
            ]).communicate()
        except:
            # FIXME: show error message
            pass

        os.chdir(main_dir)
        self.timer.Stop()

    ## Calls Pulse method on progress dialog when timer event occurs
    def __onTimerEvent(self, event=None):
        if self.progress:
            self.progress.Pulse()

    def __onTimerStop(self, event=None):
        if self.progress:
            self.progress.EndModal(0)
            self.progress = None

        if not self.IsEnabled():
            self.Enable()

        return not self.progress

    ## Action to take when 'Help' is selected from the help menu
    #
    #  Opens a help dialog if using 'alpha' test. Otherwise, opens
    #  PDF usage document. If openening usage document fails, attempts
    #  to open web browser in remote usage page.
    #  TODO: Use dialog as main method
    def OnHelp(self, event=None):  #@UnusedVariable
        if u'alpha' in GetTestList():
            HelpDialog(self).ShowModal()
        else:
            # FIXME: files should be re-cached when Debreate upgraded to new version
            # TODO: trim unneeded text
            cached = False
            manual_cache = ConcatPaths(PATH_cache, u'manual')
            manual_index = ConcatPaths(manual_cache, u'index.html')
            if not os.path.isdir(manual_cache):
                os.makedirs(manual_cache)
            elif os.path.isfile(manual_index):
                cached = True
            url_manual = u'https://debreate.wordpress.com/manual/'
            # NOTE: use urllib.request.urlopen for Python 3
            manual_data = urllib.urlopen(url_manual)
            url_state = manual_data.getcode()
            if url_state == 200:
                # cache files
                if not cached:
                    self.progress = ProgressDialog(
                        self,
                        message=GT(u'Caching manual files'),
                        style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE)
                    self.Disable()
                    self.timer.Start(100)
                    Thread(self.__cacheManualFiles, (
                        url_manual,
                        manual_cache,
                        manual_index,
                    )).Start()
                    self.progress.ShowModal()
                manual_dialog = wx.Dialog(self,
                                          title=u'Debreate Manual',
                                          size=(800, 500),
                                          style=wx.DEFAULT_DIALOG_STYLE
                                          | wx.RESIZE_BORDER)
                manual = wx.html.HtmlWindow(manual_dialog)
                wx.Yield()
                if manual.LoadFile(manual_index):
                    manual_dialog.CenterOnParent()
                    manual_dialog.ShowModal()
                else:
                    wx.Yield()
                    webbrowser.open(url_manual)
                manual_dialog.Destroy()
            else:
                # open local document
                wx.Yield()
                subprocess.call(
                    [u'xdg-open', u'{}/docs/usage.pdf'.format(PATH_app)])

    ## Opens the logs directory in the system's default file manager
    def OnLogDirOpen(self, event=None):  #@UnusedVariable
        Logger.Debug(__name__, GT(u'Opening log directory ...'))

        subprocess.check_output(
            [GetExecutable(u'xdg-open'), u'{}/logs'.format(PATH_local)],
            stderr=subprocess.STDOUT)

    ## Changes wizard page from menu
    def OnMenuChangePage(self, event=None):
        if isinstance(event, int):
            page_id = event

        else:
            page_id = event.GetId()

        self.Wizard.ShowPage(page_id)

    ## TODO: Doxygen
    def OnProjectNew(self, event=None):  #@UnusedVariable
        self.ResetPages()

    ## TODO: Doxygen
    def OnProjectOpen(self, event=None):  #@UnusedVariable
        projects_filter = u'|*.{};*.{}'.format(PROJECT_ext, PROJECT_txt)
        d = GT(u'Debreate project files')

        dia = wx.FileDialog(self, GT(u'Open Debreate Project'), os.getcwd(),
                            u'', u'{}{}'.format(d, projects_filter),
                            wx.FD_CHANGE_DIR)
        if dia.ShowModal() != wx.ID_OK:
            return

        # Get the path and set the saved project
        project = dia.GetPath()

        filename = os.path.basename(project)

        if self.OpenProject(filename):
            # Only set project open in memory if loaded completely
            self.LoadedProject = project

        else:
            self.LoadedProject = None

    ## TODO: Doxygen
    def OnProjectSave(self, event=None):
        event_id = event.GetId()

        def SaveIt(path):
            # Gather data from different pages
            data = (
                GetPage(pgid.CONTROL).GetSaveData(),
                GetPage(pgid.FILES).GetSaveData(),
                GetPage(pgid.SCRIPTS).GetSaveData(),
                GetPage(pgid.CHANGELOG).GetSaveData(),
                GetPage(pgid.COPYRIGHT).GetSaveData(),
                GetPage(pgid.MENU).GetSaveData(),
                GetPage(pgid.BUILD).GetSaveData(),
            )

            # Create a backup of the project
            overwrite = False
            if os.path.isfile(path):
                backup = u'{}.backup'.format(path)
                shutil.copy(path, backup)
                overwrite = True

            # This try statement can be removed when unicode support is enabled
            try:
                WriteFile(
                    path, u'[DEBREATE-{}]\n{}'.format(VERSION_string,
                                                      u'\n'.join(data)))

                if overwrite:
                    os.remove(backup)

            except UnicodeEncodeError:
                detail1 = GT(
                    u'Unfortunately Debreate does not support unicode yet.')
                detail2 = GT(
                    u'Remove any non-ASCII characters from your project.')

                ShowErrorDialog(GT(u'Save failed'),
                                u'{}\n{}'.format(detail1, detail2),
                                title=GT(u'Unicode Error'))

                if overwrite:
                    os.remove(path)
                    # Restore project backup
                    shutil.move(backup, path)

        def OnSaveAs():
            dbp = u'|*.dbp'
            d = GT(u'Debreate project files')
            dia = wx.FileDialog(
                self, GT(u'Save Debreate Project'), os.getcwd(), u'',
                u'{}{}'.format(d, dbp),
                wx.FD_SAVE | wx.FD_CHANGE_DIR | wx.FD_OVERWRITE_PROMPT)
            if dia.ShowModal() == wx.ID_OK:
                filename = dia.GetFilename()
                if filename.split(u'.')[-1] == u'dbp':
                    filename = u'.'.join(filename.split(u'.')[:-1])

                self.LoadedProject = u'{}/{}.dbp'.format(
                    os.path.split(dia.GetPath())[0], filename)

                SaveIt(self.LoadedProject)

        if event_id == wx.ID_SAVE:
            # Define what to do if save is pressed
            # If project already exists, don't show dialog
            if not self.IsSaved(
            ) or not self.LoadedProject or not os.path.isfile(
                    self.LoadedProject):
                OnSaveAs()

            else:
                SaveIt(self.LoadedProject)

        else:
            # If save as is press, show the save dialog
            OnSaveAs()

    ## TODO: Doxygen
    def OnQuickBuild(self, event=None):  #@UnusedVariable
        QB = QuickBuild(self)
        QB.ShowModal()
        QB.Destroy()

    ## Shows a dialog to confirm quit and write window settings to config file
    def OnQuit(self, event=None):  #@UnusedVariable
        if ConfirmationDialog(self,
                              GT(u'Quit?'),
                              text=GT(u'You will lose any unsaved information')
                              ).ShowModal() in (wx.ID_OK, wx.OK):

            maximized = self.IsMaximized()
            WriteConfig(u'maximize', maximized)

            if maximized:
                WriteConfig(u'position', GetDefaultConfigValue(u'position'))
                WriteConfig(u'size', GetDefaultConfigValue(u'size'))
                WriteConfig(u'center', True)

            else:
                WriteConfig(u'position', self.GetPositionTuple())
                WriteConfig(u'size', self.GetSizeTuple())
                WriteConfig(u'center', False)

            WriteConfig(u'workingdir', os.getcwd())

            self.Destroy()

    ## TODO: Doxygen
    def OnToggleTheme(self, event=None):  #@UnusedVariable
        self.ToggleTheme(self)

    ## Shows or hides tooltips
    def OnToggleToolTips(self, event=None):  #@UnusedVariable
        enabled = self.opt_tooltips.IsChecked()
        wx.ToolTip.Enable(enabled)

        WriteConfig(u'tooltips', enabled)

    ## Opens a dialog for creating/updating list of distribution names cache file
    def OnUpdateDistNamesCache(self, event=None):  #@UnusedVariable
        DistNamesCacheDialog().ShowModal()

    ## Updates the page menu to reflect current page
    def OnWizardBtnPage(self, event=None):  #@UnusedVariable
        ID = self.Wizard.GetCurrentPageId()
        Logger.Debug(__name__,
                     GT(u'Event: EVT_CHANGE_PAGE, Page ID: {}').format(ID))

        menu_page = self.GetMenu(menuid.PAGE)
        if not menu_page.IsChecked(ID):
            menu_page.Check(ID, True)

    ## Deletes cache directory located at ~/.local/share/debreate/cache
    def OnClearCache(self, event=None):
        if os.path.isdir(PATH_cache):
            shutil.rmtree(PATH_cache)

    ## Opens web links from the help menu
    def OpenPolicyManual(self, event=None):
        if isinstance(event, wx.CommandEvent):
            event_id = event.GetId()

        elif isinstance(event, int):
            event_id = event

        else:
            Logger.Error(
                __name__,
                u'Cannot open policy manual link with object type {}'.format(
                    type(event)))

            return

        url = self.menu_policy.GetHelpString(event_id)
        webbrowser.open(url)

    ## Retrieves filename of loaded project
    def ProjectGetLoaded(self):
        return self.LoadedProject

    ## Tests project type & calls correct method to read project file
    #
    #  \param project_file
    #	\b \e unicode|str : Path to project file
    def OpenProject(self, project_file):
        Logger.Debug(__name__, u'Opening project: {}'.format(project_file))

        if not os.path.isfile(project_file):
            ShowErrorDialog(
                GT(u'Could not open project file'),
                GT(u'File does not exist or is not a regular file: {}').format(
                    project_file))
            return False

        data = ReadFile(project_file)

        lines = data.split(u'\n')

        # FIXME: Need a better way to determine valid project
        app = lines[0].lstrip(u'[')
        if not app.startswith(u'DEBREATE'):
            ShowErrorDialog(
                GT(u'Could not open project file'),
                GT(u'Not a valid Debreate project: {}').format(project_file))
            return False

        if self.LoadedProject and not self.ResetPages():
            return False

        # *** Get Control Data *** #
        control_data = data.split(u'<<CTRL>>\n')[1].split(u'\n<</CTRL>>')[0]
        depends_data = self.Wizard.GetPage(pgid.CONTROL).Set(control_data)
        self.Wizard.GetPage(pgid.DEPENDS).Set(depends_data)

        # *** Get Files Data *** #
        files_data = data.split(u'<<FILES>>\n')[1].split(u'\n<</FILES>>')[0]
        opened = self.Wizard.GetPage(pgid.FILES).Set(files_data)

        # *** Get Scripts Data *** #
        scripts_data = data.split(u'<<SCRIPTS>>\n')[1].split(
            u'\n<</SCRIPTS>>')[0]
        self.Wizard.GetPage(pgid.SCRIPTS).Set(scripts_data)

        # *** Get Changelog Data *** #
        clog_data = data.split(u'<<CHANGELOG>>\n')[1].split(
            u'\n<</CHANGELOG>>')[0]
        self.Wizard.GetPage(pgid.CHANGELOG).Set(clog_data)

        # *** Get Copyright Data *** #
        try:
            cpright_data = data.split(u'<<COPYRIGHT>>\n')[1].split(
                u'\n<</COPYRIGHT')[0]
            self.Wizard.GetPage(pgid.COPYRIGHT).Set(cpright_data)

        except IndexError:
            pass

        # *** Get Menu Data *** #
        m_data = data.split(u'<<MENU>>\n')[1].split(u'\n<</MENU>>')[0]
        self.Wizard.GetPage(pgid.MENU).SetLauncherData(m_data, enabled=True)

        # Get Build Data
        build_data = data.split(u'<<BUILD>>\n')[1].split(u'\n<</BUILD')[
            0]  #.split(u'\n')
        self.Wizard.GetPage(pgid.BUILD).Set(build_data)

        return opened

    ## TODO: Doxygen
    def ProjectChanged(self, event=None):
        if DebugEnabled():
            Logger.Debug(__name__, u'MainWindow.OnProjectChanged:')
            print(u'  Object: {}'.format(event.GetEventObject()))

        self.ProjectDirty = True

    ## TODO: Doxygen
    def ResetPages(self):
        warn_msg = GT(u'You will lose any unsaved information.')
        warn_msg = u'{}\n\n{}'.format(warn_msg, GT(u'Continue?'))

        if ConfirmationDialog(self,
                              text=warn_msg).ShowModal() not in (wx.ID_OK,
                                                                 wx.OK):
            return False

        for page in self.Wizard.GetAllPages():
            page.Reset()

        self.SetTitle(default_title)

        # Reset the saved project field so we know that a project file doesn't exists
        self.LoadedProject = None

        return True

    ## TODO: Doxygen
    def SetSavedStatus(self, status):
        if status:  # If status is changing to unsaved this is True
            title = self.GetTitle()
            if self.IsSaved() and title != default_title:
                self.SetTitle(u'{}*'.format(title))

    ## TODO: Doxygen
    #
    #  TODO: Finish definition
    def ToggleTheme(self, window):
        for C in window.GetChildren():
            self.ToggleTheme(C)

        bg_color = window.GetBackgroundColour()
        fg_color = window.GetForegroundColour()

        window.SetBackgroundColour(fg_color)
        window.SetForegroundColour(bg_color)
示例#4
0
	def Set(self, data):
		# Clear files list
		self.lst_files.DeleteAllItems()
		files_data = data.split(u'\n')
		if int(files_data[0]):
			# Get file count from list minus first item "1"
			files_total = len(files_data)

			# Store missing files here
			missing_files = []

			progress = None

			if files_total >= efficiency_threshold:
				progress = ProgressDialog(GetMainWindow(), GT(u'Adding Files'), maximum=files_total,
						style=PD_DEFAULT_STYLE|wx.PD_CAN_ABORT)

				wx.Yield()
				progress.Show()

			current_file = files_total
			while current_file > 1:
				if progress and progress.WasCancelled():
					progress.Destroy()

					# Project continues opening even if file import is cancelled
					msg = (
						GT(u'File import did not complete.'),
						GT(u'Project files may be missing in file list.'),
						)

					ShowMessageDialog(u'\n'.join(msg), GT(u'Import Cancelled'))

					return False

				current_file -= 1
				executable = False

				file_info = files_data[current_file].split(u' -> ')
				absolute_filename = file_info[0]

				if absolute_filename[-1] == u'*':
					# Set executable flag and remove "*"
					executable = True
					absolute_filename = absolute_filename[:-1]

				filename = file_info[1]
				source_dir = absolute_filename[:len(absolute_filename) - len(filename)]
				target_dir = file_info[2]

				if not self.lst_files.AddFile(filename, source_dir, target_dir, executable):
					Logger.Warn(__name__, GT(u'File not found: {}').format(absolute_filename))
					missing_files.append(absolute_filename)

				if progress:
					update_value = files_total - current_file

					wx.Yield()
					progress.Update(update_value+1, GT(u'Imported file {} of {}').format(update_value, files_total))

			if progress:
				progress.Destroy()

			Logger.Debug(__name__, u'Missing file count: {}'.format(len(missing_files)))

			# If files are missing show a message
			if missing_files:
				alert = DetailedMessageDialog(GetMainWindow(), GT(u'Missing Files'),
						ICON_EXCLAMATION, GT(u'Could not locate the following files:'),
						u'\n'.join(missing_files))
				alert.ShowModal()

			return True
示例#5
0
	def LoadPaths(self, pathsList):
		if isinstance(pathsList, tuple):
			pathsList = list(pathsList)

		if not pathsList or not isinstance(pathsList, list):
			return False

		file_list = []
		dir_list = {}

		prep = ProgressDialog(GetMainWindow(), GT(u'Processing Files'), GT(u'Scanning files ...'),
				style=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)

		# Only update the gauge every N files (hack until I figure out timer)
		update_interval = 450
		count = 0

		prep.Show()

		if not self.chk_preserve_top.GetValue():
			for INDEX in reversed(range(len(pathsList))):
				path = pathsList[INDEX]
				if os.path.isdir(path):
					# Remove top-level directory from list
					pathsList.pop(INDEX)

					insert_index = INDEX
					for P in os.listdir(path):
						pathsList.insert(insert_index, ConcatPaths((path, P)))
						insert_index += 1

		try:
			for P in pathsList:
				if prep.WasCancelled():
					prep.Destroy()
					return False

				count += 1
				if count >= update_interval:
					wx.Yield()
					prep.Pulse()
					count = 0

				if not self.chk_individuals.GetValue() or os.path.isfile(P):
					file_list.append(P)
					continue

				if os.path.isdir(P):
					parent_dir = os.path.dirname(P)

					if parent_dir not in dir_list:
						dir_list[parent_dir] = []

					for ROOT, DIRS, FILES in os.walk(P):
						if prep.WasCancelled():
							prep.Destroy()
							return False

						wx.Yield()
						prep.SetMessage(GT(u'Scanning directory {} ...').format(ROOT))

						count += 1
						if count >= update_interval:
							wx.Yield()
							prep.Pulse()
							count = 0

						for F in FILES:
							if prep.WasCancelled():
								prep.Destroy()
								return False

							count += 1
							if count >= update_interval:
								wx.Yield()
								prep.Pulse()
								count = 0

							# os.path.dirname preserves top level directory
							ROOT = ROOT.replace(os.path.dirname(P), u'').strip(u'/')
							F = u'{}/{}'.format(ROOT, F).strip(u'/')

							if F not in dir_list[parent_dir]:
								dir_list[parent_dir].append(F)

		except:
			prep.Destroy()

			ShowErrorDialog(GT(u'Could not retrieve file list'), traceback.format_exc())

			return False

		wx.Yield()
		prep.Pulse(GT(u'Counting Files'))

		file_count = len(file_list)

		count = 0
		for D in dir_list:
			for F in dir_list[D]:
				file_count += 1

				count += 1
				if count >= update_interval:
					wx.Yield()
					prep.Pulse()
					count = 0

		prep.Destroy()

		# Add files to directory list
		for F in file_list:
			f_name = os.path.basename(F)
			f_dir = os.path.dirname(F)

			if f_dir not in dir_list:
				dir_list[f_dir] = []

			dir_list[f_dir].append(f_name)

		if file_count > warning_threshhold:
			count_warnmsg = GT(u'Importing {} files'.format(file_count))
			count_warnmsg = u'{}. {}.'.format(count_warnmsg, GT(u'This could take a VERY long time'))
			count_warnmsg = u'{}\n{}'.format(count_warnmsg, GT(u'Are you sure you want to continue?'))

			if not ConfirmationDialog(GetMainWindow(), text=count_warnmsg).Confirmed():
				return False

		return self.AddPaths(dir_list, file_count, showDialog=file_count >= efficiency_threshold)
示例#6
0
    def BuildPrep(self):
        wizard = GetWizard()
        prep_ids = []

        pages = wizard.GetAllPages()

        for P in pages:
            if P.prebuild_check:
                Logger.Debug(
                    __name__,
                    GT(u'Pre-build check for page "{}"'.format(P.GetName())))
                prep_ids.append(P.GetId())

        try:
            main_window = GetMainWindow()

            # List of page IDs to process during build
            pg_build_ids = []

            steps_count = len(prep_ids)
            current_step = 0

            msg_label1 = GT(u'Prepping page "{}"')
            msg_label2 = GT(u'Step {}/{}')
            msg_label = u'{} ({})'.format(msg_label1, msg_label2)

            prep_progress = ProgressDialog(
                main_window,
                GT(u'Preparing Build'),
                msg_label2.format(current_step, steps_count),
                maximum=steps_count,
                style=PD_DEFAULT_STYLE | wx.PD_CAN_ABORT)

            for P in pages:
                if prep_progress.WasCancelled():
                    break

                p_id = P.GetId()
                p_label = P.GetTitle()

                if p_id in prep_ids:
                    Logger.Debug(
                        __name__,
                        msg_label.format(p_label, current_step + 1,
                                         steps_count))

                    wx.Yield()
                    prep_progress.Update(
                        current_step,
                        msg_label.format(p_label, current_step + 1,
                                         steps_count))

                    if P.IsOkay():
                        pg_build_ids.append(p_id)

                    current_step += 1

            if not prep_progress.WasCancelled():
                wx.Yield()
                prep_progress.Update(current_step, GT(u'Prepping finished'))

                # Show finished dialog for short period
                time.sleep(1)

            prep_progress.Destroy()

            return pg_build_ids

        except:
            prep_progress.Destroy()

            ShowErrorDialog(GT(u'Error occurred during pre-build'),
                            traceback.format_exc())

        return None
示例#7
0
    def Build(self, outFile):
        def log_message(msg, current_step, total_steps):
            return u'{} ({}/{})'.format(msg, current_step, total_steps)

        wizard = GetWizard()
        pages_build_ids = self.BuildPrep()

        if pages_build_ids != None:
            main_window = GetMainWindow()

            # Reported at the end of build
            build_summary = []

            steps_count = len(pages_build_ids)
            current_step = 0

            # Steps from build page
            for chk in self.chk_md5, self.chk_lint, self.chk_rmstage:
                if chk.IsChecked():
                    steps_count += 1

            # Control file & .deb build step
            steps_count += 2

            stage = CreateStage()

            log_msg = GT(u'Starting build')

            wx.YieldIfNeeded()
            # FIXME: Enable PD_CAN_ABORT
            build_progress = ProgressDialog(main_window,
                                            GT(u'Building'),
                                            log_msg,
                                            maximum=steps_count)

            build_summary.append(u'{}:'.format(log_msg))

            try:
                for P in wizard.GetAllPages():
                    if build_progress.WasCancelled():
                        break

                    if P.GetId() in pages_build_ids:
                        p_label = P.GetTitle()

                        log_msg = log_message(
                            GT(u'Processing page "{}"').format(p_label),
                            current_step + 1, steps_count)

                        # FIXME: Progress bar not updating???
                        wx.YieldIfNeeded()
                        build_progress.Update(current_step, log_msg)

                        ret_code, ret_value = P.ExportBuild(stage)

                        build_summary.append(u'\n{}:\n{}'.format(
                            log_msg, ret_value))

                        if ret_code > 0:
                            build_progress.Destroy()

                            ShowErrorDialog(GT(u'Error occurred during build'),
                                            ret_value)

                            return

                        current_step += 1

                # *** Control File *** #
                if not build_progress.WasCancelled():
                    wx.YieldIfNeeded()

                    log_msg = log_message(GT(u'Creating control file'),
                                          current_step + 1, steps_count)
                    build_progress.Update(current_step, log_msg)

                    Logger.Debug(__name__, log_msg)

                    # Retrieve control page
                    pg_control = wizard.GetPage(pgid.CONTROL)
                    if not pg_control:
                        build_progress.Destroy()

                        ShowErrorDialog(
                            GT(u'Could not retrieve control page'),
                            GT(u'Please contact the developer: {}').format(
                                AUTHOR_email),
                            title=u'Fatal Error')

                        return

                    installed_size = self.OnBuildGetInstallSize(stage)

                    Logger.Debug(
                        __name__,
                        GT(u'Installed size: {}').format(installed_size))

                    build_summary.append(u'\n{}:'.format(log_msg))
                    build_summary.append(
                        pg_control.ExportBuild(
                            u'{}/DEBIAN'.format(stage).replace(u'//', u'/'),
                            installed_size))

                    current_step += 1

                # *** MD5 Checksum *** #
                if not build_progress.WasCancelled():
                    if self.chk_md5.GetValue() and GetExecutable(u'md5sum'):
                        log_msg = log_message(GT(u'Creating MD5 checksum'),
                                              current_step + 1, steps_count)
                        #log_msg = GT(u'Creating MD5 checksum')
                        #step = u'{}/{}'.format(current_step+1, steps_count)

                        Logger.Debug(__name__, log_msg)

                        wx.YieldIfNeeded()
                        build_progress.Update(current_step, log_msg)

                        build_summary.append(u'\n{}:'.format(log_msg))
                        build_summary.append(self.OnBuildMD5Sum(stage))

                        current_step += 1

                # *** Create .deb from Stage *** #
                if not build_progress.WasCancelled():
                    log_msg = log_message(GT(u'Creating .deb package'),
                                          current_step + 1, steps_count)

                    wx.YieldIfNeeded()
                    build_progress.Update(current_step, log_msg)

                    build_summary.append(u'\n{}:'.format(log_msg))
                    build_summary.append(
                        self.OnBuildCreatePackage(stage, outFile))

                    current_step += 1

                # *** Lintian *** #
                if not build_progress.WasCancelled():
                    if self.chk_lint.IsChecked():
                        log_msg = log_message(
                            GT(u'Checking package with lintian'),
                            current_step + 1, steps_count)

                        wx.YieldIfNeeded()
                        build_progress.Update(current_step, log_msg)

                        build_summary.append(u'\n{}:'.format(log_msg))
                        build_summary.append(self.OnBuildCheckPackage(outFile))

                        current_step += 1

                # *** Delete Stage *** #
                if not build_progress.WasCancelled():
                    if self.chk_rmstage.IsChecked():
                        log_msg = log_message(
                            GT(u'Removing staged build tree'),
                            current_step + 1, steps_count)

                        wx.YieldIfNeeded()
                        build_progress.Update(current_step, log_msg)

                        build_summary.append(u'\n{}:'.format(log_msg))
                        RemoveStage(stage)

                        if not os.path.isdir(stage):
                            build_summary.append(
                                GT(u'Staged build tree removed successfully'))

                        else:
                            build_summary.append(
                                GT(u'Failed to remove staged build tree'))

                        current_step += 1

                # *** Show Completion Status *** #
                wx.YieldIfNeeded()
                build_progress.Update(steps_count, GT(u'Build completed'))

                # Show finished dialog for short moment
                time.sleep(1)

                # TODO: Add error count to build summary

                build_progress.Destroy()

                build_summary = u'\n'.join(build_summary)
                summary_dialog = DetailedMessageDialog(main_window,
                                                       GT(u'Build Summary'),
                                                       ICON_INFORMATION,
                                                       GT(u'Build completed'),
                                                       build_summary)
                summary_dialog.ShowModal()

            except:
                build_progress.Destroy()

                ShowErrorDialog(GT(u'Error occurred during build'),
                                traceback.format_exc())

        return
示例#8
0
class DistNamesCacheDialog(BaseDialog):
    def __init__(self):
        BaseDialog.__init__(self,
                            title=GT(u'Update Dist Names Cache'),
                            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)

        self.SetMinSize(wx.Size(300, 150))

        txt_types = wx.StaticText(self, label=GT(u'Include the following:'))

        pnl_types = BorderedPanel(self)

        self.chk_unstable = wx.CheckBox(pnl_types, label=GT(u'Unstable'))
        self.chk_obsolete = wx.CheckBox(pnl_types, label=GT(u'Obsolete'))
        self.chk_generic = wx.CheckBox(
            pnl_types, label=GT(u'Generic (Debian names only)'))

        self.btn_preview = wx.Button(self, label=GT(u'Preview cache'))
        btn_update = wx.Button(self, label=GT(u'Update cache'))
        btn_clear = wx.Button(self, label=GT(u'Clear cache'))

        # Keep preview dialog in memory so position/size is saved
        self.preview = TextPreview(self,
                                   title=GT(u'Available Distribution Names'),
                                   size=(500, 400))

        # Is instantiated as ProgressDialog when OnUpdateCache is called
        self.progress = None
        self.timer = DebreateTimer(self)

        # For setting error messages from other threads
        self.error_message = None

        # *** Event Handling *** #

        self.btn_preview.Bind(wx.EVT_BUTTON, self.OnPreviewCache)
        btn_update.Bind(wx.EVT_BUTTON, self.OnUpdateCache)
        btn_clear.Bind(wx.EVT_BUTTON, self.OnClearCache)

        self.Bind(wx.EVT_TIMER, self.OnTimerEvent)
        self.Bind(EVT_TIMER_STOP, self.OnTimerStop)

        # *** Layout *** #

        lyt_types = BoxSizer(wx.VERTICAL)
        lyt_types.AddSpacer(5)

        for CHK in (
                self.chk_unstable,
                self.chk_obsolete,
                self.chk_generic,
        ):
            lyt_types.Add(CHK, 0, lyt.PAD_LR, 5)

        lyt_types.AddSpacer(5)

        pnl_types.SetAutoLayout(True)
        pnl_types.SetSizerAndFit(lyt_types)
        pnl_types.Layout()

        lyt_buttons = BoxSizer(wx.HORIZONTAL)
        lyt_buttons.Add(self.btn_preview, 1)
        lyt_buttons.Add(btn_update, 1)
        lyt_buttons.Add(btn_clear, 1)

        lyt_main = BoxSizer(wx.VERTICAL)
        lyt_main.Add(txt_types, 0, wx.ALIGN_CENTER | lyt.PAD_LRT, 5)
        lyt_main.Add(pnl_types, 0, wx.ALIGN_CENTER | lyt.PAD_LR, 5)
        lyt_main.Add(lyt_buttons, 1, wx.ALIGN_CENTER | wx.ALL, 5)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()

        # *** Post-layout Actions *** #

        if not os.path.isfile(FILE_distnames):
            self.btn_preview.Disable()

        if self.Parent:
            self.CenterOnParent()

    ## Checks for present error message & displays dialog
    #
    #  \return
    #	\b \e False if no errors present
    def CheckErrors(self):
        if self.error_message:
            ShowErrorDialog(self.error_message, parent=self, linewrap=410)

            # Clear error message & return
            self.error_message = None
            return True

        return False

    ## Deletes the distribution names cache file
    def OnClearCache(self, event=None):
        if os.path.isfile(FILE_distnames):
            os.remove(FILE_distnames)

            # Update list on changelog page
            distname_input = GetField(pgid.CHANGELOG, inputid.DIST)
            if isinstance(distname_input, OwnerDrawnComboBox):
                distname_input.Set(GetOSDistNames())

            self.btn_preview.Disable()

        cache_exists = os.path.exists(FILE_distnames)

        self.btn_preview.Enable(cache_exists)
        return not cache_exists

    ## Opens cache file for previewing
    def OnPreviewCache(self, event=None):
        self.preview.SetValue(ReadFile(FILE_distnames))
        self.preview.ShowModal()

    ## Calls Pulse method on progress dialog when timer event occurs
    def OnTimerEvent(self, event=None):
        if self.progress:
            self.progress.Pulse()

    ## Closes & resets the progress dialog to None when timer stops
    def OnTimerStop(self, event=None):
        if self.progress:
            self.progress.EndModal(0)
            self.progress = None

        return not self.progress

    ## Creates/Updates the distribution names cache file
    def OnUpdateCache(self, event=None):
        try:
            # Timer must be started before executing new thread
            self.timer.Start(100)

            if not self.timer.IsRunning():
                self.error_message = GT(
                    u'Could not start progress dialog timer')
                self.CheckErrors()
                return False

            self.Disable()

            # Start new thread for updating cache in background
            Thread(self.UpdateCache, None).Start()

            # Create the progress dialog & start timer
            # NOTE: Progress dialog is reset by timer stop event
            self.progress = ProgressDialog(
                self,
                message=GT(u'Contacting remote sites'),
                style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE)

            # Use ShowModal to wait for timer to stop before continuing
            self.progress.ShowModal()

            self.Enable()

            if self.CheckErrors():
                return False

            # FIXME: Should check timestamps to make sure file was updated
            cache_updated = os.path.isfile(FILE_distnames)

            if cache_updated:
                distname_input = GetField(pgid.CHANGELOG, inputid.DIST)

                if isinstance(distname_input, OwnerDrawnComboBox):
                    distname_input.Set(GetOSDistNames())

                else:
                    ShowMessageDialog(GT(
                        u'The distribution names cache has been updated but Debreate needs to restart to reflect the changes on the changelog page'
                    ),
                                      parent=self,
                                      linewrap=410)

            self.btn_preview.Enable(cache_updated)

            return cache_updated

        except:
            # Make sure dialog is re-enabled
            self.Enable()

            # Make sure progress dialog & background thread instances are reset to None
            if self.progress:
                self.progress.EndModal(0)
                self.progress = None

            cache_exists = os.path.isfile(FILE_distnames)

            err_msg = GT(
                u'An error occurred when trying to update the distribution names cache'
            )
            err_msg2 = GT(
                u'The cache file exists but may not have been updated')
            if cache_exists:
                err_msg = u'{}\n\n{}'.format(err_msg, err_msg2)

            ShowErrorDialog(err_msg, traceback.format_exc(), self)

            self.btn_preview.Enable(cache_exists)

        return False

    ## Method that does the actual updating of the names cache list
    #
    #  Called from a new thread
    #  FIXME: Show error if could not contact 1 or more remote sites???
    def UpdateCache(self, args=None):
        Logger.Debug(__name__, GT(u'Updating cache ...'))

        UpdateDistNamesCache(self.chk_unstable.GetValue(),
                             self.chk_obsolete.GetValue(),
                             self.chk_generic.GetValue())

        self.timer.Stop()
示例#9
0
    def OnUpdateCache(self, event=None):
        try:
            # Timer must be started before executing new thread
            self.timer.Start(100)

            if not self.timer.IsRunning():
                self.error_message = GT(
                    u'Could not start progress dialog timer')
                self.CheckErrors()
                return False

            self.Disable()

            # Start new thread for updating cache in background
            Thread(self.UpdateCache, None).Start()

            # Create the progress dialog & start timer
            # NOTE: Progress dialog is reset by timer stop event
            self.progress = ProgressDialog(
                self,
                message=GT(u'Contacting remote sites'),
                style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE)

            # Use ShowModal to wait for timer to stop before continuing
            self.progress.ShowModal()

            self.Enable()

            if self.CheckErrors():
                return False

            # FIXME: Should check timestamps to make sure file was updated
            cache_updated = os.path.isfile(FILE_distnames)

            if cache_updated:
                distname_input = GetField(pgid.CHANGELOG, inputid.DIST)

                if isinstance(distname_input, OwnerDrawnComboBox):
                    distname_input.Set(GetOSDistNames())

                else:
                    ShowMessageDialog(GT(
                        u'The distribution names cache has been updated but Debreate needs to restart to reflect the changes on the changelog page'
                    ),
                                      parent=self,
                                      linewrap=410)

            self.btn_preview.Enable(cache_updated)

            return cache_updated

        except:
            # Make sure dialog is re-enabled
            self.Enable()

            # Make sure progress dialog & background thread instances are reset to None
            if self.progress:
                self.progress.EndModal(0)
                self.progress = None

            cache_exists = os.path.isfile(FILE_distnames)

            err_msg = GT(
                u'An error occurred when trying to update the distribution names cache'
            )
            err_msg2 = GT(
                u'The cache file exists but may not have been updated')
            if cache_exists:
                err_msg = u'{}\n\n{}'.format(err_msg, err_msg2)

            ShowErrorDialog(err_msg, traceback.format_exc(), self)

            self.btn_preview.Enable(cache_exists)

        return False
示例#10
0
    def BuildPrep(self):
        # Declare these here in case of error before dialogs created
        save_dia = None
        prebuild_progress = None

        try:
            # List of tasks for build process
            # 'stage' should be very first task
            task_list = {}

            # Control page
            pg_control = GetPage(pgid.CONTROL)
            fld_package = GetField(pg_control, inputid.PACKAGE)
            fld_version = GetField(pg_control, inputid.VERSION)
            fld_maint = GetField(pg_control, inputid.MAINTAINER)
            fld_email = GetField(pg_control, inputid.EMAIL)
            fields_control = (
                fld_package,
                fld_version,
                fld_maint,
                fld_email,
            )

            # Menu launcher page
            pg_launcher = GetPage(pgid.MENU)

            # Check to make sure that all required fields have values
            required = list(fields_control)

            if pg_launcher.IsOkay():
                task_list[u'launcher'] = pg_launcher.Get()

                required.append(GetField(pg_launcher, inputid.NAME))

                if not GetField(pg_launcher, chkid.FNAME).GetValue():
                    required.append(GetField(pg_launcher, inputid.FNAME))

            for item in required:
                if TextIsEmpty(item.GetValue()):
                    field_name = GT(item.GetName().title())
                    page_name = pg_control.GetName()
                    if item not in fields_control:
                        page_name = pg_launcher.GetName()

                    return (dbrerrno.FEMPTY,
                            u'{} ➜ {}'.format(page_name, field_name))

            # Get information from control page for default filename
            package = fld_package.GetValue()
            # Remove whitespace
            package = package.strip(u' \t')
            package = u'-'.join(package.split(u' '))

            version = fld_version.GetValue()
            # Remove whitespace
            version = version.strip(u' \t')
            version = u''.join(version.split())

            arch = GetField(pg_control, inputid.ARCH).GetStringSelection()

            # Dialog for save destination
            ttype = GT(u'Debian packages')
            save_dia = wx.FileDialog(
                self, GT(u'Save'), os.getcwd(), wx.EmptyString,
                u'{}|*.deb'.format(ttype),
                wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT | wx.FD_CHANGE_DIR)
            save_dia.SetFilename(u'{}_{}_{}.deb'.format(
                package, version, arch))
            if not save_dia.ShowModal() == wx.ID_OK:
                return (dbrerrno.ECNCLD, None)

            build_path = os.path.split(save_dia.GetPath())[0]
            filename = os.path.split(save_dia.GetPath())[1].split(u'.deb')[0]

            # Control, menu, & build pages not added to this list
            page_checks = (
                (pgid.FILES, u'files'),
                (pgid.SCRIPTS, u'scripts'),
                (pgid.CHANGELOG, u'changelog'),
                (pgid.COPYRIGHT, u'copyright'),
            )

            # Install step is not added to this list
            # 'control' should be after 'md5sums'
            # 'build' should be after 'control'
            other_checks = (
                (self.chk_md5, u'md5sums'),
                (self.chk_strip, u'strip'),
                (self.chk_rmstage, u'rmstage'),
                (self.chk_lint, u'lintian'),
            )

            prep_task_count = len(page_checks) + len(other_checks)

            progress = 0

            wx.Yield()
            prebuild_progress = ProgressDialog(GetMainWindow(),
                                               GT(u'Preparing to build'),
                                               maximum=prep_task_count)

            if wx.MAJOR_VERSION < 3:
                # Resize dialog for better fit
                pb_size = prebuild_progress.GetSizeTuple()
                pb_size = (pb_size[0] + 200, pb_size[1])
                prebuild_progress.SetSize(pb_size)
                prebuild_progress.CenterOnParent()

            for PID, id_string in page_checks:
                wx.Yield()
                prebuild_progress.Update(progress,
                                         GT(u'Checking {}').format(id_string))

                wizard_page = GetPage(PID)
                if wizard_page.IsOkay():
                    task_list[id_string] = wizard_page.Get()

                progress += 1

            for task_check, id_string in other_checks:
                wx.Yield()
                prebuild_progress.Update(
                    progress,
                    GT(u'Testing for: {}').format(task_check.GetLabel()))

                if task_check.GetValue():
                    task_list[id_string] = None

                progress += 1

            # Close progress dialog
            wx.Yield()
            prebuild_progress.Update(progress)
            prebuild_progress.Destroy()

            return (dbrerrno.SUCCESS, (task_list, build_path, filename))

        except:
            if save_dia:
                save_dia.Destroy()

            if prebuild_progress:
                prebuild_progress.Destroy()

            return (dbrerrno.EUNKNOWN, traceback.format_exc())
示例#11
0
    def Build(self, task_list, build_path, filename):
        # Declare this here in case of error before progress dialog created
        build_progress = None

        try:
            # Other mandatory tasks that will be processed
            mandatory_tasks = (
                u'stage',
                u'install_size',
                u'control',
                u'build',
            )

            # Add other mandatory tasks
            for T in mandatory_tasks:
                task_list[T] = None

            task_count = len(task_list)

            # Add each file for updating progress dialog
            if u'files' in task_list:
                task_count += len(task_list[u'files'])

            # Add each script for updating progress dialog
            if u'scripts' in task_list:
                task_count += len(task_list[u'scripts'])

            if DebugEnabled():
                task_msg = GT(u'Total tasks: {}').format(task_count)
                print(u'DEBUG: [{}] {}'.format(__name__, task_msg))
                for T in task_list:
                    print(u'\t{}'.format(T))

            create_changelog = u'changelog' in task_list
            create_copyright = u'copyright' in task_list

            pg_control = GetPage(pgid.CONTROL)
            pg_menu = GetPage(pgid.MENU)

            stage_dir = u'{}/{}__dbp__'.format(build_path, filename)

            if os.path.isdir(u'{}/DEBIAN'.format(stage_dir)):
                try:
                    shutil.rmtree(stage_dir)

                except OSError:
                    ShowErrorDialog(
                        GT(u'Could not free stage directory: {}').format(
                            stage_dir),
                        title=GT(u'Cannot Continue'))

                    return (dbrerrno.EEXIST, None)

            # Actual path to new .deb
            deb = u'"{}/{}.deb"'.format(build_path, filename)

            progress = 0

            task_msg = GT(u'Preparing build tree')
            Logger.Debug(__name__, task_msg)

            wx.Yield()
            build_progress = ProgressDialog(
                GetMainWindow(),
                GT(u'Building'),
                task_msg,
                maximum=task_count,
                style=PD_DEFAULT_STYLE | wx.PD_ELAPSED_TIME
                | wx.PD_ESTIMATED_TIME | wx.PD_CAN_ABORT)

            DIR_debian = ConcatPaths((stage_dir, u'DEBIAN'))

            # Make a fresh build tree
            os.makedirs(DIR_debian)
            progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            def UpdateProgress(current_task, message=None):
                task_eval = u'{} / {}'.format(current_task, task_count)

                if message:
                    Logger.Debug(__name__,
                                 u'{} ({})'.format(message, task_eval))

                    wx.Yield()
                    build_progress.Update(current_task, message)

                    return

                wx.Yield()
                build_progress.Update(current_task)

            # *** Files *** #
            if u'files' in task_list:
                UpdateProgress(progress, GT(u'Copying files'))

                no_follow_link = GetField(GetPage(pgid.FILES),
                                          chkid.SYMLINK).IsChecked()

                # TODO: move this into a file functions module
                def _copy(f_src, f_tgt, exe=False):
                    # NOTE: Python 3 appears to have follow_symlinks option for shutil.copy
                    # FIXME: copying nested symbolic link may not work

                    if os.path.isdir(f_src):
                        if os.path.islink(f_src) and no_follow_link:
                            Logger.Debug(
                                __name__,
                                u'Adding directory symbolic link to stage: {}'.
                                format(f_tgt))

                            os.symlink(os.readlink(f_src), f_tgt)
                        else:
                            Logger.Debug(
                                __name__,
                                u'Adding directory to stage: {}'.format(f_tgt))

                            shutil.copytree(f_src, f_tgt)
                            os.chmod(f_tgt, 0o0755)
                    elif os.path.isfile(f_src):
                        if os.path.islink(f_src) and no_follow_link:
                            Logger.Debug(
                                __name__,
                                u'Adding file symbolic link to stage: {}'.
                                format(f_tgt))

                            os.symlink(os.readlink(f_src), f_tgt)
                        else:
                            if exe:
                                Logger.Debug(
                                    __name__,
                                    u'Adding executable to stage: {}'.format(
                                        f_tgt))
                            else:
                                Logger.Debug(
                                    __name__,
                                    u'Adding file to stage: {}'.format(f_tgt))

                            shutil.copy(f_src, f_tgt)

                            # Set FILE permissions
                            if exe:
                                os.chmod(f_tgt, 0o0755)

                            else:
                                os.chmod(f_tgt, 0o0644)

                files_data = task_list[u'files']
                for FILE in files_data:
                    file_defs = FILE.split(u' -> ')

                    source_file = file_defs[0]
                    target_file = u'{}{}/{}'.format(stage_dir, file_defs[2],
                                                    file_defs[1])
                    target_dir = os.path.dirname(target_file)

                    if not os.path.isdir(target_dir):
                        os.makedirs(target_dir)

                    # Remove asteriks from exectuables
                    exe = False
                    if source_file[-1] == u'*':
                        exe = True
                        source_file = source_file[:-1]

                    _copy(
                        source_file,
                        u'{}/{}'.format(target_dir,
                                        os.path.basename(source_file)), exe)

                    # Individual files
                    progress += 1
                    UpdateProgress(progress)

                # Entire file task
                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Strip files ***#
            # FIXME: Needs only be run if 'files' step is used
            if u'strip' in task_list:
                UpdateProgress(progress, GT(u'Stripping binaries'))

                for ROOT, DIRS, FILES in os.walk(stage_dir):  #@UnusedVariable
                    for F in FILES:
                        # Don't check files in DEBIAN directory
                        if ROOT != DIR_debian:
                            F = ConcatPaths((ROOT, F))

                            if FileUnstripped(F):
                                Logger.Debug(__name__,
                                             u'Unstripped file: {}'.format(F))

                                # FIXME: Strip command should be set as class member?
                                ExecuteCommand(GetExecutable(u'strip'), F)

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            package = GetField(pg_control, inputid.PACKAGE).GetValue()

            # Make sure that the directory is available in which to place documentation
            if create_changelog or create_copyright:
                doc_dir = u'{}/usr/share/doc/{}'.format(stage_dir, package)
                if not os.path.isdir(doc_dir):
                    os.makedirs(doc_dir)

            # *** Changelog *** #
            if create_changelog:
                UpdateProgress(progress, GT(u'Creating changelog'))

                # If changelog will be installed to default directory
                changelog_target = task_list[u'changelog'][0]
                if changelog_target == u'STANDARD':
                    changelog_target = ConcatPaths(
                        (u'{}/usr/share/doc'.format(stage_dir), package))

                else:
                    changelog_target = ConcatPaths(
                        (stage_dir, changelog_target))

                if not os.path.isdir(changelog_target):
                    os.makedirs(changelog_target)

                WriteFile(u'{}/changelog'.format(changelog_target),
                          task_list[u'changelog'][1])

                CMD_gzip = GetExecutable(u'gzip')

                if CMD_gzip:
                    UpdateProgress(progress, GT(u'Compressing changelog'))
                    c = u'{} -n --best "{}/changelog"'.format(
                        CMD_gzip, changelog_target)
                    clog_status = commands.getstatusoutput(c.encode(u'utf-8'))
                    if clog_status[0]:
                        ShowErrorDialog(GT(u'Could not compress changelog'),
                                        clog_status[1],
                                        warn=True,
                                        title=GT(u'Warning'))

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Copyright *** #
            if create_copyright:
                UpdateProgress(progress, GT(u'Creating copyright'))

                WriteFile(
                    u'{}/usr/share/doc/{}/copyright'.format(
                        stage_dir, package), task_list[u'copyright'])

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # Characters that should not be in filenames
            invalid_chars = (u' ', u'/')

            # *** Menu launcher *** #
            if u'launcher' in task_list:
                UpdateProgress(progress, GT(u'Creating menu launcher'))

                # This might be changed later to set a custom directory
                menu_dir = u'{}/usr/share/applications'.format(stage_dir)

                menu_filename = pg_menu.GetOutputFilename()

                # Remove invalid characters from filename
                for char in invalid_chars:
                    menu_filename = menu_filename.replace(char, u'_')

                if not os.path.isdir(menu_dir):
                    os.makedirs(menu_dir)

                WriteFile(u'{}/{}.desktop'.format(menu_dir, menu_filename),
                          task_list[u'launcher'])

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** md5sums file *** #
            # Good practice to create hashes before populating DEBIAN directory
            if u'md5sums' in task_list:
                UpdateProgress(progress, GT(u'Creating md5sums'))

                if not WriteMD5(stage_dir, parent=build_progress):
                    # Couldn't call md5sum command
                    build_progress.Cancel()

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Scripts *** #
            if u'scripts' in task_list:
                UpdateProgress(progress, GT(u'Creating scripts'))

                scripts = task_list[u'scripts']
                for SCRIPT in scripts:
                    script_name = SCRIPT
                    script_text = scripts[SCRIPT]

                    script_filename = ConcatPaths(
                        (stage_dir, u'DEBIAN', script_name))

                    WriteFile(script_filename, script_text)

                    # Make sure scipt path is wrapped in quotes to avoid whitespace errors
                    os.chmod(script_filename, 0755)
                    os.system((u'chmod +x "{}"'.format(script_filename)))

                    # Individual scripts
                    progress += 1
                    UpdateProgress(progress)

                # Entire script task
                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Control file *** #
            UpdateProgress(progress, GT(u'Getting installed size'))

            # Get installed-size
            installed_size = os.popen(
                (u'du -hsk "{}"'.format(stage_dir))).readlines()
            installed_size = installed_size[0].split(u'\t')
            installed_size = installed_size[0]

            # Insert Installed-Size into control file
            control_data = pg_control.Get().split(u'\n')
            control_data.insert(2,
                                u'Installed-Size: {}'.format(installed_size))

            progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # Create final control file
            UpdateProgress(progress, GT(u'Creating control file'))

            # dpkg fails if there is no newline at end of file
            control_data = u'\n'.join(control_data).strip(u'\n')
            # Ensure there is only one empty trailing newline
            # Two '\n' to show physical empty line, but not required
            # Perhaps because string is not null terminated???
            control_data = u'{}\n\n'.format(control_data)

            WriteFile(u'{}/DEBIAN/control'.format(stage_dir),
                      control_data,
                      noStrip=u'\n')

            progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Final build *** #
            UpdateProgress(progress, GT(u'Running dpkg'))

            working_dir = os.path.split(stage_dir)[0]
            c_tree = os.path.split(stage_dir)[1]
            deb_package = u'{}.deb'.format(filename)

            # Move the working directory becuase dpkg seems to have problems with spaces in path
            os.chdir(working_dir)

            # HACK to fix file/dir permissions
            for ROOT, DIRS, FILES in os.walk(stage_dir):
                for D in DIRS:
                    D = u'{}/{}'.format(ROOT, D)
                    os.chmod(D, 0o0755)
                for F in FILES:
                    F = u'{}/{}'.format(ROOT, F)
                    if os.access(F, os.X_OK):
                        os.chmod(F, 0o0755)
                    else:
                        os.chmod(F, 0o0644)

            # FIXME: Should check for working fakeroot & dpkg-deb executables
            build_status = commands.getstatusoutput(
                (u'{} {} -b "{}" "{}"'.format(GetExecutable(u'fakeroot'),
                                              GetExecutable(u'dpkg-deb'),
                                              c_tree, deb_package)))

            progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Delete staged directory *** #
            if u'rmstage' in task_list:
                UpdateProgress(progress, GT(u'Removing temp directory'))

                try:
                    shutil.rmtree(stage_dir)

                except OSError:
                    ShowErrorDialog(GT(
                        u'An error occurred when trying to delete the build tree'
                    ),
                                    parent=build_progress)

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** ERROR CHECK
            if u'lintian' in task_list:
                UpdateProgress(progress, GT(u'Checking package for errors'))

                # FIXME: Should be set as class memeber?
                CMD_lintian = GetExecutable(u'lintian')
                errors = commands.getoutput((u'{} {}'.format(CMD_lintian,
                                                             deb)))

                if errors != wx.EmptyString:
                    e1 = GT(u'Lintian found some issues with the package.')
                    e2 = GT(u'Details saved to {}').format(filename)

                    WriteFile(u'{}/{}.lintian'.format(build_path, filename),
                              errors)

                    DetailedMessageDialog(build_progress,
                                          GT(u'Lintian Errors'),
                                          ICON_INFORMATION,
                                          u'{}\n{}.lintian'.format(e1, e2),
                                          errors).ShowModal()

                progress += 1

            # Close progress dialog
            wx.Yield()
            build_progress.Update(progress)
            build_progress.Destroy()

            # Build completed successfullly
            if not build_status[0]:
                return (dbrerrno.SUCCESS, deb_package)

            if PY_VER_MAJ <= 2:
                # Unicode decoder has trouble with certain characters. Replace any
                # non-decodable characters with � (0xFFFD).
                build_output = list(build_status[1])

                # String & unicode string incompatibilities
                index = 0
                for C in build_output:
                    try:
                        GS(C)

                    except UnicodeDecodeError:
                        build_output[index] = u'�'

                    index += 1

                build_status = (build_status[0], u''.join(build_output))

            # Build failed
            return (build_status[0], build_status[1])

        except:
            if build_progress:
                build_progress.Destroy()

            return (dbrerrno.EUNKNOWN, traceback.format_exc())