def OnToggle(self, event=None): enabled = GetField(self, chkid.ENABLE).IsChecked() # Fields that should not be disabled skip_ids = ( chkid.ENABLE, btnid.BROWSE, txtid.FNAME, ) for LIST in inputid, chkid, listid, btnid: for ID in LIST.IdList: if ID not in skip_ids: field = GetField(self, ID) if isinstance(field, wx.Window): field.Enable(enabled) # Disable/Enable static text labels st_labels = GetAllTypeFields(self, wx.StaticText) for ST in st_labels: if ST.Id not in skip_ids: ST.Enable(enabled) self.OnSetCustomFilename()
def Reset(self): field_ids = ( chkid, inputid, listid, selid, ) for IDTYPE in field_ids: idlist = IDTYPE.IdList for ID in idlist: if ID not in self.IgnoreResetIds: field = GetField(self, ID) if isinstance(field, wx.Window): field.Reset() # Pages that use MultiTemplate instances multit_ids = ( pgid.MAN, pgid.LAUNCHERS, ) if self.Id in multit_ids: NBLIST = GetAllTypeFields(self, Notebook) if NBLIST: for NB in NBLIST: MT = NB.GetContainingSizer() if isinstance(MT, MultiTemplate): MT.Reset()
def SetCategory(self, event=None): try: ID = event.GetKeyCode() except AttributeError: ID = event.GetEventObject().GetId() cat = self.ti_category.GetValue() cat = cat.split() cat = u''.join(cat) lst_categories = GetField(self, listid.CAT) if ID in (wx.ID_ADD, wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): lst_categories.InsertStringItem(lst_categories.GetItemCount(), cat) elif ID in (wx.ID_REMOVE, wx.WXK_DELETE): if lst_categories.GetItemCount( ) and lst_categories.GetSelectedItemCount(): cur_cat = lst_categories.GetFirstSelected() lst_categories.DeleteItem(cur_cat) elif ID == wx.ID_CLEAR: if lst_categories.GetItemCount(): if ConfirmationDialog( GetMainWindow(), GT(u'Confirm'), GT(u'Clear categories?')).ShowModal() in (wx.ID_OK, wx.OK): lst_categories.DeleteAllItems() if event: event.Skip()
def ScrollToEnd(self): pnl_bg = GetField(self, pnlid.BACKGROUND) pnl_bg.SetScrollPos(wx.VERTICAL, pnl_bg.GetScrollLines(wx.VERTICAL)) pnl_bg.Refresh() self.Refresh()
def set_value(option): key = option.GetName() value = None if key in menu_definitions: value = menu_definitions[key] if not TextIsEmpty(value): if option in self.opts_input: option.SetValue(value) return True elif option in self.opts_choice: if option.SetStringSelection(value): return True elif option in self.opts_list: lst_categories = GetField(self, listid.CAT) if key == lst_categories.GetName(): value = value.split(u';') if value: for X, val in enumerate(value): lst_categories.InsertStringItem(X, val) return True return False
def ToggleButtons(self): parent = self.GetParent() for ID in self.TabButtonIds: button = GetField(parent, ID) if button: button.Enable(self.HasTabs())
def OnClearCategories(self, event=None): cats = GetField(self, listid.CAT) if cats.GetItemCount(): clear = ConfirmationDialog(GetMainWindow(), GT(u'Confirm'), GT(u'Clear categories?')) if clear.Confirmed(): cats.DeleteAllItems()
def GetAllItems(self): pnl_bg = GetField(self, pnlid.BACKGROUND) items = [] for item in pnl_bg.GetChildren(): if isinstance(item, wx.CheckBox): items.append(item) return tuple(items)
def OnClearCategories(self, event=None): cats = GetField(self, listid.CAT) if cats.HasSelected(): clear = ConfirmationDialog(GetMainWindow(), GT(u'Confirm'), GT(u'Clear categories?')) if clear.Confirmed(): cats.Clear()
def GetOutputFilename(self): if not GetField(self, chkid.FNAME).GetValue(): filename = GetField(self, inputid.FNAME).GetValue().strip(u' ').replace( u' ', u'_') if not TextIsEmpty(filename): return filename return GetField(self, inputid.NAME).GetValue().strip(u' ').replace( u' ', u'_')
def OnSelectLicense(self, event=None): choice = GetField(self, selid.LICENSE) if choice: template = choice.GetString(choice.GetSelection()) if template in self.custom_licenses: self.btn_template_simple.Disable() else: self.btn_template_simple.Enable() self.SetLicenseTooltip()
def GetSaveData(self): if GetField(self, chkid.ENABLE).GetValue(): data = self.GetLauncherInfo() data = u'\n'.join(data.split(u'\n')[1:]) if not GetField(self, chkid.FNAME).GetValue(): data = u'[FILENAME={}]\n{}'.format( GetField(self, inputid.FNAME).GetValue(), data) return u'<<MENU>>\n1\n{}\n<</MENU>>'.format(data) else: return u'<<MENU>>\n0\n<</MENU>>'
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
def ImportExes(self, event=None): event_id = event.GetId() if event_id == btnid.IMPORT: # First clear the Auto-Link display and the executable list self.Executables.Reset() # Get executables from "files" tab file_list = GetField(pgid.FILES, inputid.LIST) for INDEX in range(file_list.GetItemCount()): # Get the filename from the source file_name = file_list.GetFilename(INDEX, basename=True) file_path = file_list.GetPath(INDEX) # Where the file linked to will be installed file_target = file_list.GetItem(INDEX, 1) # Walk directory to find executables if file_list.IsDirectory(INDEX): for EXE in GetFiles(file_path, os.X_OK): self.Executables.Append(FileItem(EXE, file_target)) # Search for executables (distinguished by red text) elif file_list.IsExecutable(INDEX): try: # If destination doesn't start with "/" do not include executable if file_target.GetText()[0] == u'/': if file_target.GetText()[-1] == u'/' or file_target.GetText()[-1] == u' ': # In case the full path of the destination is "/" keep going if len(file_target.GetText()) == 1: dest_path = u'' else: search = True # Set the number of spaces to remove from dest path in case of multiple "/" slashes = 1 while search: # Find the number of slashes/spaces at the end of the filename endline = slashes - 1 if file_target.GetText()[-slashes] == u'/' or file_target.GetText()[-slashes] == u' ': slashes += 1 else: dest_path = file_target.GetText()[:-endline] search = False else: dest_path = file_target.GetText() self.Executables.Append(file_name, dest_path) else: Logger.Warn(__name__, u'{}: The executables destination is not valid'.format(__name__)) except IndexError: Logger.Warn(__name__, u'{}: The executables destination is not available'.format(__name__)) elif event_id in (btnid.REMOVE, wx.WXK_DELETE): self.Executables.RemoveSelected()
def WriteMD5(stage_dir, parent=None): CMD_md5sum = GetExecutable(u'md5sum') # Show an error if the 'md5sum' command does not exist # This is only a failsafe & should never actually occur if not CMD_md5sum: if not parent: parent = GetMainWindow() md5_label = GetField(pgid.BUILD, chkid.MD5).GetLabel() err_msg1 = GT(u'The "md5sum" command was not found on the system.') err_msg2 = GT(u'Uncheck the "{}" box.').format(md5_label) err_msg3 = GT( u'Please report this error to one of the following addresses:') err_url1 = u'https://github.com/AntumDeluge/debreate/issues' err_url2 = u'https://sourceforge.net/p/debreate/bugs/' Logger.Error( __name__, u'{} {} {}\n\t{}\n\t{}'.format(err_msg1, err_msg2, err_msg3, err_url1, err_url2)) md5_error = ErrorDialog(parent, text=u'{}\n{}\n\n{}'.format( err_msg1, err_msg2, err_msg3)) md5_error.AddURL(err_url1) md5_error.AddURL(err_url2) md5_error.ShowModal() return None temp_list = [] md5_list = [] # Final list used to write the md5sum file for ROOT, DIRS, FILES in os.walk(stage_dir): # Ignore the 'DEBIAN' directory if os.path.basename(ROOT) == u'DEBIAN': continue for F in FILES: F = u'{}/{}'.format(ROOT, F) md5 = GetCommandOutput(CMD_md5sum, (u'-t', F)) Logger.Debug(__name__, u'WriteMD5: GetCommandOutput: {}'.format(md5)) temp_list.append(md5) for item in temp_list: # Remove [stage_dir] from the path name in the md5sum so that it has a # true unix path # e.g., instead of "/myfolder_temp/usr/local/bin", "/usr/local/bin" sum_split = item.split(u'{}/'.format(stage_dir)) sum_join = u''.join(sum_split) md5_list.append(sum_join) # Create the md5sums file in the "DEBIAN" directory return WriteFile(u'{}/DEBIAN/md5sums'.format(stage_dir), u'{}\n'.format(u'\n'.join(md5_list)))
def Reset(self): field_ids = ( chkid, inputid, listid, selid, ) for IDTYPE in field_ids: idlist = IDTYPE.IdList for ID in idlist: if ID not in self.IgnoreResetIds: field = GetField(self, ID) if isinstance(field, wx.Window): field.Reset()
def OnCatSelect(self, event=None): btn_cat_clr = GetField(self, wx.ID_CLEAR) lst_cat = GetField(self, listid.CAT) if btn_cat_clr and lst_cat: if FieldEnabled(btn_cat_clr): if not lst_cat.HasSelected(): btn_cat_clr.Disable() else: if lst_cat.HasSelected(): btn_cat_clr.Enable()
def AddItem(self, label, checked=False): # Yield for progress dialog pulse updates wx.Yield() # FIXME: Static lines are counted item_index = self.GetItemCount() #Logger.Debug(__name__, GT(u'Lintian tag: {}; Set checked: {}').format(label, checked)) pnl_bg = GetField(self, pnlid.BACKGROUND) lyt_bg = pnl_bg.GetSizer() lyt_bg.Add(wx.CheckBox(pnl_bg, label=label), 1, wx.EXPAND) lyt_bg.Add(wx.StaticLine(pnl_bg, style=wx.LI_HORIZONTAL), 0, wx.EXPAND) if checked: check_box = self.GetItem(item_index) if isinstance(check_box, wx.CheckBox): check_box.SetValue(True) pnl_bg.Layout() self.Layout()
def OnRefreshList(self, event=None): # FIXME: Ignore symbolic links??? self.custom_licenses = GetLocalLicenses() licenses = list(self.custom_licenses) # System licenses are not added to "custom" list for LIC in GetSysLicenses(): if LIC not in licenses: licenses.append(LIC) for LIC in GetCustomLicenses(): if LIC not in licenses: licenses.append(LIC) self.custom_licenses.append(LIC) self.custom_licenses.sort(key=GS.lower) sel_templates = GetField(self, selid.LICENSE) selected = None if sel_templates.GetCount(): selected = sel_templates.GetStringSelection() sel_templates.Set(sorted(licenses, key=GS.lower)) if selected: if not sel_templates.SetStringSelection(selected): # Selected template file was not found sel_templates.SetSelection(sel_templates.GetDefaultValue()) # Update short template button enabled state self.OnSelectLicense() else: sel_templates.SetSelection(sel_templates.GetDefaultValue())
def OnOtherRemove(self, event=None): pnl_other = GetField(self, inputid.OTHER) # FIXME: Show confirmation dialog if pnl_other: if pnl_other.HasSelected(): remove = ConfirmationDialog( GetMainWindow(), GT(u'Custom Fields'), GT(u'Remove all selected custom fields?')) if remove.Confirmed(): pnl_other.RemoveSelected() btn_remove = GetField(self, wx.ID_REMOVE) if btn_remove and FieldEnabled(btn_remove): btn_remove.Enable(pnl_other.HasSelected())
def Reset(self): chk_filename = GetField(self, chkid.FNAME) chk_filename.SetValue(chk_filename.Default) GetField(self, inputid.FNAME).Clear() for IDS in inputid, chkid, listid: idlist = IDS.IdList for ID in idlist: field = GetField(self, ID) if isinstance(field, wx.Window): field.Reset() self.OnToggle()
def IsSelected(self, section): if isinstance(section, int): section = self.GetSection(section) return GetField(section, inputid.CHECK).GetValue()
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())
def ImportExes(self, event=None): event_id = event.GetId() if event_id == btnid.IMPORT: # First clear the Auto-Link display and the executable list self.Executables.Reset() file_list = GetField(pgid.FILES, inputid.LIST) exe_list = file_list.GetExecutables(False) for EXE in exe_list: INDEX = file_list.GetIndex(EXE) # Get the filename from the source file_name = file_list.GetFilename(INDEX) #file_name = EXE.GetBasename() # Where the file linked to will be installed # FIXME: FileItem.GetTarget() is not accurate file_target = file_list.GetTarget(EXE) self.Executables.Add( FileItem(file_name, ConcatPaths(file_target, file_name), ignore_timestamp=True)) # retrieve nested executables # FIXME: symlinks may cause problems here for FITEM in file_list.GetFileItems(): if FITEM.IsDirectory(): # recurse into subdirectories toplevel = FITEM.GetPath() for ROOT, DIRS, FILES in os.walk(toplevel): for FILE in FILES: fullpath = ConcatPaths(ROOT, FILE) DIR = os.path.dirname( fullpath[len(toplevel):]).strip(u'/') relpath = ConcatPaths(FITEM.GetBasename(), DIR, FILE).strip(u'/') if os.path.isfile(fullpath) and os.access( fullpath, os.X_OK): fulltarget = ConcatPaths( FITEM.GetTarget(), relpath) # check if item is already added to list duplicate = False for EXE in exe_list: existingtarget = ConcatPaths( EXE.GetTarget(), file_list.GetFilename(EXE)) if fulltarget == existingtarget: duplicate = True break if duplicate: Logger.Warn( __name__, u'Not adding executable with duplicate target: {}' .format(fulltarget)) continue Logger.Debug( __name__, u'Adding nested executable: {}'.format( relpath)) self.Executables.Add( FileItem(relpath, ConcatPaths( FITEM.GetTarget(), relpath), ignore_timestamp=True)) elif event_id in (btnid.REMOVE, wx.WXK_DELETE): self.Executables.RemoveSelected()
def GetSelectedName(self): return GetField(self, selid.LICENSE).GetStringSelection()
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())
def GetPackageName(self): return GetField(self, inputid.PACKAGE).GetValue()
def GetCtrlInfo(self): pg_depends = GetPage(pgid.DEPENDS) ctrl_list = [] synopsis = None description = None # Email will be set if maintainer changed to True maintainer = False # Text input fields for field in self.grp_input: field_name = field.GetName().title() field_value = field.GetValue() if FieldEnabled(field) and not TextIsEmpty(field_value): Logger.Debug(__name__, GT(u'Exporting {} field').format(field_name)) # Strip leading & trailing spaces, tabs, & newlines field_value = field_value.strip(u' \t\n') if field_name == u'Synopsis': synopsis = u'{}: {}'.format(u'Description', field_value) continue if field_name == u'Description': description = field_value.split(u'\n') for line_index in range(len(description)): # Remove trailing whitespace description[line_index] = description[ line_index].rstrip() if TextIsEmpty(description[line_index]): # Empty lines are formatted with one space indentation & a period description[line_index] = u' .' else: # All other lines are formatted with one space indentation description[line_index] = u' {}'.format( description[line_index]) description = u'\n'.join(description) continue if field_name in (u'Package', u'Version'): # Don't allow whitespace in package name & version ctrl_list.append(u'{}: {}'.format( field_name, u'-'.join(field_value.split(u' ')))) continue if field_name == u'Email': if maintainer and ctrl_list: # Append email to end of maintainer string for ctrl_index in range(len(ctrl_list)): if ctrl_list[ctrl_index].startswith( u'Maintainer: '): Logger.Debug(__name__, u'Found maintainer') ctrl_list[ctrl_index] = u'{} <{}>'.format( ctrl_list[ctrl_index], field_value) break continue # Don't use 'continue' on this statement if field_name == u'Maintainer': maintainer = True # The rest of the fields ctrl_list.append(u'{}: {}'.format(field_name, field_value)) # Selection box fields for field in self.grp_select: field_name = field.GetName().title() field_value = field.GetStringSelection() if FieldEnabled(field) and not TextIsEmpty(field_value): Logger.Debug(__name__, GT(u'Exporting {} field').format(field_name)) # Strip leading & trailing spaces, tabs, & newlines field_value = field_value.strip(u' \t\n') ctrl_list.append(u'{}: {}'.format(field_name, field_value)) if self.chk_essential.GetValue(): ctrl_list.append(u'Essential: yes') # Dependencies & conflicts dep_list = [] # Depends pre_list = [] # Pre-Depends rec_list = [] # Recommends sug_list = [] # Suggests enh_list = [] # Enhances con_list = [] # Conflicts rep_list = [] # Replaces brk_list = [] # Breaks all_deps = { u'Depends': dep_list, u'Pre-Depends': pre_list, u'Recommends': rec_list, u'Suggests': sug_list, u'Enhances': enh_list, u'Conflicts': con_list, u'Replaces': rep_list, u'Breaks': brk_list, } # Get amount of items to add dep_area = GetField(pg_depends, inputid.LIST) dep_count = dep_area.GetItemCount() count = 0 while count < dep_count: # Get each item from dependencies page dep_type = dep_area.GetItem(count, 0).GetText() dep_val = dep_area.GetItem(count, 1).GetText() for item in all_deps: if dep_type == item: all_deps[item].append(dep_val) count += 1 for item in all_deps: if len(all_deps[item]) != 0: ctrl_list.append(u'{}: {}'.format(item, u', '.join(all_deps[item]))) if synopsis: ctrl_list.append(synopsis) # Long description is only added if synopsis is not empty if description: ctrl_list.append(description) # dpkg requires empty newline at end of file return u'\n'.join(ctrl_list).strip(u'\n') + u'\n'
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
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())