def apply_patch(patchfile, cwd=None, posix=False, level=0): """call 'patch -p[level] [--posix] < arg1' posix mode is sometimes necessary. It keeps empty files so that dpkg-source removes their contents. """ if not os.path.exists(patchfile): raise RuntimeError('patchfile "%s" does not exist' % patchfile) fd = open(patchfile, mode='r') level_str = '-p%d' % level args = ['/usr/bin/patch', level_str] if posix: args.append('--posix') log.info('PATCH COMMAND: %s < %s', ' '.join(args), patchfile) log.info(' PATCHING in dir: %s', cwd) # print >> sys.stderr, 'PATCH COMMAND:',' '.join(args),'<',patchfile # print >> sys.stderr, ' PATCHING in dir:',cwd res = subprocess.Popen( args, cwd=cwd, stdin=fd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) returncode = None while returncode is None: returncode = res.poll() ready = select.select([res.stdout, res.stderr], [], [], 0.1) # XXX figure out how to do this without reading byte-by-byte if res.stdout in ready[0]: sys.stdout.write(res.stdout.read(1)) sys.stdout.flush() if res.stderr in ready[0]: sys.stderr.write(res.stderr.read(1)) sys.stderr.flush() # finish outputting file sys.stdout.write(res.stdout.read()) sys.stdout.flush() sys.stderr.write(res.stderr.read()) sys.stderr.flush() if returncode: log.error('ERROR running: %s', ' '.join(args)) log.error('ERROR in %s', cwd) # print >> sys.stderr, 'ERROR running: %s'%(' '.join(args),) # print >> sys.stderr, 'ERROR in',cwd raise RuntimeError('returncode %d' % returncode)
# Now for each requirement, see if a Debian package satisfies it. ops = {'<': '<<', '>': '>>', '==': '=', '<=': '<=', '>=': '>='} for req in parsed_reqs: reqname = req.project_name.lower() gooddebs = set() for pydist, debs in dd.get(reqname, {}).iteritems(): if pydist in req: ## log.info("I found Debian packages \"%s\" which provides " ## "Python package \"%s\", version \"%s\", which " ## "satisfies our version requirements: \"%s\"" ## % (', '.join(debs), req.project_name, ver, req) gooddebs |= (debs) else: log.info("I found Debian packages \"%s\" which provides " "Python package \"%s\" which " "does not satisfy our version requirements: " "\"%s\" -- ignoring." % (', '.join(debs), req.project_name, req)) if not gooddebs: if on_failure == 'warn': log.warn( "I found no Debian package which provides the required " "Python package \"%s\" with version requirements " "\"%s\"." % (req.project_name, req.specs)) elif on_failure == "raise": raise CantSatisfyRequirement( "I found no Debian package which " "provides the required Python package \"%s\" with version " "requirements \"%s\"." % (req.project_name, req.specs), req) elif on_failure == "guess":
def build_dsc( debinfo, dist_dir, repackaged_dirname, orig_sdist=None, patch_posix=0, remove_expanded_source_dir=0, debian_dir_only=False, ): """make debian source package""" # A. Find new dirname and delete any pre-existing contents # dist_dir is usually 'deb_dist' # the location of the copied original source package (it was # re-recreated in dist_dir) if debian_dir_only: fullpath_repackaged_dirname = os.path.abspath(os.curdir) else: fullpath_repackaged_dirname = os.path.join(dist_dir, repackaged_dirname) ############################################### # 1. make temporary original source tarball # Note that, for the final tarball, best practices suggest # using "dpkg-source -b". See # http://www.debian.org/doc/developers-reference/ch-best-pkging-practices.en.html # Create the name of the tarball that qualifies as the upstream # source. If the original was specified, we'll link to # it. Otherwise, we generate our own .tar.gz file from the output # of "python setup.py sdist" (done above) so that we avoid # packaging .svn directories, for example. if not debian_dir_only: repackaged_orig_tarball = ( '%(source)s_%(upstream_version)s.orig.tar.gz' % debinfo.__dict__) repackaged_orig_tarball_path = os.path.join(dist_dir, repackaged_orig_tarball) if orig_sdist is not None: if os.path.exists(repackaged_orig_tarball_path): os.unlink(repackaged_orig_tarball_path) link_func(orig_sdist, repackaged_orig_tarball_path) else: make_tarball(repackaged_orig_tarball, repackaged_dirname, cwd=dist_dir) # apply patch if debinfo.patch_file != '': apply_patch(debinfo.patch_file, posix=patch_posix, level=debinfo.patch_level, cwd=fullpath_repackaged_dirname) for fname in ['Makefile', 'makefile']: if os.path.exists(os.path.join(fullpath_repackaged_dirname, fname)): sys.stderr.write('*' * 1000 + '\n') if debinfo.force_buildsystem: sys.stderr.write('WARNING: a Makefile exists in this package. ' 'stdeb will tell debhelper 7 to use setup.py ' 'to build and install the package, and the ' 'Makefile will be ignored. You can disable ' 'this behavior with the ' '--force-buildsystem=False argument to the ' 'stdeb command.\n') else: sys.stderr.write( 'WARNING: a Makefile exists in this package. ' 'debhelper 7 will attempt to use this rather ' 'than setup.py to build and install the ' 'package. You can disable this behavior with ' 'the --force-buildsystem=True argument to the ' 'stdeb command.\n') sys.stderr.write('*' * 1000 + '\n') ############################################### # 2. create debian/ directory and contents debian_dir = os.path.join(fullpath_repackaged_dirname, 'debian') if not os.path.exists(debian_dir): os.mkdir(debian_dir) # A. debian/changelog fd = open(os.path.join(debian_dir, 'changelog'), mode='w') fd.write("""\ %(source)s (%(full_version)s) %(distname)s; urgency=low * source package automatically created by stdeb %(stdeb_version)s -- %(maintainer)s %(date822)s\n""" % debinfo.__dict__) fd.close() # B. debian/control if debinfo.uploaders: debinfo.uploaders = 'Uploaders: %s\n' % ', '.join(debinfo.uploaders) else: debinfo.uploaders = '' control = CONTROL_FILE % debinfo.__dict__ fd = open(os.path.join(debian_dir, 'control'), mode='w') fd.write(control) fd.close() # C. debian/rules debinfo.percent_symbol = '%' rules = RULES_MAIN % debinfo.__dict__ if debinfo.test_suite: rules = rules + RULES_OVERRIDE_DH_AUTO_TEST rules = rules.replace(' ', '\t') rules_fname = os.path.join(debian_dir, 'rules') fd = open(rules_fname, mode='w') fd.write(rules) fd.close() os.chmod(rules_fname, 0755) # D. debian/compat fd = open(os.path.join(debian_dir, 'compat'), mode='w') fd.write('7\n') fd.close() # E. debian/package.mime if debinfo.mime_file != '': if not os.path.exists(debinfo.mime_file): raise ValueError( 'a MIME file was specified, but does not exist: %s' % (debinfo.mime_file, )) link_func(debinfo.mime_file, os.path.join(debian_dir, debinfo.package + '.mime')) if debinfo.shared_mime_file != '': if not os.path.exists(debinfo.shared_mime_file): raise ValueError( 'a shared MIME file was specified, but does not exist: %s' % (debinfo.shared_mime_file, )) link_func( debinfo.shared_mime_file, os.path.join(debian_dir, debinfo.package + '.sharedmimeinfo')) # F. debian/copyright if debinfo.copyright_file != '': link_func(debinfo.copyright_file, os.path.join(debian_dir, 'copyright')) # H. debian/<package>.install if len(debinfo.install_file_lines): fd = open(os.path.join(debian_dir, '%s.install' % debinfo.package), mode='w') fd.write('\n'.join(debinfo.install_file_lines) + '\n') fd.close() # I. debian/<package>.udev if debinfo.udev_rules != '': fname = debinfo.udev_rules if not os.path.exists(fname): raise ValueError('udev rules file specified, but does not exist') link_func(fname, os.path.join(debian_dir, '%s.udev' % debinfo.package)) # J. debian/source/format os.mkdir(os.path.join(debian_dir, 'source')) fd = open(os.path.join(debian_dir, 'source', 'format'), mode='w') fd.write('1.0\n') fd.close() if debian_dir_only: return ############################################### # 3. unpack original source tarball debianized_package_dirname = fullpath_repackaged_dirname + '.debianized' if os.path.exists(debianized_package_dirname): raise RuntimeError('debianized_package_dirname exists: %s' % debianized_package_dirname) # A. move debianized tree away os.rename(fullpath_repackaged_dirname, debianized_package_dirname) if orig_sdist is not None: # B. expand repackaged original tarball tmp_dir = os.path.join(dist_dir, 'tmp-expand') os.mkdir(tmp_dir) try: expand_tarball(orig_sdist, cwd=tmp_dir) orig_tarball_top_contents = os.listdir(tmp_dir) # make sure original tarball has exactly one directory assert len(orig_tarball_top_contents) == 1 orig_dirname = orig_tarball_top_contents[0] fullpath_orig_dirname = os.path.join(tmp_dir, orig_dirname) # C. move original repackaged tree to .orig target = fullpath_repackaged_dirname + '.orig' if os.path.exists(target): # here from previous invocation, probably shutil.rmtree(target) os.rename(fullpath_orig_dirname, target) finally: shutil.rmtree(tmp_dir) if 1: # check versions of debhelper and python-all debhelper_version_str = get_version_str('debhelper') if len(debhelper_version_str) == 0: log.warn('This version of stdeb requires debhelper >= %s, but you ' 'do not have debhelper installed. ' 'Could not check compatibility.' % DH_MIN_VERS) else: if not dpkg_compare_versions(debhelper_version_str, 'ge', DH_MIN_VERS): log.warn('This version of stdeb requires debhelper >= %s. ' 'Use stdeb 0.3.x to generate source packages ' 'compatible with older versions of debhelper.' % (DH_MIN_VERS, )) python_defaults_version_str = get_version_str('python-all') if len(python_defaults_version_str) == 0: log.warn('This version of stdeb requires python-all >= %s, ' 'but you do not have this package installed. ' 'Could not check compatibility.' % PYTHON_ALL_MIN_VERS) else: if not dpkg_compare_versions(python_defaults_version_str, 'ge', PYTHON_ALL_MIN_VERS): log.warn( 'This version of stdeb requires python-all >= %s. ' 'Use stdeb 0.6.0 or older to generate source packages ' 'that use python-support.' % (PYTHON_ALL_MIN_VERS, )) # D. restore debianized tree os.rename(fullpath_repackaged_dirname + '.debianized', fullpath_repackaged_dirname) # Re-generate tarball using best practices see # http://www.debian.org/doc/developers-reference/ch-best-pkging-practices.en.html # call "dpkg-source -b new_dirname orig_dirname" log.info('CALLING dpkg-source -b %s %s (in dir %s)' % (repackaged_dirname, repackaged_orig_tarball, dist_dir)) dpkg_source('-b', repackaged_dirname, repackaged_orig_tarball, cwd=dist_dir) if 1: shutil.rmtree(fullpath_repackaged_dirname) if not remove_expanded_source_dir: # expand the debian source package dsc_name = debinfo.source + '_' + debinfo.dsc_version + '.dsc' dpkg_source('-x', dsc_name, cwd=dist_dir)
def runit(cmd, usage): if cmd not in ['sdist_dsc', 'bdist_deb']: raise ValueError('unknown command %r' % cmd) # process command-line options bool_opts = list(map(translate_longopt, stdeb_cmd_bool_opts)) parser = FancyGetopt(stdeb_cmdline_opts + [ ('help', 'h', "show detailed help message"), ]) optobj = OptObj() args = parser.getopt(object=optobj) for option in optobj.__dict__: value = getattr(optobj, option) is_string = type(value) == str if option in bool_opts and is_string: setattr(optobj, option, strtobool(value)) if hasattr(optobj, 'help'): print(usage) parser.set_option_table(stdeb_cmdline_opts) parser.print_help("Options:") return 0 if len(args) != 1: log.error('not given single argument (distfile), args=%r', args) print(usage) return 1 sdist_file = args[0] final_dist_dir = optobj.__dict__.get('dist_dir', 'deb_dist') tmp_dist_dir = os.path.join(final_dist_dir, 'tmp_py2dsc') if os.path.exists(tmp_dist_dir): shutil.rmtree(tmp_dist_dir) os.makedirs(tmp_dist_dir) if not os.path.isfile(sdist_file): log.error("Package %s not found." % sdist_file) sys.exit(1) patch_file = optobj.__dict__.get('patch_file', None) patch_level = int(optobj.__dict__.get('patch_level', 0)) patch_posix = int(optobj.__dict__.get('patch_posix', 0)) expand_dir = os.path.join(tmp_dist_dir, 'stdeb_tmp') if os.path.exists(expand_dir): shutil.rmtree(expand_dir) if not os.path.exists(tmp_dist_dir): os.mkdir(tmp_dist_dir) os.mkdir(expand_dir) expand_sdist_file(os.path.abspath(sdist_file), cwd=expand_dir) # now the sdist package is expanded in expand_dir expanded_root_files = os.listdir(expand_dir) assert len(expanded_root_files) == 1 repackaged_dirname = expanded_root_files[0] fullpath_repackaged_dirname = os.path.join(tmp_dist_dir, repackaged_dirname) base_dir = os.path.join(expand_dir, expanded_root_files[0]) if os.path.exists(fullpath_repackaged_dirname): # prevent weird build errors if this dir exists shutil.rmtree(fullpath_repackaged_dirname) os.renames(base_dir, fullpath_repackaged_dirname) del base_dir # no longer useful ############################################## if patch_file is not None: log.info('py2dsc applying patch %s', patch_file) apply_patch(patch_file, posix=patch_posix, level=patch_level, cwd=fullpath_repackaged_dirname) patch_already_applied = 1 else: patch_already_applied = 0 ############################################## abs_dist_dir = os.path.abspath(final_dist_dir) extra_args = [] for long in parser.long_opts: if long in ['dist-dir=', 'patch-file=']: continue # dealt with by this invocation attr = parser.get_attr_name(long).rstrip('=') if hasattr(optobj, attr): val = getattr(optobj, attr) if attr == 'extra_cfg_file': val = os.path.abspath(val) if long in bool_opts or long.replace('-', '_') in bool_opts: extra_args.append('--%s' % long) else: extra_args.append('--' + long + str(val)) if patch_already_applied == 1: extra_args.append('--patch-already-applied') if cmd == 'bdist_deb': extra_args.append('bdist_deb') args = [ sys.executable, 'setup.py', '--command-packages', 'stdeb.command', 'sdist_dsc', '--dist-dir=%s' % abs_dist_dir, '--use-premade-distfile=%s' % os.path.abspath(sdist_file) ] + extra_args log.info('-=' * 35 + '-') # print >> sys.stderr, '-='*20 # print >> sys.stderr, "Note that the .cfg file(s), if present, have not "\ # "been read at this stage. If options are necessary, pass them "\ # "from the command line" log.info("running the following command in directory: %s\n%s", fullpath_repackaged_dirname, ' '.join(args)) log.info('-=' * 35 + '-') try: returncode = subprocess.call( args, cwd=fullpath_repackaged_dirname, ) except Exception: log.error('ERROR running: %s', ' '.join(args)) log.error('ERROR in %s', fullpath_repackaged_dirname) raise if returncode: log.error('ERROR running: %s', ' '.join(args)) log.error('ERROR in %s', fullpath_repackaged_dirname) # log.error(' stderr: %s'res.stderr.read()) # print >> sys.stderr, 'ERROR running: %s'%(' '.join(args),) # print >> sys.stderr, res.stderr.read() return returncode # raise RuntimeError('returncode %d'%returncode) # result = res.stdout.read().strip() shutil.rmtree(tmp_dist_dir) return returncode