def _parse_deb_version(changelog='debian/changelog'): try: clog = Changelog() clog.parse_changelog(open(changelog), max_blocks=1) return clog.full_version except IOError: raise LGPException("Debian changelog '%s' cannot be found" % changelog) except ChangelogParseError: raise LGPException("Malformed Debian changelog '%s'" % changelog)
def get_debian_name(): """obtain the debian package name The information is found in debian/control withe the 'Source:' field """ try: control = osp.join('debian', 'control') deb822 = Deb822(open(control), fields='Source') return deb822['Source'] except IOError as err: raise LGPException('a Debian control file should exist in "%s"' % control) except KeyError as err: raise LGPException("No 'Source' field in '%s'" % control)
def _parse_deb_project(changelog='debian/changelog'): clog = Changelog() try: clog.parse_changelog(open(changelog), max_blocks=1) return clog.package except ChangelogParseError: raise LGPException("Malformed Debian changelog '%s'" % changelog)
def _run_command(self, cmd, **args): """run an internal declared command as new subprocess""" if isinstance(cmd, list): cmdline = ' '.join(cmd) else: cmd = COMMANDS[self.package_format][cmd] if callable(cmd): try: return cmd() except IOError as err: raise LGPException(err) cmdline = Template(cmd) setup_file = self._normpath(self.config.setup_file) cmdline = cmdline.substitute(setup=setup_file, **args) self.logger.debug('run subprocess command: %s' % cmdline) if args: self.logger.debug('command substitutions: %s' % args) process = Popen(cmdline.split(), stdout=PIPE) pipe = process.communicate()[0].strip() if process.returncode > 0: process.cmd = cmdline.split() raise LGPCommandException( "lgp aborted by the '%s' command child process" % cmdline, process) if not isinstance(pipe, string_types): pipe = pipe.decode('utf-8') # py3k return pipe
def _check_version_mismatch(self): upstream_version = self.get_upstream_version() #debian_upstream_version = self.get_versions()[0] debian_upstream_version = self.get_debian_version().rsplit('-', 1)[0] assert debian_upstream_version == self.get_versions( )[0], "get_versions() failed" if upstream_version != debian_upstream_version: msg = "version mismatch: upstream says '%s' and debian/changelog says '%s'" msg %= (upstream_version, debian_upstream_version) raise LGPException(msg)
def make_rpm_source_package(self, specfile, distrib, tmpdir): """create a srpm""" if specfile is None: self.logger.error( "specfile not found, please use the '--specfile' option") raise LGPException("cannot build source distribution") if self.config.suffix is not None: # patch the spec file to inject the ~revision in the version suffix = self.config.suffix or '+%s' % int(time.time()) with tempfile.NamedTemporaryFile() as out: for line in open(specfile): if line.lower().startswith('release:'): line = line.strip() + suffix + '\n' out.write(line) out.flush() shutil.move(out.name, specfile) out.delete = False os.chmod(specfile, 0o644) # change directory to build source package # note: call os.chdir() HERE is needed in make_rpm_binary_package() below os.chdir(tmpdir) try: cmd = [ "sudo", "mock", "--buildsrpm", "--spec", specfile, "--resultdir", os.getcwd(), "-r", distrib, "--uniqueext", str(os.getpid()), "--sources", osp.dirname(self._upstream_tarball) ] self.logger.debug("running mock command: %s ..." % " ".join(cmd)) check_call(cmd, stdout=sys.stdout) except CalledProcessError as err: msg = "cannot build valid srpm file with command %s" % cmd raise LGPCommandException(msg, err) srpms = glob(osp.join(os.getcwd(), '*.src.rpm')) try: srpm, = srpms except ValueError: raise LGPException("couldn't find the SRPM") return srpm
def _normpath(self, path): """helper method to normalize filepath arguments before changing current directory (will return absolute paths) XXX could be coded directly by option checker (optparse) """ if path: assert self.old_current_directory path = osp.abspath( osp.join(self.old_current_directory, osp.expanduser(path))) if not osp.exists(path): msg = "file given in command line cannot be found:\n\t%s" raise LGPException(msg % path) return path
def _check_file(filename): if osp.isfile(filename): hash1 = hashlib.md5(open(fullpath).read()).hexdigest() hash2 = hashlib.md5(open(filename).read()).hexdigest() if hash1 == hash2: self.logger.debug("overwrite same file file '%s'" % filename) else: msg = "theses files shouldn't be different:\n"\ "- %s (%s)\n"\ "- %s (%s)" self.logger.warn(msg % (fullpath, hash1, filename, hash2)) os.system('diff -u %s %s' % (fullpath, filename)) raise LGPException( "bad md5 sums of source archives (tarball)")
def _set_package_format(self): """set the package format to be able to run COMMANDS setup_file must not be redefined since we can call this method several times """ setup_file = self._normpath(self.config.setup_file) if setup_file: self.logger.info('use specific setup file: %s', setup_file) if osp.isfile('__pkginfo__.py') and not setup_file: # Logilab's specific format # FIXME Format is buggy if setup_file was set to 'setup.py' from logilab.packaging.lib import TextReporter self.config._package = PackageInfo(reporter=TextReporter( open(os.devnull, "w+")), directory=self.config.pkg_dir) assert osp.isfile('setup.py'), "setup.py is still mandatory" # other script can be used if compatible with the expected targets in COMMANDS elif osp.isfile(setup_file): if osp.basename(setup_file) == 'setup.py': # case for python project (distutils, setuptools) self.config._package = run_setup(setup_file, None, stop_after="init") else: # generic case: the setup file should only honor targets as: # sdist, project, version, clean (see COMMANDS) self.config._package = open(setup_file) if not os.stat(setup_file).st_mode & stat.S_IEXEC: raise LGPException( 'setup file %s has no execute permission' % setup_file) else: if self.config.rpm: class rpm(object): pass self.config._package = rpm() else: class debian(object): pass self.config._package = debian() self.logger.debug("use setup package class format: %s" % self.package_format)
def get_debian_architecture(): """get debian architecture(s) to use in build The information is found in debian/control with the 'Architecture:' field """ try: control = osp.join('debian', 'control') for line in open(control): line = line.split(' ', 1) if line[0] == "Architecture:": archi = line[1].rstrip().split(' ') if "source" in archi: archi.pop('source') return archi except IOError as err: raise LGPException('a Debian control file should exist in "%s"' % control)
def go_into_package_dir(self, arguments): """go into package directory """ self.old_current_directory = os.getcwd( ) # use for relative filename in parameters if arguments: if os.path.exists(arguments[0]): self.config.pkg_dir = osp.abspath(arguments[0]) else: raise LGPException("project directory doesn't exist: %s" % arguments[0]) if os.path.isfile(self.config.pkg_dir): self.config.pkg_dir = os.path.dirname(self.config.pkg_dir) os.chdir(self.config.pkg_dir) self.logger.debug('change the current working directory to: %s' % self.config.pkg_dir) else: self.config.pkg_dir = self.old_current_directory
def manage_current_distribution(self, distrib): """manage debian files depending of the current distrib from options We copy debian_dir directory into tmp build depending of the target distribution in all cases, we copy the debian directory of the default version (unstable) If a file should not be included, touch an empty file in the overlay directory. This is specific to Logilab (debian directory is in project directory) """ try: # don't forget the final slash! export(osp.join(self.config.pkg_dir, 'debian'), osp.join(self.origpath, 'debian/'), verbose=(self.config.verbose == 2)) except IOError as err: raise LGPException(err) debian_dir = self.get_debian_dir(distrib) if debian_dir != "debian": self.logger.info("overriding files from '%s' directory..." % debian_dir) # don't forget the final slash! export(osp.join(self.config.pkg_dir, debian_dir), osp.join(self.origpath, 'debian/'), verbose=self.config.verbose) from debian.changelog import Changelog debchangelog = osp.join(self.origpath, 'debian', 'changelog') changelog = Changelog(open(debchangelog)) # substitute distribution string in changelog if distrib: # squeeze python-debian doesn't handle unicode well, see Debian bug#561805 changelog.distributions = str(distrib) # append suffix string (or timestamp if suffix is empty) to debian revision if self.config.suffix is not None: suffix = self.config.suffix or '+%s' % int(time.time()) self.logger.debug("suffix '%s' added to package version" % suffix) changelog.version = str(changelog.version) + suffix changelog.write_to_open_file(open(debchangelog, 'w')) return self.origpath
def get_checklist(self, all=False): if all: return [ funct for (name, funct) in globals().items() if name.startswith('check_') ] try: checks = CHECKS['default'] # we try to compile a consistent set of checks to apply if os.path.exists('setup.py'): checks.update(CHECKS['distutils']) if os.path.exists('__pkginfo__.py'): checks.update(CHECKS['pkginfo']) if os.path.exists('debian'): checks.update(CHECKS['debian']) if self.config.set_checks: checks = set() for c in itertools.chain(self.config.set_checks, self.config.include_checks): if c in CHECKS: checks.update(CHECKS[c]) else: checks.add(c) for c in self.config.exclude_checks: if c in CHECKS: checks.difference_update(CHECKS[c]) else: if c in checks: checks.remove(c) self.checklist = [globals()["check_%s" % name] for name in checks] except KeyError as err: msg = "check function or category %s was not found. Use lgp check --list" raise LGPException(msg % str(err)) return self.checklist
def make_orig_tarball(self, tmpdir="."): """make upstream pristine tarballs (Debian way) Create the tarball from working directory for the initial revision. For later ones call uscan to download the source tarball by looking at debian/watch (if the tarball wasn't passed on the command line). Finally copy pristine tarball with expected upstream filename convention. This method is responsible for setting config.orig_tarball to its right location. .. see:: http://www.debian.org/doc/debian-policy/ch-source.html """ has_debian_dir = osp.isdir('debian') if has_debian_dir: # _check_version_mismatch() is 100% Debian-specific self._check_version_mismatch() is_initial_debian_revision = self.is_initial_debian_revision() else: # always rebuild the source/orig tarball if debian/ is missing, # skip uscan altogether is_initial_debian_revision = True tarball = self.config.orig_tarball if tarball and is_initial_debian_revision: self.logger.warn("you are passing a pristine tarball in command " "line for an initial Debian revision") upstream_name = self.get_upstream_name() fileparts = (upstream_name, self.get_upstream_version()) if has_debian_dir: # note: tarball format can be guaranteed by uscan's repack option debian_tarball = '%s_%s.orig.tar.gz' % fileparts upstream_tarball = '%s-%s.tar.gz' % fileparts # run uscan to download the source tarball by looking at debian/watch if tarball is None and not is_initial_debian_revision: self.logger.info('running uscan to download the source tarball by ' 'looking at debian/watch') try: check_call([ "uscan", "--noconf", "--download-current-version", "--no-symlink", "--destdir", tmpdir ]) except CalledProcessError as err: debian_name = self.get_debian_name() debian_revision = self.get_debian_version().rsplit('-', 1)[1] self.logger.error("Debian source archive (pristine tarball) is"\ " required when you don't build the first "\ " revision of a debian package "\ "(use '--orig-tarball' option)") self.logger.info("If you haven't the original tarball version," " you could run: " "'apt-get source --tar-only %s'" % debian_name) msg = ('unable to build upstream tarball of %s package for ' 'Debian revision "%s"' % (debian_name, debian_revision)) raise LGPCommandException(msg, err) else: tarball = osp.join(tmpdir, upstream_tarball) # create new pristine tarball from working directory if initial revision elif tarball is None and is_initial_debian_revision: self.logger.info("create pristine tarball from working directory") try: self._run_command("sdist", dist_dir=tmpdir) except CalledProcessError as err: self.logger.error("creation of the source archive failed") self.logger.error( "check if the version '%s' is really tagged in " "your repository" % self.get_upstream_version()) raise LGPCommandException( "source distribution wasn't properly built", err) else: tarball = osp.join(tmpdir, upstream_tarball) # Sanity check if not osp.basename(tarball).startswith(upstream_name): msg = "pristine tarball filename doesn't start with "\ "upstream name '%s'. really suspect..." self.logger.error(msg % upstream_name) # Make a copy of tarball ('path/to/xxx-version.tar.gz')... # ... with the debian name convention 'path/to/xxx_version.orig.tar.gz' if has_debian_dir: debian_tarball = osp.abspath(osp.join(tmpdir, debian_tarball)) try: if not osp.exists(debian_tarball): shutil.copy(tarball, debian_tarball) except EnvironmentError as err: msg = "pristine tarball can't be copied from given location: %s" self.logger.critical(msg % tarball) raise LGPException(err) self._debian_tarball = debian_tarball # self._upstream_tarball = tarball return tarball
def _prune_pkg_dir(self): super(Builder, self)._prune_pkg_dir() if self.package_format == 'debian' and not osp.isdir('debian'): msg = ("You are not in a valid project root directory. " "Lgp expects a Debian directory here.") raise LGPException(msg)
def get_basetgz(self, distrib, arch, check=True): basetgz = osp.join(self.config.basetgz, "%s-%s.tgz" % (distrib, arch)) if check and not osp.exists(basetgz): msg = "lgp image '%s' not found. Please create it with lgp setup" raise LGPException(msg % basetgz) return basetgz