Пример #1
0
    def OnBuild(self, event=None):
        # Build preparation
        ret_code, build_prep = self.BuildPrep()

        if ret_code == dbrerrno.ECNCLD:
            return

        if ret_code == dbrerrno.FEMPTY:
            err_dia = DetailedMessageDialog(
                GetMainWindow(),
                GT(u'Cannot Continue'),
                ICON_EXCLAMATION,
                text=u'{}\n{}'.format(
                    GT(u'One of the required fields is empty:'), build_prep))
            err_dia.ShowModal()
            err_dia.Destroy()

            return

        if ret_code == dbrerrno.SUCCESS:
            task_list, build_path, filename = build_prep

            # Actual build
            ret_code, result = self.Build(task_list, build_path, filename)

            # FIXME: Check .deb package timestamp to confirm build success
            if ret_code == dbrerrno.SUCCESS:
                DetailedMessageDialog(
                    GetMainWindow(),
                    GT(u'Success'),
                    ICON_INFORMATION,
                    text=GT(u'Package created successfully')).ShowModal()

                # Installing the package
                if FieldEnabled(
                        self.chk_install) and self.chk_install.GetValue():
                    self.InstallPackage(result)

                return

            if result:
                ShowErrorDialog(GT(u'Package build failed'), result)

            else:
                ShowErrorDialog(GT(u'Package build failed with unknown error'))

            return

        if build_prep:
            ShowErrorDialog(GT(u'Build preparation failed'), build_prep)

        else:
            ShowErrorDialog(GT(u'Build preparation failed with unknown error'))
Пример #2
0
    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()
Пример #3
0
	def ImportFromFile(self, filename):
		Logger.Debug(__name__, GT(u'Importing page info from {}').format(filename))

		if not os.path.isfile(filename):
			return dbrerrno.ENOENT

		files_data = ReadFile(filename, split=True)

		# Lines beginning with these characters will be ignored
		ignore_characters = (
			u'',
			u' ',
			u'#',
		)

		target = None
		targets_list = []

		for L in files_data:
			if not TextIsEmpty(L) and L[0] not in ignore_characters:
				if u'[' in L and u']' in L:
					target = L.split(u'[')[-1].split(u']')[0]
					continue

				if target:
					executable = (len(L) > 1 and L[-2:] == u' *')
					if executable:
						L = L[:-2]

					targets_list.append((target, L, executable))

		missing_files = []

		for T in targets_list:
			# FIXME: Create method in FileList class to retrieve all missing files
			if not os.path.exists(T[1]):
				missing_files.append(T[1])

			source_file = os.path.basename(T[1])
			source_dir = os.path.dirname(T[1])

			self.lst_files.AddFile(source_file, source_dir, T[0], executable=T[2])

		if len(missing_files):
			main_window = GetMainWindow()

			err_line1 = GT(u'The following files/folders are missing from the filesystem.')
			err_line2 = GT(u'They will be highlighted on the Files page.')
			DetailedMessageDialog(main_window, title=GT(u'Warning'), icon=ICON_ERROR,
					text=u'\n'.join((err_line1, err_line2)),
					details=u'\n'.join(missing_files)).ShowModal()

		return 0
Пример #4
0
    def AddInfo(self, event=None):
        new_changes = self.ti_changes.GetValue()

        if TextIsEmpty(new_changes):
            DetailedMessageDialog(
                GetMainWindow(), GT(u'Warning'), ICON_WARNING,
                GT(u'"Changes" section is empty')).ShowModal()

            self.ti_changes.SetInsertionPointEnd()
            self.ti_changes.SetFocus()

            return

        package = self.ti_package.GetValue()
        version = self.ti_version.GetValue()
        dist = self.ti_dist.GetValue()
        urgency = self.sel_urgency.GetStringSelection()
        maintainer = self.ti_maintainer.GetValue()
        email = self.ti_email.GetValue()

        new_changes = FormatChangelog(new_changes, package, version, dist,
                                      urgency, maintainer, email,
                                      self.chk_indentation.GetValue())

        # Clean up leading & trailing whitespace in old changes
        old_changes = self.dsp_changes.GetValue().strip(u' \t\n\r')

        # Only append newlines if log isn't already empty
        if not TextIsEmpty(old_changes):
            new_changes = u'{}\n\n\n{}'.format(new_changes, old_changes)

        # Add empty line to end of log
        if not new_changes.endswith(u'\n'):
            new_changes = u'{}\n'.format(new_changes)

        self.dsp_changes.SetValue(new_changes)

        # Clear "Changes" text
        self.ti_changes.Clear()
        self.ti_changes.SetFocus()
Пример #5
0
	def OnGenerate(self, event=None):
		main_window = GetMainWindow()

		# Get the amount of links to be created
		total = self.Executables.GetCount()

		if total > 0:
			non_empty_scripts = []

			for DS in self.script_objects[1][0], self.script_objects[2][0]:
				if not TextIsEmpty(DS.GetValue()):
					non_empty_scripts.append(DS.GetName())

			# Warn about overwriting previous post-install & pre-remove scripts
			if non_empty_scripts:
				warn_msg = GT(u'The following scripts will be overwritten if you continue: {}')
				warn_msg = u'{}\n\n{}'.format(warn_msg.format(u', '.join(non_empty_scripts)), GT(u'Continue?'))

				overwrite = ConfirmationDialog(main_window, text=warn_msg)

				if not overwrite.Confirmed():
					return

				overwrite.Destroy()
				del warn_msg, overwrite

			# Get destination for link from Auto-Link input textctrl
			link_path = self.ti_autolink.GetValue()

			# Warn about linking in a directory that does not exist on the current filesystem
			if not os.path.isdir(link_path):
				warn_msg = GT(u'Path "{}" does not exist.')
				warn_msg = u'{}\n\n{}'.format(warn_msg, GT(u'Continue?'))

				overwrite = ConfirmationDialog(main_window, text=warn_msg.format(link_path))

				if not overwrite.Confirmed():
					return

				overwrite.Destroy()
				del warn_msg, overwrite

			# Create a list of commands to put into the script
			postinst_list = []
			prerm_list = []

			for INDEX in range(total):
				source_path = self.Executables.GetPath(INDEX)
				filename = self.Executables.GetBasename(INDEX)

				if u'.' in filename:
					linkname = u'.'.join(filename.split(u'.')[:-1])
					link = u'{}/{}'.format(link_path, linkname)

				else:
					link = u'{}/{}'.format(link_path, filename)

				postinst_list.append(u'ln -fs "{}" "{}"'.format(source_path, link))
				prerm_list.append(u'rm -f "{}"'.format(link))

			postinst = u'\n\n'.join(postinst_list)
			prerm = u'\n\n'.join(prerm_list)

			self.script_objects[1][0].SetValue(postinst)
			self.script_objects[2][0].SetValue(prerm)

			DetailedMessageDialog(main_window, GT(u'Success'),
					text=GT(u'Post-Install and Pre-Remove scripts generated')).ShowModal()
Пример #6
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
Пример #7
0
    def OnBuild(self, event=None):
        if event:
            event.Skip()

        # Show control file preview for editing
        if UsingTest(u'alpha') and self.chk_editctrl.GetValue():
            self.EditControl()

        wizard = GetWizard()

        pg_control = wizard.GetPage(pgid.CONTROL)
        pg_files = wizard.GetPage(pgid.FILES)
        pg_launcher = wizard.GetPage(pgid.LAUNCHERS)

        required_fields = {
            GT(u'Control'): pg_control.GetRequiredFields(),
        }

        # Check if launchers are enabled for build
        if pg_launcher.GetLaunchersCount():
            required_fields[GT(
                u'Menu Launcher')] = pg_launcher.GetRequiredFields()

            # FIXME: Old code won't work with multiple launchers
            for RF in required_fields[GT(u'Menu Launcher')]:
                Logger.Debug(
                    __name__,
                    GT(u'Required field (Menu Launcher): {}').format(
                        RF.GetName()))

        for p_name in required_fields:
            Logger.Debug(__name__, GT(u'Page name: {}').format(p_name))
            for F in required_fields[p_name]:
                if not isinstance(F, wx.StaticText) and TextIsEmpty(
                        F.GetValue()):
                    f_name = F.GetName()

                    msg_l1 = GT(u'One of the required fields is empty:')
                    msg_full = u'{}: {} ➜ {}'.format(msg_l1, p_name, f_name)

                    Logger.Warn(__name__, msg_full)

                    DetailedMessageDialog(GetMainWindow(),
                                          GT(u'Cannot Continue'),
                                          ICON_EXCLAMATION,
                                          text=msg_full).ShowModal()

                    for P in wizard.GetAllPages():
                        if P.GetTitle() == p_name:
                            Logger.Debug(
                                __name__,
                                GT(u'Showing page with required field: {}').
                                format(p_name))
                            wizard.ShowPage(P.GetId())

                    return

        if GetField(pg_files, inputid.LIST).MissingFiles():
            ShowErrorDialog(GT(u'Files are missing in file list'),
                            warn=True,
                            title=GT(u'Warning'))

            wizard.ShowPage(pgid.FILES)

            return

        ttype = GT(u'Debian Packages')
        save_dialog = GetFileSaveDialog(GetMainWindow(), GT(u'Build Package'),
                                        u'{} (*.deb)|*.deb'.format(ttype),
                                        u'deb')

        package = GetFieldValue(pg_control, inputid.PACKAGE)
        version = GetFieldValue(pg_control, inputid.VERSION)
        arch = GetFieldValue(pg_control, inputid.ARCH)
        save_dialog.SetFilename(u'{}_{}_{}.deb'.format(package, version, arch))

        if ShowDialog(save_dialog):
            self.Build(save_dialog.GetPath())
Пример #8
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
Пример #9
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())