Implemented command line parsing using argparse
authorEd Bartosh <eduard.bartosh@intel.com>
Tue, 25 Sep 2012 15:07:17 +0000 (18:07 +0300)
committerEd Bartosh <eduard.bartosh@intel.com>
Fri, 28 Sep 2012 08:25:12 +0000 (11:25 +0300)
Fixes: #259
Change-Id: I25c6eabfedc30f089a0c5cb4cebaac3b31b97c8c

12 files changed:
gitbuildsys/cmd_build.py
gitbuildsys/cmd_changelog.py
gitbuildsys/cmd_chroot.py
gitbuildsys/cmd_export.py
gitbuildsys/cmd_import.py
gitbuildsys/cmd_remotebuild.py
gitbuildsys/cmd_submit.py
gitbuildsys/parsing.py [new file with mode: 0644]
tests/test_changelog.py
tests/test_help.py
tests/test_import.py
tools/gbs

index 93bfd942f1d4ddb2fe4c1ba494c55d456f5e1641..c6c7c90efa80057732ecad522b842668afa4d3da 100644 (file)
@@ -59,7 +59,7 @@ SUPPORTEDARCHS = [
             'armv7l',
           ]
 
-def prepare_repos_and_build_conf(opts, arch):
+def prepare_repos_and_build_conf(args, arch):
     '''generate repos and build conf options for depanneur'''
 
     cmd_opts = []
@@ -73,13 +73,13 @@ def prepare_repos_and_build_conf(opts, arch):
         os.makedirs(cachedir)
     msger.info('generate repositories ...')
 
-    if opts.skip_conf_repos:
+    if args.skip_conf_repos:
         repos = []
     else:
         repos = [i.url for i in configmgr.get_current_profile().repos]
 
-    if opts.repositories:
-        for i in opts.repositories:
+    if args.repositories:
+        for i in args.repositories:
             try:
                 opt_repo = SafeURL(i)
             except ValueError, err:
@@ -97,8 +97,8 @@ def prepare_repos_and_build_conf(opts, arch):
                     'following repos:\n%s' % (arch, '\n'.join(repos)))
     cmd_opts += [('--repository=%s' % url.full) for url in repourls]
 
-    if opts.dist:
-        distconf = opts.dist
+    if args.dist:
+        distconf = args.dist
         if not os.path.exists(distconf):
             msger.error('specified build conf %s does not exists' % distconf)
     else:
@@ -129,32 +129,32 @@ def prepare_repos_and_build_conf(opts, arch):
 
     return cmd_opts
 
-def prepare_depanneur_opts(opts):
+def prepare_depanneur_opts(args):
     '''generate extra options for depanneur'''
 
     cmd_opts = []
-    if opts.exclude:
-        cmd_opts += ['--exclude=%s' % i for i in opts.exclude]
-    if opts.exclude_from_file:
-        cmd_opts += ['--exclude-from-file=%s' % opts.exclude_from_file]
-    if opts.overwrite:
+    if args.exclude:
+        cmd_opts += ['--exclude=%s' % i for i in args.exclude]
+    if args.exclude_from_file:
+        cmd_opts += ['--exclude-from-file=%s' % args.exclude_from_file]
+    if args.overwrite:
         cmd_opts += ['--overwrite']
-    if opts.clean_once:
+    if args.clean_once:
         cmd_opts += ['--clean-once']
-    if opts.debug:
+    if args.debug:
         cmd_opts += ['--debug']
-    if opts.incremental:
+    if args.incremental:
         cmd_opts += ['--incremental']
-    if opts.keepgoing:
+    if args.keepgoing:
         cmd_opts += ['--keepgoing']
-    if opts.no_configure:
+    if args.no_configure:
         cmd_opts += ['--no-configure']
-    if opts.binary_list:
-        if not os.path.exists(opts.binary_list):
+    if args.binary_list:
+        if not os.path.exists(args.binary_list):
             msger.error('specified binary list file %s not exists' %\
-                        opts.binary_list)
-        cmd_opts += ['--binary=%s' % opts.binary_list]
-    cmd_opts += ['--threads=%s' % opts.threads]
+                        args.binary_list)
+        cmd_opts += ['--binary=%s' % args.binary_list]
+    cmd_opts += ['--threads=%s' % args.threads]
 
     return cmd_opts
 
@@ -266,19 +266,13 @@ def setup_qemu_emulator():
     return qemu_emulator
 
 
-def do(opts, args):
-    """
-    Main of build module
-    """
-    workdir = os.getcwd()
-    if len(args) > 1:
-        msger.error('only one work directory can be specified in args.')
-    if len(args) == 1:
-        workdir = os.path.abspath(args[0])
+def main(args):
+    """gbs build entry point."""
 
-    if opts.commit and opts.include_all:
+    if args.commit and args.include_all:
         raise errors.Usage('--commit can\'t be specified together with '\
                            '--include-all')
+    workdir = args.gitdir
 
     try:
         repo = RpmGitRepository(workdir)
@@ -287,8 +281,8 @@ def do(opts, args):
         pass
 
     hostarch = get_hostarch()
-    if opts.arch:
-        buildarch = opts.arch
+    if args.arch:
+        buildarch = args.arch
     else:
         buildarch = hostarch
         msger.info('No arch specified, using system arch: %s' % hostarch)
@@ -302,8 +296,8 @@ def do(opts, args):
     build_root = os.path.expanduser('~/GBS-ROOT/')
     if 'TIZEN_BUILD_ROOT' in os.environ:
         build_root = os.environ['TIZEN_BUILD_ROOT']
-    if opts.buildroot:
-        build_root = opts.buildroot
+    if args.buildroot:
+        build_root = args.buildroot
     os.environ['TIZEN_BUILD_ROOT'] = build_root
 
     # get virtual env from system env first
@@ -314,19 +308,19 @@ def do(opts, args):
 
     cmd += ['--arch=%s' % buildarch]
 
-    if opts.clean:
+    if args.clean:
         cmd += ['--clean']
 
     # check & prepare repos and build conf
-    cmd += prepare_repos_and_build_conf(opts, buildarch)
+    cmd += prepare_repos_and_build_conf(args, buildarch)
 
     cmd += ['--path=%s' % workdir]
 
-    if opts.ccache:
+    if args.ccache:
         cmd += ['--ccache']
 
-    if opts.extra_packs:
-        cmd += ['--extra-packs=%s' % opts.extra_packs]
+    if args.extra_packs:
+        cmd += ['--extra-packs=%s' % args.extra_packs]
 
     if hostarch != buildarch and buildarch in CHANGE_PERSONALITY:
         cmd = [ CHANGE_PERSONALITY[buildarch] ] + cmd
@@ -338,19 +332,19 @@ def do(opts, args):
             msger.error('%s' % exc)
 
     # Extra depanneur special command options
-    cmd += prepare_depanneur_opts(opts)
+    cmd += prepare_depanneur_opts(args)
 
     # Extra options for gbs export
-    if opts.include_all:
+    if args.include_all:
         cmd += ['--include-all']
-    if opts.commit:
-        cmd += ['--commit=%s' % opts.commit]
-    if opts.upstream_branch:
-        cmd += ['--upstream-branch=%s' % opts.upstream_branch]
-    if opts.upstream_tag:
-        cmd += ['--upstream-tag=%s' % opts.upstream_tag]
-    if opts.squash_patches_until:
-        cmd += ['--squash-patches-until=%s' % opts.squash_patches_until]
+    if args.commit:
+        cmd += ['--commit=%s' % args.commit]
+    if args.upstream_branch:
+        cmd += ['--upstream-branch=%s' % args.upstream_branch]
+    if args.upstream_tag:
+        cmd += ['--upstream-tag=%s' % args.upstream_tag]
+    if args.squash_patches_until:
+        cmd += ['--squash-patches-until=%s' % args.squash_patches_until]
 
     msger.debug("running command: %s" % ' '.join(cmd))
     retcode = os.system(' '.join(cmd))
index f205d8f9437bb6599035f6e411cd505c0f33b859..d477b694cb5f92e89f304c9c6f3c6fa470a5d531 100644 (file)
@@ -79,10 +79,11 @@ def make_log_entries(commits, git_repo):
     return entries
 
 
-def do(opts, _args):
+def main(args):
+    """gbs changelog entry point."""
 
     try:
-        repo = RpmGitRepository('.')
+        repo = RpmGitRepository(args.gitdir)
     except GitRepositoryError, err:
         msger.error(str(err))
 
@@ -103,9 +104,9 @@ def do(opts, _args):
         else:
             msger.error("Found no changes nor spec files under packaging dir")
 
-    # get the commit start from the opts.since
-    if opts.since:
-        since = opts.since
+    # get the commit start from the args.since
+    if args.since:
+        since = args.since
     else:
         since = get_latest_rev(fn_changes)
 
@@ -114,8 +115,8 @@ def do(opts, _args):
         try:
             commitid_since = repo.rev_parse(since)
         except GitRepositoryError:
-            if opts.since:
-                msger.error("Invalid commit: %s" % (opts.since))
+            if args.since:
+                msger.error("Invalid commit: %s" % (since))
             else:
                 msger.error("Can't find last commit ID in the log, "\
                             "please specify it by '--since'")
@@ -126,9 +127,9 @@ def do(opts, _args):
     if not commits:
         msger.error("Nothing found between %s and HEAD" % commitid_since)
 
-    if opts.message:
+    if args.message:
         author = repo.get_author_info()
-        lines = ["- %s" % line for line in opts.message.split(os.linesep) \
+        lines = ["- %s" % line for line in args.message.split(os.linesep) \
                                             if line.strip()]
         new_entries = ["* %s %s <%s> %s" % \
                            (datetime.datetime.now().strftime("%a %b %d %Y"),
index e69dd90471bad623b68538ffbc0dcb6da0da45dd..c126e8f7a2c42cd5483c121b5f8a0807408a8408 100644 (file)
 import os
 import subprocess
 
-from gitbuildsys import msger, errors
+from gitbuildsys import msger
 
-def do(opts, args):
+def main(args):
+    """gbs chroot entry point."""
 
-    if len(args) != 1:
-        raise errors.Usage('no build root directory specified')
+    build_root = args.buildroot
 
-    build_root = os.path.abspath(args[0])
     running_lock = '%s/not-ready' % build_root
-    if os.path.exists(running_lock) or not os.path.exists(build_root):
+    if os.path.exists(running_lock):
         msger.error('build root %s is not ready' % build_root)
 
     msger.info('chroot %s' % build_root)
     user = 'abuild'
-    if opts.root:
+    if args.root:
         user = 'root'
     cmd = ['sudo', 'chroot', build_root, 'su', user]
 
index b23b1335b171cb00bb25714850317eff21f726b1..b8a14fba9efc2e8abf4193c0504f9354ce1e47ca 100644 (file)
@@ -24,7 +24,7 @@ import re
 import shutil
 import errno
 
-from gitbuildsys import msger, runner, utils, errors
+from gitbuildsys import msger, utils, errors
 from gitbuildsys.conf import configmgr
 
 from gbp.scripts.buildpackage_rpm import main as gbp_build
@@ -45,12 +45,12 @@ def mkdir_p(path):
         else:
             raise
 
-def is_native_pkg(repo, opts):
+def is_native_pkg(repo, args):
     """
     Determine if the package is "native"
     """
-    if opts.upstream_branch:
-        upstream_branch = opts.upstream_branch
+    if args.upstream_branch:
+        upstream_branch = args.upstream_branch
     else:
         upstream_branch = configmgr.get('upstream_branch', 'general')
     return not repo.has_branch(upstream_branch)
@@ -59,17 +59,17 @@ def transform_var_format_from_shell_to_python(whole):
     '''replace string like ${xxx} with %(xxx)s'''
     return re.sub(r'\$\{([^}]+)\}', r'%(\1)s', whole)
 
-def create_gbp_export_args(repo, commit, export_dir, tmp_dir, spec, opts,
+def create_gbp_export_args(repo, commit, export_dir, tmp_dir, spec, args,
                            force_native=False):
     """
     Construct the cmdline argument list for git-buildpackage export
     """
-    if opts.upstream_branch:
-        upstream_branch = opts.upstream_branch
+    if args.upstream_branch:
+        upstream_branch = args.upstream_branch
     else:
         upstream_branch = configmgr.get('upstream_branch', 'general')
-    if opts.upstream_tag:
-        upstream_tag = opts.upstream_tag
+    if args.upstream_tag:
+        upstream_tag = args.upstream_tag
     else:
         upstream_tag = configmgr.get('upstream_tag', 'general')
         upstream_tag = transform_var_format_from_shell_to_python(upstream_tag)
@@ -77,13 +77,13 @@ def create_gbp_export_args(repo, commit, export_dir, tmp_dir, spec, opts,
     msger.debug("Using upstream tag format: '%s'" % upstream_tag)
 
     # Get patch squashing option
-    if opts.squash_patches_until:
-        squash_patches_until = opts.squash_patches_until
+    if args.squash_patches_until:
+        squash_patches_until = args.squash_patches_until
     else:
         squash_patches_until = configmgr.get('squash_patches_until', 'general')
 
     # Now, start constructing the argument list
-    args = ["argv[0] placeholder",
+    argv = ["argv[0] placeholder",
             "--git-ignore-new",
             "--git-upstream-branch=upstream",
             "--git-export-dir=%s" % export_dir,
@@ -93,21 +93,21 @@ def create_gbp_export_args(repo, commit, export_dir, tmp_dir, spec, opts,
             "--git-export=%s" % commit,
             "--git-upstream-branch=%s" % upstream_branch,
             "--git-upstream-tag=%s" % upstream_tag]
-    if force_native or is_native_pkg(repo, opts):
-        args.extend(["--git-no-patch-export",
+    if force_native or is_native_pkg(repo, args):
+        argv.extend(["--git-no-patch-export",
                      "--git-upstream-tree=%s" % commit])
     else:
-        args.extend(["--git-patch-export",
+        argv.extend(["--git-patch-export",
                      "--git-patch-export-compress=100k",
                      "--git-force-create",
                      "--git-patch-export-squash-until=%s" %
                             squash_patches_until,
                      "--git-patch-export-ignore-path=^packaging/.*"])
         if repo.has_branch("pristine-tar"):
-            args.extend(["--git-pristine-tar"])
+            argv.extend(["--git-pristine-tar"])
 
-    if opts.source_rpm:
-        args.extend(['--git-builder=rpmbuild',
+    if args.source_rpm:
+        argv.extend(['--git-builder=rpmbuild',
                      '--git-rpmbuild-builddir=.',
                      '--git-rpmbuild-builddir=.',
                      '--git-rpmbuild-rpmdir=.',
@@ -118,11 +118,11 @@ def create_gbp_export_args(repo, commit, export_dir, tmp_dir, spec, opts,
                      '--short-circuit', '-bs',
                      ])
     else:
-        args.extend(["--git-builder=osc", "--git-export-only"])
+        argv.extend(["--git-builder=osc", "--git-export-only"])
 
-    return args
+    return argv
 
-def export_sources(repo, commit, export_dir, spec, opts):
+def export_sources(repo, commit, export_dir, spec, args):
     """
     Export packaging files using git-buildpackage
     """
@@ -130,10 +130,10 @@ def export_sources(repo, commit, export_dir, spec, opts):
                             directory=True)
 
     gbp_args = create_gbp_export_args(repo, commit, export_dir, tmp.path,
-                                      spec, opts)
+                                      spec, args)
     try:
         ret = gbp_build(gbp_args)
-        if ret == 2 and not is_native_pkg(repo, opts):
+        if ret == 2 and not is_native_pkg(repo, args):
             # Try falling back to old logic of one monolithic tarball
             msger.warning("Generating upstream tarball and/or generating "\
                           "patches failed. GBS tried this as you have "\
@@ -146,7 +146,7 @@ def export_sources(repo, commit, export_dir, spec, opts):
             msger.info("Falling back to the old method of generating one "\
                        "monolithic source archive")
             gbp_args = create_gbp_export_args(repo, commit, export_dir,
-                                              tmp.path, spec, opts,
+                                              tmp.path, spec, args,
                                               force_native=True)
             ret = gbp_build(gbp_args)
         if ret:
@@ -155,27 +155,20 @@ def export_sources(repo, commit, export_dir, spec, opts):
         msger.error("Repository error: %s" % excobj)
 
 
-def do(opts, args):
-    """
-    The main plugin call
-    """
-    workdir = os.getcwd()
+def main(args):
+    """gbs export entry point."""
 
-    if len(args) > 1:
-        msger.error('only one work directory can be specified in args.')
-    if len(args) == 1:
-        workdir = os.path.abspath(args[0])
-
-    if opts.commit and opts.include_all:
+    if args.commit and args.include_all:
         raise errors.Usage('--commit can\'t be specified together with '\
                            '--include-all')
 
+    workdir = args.gitdir
     try:
         repo = RpmGitRepository(workdir)
     except GitRepositoryError, err:
         msger.error(str(err))
 
-    utils.git_status_checker(repo, opts)
+    utils.git_status_checker(repo, args)
     workdir = repo.path
 
     if not os.path.exists("%s/packaging" % workdir):
@@ -183,11 +176,11 @@ def do(opts, args):
 
     # Only guess spec filename here, parse later when we have the correct
     # spec file at hand
-    specfile = utils.guess_spec(workdir, opts.spec)
+    specfile = utils.guess_spec(workdir, args.spec)
 
     outdir = "%s/packaging" % workdir
-    if opts.outdir:
-        outdir = opts.outdir
+    if args.outdir:
+        outdir = args.outdir
     mkdir_p(outdir)
     outdir = os.path.abspath(outdir)
     tmpdir     = configmgr.get('tmpdir', 'general')
@@ -196,14 +189,14 @@ def do(opts, args):
     export_dir = tempd.path
 
     with utils.Workdir(workdir):
-        if opts.commit:
-            commit = opts.commit
-        elif opts.include_all:
+        if args.commit:
+            commit = args.commit
+        elif args.include_all:
             commit = 'WC.UNTRACKED'
         else:
             commit = 'HEAD'
         relative_spec = specfile.replace('%s/' % workdir, '')
-        export_sources(repo, commit, export_dir, relative_spec, opts)
+        export_sources(repo, commit, export_dir, relative_spec, args)
 
     try:
         spec = rpm.parse_spec(os.path.join(export_dir,
@@ -218,7 +211,6 @@ def do(opts, args):
                                   spec.release)
         shutil.rmtree(outdir, ignore_errors=True)
         shutil.move(export_dir, outdir)
-
         msger.info('source rpm generated to:\n     %s/%s.src.rpm' % \
                    (outdir, os.path.basename(outdir)))
 
index a7641896b79446dc7596ae93eb66e1e83356d50a..71bee7ea161d5b68ce628e712c6ba30ee2d2a8f1 100644 (file)
@@ -25,31 +25,27 @@ from gitbuildsys import msger
 from gbp.scripts.import_srpm import main as gbp_import_srpm
 from gbp.scripts.import_orig_rpm import main as gbp_import_orig
 
-def do(opts, args):
+def main(args):
+    """gbs import entry point."""
 
-    if opts.author_name:
-        os.environ["GIT_AUTHOR_NAME"] = opts.author_name
-    if opts.author_email:
-        os.environ["GIT_AUTHOR_EMAIL"] = opts.author_email
+    if args.author_name:
+        os.environ["GIT_AUTHOR_NAME"] = args.author_name
+    if args.author_email:
+        os.environ["GIT_AUTHOR_EMAIL"] = args.author_email
 
-    if len(args) < 1:
-        msger.error('missing argument, please reference gbs import --help.')
-    if len(args) > 1:
-        msger.error('too many arguments! Please reference gbs import --help.')
-    if not os.path.exists(args[0]):
-        msger.error('%s not exist' % args[0])
+    path = args.path
 
     params = ["argv[0] placeholder", "--packaging-dir=packaging",
-              "--upstream-branch=%s" % opts.upstream_branch, args[0]]
+              "--upstream-branch=%s" % args.upstream_branch, path]
 
-    if args[0].endswith('.src.rpm') or args[0].endswith('.spec'):
+    if path.endswith('.src.rpm') or path.endswith('.spec'):
         params.append("--no-patch-import")
         if gbp_import_srpm(params):
-            msger.error("Failed to import %s" % args[0])
+            msger.error("Failed to import %s" % path)
     else:
-        if opts.no_merge:
+        if args.no_merge:
             params.insert(1, '--no-merge')
         if gbp_import_orig(params):
-            msger.error('Failed to import %s' % args[0])
+            msger.error('Failed to import %s' % path)
 
     msger.info('done.')
index 06ef1b2bc262a52775f26f85c7adc2fef7821c9f..e759d75637a42becb68616261afd3c7a1af3d635 100644 (file)
@@ -45,27 +45,11 @@ passx=%(passwdx)s
 """
 
 
-def do(opts, args):
-
-    obs_repo = None
-    obs_arch = None
-
-    if len(args) == 0:
-        workdir = os.getcwd()
-    elif len(args) == 1:
-        workdir = os.path.abspath(args[0])
-    elif len(args) == 2 and opts.buildlog:
-        workdir = os.getcwd()
-        obs_repo = args[0]
-        obs_arch = args[1]
-    elif len(args) == 3 and opts.buildlog:
-        workdir = os.path.abspath(args[0])
-        obs_repo = args[1]
-        obs_arch = args[2]
-    else:
-        msger.error('Invalid arguments, see gbs remotebuild -h for more info')
+def main(args):
+    """gbs remotebuild entry point."""
 
     obsconf = configmgr.get_current_profile().obs
+
     if not obsconf or not obsconf.url:
         msger.error('no obs api found, please add it to gbs conf and try again')
 
@@ -75,25 +59,32 @@ def do(opts, args):
         msger.error('empty user is not allowed for remotebuild, '
                     'please add user/passwd to gbs conf, and try again')
 
-    if opts.commit and opts.include_all:
-        raise errors.Usage('--commit can\'t be specified together with '\
+    if args.commit and args.include_all:
+        raise errors.Usage('--commit can\'t be specified together with '
                            '--include-all')
 
+    obs_repo = args.repository
+    obs_arch = args.arch
+
+    if args.buildlog and None in (obs_repo, obs_arch):
+        msger.error('please specify arch(-A) and repository(-R)')
+
+    workdir = args.gitdir
+
     try:
         repo = RpmGitRepository(workdir)
     except GitRepositoryError, err:
         msger.error(str(err))
 
-    if not (opts.buildlog or opts.status):
-        utils.git_status_checker(repo, opts)
-    workdir = repo.path
+    if not (args.buildlog or args.status):
+        utils.git_status_checker(repo, args)
 
     # TODO: check ./packaging dir at first
     specs = glob.glob('%s/packaging/*.spec' % workdir)
     if not specs:
         msger.error('no spec file found under /packaging sub-directory')
 
-    specfile = utils.guess_spec(workdir, opts.spec)
+    specfile = utils.guess_spec(workdir, args.spec)
     # get 'name' and 'version' from spec file
     try:
         spec = gbp.rpm.parse_spec(specfile)
@@ -104,16 +95,13 @@ def do(opts, args):
         msger.error('can\'t get correct name.')
     package = spec.name
 
-    if opts.base_obsprj is None:
-        base_prj = obsconf.base or 'Tizen:Main'
-    else:
-        base_prj = opts.base_obsprj
+    base_prj = args.base_obsprj
 
-    if opts.target_obsprj is None:
+    if args.target_obsprj is None:
         target_prj = obsconf.target or \
             "home:%s:gbs:%s" % (apiurl.user, base_prj)
     else:
-        target_prj = opts.target_obsprj
+        target_prj = args.target_obsprj
 
     api_passwd = apiurl.passwd if apiurl.passwd else ''
     # Create temporary oscrc
@@ -135,7 +123,7 @@ def do(opts, args):
     api = OSC(apiurl, oscrc=oscrcpath)
 
     try:
-        if opts.buildlog:
+        if args.buildlog:
             archlist = []
             status = api.get_results(target_prj, package)
 
@@ -158,7 +146,7 @@ def do(opts, args):
 
             return 0
 
-        if opts.status:
+        if args.status:
             results = []
 
             status = api.get_results(target_prj, package)
@@ -175,7 +163,7 @@ def do(opts, args):
         if not api.exists(target_prj):
             # FIXME: How do you know that a certain user does not have
             # permissions to create any project, anywhewre?
-            if opts.target_obsprj and \
+            if args.target_obsprj and \
                    not target_prj.startswith('home:%s:' % apiurl.user):
                 msger.error('no permission to create project %s, only sub '
                             'projects of home:%s are '
@@ -194,17 +182,17 @@ def do(opts, args):
         msger.error(str(err))
 
     with utils.Workdir(workdir):
-        if opts.commit:
-            commit = opts.commit
-        elif opts.include_all:
+        if args.commit:
+            commit = args.commit
+        elif args.include_all:
             commit = 'WC.UNTRACKED'
         else:
             commit = 'HEAD'
         relative_spec = specfile.replace('%s/' % workdir, '')
-        export_sources(repo, commit, exportdir, relative_spec, opts)
+        export_sources(repo, commit, exportdir, relative_spec, args)
 
     try:
-        commit_msg = repo.get_commit_info(opts.commit or 'HEAD')['subject']
+        commit_msg = repo.get_commit_info(args.commit or 'HEAD')['subject']
     except GitRepositoryError, exc:
         msger.error('failed to get commit info: %s' % exc)
 
index 6ef9f29816d7ae6bd665284cd969c1c98c490ba3..0c9cb36c2df7a93a11a23161bb86f7d67d934004 100644 (file)
@@ -16,8 +16,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc., 59
 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
-"""Implementation of subcmd: submit
-"""
+"""Implementation of subcmd: submit"""
 
 import os
 import time
@@ -27,31 +26,25 @@ from gitbuildsys import msger, errors
 from gbp.rpm.git import GitRepositoryError, RpmGitRepository
 
 
-def do(opts, args):
+def main(args):
+    """gbs submit entry point."""
 
-    workdir = os.getcwd()
-    if len(args) > 1:
-        msger.error('only one work directory can be specified in args.')
-    if len(args) == 1:
-        workdir = os.path.abspath(args[0])
-
-    if opts.msg is None:
-        raise errors.Usage('message for tag must be specified with -m option')
+    workdir = args.gitdir
 
     try:
         repo = RpmGitRepository(workdir)
-        commit = repo.rev_parse(opts.commit)
-        if opts.target:
-            target_branch = opts.target
+        commit = repo.rev_parse(args.commit)
+        if args.target:
+            target_branch = args.target
         else:
             target_branch = repo.get_branch()
     except GitRepositoryError, err:
         msger.error(str(err))
 
-    if not opts.target:
+    if not args.target:
         try:
             upstream = repo.get_upstream_branch(target_branch)
-            if upstream and upstream.startswith(opts.remote):
+            if upstream and upstream.startswith(args.remote):
                 target_branch = os.path.basename(upstream)
             else:
                 msger.warning('can\'t find upstream branch for current branch '\
@@ -67,14 +60,14 @@ def do(opts, args):
         tagname = 'submit/%s/%s' % (target_branch, time.strftime( \
                                     '%Y%m%d.%H%M%S', time.gmtime()))
         msger.info('creating tag: %s' % tagname)
-        repo.create_tag(tagname, msg=opts.msg, commit=commit, sign=opts.sign,
-                                                 keyid=opts.user_key)
+        repo.create_tag(tagname, msg=args.msg, commit=commit, sign=args.sign,
+                                                 keyid=args.user_key)
     except GitRepositoryError, err:
         msger.error('failed to create tag %s: %s ' % (tagname, str(err)))
 
     try:
         msger.info('pushing tag to remote server')
-        repo.push_tag(opts.remote, tagname)
+        repo.push_tag(args.remote, tagname)
     except GitRepositoryError, err:
         repo.delete_tag(tagname)
         msger.error('failed to push tag %s :%s' % (tagname, str(err)))
diff --git a/gitbuildsys/parsing.py b/gitbuildsys/parsing.py
new file mode 100644 (file)
index 0000000..68c0cea
--- /dev/null
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# vim: ai ts=4 sts=4 et sw=4
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation; version 2 of the License
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+"""Local additions to commandline parsing."""
+
+import re
+import functools
+
+from argparse import RawDescriptionHelpFormatter
+
+class GbsHelpFormatter(RawDescriptionHelpFormatter):
+    """Changed default arparse help output by request from cmdln lovers."""
+
+    def __init__(self, *args, **kwargs):
+        super(GbsHelpFormatter, self).__init__(*args, **kwargs)
+        self._aliases = {}
+
+    def add_argument(self, action):
+        """Collect aliases."""
+
+        if action.choices:
+            for item, parser in action.choices.iteritems():
+                self._aliases[str(item)] = parser.get_default('alias')
+
+        return super(GbsHelpFormatter, self).add_argument(action)
+
+    def format_help(self):
+        """
+        There is no safe and documented way in argparser to reformat
+        help output through APIs as almost all of them are private,
+        so this method just parses the output and changes it.
+        """
+        result = []
+        subcomm = False
+        for line in super(GbsHelpFormatter, self).format_help().split('\n'):
+            if line.strip().startswith('{'):
+                continue
+            if line.startswith('optional arguments:'):
+                line = 'Global Options:'
+            if line.startswith('usage:'):
+                line = "Usage: gbs [GLOBAL-OPTS] SUBCOMMAND [OPTS]"
+            if subcomm:
+                match = re.match("[ ]+([^ ]+)[ ]+(.+)", line)
+                if match:
+                    name, help_text  = match.group(1), match.group(2)
+                    alias = self._aliases.get(name) or ''
+                    if alias:
+                        alias = "(%s)" % alias
+                    line = "  %-22s%s" % ("%s %s" % (name, alias), help_text)
+            if line.strip().startswith('subcommands:'):
+                line = 'Subcommands:'
+                subcomm = True
+            result.append(line)
+        return '\n'.join(result)
+
+def subparser(func):
+    """Convenient decorator for subparsers."""
+    @functools.wraps(func)
+    def wrapper(parser):
+        """
+        Create subparser
+        Set first line of function's docstring as a help
+        and the rest of the lines as a description.
+        Set attribute 'module' of subparser to 'cmd'+first part of function name
+        """
+        splitted = func.__doc__.split('\n')
+        name = func.__name__.split('_')[0]
+        subpar = parser.add_parser(name, help=splitted[0],
+                                   description='\n'.join(splitted[1:]),
+                                   formatter_class=RawDescriptionHelpFormatter)
+        subpar.set_defaults(module="cmd_%s" % name)
+        return func(subpar)
+    return wrapper
index 905c43050d198db0006e0210042a3a92ee1925e0..c948e43128bd0837cb27c6b62eeb4e47966a186e 100644 (file)
@@ -32,7 +32,7 @@ from gbp.git.repository import GitRepository
 
 from gitbuildsys import cmd_changelog
 
-GBS = imp.load_source("gbs", "./tools/gbs").Gbs().main
+GBS = imp.load_source("gbs", "./tools/gbs").main
 ENV = {}
 
 def setup_module():
index 724f19bbb7ecff6853ff0684fe8abb392d61c0bb..8a9464560a185feb527c45e040e4b0e3fb230c73 100644 (file)
@@ -23,7 +23,7 @@ import imp
 
 from nose.tools import eq_
 
-GBS = imp.load_source("gbs", "./tools/gbs").Gbs().main
+GBS = imp.load_source("gbs", "./tools/gbs").main
 
 class TestHelp(unittest.TestCase):
     """Test help output of gbs commands"""
@@ -31,16 +31,20 @@ class TestHelp(unittest.TestCase):
     @staticmethod
     def test_subcommand_help():
         """Test running gbs help with all possible subcommands."""
-        for sub in [ "build", "lb", "remotebuild", "rb", "changelog",
+        for sub in [ "build", "lb", "remotebuild", "rb", "changelog", "ch",
                      "submit", "sr", "export", "ex", "import", "im",
-                     "chroot"]:
+                     "chroot", "chr"]:
 
-            eq_(GBS(argv=["gbs", "help", sub]), None)
-            eq_(GBS(argv=["gbs", sub, "--help"]), 0)
+            try:
+                print '>>>sub', sub
+                GBS(argv=["gbs", sub, "--help"])
+            except SystemExit, err:
+                eq_(err.code, 0)
 
     @staticmethod
     def test_help():
         """Test running gbs --help and gbs help."""
-        eq_(GBS(argv=["gbs", "--help"]), 0)        
-        eq_(GBS(argv=["gbs", "help"]), None)
-
+        try:
+            GBS(argv=["gbs", "--help"])
+        except SystemExit, err:
+            eq_(err.code, 0)
index a554f597e8b7cbca143de04902feb920824a7900..3fa70d919d84cc753f8db4c7bb000631f3cd6965 100644 (file)
@@ -30,7 +30,7 @@ from nose.tools import eq_, raises
 
 from gbp.git.repository import GitRepository
 
-GBS = imp.load_source("gbs", "./tools/gbs").Gbs
+GBS = imp.load_source("gbs", "./tools/gbs").main
 
 def with_data(fname):
     """
@@ -66,7 +66,6 @@ class TestImport(unittest.TestCase):
 
     def __init__(self, method):
         super(TestImport, self).__init__(method)
-        self.gbs = GBS().main
 
     def setUp(self):
         self.tmp = tempfile.mkdtemp(prefix="test-gbs-import-")
@@ -81,7 +80,7 @@ class TestImport(unittest.TestCase):
     @with_data("ail-0.2.29-2.3.src.rpm")
     def test_import_srcrpm(self, srcrpm):
         """Test importing from source rpm."""
-        eq_(self.gbs(argv=["gbs", "import", srcrpm]), None)
+        eq_(GBS(argv=["gbs", "import", srcrpm]), None)
         repo = GitRepository("./ail")
         eq_(repo.get_local_branches(), ['master', 'upstream'])
         eq_(repo.get_tags(), ['upstream/0.2.29', 'vendor/0.2.29-2.3'])
@@ -89,8 +88,8 @@ class TestImport(unittest.TestCase):
     @with_data("bluez_unpacked")
     def test_import_spec(self, srcdir):
         """Test importing from spec."""
-        eq_(self.gbs(argv=["gbs", "import",
-                           os.path.join(srcdir, 'bluez.spec')]), None)
+        eq_(GBS(argv=["gbs", "import",
+                      os.path.join(srcdir, 'bluez.spec')]), None)
         repo = GitRepository("./bluez")
         eq_(repo.get_local_branches(), ['master', 'upstream'])
         eq_(repo.get_tags(), ['upstream/4.87', 'vendor/4.87-1'])
@@ -103,7 +102,7 @@ class TestImport(unittest.TestCase):
         # Create empty git repo
         repo = GitRepository.create("./repo_dir")
         os.chdir(repo.path)
-        eq_(self.gbs(argv=["gbs", "import", srcrpm]), None)
+        eq_(GBS(argv=["gbs", "import", srcrpm]), None)
         eq_(repo.get_local_branches(), ['master', 'upstream'])
         eq_(repo.get_tags(), ['upstream/0.2.29', 'vendor/0.2.29-2.5'])
 
@@ -112,9 +111,9 @@ class TestImport(unittest.TestCase):
     @with_data("app-core-1.2-19.3.src.rpm")
     def test_set_author_name_email(self, srcrpm):
         """Test --author-name and --author-email command line options."""
-        eq_(self.gbs(argv=["gbs", "import", "--author-name=test",
-                           "--author-email=test@otctools.jf.intel.com",
-                           srcrpm]), None)
+        eq_(GBS(argv=["gbs", "import", "--author-name=test",
+                      "--author-email=test@otctools.jf.intel.com",
+                      srcrpm]), None)
         repo = GitRepository("./app-core")
         eq_(repo.get_local_branches(), ['master', 'upstream'])
         eq_(repo.get_tags(), ['upstream/1.2', 'vendor/1.2-19.3'])
@@ -122,8 +121,8 @@ class TestImport(unittest.TestCase):
     @with_data("ail-0.2.29-2.3.src.rpm")
     def test_specify_upstream(self, srcrpm):
         """Test --upstream command line option."""
-        eq_(self.gbs(argv=["gbs", "import", "--upstream=upstream",
-                             srcrpm]), None)
+        eq_(GBS(argv=["gbs", "import", "--upstream=upstream",
+                      srcrpm]), None)
         repo = GitRepository("./ail")
         eq_(repo.get_local_branches(), ['master', 'upstream'])
         eq_(repo.get_tags(), ['upstream/0.2.29', 'vendor/0.2.29-2.3'])
@@ -132,31 +131,31 @@ class TestImport(unittest.TestCase):
     @with_data("bison-1.27.tar.gz")
     def test_is_not_git_repository(self, tarball):
         """Test raising exception when importing tarball outside of git."""
-        self.gbs(argv=["gbs", "import", tarball])
+        GBS(argv=["gbs", "import", tarball])
 
     @raises(SystemExit)
     @with_data("bad.src.rpm")
     def test_error_reading_pkg_header(self, srcrpm):
         """Test raising exception when importing from bad package."""
-        self.gbs(argv=["gbs", "import", srcrpm])
+        GBS(argv=["gbs", "import", srcrpm])
 
     @raises(SystemExit)
     @with_data("bad.spec")
     def test_cant_parse_specfile(self, spec):
         """Test raising exception when importing from non-parseable spec."""
-        self.gbs(argv=["gbs", "import", spec])
+        GBS(argv=["gbs", "import", spec])
 
     @raises(SystemExit)
     def test_missing_argument(self):
         """Test raising exception when running gbs without any arguments."""
-        self.gbs(argv=["gbs", "import"])
+        GBS(argv=["gbs", "import"])
 
     @raises(SystemExit)
     def test_too_many_arguments(self):
         """Test raising exception when running gbs with too many arguments."""
-        self.gbs(argv=["gbs", "import", "1", "2"])
+        GBS(argv=["gbs", "import", "1", "2"])
 
     @raises(SystemExit)
     def test_path_doesnt_exist(self):
         """Test raising exception when running gbs with not existing path."""
-        self.gbs(argv=["gbs", "import", "I don't exist!"])
+        GBS(argv=["gbs", "import", "I don't exist!"])
index 7377284e976322f63999f7b577166af710803f57..202a18c294a739bf10c2917ca965af8fc941003a 100755 (executable)
--- a/tools/gbs
+++ b/tools/gbs
 # with this program; if not, write to the Free Software Foundation, Inc., 59
 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
+"""Gbs - commandline tool for Tizen developers. Main module."""
+
 import sys
-import re
+import os
+
+from argparse import ArgumentParser
 
 from gitbuildsys import __version__
 from gitbuildsys import msger, errors
-from osc import cmdln
-
-def handle_repository(option, opt_str, value, parser):
-    if not value:
-        raise errors.Usage("option %s: need value" % opt_str)
-    if value[0] == '-':
-        raise errors.Usage("option %s: invalid value %s" % (opt_str, value))
-    if getattr(parser.values, option.dest) is None:
-        setattr(parser.values, option.dest, [])
-    getattr(parser.values, option.dest).append(value)
-
-def handle_project(option, opt_str, value, parser):
-    if not value:
-        raise errors.Usage("option %s: need value" % opt_str)
-    if value[0] == '-':
-        raise errors.Usage("option %s: invalid project name %s, cannot " \
-                           "start with '-'" % (opt_str, value))
-    if not re.match(r'^(\w|:|\.|-)+$', value):
-        raise errors.Usage("option %s: invalid project name %s, only word " \
-                           "character, ':', '.' and '-' are supported" \
-                           % (opt_str, value))
-    setattr(parser.values, option.dest, value)
-
-class Gbs(cmdln.Cmdln):
-    """gbs - the command line tool for Tizen package developers
-
-    Usage: gbs [GLOBAL-OPTS] SUBCOMMAND [OPTS] [ARGS...]
-    Try 'gbs help SUBCOMAND' for help on a specific subcommand.
-
-    ${command_list}
-    global ${option_list}
-    ${help_list}
+from gitbuildsys.parsing import subparser, GbsHelpFormatter
+
+@subparser
+def import_parser(parser):
+    """import spec file/source rpm/tar ball to git repository
+    Examples:
+      $ gbs import /path/to/specfile.spec
+      $ gbs import /path/to/package-version.src.rpm
+      $ gbs import /path/to/tarball.tar.gz
     """
 
-    name = 'gbs'
-    version = __version__
-
-    def get_optparser(self):
-        optparser = cmdln.CmdlnOptionParser(self, version=self.version)
-        optparser.add_option('-d', '--debug', action='store_true',
-                                              dest='debug',
-                                              help='print debug message')
-        optparser.add_option('-v', '--verbose', action='store_true',
-                                                dest='verbose',
-                                                help='verbose information')
-        optparser.add_option('-c', '--conf', dest='conf',
-                                             help='specify config file for gbs')
-        return optparser
-
-    def postoptparse(self):
-        from gitbuildsys.conf import configmgr
-        if self.options.verbose:
-            msger.set_loglevel('verbose')
-
-        if self.options.debug:
-            msger.set_loglevel('debug')
-
-        if self.options.conf:
-            configmgr.reset_from_conf(self.options.conf)
-    @cmdln.alias('sr')
-    @cmdln.option('-m', '--msg',
-                  default=None,
-                  dest='msg',
-                  help='specify tag message info')
-    @cmdln.option('-c', '--commit',
-                  default='HEAD',
-                  dest='commit',
-                  help='specify a commit ID to submit')
-    @cmdln.option('-s', '--sign',
-                  action='store_true',
-                  default=False,
-                  dest='sign',
-                  help='make a GPG-signed tag')
-    @cmdln.option('-u', '--user-key',
-                  default=None,
-                  dest='user_key',
-                  help='using the given key to make a GPG-signed tag')
-    @cmdln.option('-t', '--target',
-                  default=None,
-                  dest='target',
-                  help='specify target version to submit, eg: trunk.')
-    @cmdln.option('-r', '--remote',
-                  default='origin',
-                  dest='remote',
-                  help='specify gerrit project server, default value is '\
-                  'origin for example:\nssh://user@review.tizen.org:29418'\
-                  '/public/base/gcc')
-    def do_submit(self, _subcmd, opts, *args):
-        """${cmd_name}: submit tag to gerrit and trigger building in OBS
-
-        Usage:
-            gbs submit -m <message for tag> [options]
-
-        Examples:
-            gbs submit -m 'release for 0.1'
-            gbs submit -c <commit_ID> -m 'release for 0.2'
-            gbs submit -m 'release for 0.3' -s
-            gbs submit -r ssh://user@review.tizen.org:29418/public/base/gcc\
- -m 'release for 0.4'
-
-        ${cmd_option_list}
-        """
-
-        from gitbuildsys import cmd_submit as cmd
-        cmd.do(opts, args)
-
-    @cmdln.alias('ex')
-    @cmdln.option('-o', '--outdir',
-                  default=None,
-                  dest='outdir',
-                  help='output directory')
-    @cmdln.option('--spec',
-                  default=None,
-                  dest='spec',
-                  help='specify a spec file to use')
-    @cmdln.option('-c', '--commit',
-                  default=None,
-                  dest='commit',
-                  help='specify a commit ID to export')
-    @cmdln.option('--include-all',
-                  action='store_true',
-                  default=False,
-                  dest='include_all',
-                  help='uncommitted changes and untracked files would be '\
-                       'included while generating tar ball')
-    @cmdln.option('--source-rpm',
-                  action='store_true',
-                  default=False,
-                  dest='source_rpm',
-                  help='generate source rpm')
-    @cmdln.option('--upstream-branch',
-                  default=None,
-                  dest='upstream_branch',
-                  help='upstream branch')
-    @cmdln.option('--upstream-tag',
-                  default=None,
-                  dest='upstream_tag',
-                  help="upstream tag format, '${upstreamversion}' is "\
-                       "expanded to the version in the spec file. "\
-                       "E.g. 'v${upstreamversion}'")
-    @cmdln.option('--squash-patches-until',
-                  default=None,
-                  dest='squash_patches_until',
-                  help="when generating patches, squash patches up to given "\
-                       "commit-ish into one monolithic diff file. Format is "\
-                       "the commit-ish optionally followed by a colon and "\
-                       "diff filename base.")
-    def do_export(self, _subcmd, opts, *args):
-        """${cmd_name}: export files and prepare for build
-
-        Usage:
-            gbs export
-
-        ${cmd_option_list}
-        """
-
-        from gitbuildsys import cmd_export as cmd
-        cmd.do(opts, args)
-
-    @cmdln.alias('lb')
-    @cmdln.option('-D', '--dist',
-                  default=None,
-                  dest='dist',
-                  help='specify distribution configuration file, which should ' \
-                       'be a full path')
-    @cmdln.option('-R', '--repository',
-                  action="callback",
-                  default=None,
-                  type='string',
-                  dest='repositories',
-                  callback=handle_repository,
-                  help='specify package repositories, only rpm-md format ' \
-                       'is supported')
-    @cmdln.option('-B', '--buildroot',
-                  default=None,
-                  dest='buildroot',
-                  help='specify build root to setup chroot environment. '\
-                       'By default, ~/GBS-ROOT/ will be used, and if no ' \
-                       '-B option, but TIZEN_BUILD_ROOT env exists, then '\
-                       '${TIZEN_BUILD_ROOT} will used as build root')
-    @cmdln.option('-A', '--arch',
-                  default=None,
-                  dest='arch',
-                  help='build target arch ')
-    @cmdln.option('-C', '--clean',
-                  action='store_true',
-                  default=False,
-                  dest='clean',
-                  help='delete old build root before initialization')
-    @cmdln.option('--ccache',
-                  action="store_true",
-                  default=False,
-                  dest='ccache',
-                  help='use ccache to speed up rebuilds')
-    @cmdln.option('--skip-conf-repos',
-                  action="store_true",
-                  default=False,
-                  dest='skip_conf_repos',
-                  help='skip repositories mentioned in config file')
-    @cmdln.option('-c', '--commit',
-                  default=None,
-                  dest='commit',
-                  help='specify a commit ID to build')
-    @cmdln.option('--spec',
-                  default=None,
-                  dest='spec',
-                  help='specify a spec file to use')
-    @cmdln.option('--extra-packs',
-                  default=None,
-                  dest='extra_packs',
-                  help='specify extra packages to install to build root '\
-                       'multiple packages can be separated by comma')
-    @cmdln.option('--include-all',
-                  action='store_true',
-                  default=False,
-                  dest='include_all',
-                  help='uncommitted changes and untracked files would be '\
-                       'included while generating tar ball')
-    @cmdln.option('--upstream-branch',
-                  default=None,
-                  dest='upstream_branch',
-                  help='upstream branch')
-    @cmdln.option('--upstream-tag',
-                  default=None,
-                  dest='upstream_tag',
-                  help="upstream tag format, '${upstreamversion}' is "\
-                       "expanded to the version in the spec file. "\
-                       "E.g. 'v${upstreamversion}'")
-    @cmdln.option('--squash-patches-until',
-                  default=None,
-                  dest='squash_patches_until',
-                  help="when generating patches, squash patches up to given "\
-                       "commit-ish into one monolithic diff file. Format is "\
-                       "the commit-ish optionally followed by a colon and "\
-                       "diff filename base.")
+    parser.add_argument('path', type=os.path.abspath,
+                        help='path to spec, srcrpm or tarball')
+
+    parser.add_argument('--author-name', help='author name of git commit')
+    parser.add_argument('--author-email', help='author email of git commit')
+    parser.add_argument('--upstream_branch', default='upstream',
+                        help='specify upstream branch for new package version')
+    parser.add_argument('--no-merge', action='store_true',
+                         help='don\'t merge new upstream branch to master')
+
+    parser.set_defaults(alias="im")
+    return parser
+
+@subparser
+def export_parser(parser):
+    """export files and prepare for build
+    Examples:
+      $ gbs export --spec packaging/my.spec --commit d64065c
+      $ gbs export --source-rpm -o /tmp/
+      $ gbs export --include-all
+    """
+
+    parser.add_argument('gitdir', nargs='?', type=os.path.abspath,
+                        default=os.getcwd(),
+                        help='path to git repository')
+
+    parser.add_argument('-o', '--outdir', help='output directory')
+    parser.add_argument('--spec', help='specify a spec file to use')
+    parser.add_argument('-c', '--commit', help='specify a commit ID to export')
+    parser.add_argument('--include-all', action='store_true',
+                        help='uncommitted changes and untracked files '
+                        'would be included while generating tar ball')
+    parser.add_argument('--source-rpm', action='store_true',
+                        help='generate source rpm')
+    parser.add_argument('--upstream-branch', help='upstream branch')
+    parser.add_argument('--upstream-tag',
+                        help="upstream tag format, '${upstreamversion}' is "
+                        'expanded to the version in the spec file. '
+                        "E.g. 'v${upstreamversion}'")
+    parser.add_argument('--squash-patches-until',
+                         help='when generating patches, squash patches up '
+                         'to given commit-ish into one monolithic diff file. '
+                         'Format is the commit-ish optionally followed by a '
+                         'colon and diff filename base.')
+
+    parser.set_defaults(alias="ex")
+    return parser
+
+@subparser
+def build_parser(parser):
+    """local build package
+    Examples:
+      $ mkdir tizen-packages
+      $ cp package1 package2 package3 ... tizen-packages/
+      $ gbs build -A ia32 tizen-packages # build all packages from tizen-packages
+      $ cd tizen-packages/
+      $ gbs build -A ia32 # build all packages under current dir
+      $ gbs build -A ia32 --overwrite --include-all
+      $ gbs build -A i586 --threads=2
+      $ gbs build -A i586 --threads=2 --exclude=dlog --exclude=eglibc
+      $ gbs build -A i586 --threads=4 --binary-list=/path/to/pkgs.list
+      $ cd package1/
+      $ gbs build -A i586 --incremental # only support build one package
+    """
+
+    parser.add_argument('gitdir', nargs='?', type=os.path.abspath,
+                        default=os.getcwd(),
+                        help='path to git repository')
+
+    parser.add_argument('-D', '--dist',
+                        help='specify distribution configuration file, '
+                        'which should be a full path')
+    parser.add_argument('-R', '--repository', dest='repositories',
+                        action="append", help='specify package repositories, '
+                        'only rpm-md format is supported')
+    parser.add_argument('-B', '--buildroot',
+                        help='specify build root to setup chroot environment. '
+                        'By default, ~/GBS-ROOT/ will be used, and if no '
+                        '-B option, but TIZEN_BUILD_ROOT env exists, then '
+                        '${TIZEN_BUILD_ROOT} will used as build root')
+    parser.add_argument('-A', '--arch', help='build target arch ')
+    parser.add_argument('-C', '--clean', action='store_true',
+                        help='delete old build root before initialization')
+    parser.add_argument('--ccache', action="store_true",
+                        help='use ccache to speed up rebuilds')
+    parser.add_argument('--skip-conf-repos', action="store_true",
+                        help='skip repositories mentioned in config file')
+    parser.add_argument('-c', '--commit', help='specify a commit ID to build')
+    parser.add_argument('--spec', help='specify a spec file to use')
+    parser.add_argument('--extra-packs',
+                        help='specify extra packages to install to build root '
+                        'multiple packages can be separated by comma')
+    parser.add_argument('--include-all', action='store_true',
+                        help='uncommitted changes and untracked files would be '
+                        'included while generating tar ball')
+    parser.add_argument('--upstream-branch', help='upstream branch')
+    parser.add_argument('--upstream-tag',
+                        help="upstream tag format, '${upstreamversion}' is "
+                       "expanded to the version in the spec file. "
+                        "E.g. 'v${upstreamversion}'")
+    parser.add_argument('--squash-patches-until',
+                        help='when generating patches, squash patches up '
+                        'to given commit-ish into one monolithic diff file. '
+                        'Format is the commit-ish optionally followed by a '
+                        'colon and diff filename base.')
 
     # depanneur special options
-    @cmdln.option('--clean-once',
-                  action='store_true',
-                  default=False,
-                  dest='clean_once',
-                  help='clean the build environment only once when you start '\
-                       'building multiple packages, after that use existing '\
-                       'environment for all packages.')
-    @cmdln.option('--overwrite',
-                  action='store_true',
-                  default=False,
-                  dest='overwrite',
-                  help='overwrite existing binaries and build them anyway')
-    @cmdln.option('--incremental',
-                  action='store_true',
-                  default=False,
-                  dest='incremental',
-                  help='build a package from the local git tree incremental.' \
-                       'If the build fails, changes can be done directly to ' \
-                       'the source and build can continue from where it ' \
-                       'stopped')
-    @cmdln.option('--debug',
-                  action='store_true',
-                  default=False,
-                  dest='debug',
-                  help='debug output')
-    @cmdln.option('--binary-list',
-                  default=None,
-                  dest='binary_list',
-                  help='specify a binary list file. Packages listed in '\
-                       'this file will be selected to be built. The format '\
-                       'of binary-list file is one package for one line, '\
+    parser.add_argument('--clean-once', action='store_true',
+                        help='clean the build environment only once when you '
+                        'start building multiple packages, after that use '
+                        'existing environment for all packages.')
+    parser.add_argument('--overwrite', action='store_true',
+                        help='overwrite existing binaries and build '
+                        'them anyway')
+    parser.add_argument('--incremental', action='store_true',
+                       help='build a package from the local git tree '
+                        'incremental. If the build fails, changes can be done '
+                        'directly to the source and build can continue from '
+                        'where it stopped')
+    parser.add_argument('--debug', action='store_true', help='debug output')
+    parser.add_argument('--binary-list',
+                        help='specify a binary list file. Packages listed in '
+                       'this file will be selected to be built. The format '
+                       'of binary-list file is one package for one line, '
                        'and only binary RPM name is accepted')
-    @cmdln.option('--threads',
-                  default=1,
-                  dest='threads',
-                  help='number of threads to build package in parallel')
-    @cmdln.option('--exclude',
-                  action="append",
-                  default=None,
-                  type="string",
-                  dest='exclude',
-                  help='specify a package to be excluded to be built')
-    @cmdln.option('--exclude-from-file',
-                  default=None,
-                  dest='exclude_from_file',
-                  help='specify an exclude package list text file, the '\
-                       'format is one package in one line, and only binary '\
-                       'RPM package name is accepted. Packages listed in '\
-                       'this file will be skipped to be built.')
-    @cmdln.option('--keepgoing',
-                  action='store_true',
-                  default=False,
-                  dest='keepgoing',
-                  help='if a package build fails, do not abort and continue '\
-                       'building other packages in the queue')
-    @cmdln.option('--no-configure',
-                  action='store_true',
-                  default=False,
-                  dest='no_configure',
-                  help='this option disables running configure scripts and auto '\
-                       'generation of auto-tools to make incremental build ' \
-                       'possible. This requires the configure scripts in the '\
-                       'spec to be refereneced using the %configure, %reconfigre '\
-                       'and %autogen macros')
-
-    def do_build(self, _subcmd, opts, *args):
-        """${cmd_name}: local build package
-
-        Usage:
-            gbs build -R repository [options] [package git dir]
-
-            [package git dir] is optional, if not specified, current dir would
-            be used, and all packages in this dir would be scheduled to be built.
-
-        Examples:
-            $ mkdir tizen-packages
-            $ cp package1 package2 package3 ... tizen-packages/
-            $ gbs build -A ia32 tizen-packages # build all packages under tizen-packages
-            $ cd tizen-packages/
-            $ gbs build -A ia32 # build all packages under current dir
-            $ gbs build -A ia32 --overwrite --include-all
-            $ gbs build -A i586 --threads=2
-            $ gbs build -A i586 --threads=2 --exclude=dlog --exclude=eglibc
-            $ gbs build -A i586 --threads=4 --binary-list=/path/to/pkgs.list
-            $ cd package1/
-            $ gbs build -A i586 --incremental # only support build one package
-
-        ${cmd_option_list}
-        """
-
-        from gitbuildsys import cmd_build as cmd
-        cmd.do(opts, args)
-
-    @cmdln.alias('ch')
-    @cmdln.option('-r', '--root',
-                  action='store_true',
-                  default=False,
-                  dest='root',
-                  help='chroot as root instead of abuild by default')
-    def do_chroot(self, subcmd, opts, *args):
-        """${cmd_name}: chroot to build root
-
-        Usage:
-            gbs chroot [options] <build root dir>
-
-        Note: The default location of build root located at:
-        ~/GBS-ROOT/local/scratch.{arch}.*, which will be different
-        if -B option specified while running gbs build
-
-        ${cmd_option_list}
-        """
-
-        from gitbuildsys import cmd_chroot as cmd
-        cmd.do(opts, args)
-
-    @cmdln.alias('rb')
-    @cmdln.option('-T', '--target-obsprj',
-                  action='callback',
-                  default=None,
-                  dest='target_obsprj',
-                  type='string',
-                  callback=handle_project,
-                  help='OBS project where package will be checked in ' \
-                       '(default is home:<userid>:gbs:Tizen:Main)')
-    @cmdln.option('-B', '--base-obsprj',
-                  action='callback',
-                  default=None,
-                  dest='base_obsprj',
-                  type='string',
-                  callback=handle_project,
-                  help='OBS project to branch from (default is Tizen:Main)')
-    @cmdln.option('--spec',
-                  default=None,
-                  dest='spec',
-                  help='specify a spec file to use')
-    @cmdln.option('-c', '--commit',
-                  default=None,
-                  dest='commit',
-                  help='specify a commit ID to build')
-    @cmdln.option('--buildlog',
-                  action='store_true',
-                  default=False,
-                  dest='buildlog',
-                  help='get buildlog from build sever')
-    @cmdln.option('--status',
-                  action='store_true',
-                  default=False,
-                  dest='status',
-                  help='get build status from build server')
-    @cmdln.option('--include-all',
-                  action='store_true',
-                  default=False,
-                  dest='include_all',
-                  help='uncommitted changes and untracked files will be '\
-                       'included while generating tar ball')
-    @cmdln.option('--upstream-branch',
-                  default=None,
-                  dest='upstream_branch',
-                  help='upstream branch')
-    @cmdln.option('--upstream-tag',
-                  default=None,
-                  dest='upstream_tag',
-                  help="upstream tag format, '${upstreamversion}' is "\
-                       "expanded to the version in the spec file. "\
-                       "E.g. 'v${upstreamversion}'")
-    @cmdln.option('--squash-patches-until',
-                  default=None,
-                  dest='squash_patches_until',
-                  help="when generating patches, squash patches up to given "\
-                       "commit-ish into one monolithic diff file. Format is "\
-                       "the commit-ish optionally followed by a colon and "\
-                       "diff filename base.")
-    def do_remotebuild(self, subcmd, opts, *args):
-        """${cmd_name}: remote build package
-
-        Usage:
-            gbs remotebuild [options] [package git dir] \
-[--buildlog repo arch] [--status]
-
-            [package git dir] is optional, if not specified, current dir would
-            be used.
-
-        Examples:
-          $ gbs remotebuild
-          $ gbs remotebuild -B Test
-          $ gbs remotebuild -B Test -T home:<userid>:gbs
-          $ gbs remotebuild <package git directory>
-
-        ${cmd_option_list}
-        """
-
-        from gitbuildsys import cmd_remotebuild as cmd
-        cmd.do(opts, args)
-
-
-    @cmdln.alias('im')
-    @cmdln.option('--author-name',
-                  default=None,
-                  dest='author_name',
-                  help='author name of git commit')
-    @cmdln.option('--author-email',
-                  default=None,
-                  dest='author_email',
-                  help='author email of git commit')
-    @cmdln.option('--upstream_branch',
-                  default='upstream',
-                  dest='upstream_branch',
-                  help='specify upstream branch for new version of package')
-    @cmdln.option('--no-merge',
-                  action='store_true',
-                  default=False,
-                  dest='no_merge',
-                  help='don\'t merge new upstream branch to master')
-    def do_import(self, subcmd, opts, *args):
-        """${cmd_name}: import spec file/source rpm/tar ball to git repository
-
-        Usage:
-            gbs import [options] specfile | source rpm | tar ball
-
-        Examples:
-          $ gbs import /path/to/specfile/
-          $ gbs import /path/to/src.rpm
-          $ gbs import /path/to/tarball
-
-        ${cmd_option_list}
-        """
-
-        from gitbuildsys import cmd_import as cmd
-        cmd.do(opts, args)
-
-
-    @cmdln.alias('ch')
-    @cmdln.option('-s', '--since',
-                  default=None,
-                  dest='since',
-                  help='commit to start from')
-    @cmdln.option('-m', '--message',
-                  default=None,
-                  dest='message',
-                  help='use given message as the changelog entry')
-    def do_changelog(self, _subcmd, opts, *args):
-        """${cmd_name}: update the changelog file with the git commit messages
-
-        Usage:
-            gbs changelog [options]
-
-        Examples:
-          $ gbs changelog
-          $ gbs changelog --since=COMMIT_ID
-          $ gbs changelog -m 'new upstream release 0.0.1'
-
-        ${cmd_option_list}
-        """
-
-        from gitbuildsys import cmd_changelog as cmd
-        cmd.do(opts, args)
-
-    @cmdln.hide()
-    def do_man(self, argv):
-        """${cmd_name}: generates a man page
-
-        usage:
-            ${name} man
-        """
-        return cmdln.Cmdln.do_man(self, argv)
+    parser.add_argument('--threads', type=int, default=1,
+                        help='number of threads to build package in parallel')
+    parser.add_argument('--exclude', action="append",
+                        help='specify a package to be excluded to be built')
+    parser.add_argument('--exclude-from-file',
+                        help='specify an exclude package list text file, the '
+                        'format is one package in one line, and only binary '
+                        'RPM package name is accepted. Packages listed in '
+                        'this file will be skipped to be built.')
+    parser.add_argument('--keepgoing', action='store_true',
+                        help='if a package build fails, do not abort and '
+                        'continue building other packages in the queue')
+    parser.add_argument('--no-configure', action='store_true',
+                        help='this option disables running configure scripts '
+                        'and auto generation of auto-tools to make incremental '
+                        'build possible. This requires the configure scripts '
+                        'in the spec to be refereneced using the %%configure, '
+                        '%%reconfigre and %%autogen macros')
+
+    parser.set_defaults(alias="lb")
+    return parser
+
+@subparser
+def remotebuild_parser(parser):
+    """remote build package
+    Examples:
+      $ gbs remotebuild
+      $ gbs remotebuild -B Test
+      $ gbs remotebuild -B Test -T home:<userid>:gbs
+      $ gbs remotebuild <package git directory>
+    """
+
+    parser.add_argument('gitdir', nargs='?', type=os.path.abspath,
+                        default=os.getcwd(),
+                        help='path to git repository')
+
+    parser.add_argument('-T', '--target-obsprj',
+                        help='OBS project where package will be checked in '
+                        '(default is home:<userid>:gbs:Tizen:Main)')
+    parser.add_argument('-B', '--base-obsprj', default="Tizen:Main",
+                        help='OBS project to branch from')
+    parser.add_argument('--spec', help='specify a spec file to use')
+    parser.add_argument('-c', '--commit', help='specify a commit ID to build')
+    parser.add_argument('--buildlog', action='store_true',
+                        help='get buildlog from build sever')
+    parser.add_argument('--status', action='store_true',
+                        help='get build status from build server')
+    parser.add_argument('-R', '--repository',
+                        help='OBS repository for --buildlog')
+    parser.add_argument('-A', '--arch',
+                        help='OBS build architecture for --buildlog')
+    parser.add_argument('--include-all', action='store_true',
+                        help='uncommitted changes and untracked files will be '
+                        'included while generating tar ball')
+    parser.add_argument('--upstream-branch', help='upstream branch')
+    parser.add_argument('--upstream-tag',
+                        help="upstream tag format, '${upstreamversion}' is "
+                        "expanded to the version in the spec file. "
+                        "E.g. 'v${upstreamversion}'")
+    parser.add_argument('--squash-patches-until',
+                        help='when generating patches, squash patches up to '
+                        'given commit-ish into one monolithic diff file. '
+                        'Format is the commit-ish optionally followed by a '
+                        'colon and diff filename base.')
+
+    parser.set_defaults(alias="rb")
+    return parser
+
+@subparser
+def chroot_parser(parser):
+    """chroot to build root
+    Examples:
+      $ gbs chroot /var/tmp/mybuildroot
+      $ gbs chroot --root /var/tmp/mybuildroot
+
+    Note: The default location of build root located at:
+    ~/GBS-ROOT/local/scratch.{arch}.*, which will be different
+    if -B option specified while running gbs build
+    """
+
+    parser.add_argument('buildroot', type=os.path.abspath,
+                        help='path to build root')
+
+    parser.add_argument('-r', '--root', action='store_true',
+                        help='chroot as root instead of abuild by default')
+
+    parser.set_defaults(alias="chr")
+    return parser
+
+@subparser
+def changelog_parser(parser):
+    """update the changelog file with the git commit messages
+    Examples:
+      $ gbs changelog
+      $ gbs changelog --since=COMMIT_ID
+      $ gbs changelog -m 'new upstream release 0.0.1'
+    """
+
+    parser.add_argument('gitdir', nargs='?', type=os.path.abspath,
+                        default=os.getcwd(),
+                        help='path to git repository')
+
+    parser.add_argument('-s', '--since',
+                        help='commit to start from')
+    parser.add_argument('-m', '--message',
+                        help='use given message as the changelog entry')
+    parser.set_defaults(alias='ch')
+    return parser
+
+@subparser
+def submit_parser(parser):
+    """submit tag to gerrit and trigger building in OBS
+    Examples:
+      $ gbs submit -m 'release for 0.1'
+      $ gbs submit -c <commit_ID> -m 'release for 0.2'
+      $ gbs submit -m 'release for 0.3' -s
+      $ gbs submit -r ssh://user@review.tizen.org:29418/public/base/gcc -m 'release for 0.4'
+    """
+
+    parser.add_argument('gitdir', nargs='?', type=os.path.abspath,
+                        default=os.getcwd(),
+                        help='path to git repository')
+
+    parser.add_argument('-m', '--msg', required=True,
+                        help='specify tag message info')
+    parser.add_argument('-c', '--commit', default='HEAD',
+                        help='specify a commit ID to submit')
+    parser.add_argument('-s', '--sign', action='store_true',
+                        help='make a GPG-signed tag')
+    parser.add_argument('-u', '--user-key',
+                        help='using the given key to make a GPG-signed tag')
+    parser.add_argument('-t', '--target',
+                        help='specify target version to submit, eg: trunk.')
+    parser.add_argument('-r', '--remote', default='origin',
+                        help='specify gerrit project server, default value is '
+                        'origin for example:\nssh://user@review.tizen.org:29418'
+                        '/public/base/gcc')
+
+    parser.set_defaults(alias="sr")
+    return parser
+
+def main(argv):
+    """Script entry point."""
+
+    # Create top level parser
+    epilog = "Try 'gbs SUBCOMAND --help' for help on a specific subcommand."
+    description = "gbs - the command line tool for Tizen package developers"
+    parser = ArgumentParser(description=description, epilog=epilog,
+                            formatter_class=GbsHelpFormatter)
+
+    parser.add_argument('-V', '--version', action='version',
+                        version='%(prog)s ' + __version__)
+    parser.add_argument('-c', '--conf', help='specify config file for gbs')
+    parser.add_argument('-d', '--debug', action='store_true',
+                        help='debug output')
+    parser.add_argument('-v', '--verbose', action='store_true',
+                        help='verbose output')
+
+    # hacked by the request of cmdln lovers
+    parser.format_usage = parser.format_help
+
+    # Create parsers for subcommands
+    subparsers = parser.add_subparsers(title='subcommands')
+
+    # collect aliases
+    aliases = {}
+    for name, obj in globals().iteritems():
+        if name.endswith('_parser') and callable(obj):
+            aliases[obj(subparsers).get_default('alias')] = name.split('_')[0]
+
+    # replace aliases with real commands
+    positionals = [(i, arg) for i, arg in enumerate(argv[1:]) \
+                       if not arg.startswith('-')]
+    if positionals:
+        i, cmd = positionals[0]
+        if cmd in aliases:
+            argv[i+1] = aliases[cmd]
+
+    # Parse arguments
+    args = parser.parse_args(argv[1:])
+
+    # Set log level for --debug and --verbose
+    if args.verbose:
+        msger.set_loglevel('verbose')
+
+    if args.debug:
+        msger.set_loglevel('debug')
+
+    # Process configuration file if --conf is used
+    if args.conf:
+        from gitbuildsys.conf import configmgr
+        configmgr.reset_from_conf(args.conf)
+
+    # Import target module and call 'main' from it
+    module = __import__("gitbuildsys.%s" % args.module, fromlist=[args.module])
+    return module.main(args)
 
 
 if __name__ == '__main__':
     try:
-        sys.exit(Gbs().main())
-
+        sys.exit(main(sys.argv))
     except KeyboardInterrupt:
         msger.error('\n^C caught, program aborted.')