diff options
author | Yaakov M. Nemoy <loupgaroublond@gmail.com> | 2009-01-08 16:43:14 -0500 |
---|---|---|
committer | Yaakov M. Nemoy <loupgaroublond@gmail.com> | 2009-01-08 16:43:14 -0500 |
commit | c27ff3ce07de79215b58f5fd10ff175424cba7cb (patch) | |
tree | d7ab5511655050adf4905468b2b7e9ff15afde47 | |
parent | 3bcae770792afe5d5fff2af127cad2c1d0d59d57 (diff) | |
download | fedora-devshell-c27ff3ce07de79215b58f5fd10ff175424cba7cb.tar.gz fedora-devshell-c27ff3ce07de79215b58f5fd10ff175424cba7cb.tar.xz fedora-devshell-c27ff3ce07de79215b58f5fd10ff175424cba7cb.zip |
adds 135 kilocraps of documentation
135 kilocraps = 1 metric craptonne
-rw-r--r-- | modules/build.py | 60 | ||||
-rw-r--r-- | modules/cabal.py | 79 | ||||
-rw-r--r-- | modules/darcs.py | 67 | ||||
-rw-r--r-- | modules/directory.py | 35 | ||||
-rw-r--r-- | modules/dirfactory.py | 2 | ||||
-rw-r--r-- | modules/mock.py | 9 | ||||
-rw-r--r-- | modules/package.py | 15 | ||||
-rw-r--r-- | modules/profile.py | 19 | ||||
-rw-r--r-- | modules/revisioncontrol.py | 2 | ||||
-rw-r--r-- | modules/sourceball.py | 10 |
10 files changed, 275 insertions, 23 deletions
diff --git a/modules/build.py b/modules/build.py index 7fcc1b0..93b2056 100644 --- a/modules/build.py +++ b/modules/build.py @@ -36,8 +36,16 @@ from modules.package import Package from modules.profile import Profile class Build(Directory): + '''A wrapper around rpmbuild functions + ''' _type = 'build' def setup_source(self, package): + '''Given a package, set's it up in the buildroot for being built with rpmbuild + + package is a directory name to a Package (Directory). + + returns nothing + ''' pkg = DirFactory(package) with pwd(pkg.dir): symlink(pkg.spec_file, @@ -49,12 +57,47 @@ class Build(Directory): symlink(patch, join(self.dir, 'SOURCES', patch)) def build_quick_rpm(self, package, profile=None): + '''Builds an rpm on the local system using rpmbuild (aka no clean chroots) + + This method may be useful in determining the difference between + a chroot and the working system. It can also help make sure the + package can be built in the first place, and quickly. + + package is a directory name to a Package (Directory). + profile is a Profile object, this parameter should only be used internally + + returns nothing + ''' self.rpmbuild('-ba', package, profile) def build_source_rpm(self, package, profile=None): + '''Builds an source rpm on the local system using rpmbuild + + This method is necessary to build SRPMs for many other modules. + The user may find that certain dist related macros in the RPM are + set to the system wide defaults. The parameter 'profile' is used heavily + internally to mimic aspects of the Fedora Build system in order + override the defaults. Without the assistance of other modules + that are profile aware, profile makes no sense here. + + package is a directory name to a Package (Directory). + profile is a Profile object, this parameter should only be used internally + + returns nothing + ''' self.rpmbuild('-bs', package, profile) def rpmbuild(self, param, package, profile=None): + '''The work horse of building rpms + + Given a directive param for rpmbuild, the package, and a possible + profile, it runs rpmbuild appropriatly. + + package is a directory name to a Package (Directory). + profile is a Profile object, this parameter should only be used internally + + returns nothing + ''' pkg = DirFactory(package) if profile: defines = join_defines(profile.dist_defines, @@ -75,6 +118,15 @@ class Build(Directory): log.debug(p.returncode) def fetch_rpms(self, target_dir): + '''fetches all rpm files from the buildroot to another target directory + + As a module author, it is a good idea to use this to clean up + afterwards. This module pulls all RPMs said module may have made + as well as any other RPMs from earlier incantations of other modules. + Please be courteous to other module authors and run this liberally. + + target_dir is a path to the destination for all the rpms + ''' with pwd(self.dir): for path, dirs, files in walk('.'): for f in files: @@ -82,6 +134,14 @@ class Build(Directory): move(join(path, f), join(target_dir, f)) def fetch_build(self, package): + '''fetches the built source tree from the execution of rpmbuild for review + + This is usefull to see if rpmbuild did anything funny in execution + of the spec file. Hopefully you'll never need this. The results + end up in the top level directory of the given package + + package is a directory name to a Package (Directory). + ''' pkg = DirFactory(package) with pwd(self.dir): source = pkg.cfg['source'] diff --git a/modules/cabal.py b/modules/cabal.py index 529b023..59c8d48 100644 --- a/modules/cabal.py +++ b/modules/cabal.py @@ -36,7 +36,17 @@ from modules.dirfactory import DirFactory from modules.sourceball import SourceBall class Cabal(Module): + '''A wrapper around common cabal operations + + This provides a useful api for other modules around the cabal build system + ''' def __init__(self, name, root='~/haskell'): + '''creates a new cabal module + + this api is deprecated, because it should be more autodetecting + + name is a Package (Directory) that uses cabal for its build system + ''' self.name = name self.root = expanduser(root) with pwd(self.root): @@ -45,18 +55,32 @@ class Cabal(Module): self.compiler = haskell_compiler def copy_in(self, tarball): + '''copies a tarball into the package + + tarball is a path to some tarball + ''' tarball = abspath(tarball) with pwd(self.root): self.package.add_sourceball(tarball) def darcs_get(self, url, tgt): + '''creates a darcs variant of a cabal package using darcs source + + url is a url to some darcs repo + tgt is the local name of the darcs repo + ''' self.package.checkout(tgt, url) def find_setup(self, orig=''): + '''returns the name of the Setup.?hs script for cabal. + ''' setup_re = compile("Setup\.l?hs") - return one(listdir(getcwd()), setup_re.search) + with pwd(self.package.dir): + return one(listdir(getcwd()), setup_re.search) def compile_setup(self, orig=''): + '''compiles the setup script for faster execution + ''' log.debug('dir is ' + self.package.dir) with pwd(self.package.dir): with log_file('ghc.log') as ghc_out: @@ -70,6 +94,14 @@ class Cabal(Module): p.communicate() def configure(self, target='home', orig=''): + '''runs the configure stage of cabal + + target is either 'home' or 'root' and will configure the package to + install either to the user's home directory, or to the system + wide haskell. + + Some help is needed making this more flexible + ''' user = True if target == 'home' else False self.compile_setup(orig) with pwd(self.package.dir): @@ -83,7 +115,12 @@ class Cabal(Module): p.wait() def build(self, orig=''): - '''This is not safe to run on an unconfigured source dir''' + '''runs the build stage of cabal + + This is not safe to run on an unconfigured source dir, because + this module does not track the state of cabal systems. The user + must do this on their own. + ''' self.compile_setup(orig) with pwd(self.package.dir): with log_file('cabal.log') as cabal_out: @@ -94,7 +131,12 @@ class Cabal(Module): p.wait() def install(self, orig=''): - '''This is not safe to run on an unconfigured source dir''' + '''runs the install stage of cabal + + This is not safe to run on an unconfigured source dir, because + this module does not track the state of cabal systems. The user + must do this on their own. + ''' self.compile_setup(orig) with pwd(self.package.dir): with log_file('cabal.log') as cabal_out: @@ -105,40 +147,62 @@ class Cabal(Module): p.wait() def install_tag(self, tag): + '''assuming a package that supports tagging, install a specific tag + + tag is the name of the tag in the DVCS + ''' with pwd(self.package.dir): with self.package.tag(tag): self.install() def install_source(self, target='home', orig=''): + '''perform configure, build, and install steps in one + ''' self.configure(target, orig) self.build(orig) self.install(orig) def install_sourceball(self, tarball, target='home'): + '''given a tarball, copy it in and install it + ''' self.copy_in(tarball) self.install_source(target, '') def get_from_hackage(self, pkg, ver): + '''get a specific package from hackage + + pkg is the name of the package desired + ver is the version wanted + ''' sb_file = self.hackage_url(pkg, ver) self.copy_in(sb_file) def get_latest(self, pkg): + '''get the latest version of a package from hackage + + pkg is the package desired + ''' ver = self.latest_version(pkg) self.get_from_hackage(pkg, ver) def install_from_hackage(self, pkg, ver, target='home'): + '''get and install a specific package from hackage + + pkg is the desired package + ver is the version wanted + target is the location to install to, either 'home' or 'root' + ''' self.get_from_hackage(pkg, ver) self.install_source(target, '') def install_latest(self, pkg, target='home'): + '''get and install the latest version of a package from hackage''' self.get_latest(pkg) self.install_source(target, '') - def source_dir(self, *args): - with pwd(self.package.dir): - return abspath(self.package.cfg['source'] + (self.orig_src_dir if original else "")) - def latest_version(self, pkg): + '''download information from hackage to find out the latest version of a package + ''' hackage_title = compile(r'<title.*?>HackageDB: (.*)-(.*)</title.*?>', DOTALL) site = 'http://hackage.haskell.org/cgi-bin/hackage-scripts/package/' + pkg u = urlopen(site) @@ -152,6 +216,7 @@ class Cabal(Module): raise ExecutionException("package does not match package name, can't determine version, sorry") def hackage_url(self, pkg, ver): + '''returns the url for tarball for a hackage package''' return 'http://hackage.haskell.org/packages/archive/' + \ pkg + '/' + ver + '/' + pkg + '-' + ver + '.tar.gz' diff --git a/modules/darcs.py b/modules/darcs.py index dddaee2..8bee4a4 100644 --- a/modules/darcs.py +++ b/modules/darcs.py @@ -32,29 +32,47 @@ hash_re = re.compile(r'hash=\'(\w|-|.*?)\'', re.MULTILINE) date_re = re.compile(r'date=\'(\d*?)\'', re.MULTILINE) class Darcs(RevisionControl): + '''manages single source packages where the primary source is darcs + ''' _type = 'darcs' - def load_dir(self, dir): - super(RevisionControl, self).load_dir(dir) - @property def vc_url(self): + '''the url where the source was fetched from originally + + this may be moved to revision control in the future + ''' return self.cfg['vc_url'] @property def hackage_name(self): + '''assuming this package is a haskell package, what is the canonical name + + this name may be changed in the future + ''' return self.cfg['hackage_name'] def source(self, *args): + '''the name of the directory where the source is being kept currently + + hackage_name is the canonical name of the package, this is + just for handling branching and other nasty devilish tricks + ''' return self.cfg['source'] @contextmanager def src_dir(self, *args): + '''executes a code block inside a specific branch and or checkout + ''' with src(*args): with pwd(self.cfg['source']): yield @contextmanager def src(self, *args): + '''executes a code block with a particular branch or checkout + + if there are no args, this block is executed in the raw + ''' if args: old_src = self.cfg['source'] self.set_cur_to(*args) @@ -66,6 +84,14 @@ class Darcs(RevisionControl): def get(self, src, tgt, *args): + '''sets up a branch given arguments, taken from a source with a target + + the idioms of branching in different VCSes vary, and a common + api for this in devshell has not yet been realized + + currently, this creates a new temporary local branch in darcs + and sets the source to it + ''' with pwd(self.dir): self.cfg['source'] = tgt with log_file('darcs.log') as darcs_out: @@ -76,11 +102,25 @@ class Darcs(RevisionControl): self.set_current_head() def checkout(self, tgt, url, *args): + '''checks out source from an upstream url + + the difference between this and get is that it reflects an initial pull + idiom found in other VCSes. It also handles setting vc_url and the + canonical name, based on tgt + ''' self.cfg['vc_url'] = url self.cfg['hackage_name'] = split(tgt)[1] self.get(url, tgt, *args) def set_current_head(self): + '''sets the current internal state to reflect the current head + + chances are, the user should rarely mess with this. + + this may change, because rather than using .patch files for rpm + handling, we may ask the user to commit all the changes to darcs, + and then have devshell generate .patch files automatically instead + ''' log.debug(getcwd()) with pwd(self.cfg['source']): p = Popen(['darcs', 'changes', '--xml-output', '--last=1'], @@ -91,48 +131,57 @@ class Darcs(RevisionControl): self.cfg['head'] = (hash, date) def set_cur_to(self, *args): + '''passes arbitrary args to darcs get and makes a branch out of it + + this is not really optimal, because it only does things temporary, + we need to look at as systematic way to handle branching. + looking at a potential git and a potential cvs module may help + ''' cur_src_dir = self.cfg['source'] new_src_dir = cur_src_dir + '_tmp' self.get(cur_src_dir, new_src_dir, *args) def set_cur_to_patch(self, hash): + '''sets the current branch to fork off a particular hash''' self.set_cur_to('--to-match', 'hash ' + hash) def set_cur_to_tag(self, tag): + '''sets the current branch to fork off a particular tag''' self.set_cur_to('--tag', tag) @property def date(self): + '''get's the timestamp of the latest patch on HEAD of the current branch''' return self.cfg['head'][1] @property def hash(self): + '''gets the hash of the latest patch on HEAD of the current branch''' return self.cfg['head'][0] def print_date(self): + '''prints the timestamp of the latest patch on HEAD of the current branch''' log.info('The timestamp is ' + self.date) def print_hash(self): + '''prints the hash of the latest patch on HEAD of the current branch''' log.info('The hash is ' + self.hash) - def set_cur_to_patch(self, hash): - self.set_cur_to('--to-match', 'hash ' + hash) - - def set_cur_to_tag(self, tag): - self.set_cur_to('--tag', tag) - @contextmanager def patch(self, hash): + '''executes a block of code on top a particular patch''' with self.src('--to-match', 'hash ' + hash): yield @contextmanager def tag(self, tag): + '''executes a particular block of code on top of a particular tag''' with self.src('--tag', tag): yield def setup_sourceball(self): + '''gets darcs to spit out a sourceball to be used in rpm making by other modules''' log.debug('someone set us up the bomb') with pwd(self.dir): with pwd(self.source_dir()): diff --git a/modules/directory.py b/modules/directory.py index 139a2c9..16e5a58 100644 --- a/modules/directory.py +++ b/modules/directory.py @@ -29,8 +29,17 @@ from base.util import pwd, copytree from modules.dirfactory import DirFactory class Directory(Module): + '''a generic base class for any module that has to maintain state + on the file system in a directory + ''' _type = 'directory' def __init__(self, name=None): + ''' initializer + + name is a path to the directory we are loading, if not given + name is gleaned by assuming the current directory is the + package desired + ''' if not name: log.debug('no name with directory') cwd = getcwd() @@ -50,6 +59,8 @@ class Directory(Module): self.make_dir(dir) def is_sysdir_dir(self, dir): + '''given a directory, determine if the system directory is + already a directory or not''' with pwd(dir): cfg = ConfigObj('.devshell') try: @@ -62,6 +73,7 @@ class Directory(Module): return False def load_dir(self, dir): + '''presuming dir is a directory, load it's state''' log.debug('directory.load_dir') with pwd(dir): parent, name = split(getcwd()) @@ -75,10 +87,12 @@ class Directory(Module): pass def make_dir(self, dir): + '''since dir is not a directory, make it into one''' log.debug('directory.make_dir') with pwd(dir): self.cfg = ConfigObj('.devshell') parent, name = split(getcwd()) + # type is defined by the subclass self.cfg['type'] = self._type self.cfg['name'] = name self.parent = parent @@ -86,34 +100,55 @@ class Directory(Module): @property def name(self): + ''' the name of the directory/module as defined by the user + ''' return self.cfg['name'] @property def parent(self): + ''' the parent directory holding the directory on the file system + + this is determined at run time, and used to build absolute path names + out of elements of the directory + ''' return self.cfg['parent'] @property def dir(self): + '''absolute pathname to the directory where it is held on the file system''' return join(self.parent, self.name) def close(self): + '''called by devshell, closes the open objects''' log.debug('writing self.cfg for directory') with pwd(self.dir): self.cfg.write() def rename(self, new_name): + '''renames the directory internally, assuming it's been renamed + on the file system + + subclass authors take note, this must be reimplemented, and the + superclass version called when any property or state depends on + self.name in any way shape or form. + ''' self.cfg['name'] = new_name def move(self, new_loc): + '''given a new location, moves everything there, and renames''' new_loc = abspath(new_loc) new_parent, new_name = split(new_loc) old_dir = self.dir copytree(self.dir, new_loc) self.parent = new_parent self.rename(new_name) + with pwd(self.dir): + self.cfg.write() rm(old_dir) def copy(self, new_loc): + '''makes a copy of the contents in a new location, renames, + and returns a reference to a new object rpresenting the new directory''' new_loc = abspath(new_loc) new_parent, new_name = split(new_loc) copytree(self.dir, new_loc) diff --git a/modules/dirfactory.py b/modules/dirfactory.py index 30d5e59..1c2e5a2 100644 --- a/modules/dirfactory.py +++ b/modules/dirfactory.py @@ -40,6 +40,7 @@ foo = dict(foo) dirs = dict([[key.lower(), val] for key, val in foo.iteritems() if isclass(val) and issubclass(val, Directory)]) class DirFactory(Module): + '''creates a new object of type defined by a directory's .devshell file''' def __new__(cls, name=None): if not name: log.debug('no name with dirfactory') @@ -58,6 +59,7 @@ class DirFactory(Module): return foo def whatis_sysdir(dir): + ''' given a dir, determine it's type''' with pwd(dir): cfg = ConfigObj('.devshell') try: diff --git a/modules/mock.py b/modules/mock.py index e15a063..d7cf00d 100644 --- a/modules/mock.py +++ b/modules/mock.py @@ -30,11 +30,18 @@ from modules.package import Package from modules.profile import Profile class Mock(Module): + '''wrapper around mock for integrating with profiles and packages''' def __init__(self, profile, build): + '''initializer + + profile is a path to a profile directory + build is a path to a buildroot directory + ''' self.build = Build(build) self.profile = Profile(profile) def build_rpm(self, package): + '''builds an rpm from some package''' pkg = DirFactory(package) self.build.build_source_rpm(package, self.profile) @@ -49,7 +56,7 @@ class Mock(Module): log.debug('config_dir is ' + config_dir) cmd = ['mock', '-r', mock_cfg, '--configdir=%s' % config_dir, - '--resultdir=%s' % result_dir, + '--resultdir=%s' % result_dir, srpm_name] log.debug('cmd is ' + str(cmd)) with pwd(result_dir): diff --git a/modules/package.py b/modules/package.py index c697067..af0a48e 100644 --- a/modules/package.py +++ b/modules/package.py @@ -38,6 +38,9 @@ class Package(Directory): super(Package, self).make_dir(dir) def add_spec(self, spec_file): + '''add's a spec file to the package, and sets the canonical package + name based on the spec file, possibly renaming the spec file to match + within fedora guidelines''' log.debug('spec_file is %s' % spec_file) log.debug('spec_file_name is %s' % self.name + '.spec') #TODO: get the spec file name, copy @@ -55,30 +58,38 @@ class Package(Directory): @property def spec_file(self): + '''returns the name of the spec file as it should be accordingto + fedora guidelines, good for double checking''' return self.pkg_name + '.spec' @property def pkg_name(self): + '''canonical name of the package in a source repository''' return self.cfg['pkg_name'] def get_srpm_name(self, profile): + '''given a profile, determines that the source rpm should be called''' with pwd(self.dir): ver, rel = ver_rel(self.spec_file, profile.dist_defines) return '%s-%s-%s.src.rpm' % (self.pkg_name, ver, rel) def ver(self, profile=None): + '''given a profile, determines the version of the current spec file''' with pwd(self.dir): ver, rel = ver_rel(self.spec_file, profile.dist_defines if profile else '') return ver def source_dir(self, *args): - return join(self.dir, self.source(*args) - + '''provides an absolute pathname for where the primary source is unpacked''' + return join(self.dir, self.source(*args)) + def source(self, *args): + '''base method that should return where the source is kept to some yet unknown criteria''' raise NotImplementedError @property def sourceball(self): + '''the current sourceball in use, for building packages''' return self.cfg['sourceball'] __all__ = ['Package'] diff --git a/modules/profile.py b/modules/profile.py index 56639cb..dc4d44a 100644 --- a/modules/profile.py +++ b/modules/profile.py @@ -27,6 +27,7 @@ from base.vars import MOCK_CFG_DIR from modules.directory import Directory class Profile(Directory): + '''a profile module to resemble various architectures, branches, build targets, etc...''' _type = 'profile' @property def dist(self): @@ -36,39 +37,51 @@ class Profile(Directory): @property def distvar(self): - 'eg: fedora or rhel' + '''eg: fedora or rhel''' return self.cfg['distvar'] @property def distval(self): - 'eg: 10 or 3 (string form)' + '''eg: 10 or 3 (string form)''' return self.cfg['distval'] @property def koji_target(self): - 'eg: dist-f11' + '''eg: dist-f11''' return self.cfg['koji_target'] @property def dist_defines(self): + '''a list of parameters for an rpm aware function to redefine + certain macros to match the profile''' return dist_defines(self.dist, self.distvar, self.distval) # i'm not sure this is 100% relevant, mock cfg's might be named only after the arch used @property def mock_cfg(self): + '''the name of the mock config/profile to be used to compile packages for this profile + + this will probably change once we figure out how to handle branches and build targets better + ''' # TODO: buildarchs need to be handled somehow # yes i'm lame and i did this i386 only for now return get_mock_cfg(self.distvar, self.distval, 'i386') @property def mock_cfg_dir(self): + '''directory where profile wide mock settings are kept''' return join(self.dir, 'mock') @property def result_dir(self): + '''where to store results from mock''' return self.dir def configure_from_system(self, branch): + '''sets up a profile based on system profiles + + branch is a branch name from the fedora-cvs + ''' self.cfg['branch'] = branch d = distro[branch] self.cfg['koji_target'] = d[TARGET] diff --git a/modules/revisioncontrol.py b/modules/revisioncontrol.py index 0e87065..80efaa2 100644 --- a/modules/revisioncontrol.py +++ b/modules/revisioncontrol.py @@ -19,7 +19,7 @@ from modules.package import Package class RevisionControl(Package): - + '''base class for any module that implements a VCS system and wraps it for getting source for packages''' pass __all__ = ['RevisionControl'] diff --git a/modules/sourceball.py b/modules/sourceball.py index 4a39e94..ac306cc 100644 --- a/modules/sourceball.py +++ b/modules/sourceball.py @@ -30,17 +30,27 @@ from base.util import pwd, copy from modules.package import Package class SourceBall(Package): + '''a type of package that is a single sourceball, a spec file, and some patches''' _type = 'sourceball' def orig_dir(self, dir): + '''where is the original source kept + + use for making patches against modified source''' return dir + '_orig' def source(self, *args): + '''gives source directory + + first parameter, if 'orig' gives the original source, otherwise + you get the modified source + ''' if args[0] == 'orig': return self.orig_dir(self.cfg['source']) else: return self.cfg['source'] def add_sourceball(self, sourceball_name, extract_dir=None): + '''given a path to a sourceball, set it up here''' log.debug('addincg sourceball with dir ' + self.dir) with pwd(self.dir): try: |