Use OptionParser.add_option_group(str).
[cvs2svn.git] / cvs2svn_lib / run_options.py
blobcd82a894b33065fdf5b070d54b7e7fa0c57fd31e
1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2007 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module contains classes to set cvs2svn run options."""
20 import sys
21 import os
22 import re
23 import optparse
24 import time
26 from cvs2svn_lib.version import VERSION
27 from cvs2svn_lib import config
28 from cvs2svn_lib.common import warning_prefix
29 from cvs2svn_lib.common import error_prefix
30 from cvs2svn_lib.common import FatalError
31 from cvs2svn_lib.common import CVSTextDecoder
32 from cvs2svn_lib.log import Log
33 from cvs2svn_lib.context import Ctx
34 from cvs2svn_lib.svn_output_option import DumpfileOutputOption
35 from cvs2svn_lib.svn_output_option import NewRepositoryOutputOption
36 from cvs2svn_lib.svn_output_option import ExistingRepositoryOutputOption
37 from cvs2svn_lib.project import Project
38 from cvs2svn_lib.pass_manager import InvalidPassError
39 from cvs2svn_lib.revision_manager import NullRevisionRecorder
40 from cvs2svn_lib.revision_manager import NullRevisionExcluder
41 from cvs2svn_lib.rcs_revision_manager import RCSRevisionReader
42 from cvs2svn_lib.cvs_revision_manager import CVSRevisionReader
43 from cvs2svn_lib.checkout_internal import InternalRevisionRecorder
44 from cvs2svn_lib.checkout_internal import InternalRevisionExcluder
45 from cvs2svn_lib.checkout_internal import InternalRevisionReader
46 from cvs2svn_lib.symbol_strategy import AllBranchRule
47 from cvs2svn_lib.symbol_strategy import AllTagRule
48 from cvs2svn_lib.symbol_strategy import BranchIfCommitsRule
49 from cvs2svn_lib.symbol_strategy import ExcludeRegexpStrategyRule
50 from cvs2svn_lib.symbol_strategy import ForceBranchRegexpStrategyRule
51 from cvs2svn_lib.symbol_strategy import ForceTagRegexpStrategyRule
52 from cvs2svn_lib.symbol_strategy import ExcludeTrivialImportBranchRule
53 from cvs2svn_lib.symbol_strategy import HeuristicStrategyRule
54 from cvs2svn_lib.symbol_strategy import UnambiguousUsageRule
55 from cvs2svn_lib.symbol_strategy import HeuristicPreferredParentRule
56 from cvs2svn_lib.symbol_strategy import SymbolHintsFileRule
57 from cvs2svn_lib.symbol_strategy import TrunkPathRule
58 from cvs2svn_lib.symbol_strategy import BranchesPathRule
59 from cvs2svn_lib.symbol_strategy import TagsPathRule
60 from cvs2svn_lib.symbol_transform import ReplaceSubstringsSymbolTransform
61 from cvs2svn_lib.symbol_transform import RegexpSymbolTransform
62 from cvs2svn_lib.symbol_transform import NormalizePathsSymbolTransform
63 from cvs2svn_lib.property_setters import AutoPropsPropertySetter
64 from cvs2svn_lib.property_setters import CVSBinaryFileDefaultMimeTypeSetter
65 from cvs2svn_lib.property_setters import CVSBinaryFileEOLStyleSetter
66 from cvs2svn_lib.property_setters import CVSRevisionNumberSetter
67 from cvs2svn_lib.property_setters import DefaultEOLStyleSetter
68 from cvs2svn_lib.property_setters import EOLStyleFromMimeTypeSetter
69 from cvs2svn_lib.property_setters import ExecutablePropertySetter
70 from cvs2svn_lib.property_setters import KeywordsPropertySetter
71 from cvs2svn_lib.property_setters import MimeMapper
72 from cvs2svn_lib.property_setters import SVNBinaryFileKeywordsPropertySetter
75 class GetoptOptions(object):
76 """Backwards compatibility adapter for getopt-style options.
78 optparse-compatible options can be created with the __call__()
79 method. When such an option is seen, it appends (opt, value) tuples
80 to self.opts. These can be processed in a getopt-style option
81 processing loop."""
83 def __init__(self):
84 self.opts = []
86 def __call__(self, *args, **kw):
87 """Create an optparse-compatible Option object.
89 The arguments are compatible with those of the optparse.Options
90 constructor, except that action is allways set to 'callback' and
91 the callback is always set to self.callback. In particular, if
92 the option should take an argument, then the 'type' keyword
93 argument should be used."""
95 kw['action'] = 'callback'
96 kw['callback'] = self.callback
97 return optparse.Option(*args, **kw)
99 def callback(self, option, opt_str, value, parser):
100 self.opts.append((opt_str, value,))
103 usage = """\
104 Usage: %prog --options OPTIONFILE
105 %prog [OPTION...] OUTPUT-OPTION CVS-REPOS-PATH"""
107 description="""\
108 Convert a CVS repository into a Subversion repository, including history.
112 class RunOptions:
113 """A place to store meta-options that are used to start the conversion."""
115 def __init__(self, progname, cmd_args, pass_manager):
116 """Process the command-line options, storing run options to SELF.
118 PROGNAME is the name of the program, used in the usage string.
119 CMD_ARGS is the list of command-line arguments passed to the
120 program. PASS_MANAGER is an instance of PassManager, needed to
121 help process the -p and --help-passes options."""
123 self.pass_manager = pass_manager
124 self.start_pass = 1
125 self.end_pass = self.pass_manager.num_passes
126 self.profiling = False
127 self.progname = progname
129 self.projects = []
131 # A list of one list of SymbolStrategyRules for each project:
132 self.project_symbol_strategy_rules = []
134 go = GetoptOptions()
136 parser = self.parser = optparse.OptionParser(
137 usage=usage,
138 description=description,
139 add_help_option=False,
143 group = parser.add_option_group('Configuration via options file')
144 group.add_option(go(
145 '--options', type='string',
146 help=(
147 'read the conversion options from PATH. This '
148 'method allows more flexibility than using '
149 'command-line options. See documentation for info'
151 metavar='PATH',
154 group = parser.add_option_group('Output options')
155 group.add_option(go(
156 '--svnrepos', '-s', type='string',
157 help='path where SVN repos should be created',
158 metavar='PATH',
160 group.add_option(go(
161 '--existing-svnrepos',
162 help='load into existing SVN repository (for use with --svnrepos)',
164 group.add_option(go(
165 '--fs-type', type='string',
166 help=(
167 'pass --fs-type=TYPE to "svnadmin create" (for use with '
168 '--svnrepos)'
170 metavar='TYPE',
172 group.add_option(go(
173 '--bdb-txn-nosync',
174 help=(
175 'pass --bdb-txn-nosync to "svnadmin create" (for use with '
176 '--svnrepos)'
179 group.add_option(go(
180 '--create-option', type='string',
181 help='pass OPT to "svnadmin create" (for use with --svnrepos)',
182 metavar='OPT',
184 group.add_option(go(
185 '--dumpfile', type='string',
186 help='just produce a dumpfile; don\'t commit to a repos',
187 metavar='PATH',
189 group.add_option(
190 '--dry-run',
191 action='callback', callback=self.callback_dry_run,
192 help=(
193 'do not create a repository or a dumpfile; just print what '
194 'would happen.'
198 # Deprecated options:
199 group.add_option(go('--dump-only', help=optparse.SUPPRESS_HELP))
200 group.add_option(go('--create', help=optparse.SUPPRESS_HELP))
203 group = parser.add_option_group('Conversion options')
204 group.add_option(go(
205 '--trunk-only',
206 help='convert only trunk commits, not tags nor branches',
208 group.add_option(go(
209 '--trunk', type='string',
210 help=(
211 'path for trunk (default: %s)'
212 % (config.DEFAULT_TRUNK_BASE,)
214 metavar='PATH',
216 group.add_option(go(
217 '--branches', type='string',
218 help=(
219 'path for branches (default: %s)'
220 % (config.DEFAULT_BRANCHES_BASE,)
222 metavar='PATH',
224 group.add_option(go(
225 '--tags', type='string',
226 help=(
227 'path for tags (default: %s)'
228 % (config.DEFAULT_TAGS_BASE,)
230 metavar='PATH',
232 group.add_option(go(
233 '--no-prune',
234 help='don\'t prune empty directories',
236 group.add_option(go(
237 '--encoding', type='string',
238 help=(
239 'encoding for paths and log messages in CVS repos. '
240 'If option is specified multiple times, encoders '
241 'are tried in order until one succeeds. See '
242 'http://docs.python.org/lib/standard-encodings.html '
243 'for a list of standard Python encodings.'
245 metavar='ENC',
247 group.add_option(go(
248 '--fallback-encoding', type='string',
249 help='If all --encodings fail, use lossy encoding with ENC',
250 metavar='ENC',
252 group.add_option(go(
253 '--symbol-transform', type='string',
254 help=(
255 'transform symbol names from P to S, where P and S '
256 'use Python regexp and reference syntax '
257 'respectively. P must match the whole symbol name'
259 metavar='P:S',
261 group.add_option(go(
262 '--symbol-hints', type='string',
263 help='read symbol conversion hints from PATH',
264 metavar='PATH',
266 group.add_option(go(
267 '--force-branch', type='string',
268 help='force symbols matching REGEXP to be branches',
269 metavar='REGEXP',
271 group.add_option(go(
272 '--force-tag', type='string',
273 help='force symbols matching REGEXP to be tags',
274 metavar='REGEXP',
276 group.add_option(go(
277 '--exclude', type='string',
278 help='exclude branches and tags matching REGEXP',
279 metavar='REGEXP',
281 group.add_option(go(
282 '--keep-trivial-imports',
283 help=(
284 'do not exclude branches that were only used for '
285 'a single import (usually these are unneeded)'
288 group.add_option(go(
289 '--symbol-default', type='string',
290 help=(
291 'specify how ambiguous symbols are converted. '
292 'OPT is "heuristic" (default), "strict", "branch", '
293 'or "tag"'
295 metavar='OPT',
297 group.add_option(go(
298 '--keep-cvsignore',
299 help=(
300 'keep .cvsignore files (in addition to creating '
301 'the analogous svn:ignore properties)'
304 group.add_option(go(
305 '--no-cross-branch-commits',
306 help='prevent the creation of cross-branch commits',
308 group.add_option(go(
309 '--retain-conflicting-attic-files',
310 help=(
311 'if a file appears both in and out of '
312 'the CVS Attic, then leave the attic version in a '
313 'SVN directory called "Attic"'
316 group.add_option(go(
317 '--username', type='string',
318 help='username for cvs2svn-synthesized commits',
319 metavar='NAME',
321 group.add_option(go(
322 '--cvs-revnums',
323 help='record CVS revision numbers as file properties',
325 group.add_option(go(
326 '--mime-types', type='string',
327 help=(
328 'specify an apache-style mime.types file for setting '
329 'svn:mime-type'
331 metavar='FILE',
333 group.add_option(go(
334 '--eol-from-mime-type',
335 help='set svn:eol-style from mime type if known',
337 group.add_option(go(
338 '--auto-props', type='string',
339 help=(
340 'set file properties from the auto-props section '
341 'of a file in svn config format'
343 metavar='FILE',
345 group.add_option(go(
346 '--default-eol', type='string',
347 help=(
348 'default svn:eol-style for non-binary files with '
349 'undetermined mime types. VALUE is "binary" '
350 '(default), "native", "CRLF", "LF", or "CR"'
352 metavar='VALUE',
354 group.add_option(go(
355 '--keywords-off',
356 help=(
357 'don\'t set svn:keywords on any files (by default, '
358 'cvs2svn sets svn:keywords on non-binary files to "%s")'
359 % (config.SVN_KEYWORDS_VALUE,)
363 # Deprecated options:
364 group.add_option(go('--no-default-eol', help=optparse.SUPPRESS_HELP))
365 group.add_option(go(
366 '--auto-props-ignore-case', help=optparse.SUPPRESS_HELP
370 group = parser.add_option_group('Extraction options')
371 group.add_option(go(
372 '--use-rcs',
373 help='use RCS to extract revision contents',
375 group.add_option(go(
376 '--use-cvs',
377 help=(
378 'use CVS to extract revision contents '
379 '(only use this if having problems with RCS)'
382 group.add_option(go(
383 '--use-internal-co',
384 help=(
385 'use internal code to extract revision contents '
386 '(very fast but disk space intensive) (default)'
391 group = parser.add_option_group('Environment options')
392 group.add_option(go(
393 '--tmpdir', type='string',
394 help=(
395 'directory to use for temporary data files '
396 '(default "cvs2svn-tmp")'
398 metavar='PATH',
400 group.add_option(go(
401 '--svnadmin', type='string',
402 help='path to the "svnadmin" program',
403 metavar='PATH',
405 group.add_option(go(
406 '--co', type='string',
407 help='path to the "co" program (required if --use-rcs)',
408 metavar='PATH',
410 group.add_option(go(
411 '--cvs', type='string',
412 help='path to the "cvs" program (required if --use-cvs)',
413 metavar='PATH',
415 group.add_option(go(
416 '--sort', type='string',
417 help='path to the GNU "sort" program',
418 metavar='PATH',
422 group = parser.add_option_group('Partial conversions')
423 group.add_option(
424 '--pass', type='string',
425 action='callback', callback=self.callback_passes,
426 help='execute only specified PASS of conversion',
427 metavar='PASS',
429 group.add_option(
430 '--passes', '-p', type='string',
431 action='callback', callback=self.callback_passes,
432 help=(
433 'execute passes START through END, inclusive (PASS, '
434 'START, and END can be pass names or numbers)'
436 metavar='[START]:[END]',
440 group = parser.add_option_group('Information options')
441 group.add_option(
442 '--version',
443 action='callback', callback=self.callback_version,
444 help='print the version number',
446 group.add_option(
447 '--help', '-h',
448 action="help",
449 help='print this usage message and exit with success',
451 group.add_option(
452 '--help-passes',
453 action='callback', callback=self.callback_help_passes,
454 help='list the available passes and their numbers',
456 group.add_option(
457 '--verbose', '-v',
458 action='callback', callback=self.callback_verbose,
459 help='verbose (may be specified twice for debug output)',
461 group.add_option(
462 '--quiet', '-q',
463 action='callback', callback=self.callback_quiet,
464 help='quiet (may be specified twice for very quiet)',
466 group.add_option(go(
467 '--write-symbol-info', type='string',
468 help='write information and statistics about CVS symbols to PATH.',
469 metavar='PATH',
471 group.add_option(go(
472 '--skip-cleanup',
473 help='prevent the deletion of intermediate files',
475 group.add_option(
476 '--profile',
477 action='callback', callback=self.callback_profile,
478 help='profile with \'hotshot\' (into file cvs2svn.hotshot)',
481 (self.options, self.args) = parser.parse_args()
482 self.opts = go.opts
484 # Next look for any --options options, process them, and remove
485 # them from the list, as they affect the processing of other
486 # options:
487 options_file_found = False
488 for (opt, value) in self.get_options('--options'):
489 self.process_options_file(value)
490 options_file_found = True
492 # Now the log level has been set; log the time when the run started:
493 Log().verbose(
494 time.strftime(
495 'Conversion start time: %Y-%m-%d %I:%M:%S %Z',
496 time.localtime(Log().start_time)
500 if options_file_found:
501 # All of the options that are compatible with --options have
502 # been consumed above. It is an error if any other options or
503 # arguments are left:
504 self.verify_options_consumed()
505 else:
506 # --options was not specified. So we can process other options
507 # that are not compatible with --options.
508 self.process_remaining_options()
510 # Check for problems with the options:
511 self.check_options()
513 def add_project(
514 self,
515 project_cvs_repos_path,
516 trunk_path=None, branches_path=None, tags_path=None,
517 initial_directories=[],
518 symbol_transforms=None,
519 symbol_strategy_rules=[],
521 """Add a project to be converted.
523 Most arguments are passed straight through to the Project
524 constructor. SYMBOL_STRATEGY_RULES is an iterable of
525 SymbolStrategyRules that will be applied to symbols in this
526 project."""
528 initial_directories = [
529 path
530 for path in [trunk_path, branches_path, tags_path]
531 if path
532 ] + list(initial_directories)
534 symbol_strategy_rules = list(symbol_strategy_rules)
536 # Add rules to set the SVN paths for LODs depending on whether
537 # they are the trunk, tags, or branches:
538 if trunk_path is not None:
539 symbol_strategy_rules.append(TrunkPathRule(trunk_path))
540 if branches_path is not None:
541 symbol_strategy_rules.append(BranchesPathRule(branches_path))
542 if tags_path is not None:
543 symbol_strategy_rules.append(TagsPathRule(tags_path))
545 id = len(self.projects)
546 project = Project(
548 project_cvs_repos_path,
549 initial_directories=initial_directories,
550 symbol_transforms=symbol_transforms,
553 self.projects.append(project)
554 self.project_symbol_strategy_rules.append(symbol_strategy_rules)
556 def clear_projects(self):
557 """Clear the list of projects to be converted.
559 This method is for the convenience of options files, which may
560 want to import one another."""
562 del self.projects[:]
563 del self.project_symbol_strategy_rules[:]
565 def callback_help_passes(self, option, opt_str, value, parser):
566 self.pass_manager.help_passes()
567 sys.exit(0)
569 def callback_version(self, option, opt_str, value, parser):
570 sys.stdout.write(
571 '%s version %s\n' % (os.path.basename(self.progname), VERSION)
573 sys.exit(0)
575 def callback_verbose(self, option, opt_str, value, parser):
576 Log().increase_verbosity()
578 def callback_quiet(self, option, opt_str, value, parser):
579 Log().decrease_verbosity()
581 def callback_passes(self, option, opt_str, value, parser):
582 if value.find(':') >= 0:
583 start_pass, end_pass = value.split(':')
584 self.start_pass = self.pass_manager.get_pass_number(start_pass, 1)
585 self.end_pass = self.pass_manager.get_pass_number(
586 end_pass, self.pass_manager.num_passes
588 else:
589 self.end_pass = \
590 self.start_pass = \
591 self.pass_manager.get_pass_number(value)
593 def callback_dry_run(self, option, opt_str, value, parser):
594 Ctx().dry_run = True
596 def callback_profile(self, option, opt_str, value, parser):
597 self.profiling = True
599 def process_remaining_options(self):
600 """Process the options that are not compatible with --options."""
602 # Convenience var, so we don't have to keep instantiating this Borg.
603 ctx = Ctx()
605 options = self.options
607 options.svnrepos = None
608 options.existing_svnrepos = False
609 options.fs_type = None
610 options.bdb_txn_nosync = False
611 options.create_options = []
612 options.dump_only = False
613 options.dumpfile = None
614 options.use_rcs = False
615 options.use_cvs = False
616 options.use_internal_co = False
617 options.keep_trivial_imports = False
618 options.symbol_strategy_default = 'heuristic'
619 options.mime_types_file = None
620 options.auto_props_file = None
621 options.auto_props_ignore_case = True
622 options.eol_from_mime_type = False
623 options.default_eol = None
624 options.keywords_off = False
625 options.co_executable = config.CO_EXECUTABLE
626 options.cvs_executable = config.CVS_EXECUTABLE
627 options.trunk_base = config.DEFAULT_TRUNK_BASE
628 options.branches_base = config.DEFAULT_BRANCHES_BASE
629 options.tags_base = config.DEFAULT_TAGS_BASE
630 options.encodings = ['ascii']
631 options.fallback_encoding = None
632 options.force_branch = False
633 options.force_tag = False
634 options.symbol_transforms = []
635 options.symbol_strategy_rules = []
637 for opt, value in self.opts:
638 if opt in ['-s', '--svnrepos']:
639 options.svnrepos = value
640 elif opt == '--existing-svnrepos':
641 options.existing_svnrepos = True
642 elif opt == '--dumpfile':
643 options.dumpfile = value
644 elif opt == '--use-rcs':
645 options.use_rcs = True
646 elif opt == '--use-cvs':
647 options.use_cvs = True
648 elif opt == '--use-internal-co':
649 options.use_internal_co = True
650 elif opt == '--trunk-only':
651 ctx.trunk_only = True
652 elif opt == '--trunk':
653 options.trunk_base = value
654 elif opt == '--branches':
655 options.branches_base = value
656 elif opt == '--tags':
657 options.tags_base = value
658 elif opt == '--no-prune':
659 ctx.prune = False
660 elif opt == '--encoding':
661 options.encodings.insert(-1, value)
662 elif opt == '--fallback-encoding':
663 options.fallback_encoding = value
664 elif opt == '--symbol-hints':
665 options.symbol_strategy_rules.append(SymbolHintsFileRule(value))
666 elif opt == '--force-branch':
667 options.symbol_strategy_rules.append(
668 ForceBranchRegexpStrategyRule(value)
670 options.force_branch = True
671 elif opt == '--force-tag':
672 options.symbol_strategy_rules.append(
673 ForceTagRegexpStrategyRule(value)
675 options.force_tag = True
676 elif opt == '--exclude':
677 options.symbol_strategy_rules.append(ExcludeRegexpStrategyRule(value))
678 elif opt == '--keep-trivial-imports':
679 options.keep_trivial_imports = True
680 elif opt == '--symbol-default':
681 if value not in ['branch', 'tag', 'heuristic', 'strict']:
682 raise FatalError(
683 '%r is not a valid option for --symbol_default.' % (value,))
684 options.symbol_strategy_default = value
685 elif opt == '--keep-cvsignore':
686 ctx.keep_cvsignore = True
687 elif opt == '--no-cross-branch-commits':
688 ctx.cross_branch_commits = False
689 elif opt == '--retain-conflicting-attic-files':
690 ctx.retain_conflicting_attic_files = True
691 elif opt == '--symbol-transform':
692 [pattern, replacement] = value.split(":")
693 try:
694 options.symbol_transforms.append(
695 RegexpSymbolTransform(pattern, replacement))
696 except re.error:
697 raise FatalError("'%s' is not a valid regexp." % (pattern,))
698 elif opt == '--username':
699 ctx.username = value
700 elif opt == '--fs-type':
701 options.fs_type = value
702 elif opt == '--bdb-txn-nosync':
703 options.bdb_txn_nosync = True
704 elif opt == '--create-option':
705 options.create_options.append(value)
706 elif opt == '--cvs-revnums':
707 ctx.svn_property_setters.append(CVSRevisionNumberSetter())
708 elif opt == '--mime-types':
709 options.mime_types_file = value
710 elif opt == '--auto-props':
711 options.auto_props_file = value
712 elif opt == '--auto-props-ignore-case':
713 # "ignore case" is now the default, so this option doesn't
714 # affect anything.
715 options.auto_props_ignore_case = True
716 elif opt == '--eol-from-mime-type':
717 options.eol_from_mime_type = True
718 elif opt == '--default-eol':
719 try:
720 # Check that value is valid, and translate it to the proper case
721 options.default_eol = {
722 'binary' : None, 'native' : 'native',
723 'crlf' : 'CRLF', 'lf' : 'LF', 'cr' : 'CR',
724 }[value.lower()]
725 except KeyError:
726 raise FatalError(
727 'Illegal value specified for --default-eol: %s' % (value,)
729 elif opt == '--no-default-eol':
730 # For backwards compatibility:
731 options.default_eol = None
732 elif opt == '--keywords-off':
733 options.keywords_off = True
734 elif opt == '--tmpdir':
735 ctx.tmpdir = value
736 elif opt == '--write-symbol-info':
737 ctx.symbol_info_filename = value
738 elif opt == '--skip-cleanup':
739 ctx.skip_cleanup = True
740 elif opt == '--svnadmin':
741 ctx.svnadmin_executable = value
742 elif opt == '--co':
743 options.co_executable = value
744 elif opt == '--cvs':
745 options.cvs_executable = value
746 elif opt == '--sort':
747 ctx.sort_executable = value
748 elif opt == '--dump-only':
749 options.dump_only = True
750 Log().error(
751 warning_prefix +
752 ': The --dump-only option is deprecated (it is implied\n'
753 'by --dumpfile).\n'
755 elif opt == '--create':
756 Log().error(
757 warning_prefix +
758 ': The behaviour produced by the --create option is now the '
759 'default,\nand passing the option is deprecated.\n'
762 # Consistency check for options and arguments.
763 if len(self.args) == 0:
764 self.usage()
765 sys.exit(1)
767 if len(self.args) > 1:
768 Log().error(error_prefix + ": must pass only one CVS repository.\n")
769 self.usage()
770 sys.exit(1)
772 cvsroot = self.args[0]
774 if options.dump_only and not options.dumpfile:
775 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
777 if not options.svnrepos and not options.dumpfile and not ctx.dry_run:
778 raise FatalError("must pass one of '-s' or '--dumpfile'.")
780 def not_both(opt1val, opt1name, opt2val, opt2name):
781 if opt1val and opt2val:
782 raise FatalError("cannot pass both '%s' and '%s'."
783 % (opt1name, opt2name,))
785 not_both(options.svnrepos, '-s',
786 options.dumpfile, '--dumpfile')
788 not_both(options.dumpfile, '--dumpfile',
789 options.existing_svnrepos, '--existing-svnrepos')
791 not_both(options.bdb_txn_nosync, '--bdb-txn-nosync',
792 options.existing_svnrepos, '--existing-svnrepos')
794 not_both(options.dumpfile, '--dumpfile',
795 options.bdb_txn_nosync, '--bdb-txn-nosync')
797 not_both(options.fs_type, '--fs-type',
798 options.existing_svnrepos, '--existing-svnrepos')
800 not_both(options.use_rcs, '--use-rcs',
801 options.use_cvs, '--use-cvs')
803 not_both(options.use_rcs, '--use-rcs',
804 options.use_internal_co, '--use-internal-co')
806 not_both(options.use_cvs, '--use-cvs',
807 options.use_internal_co, '--use-internal-co')
809 not_both(ctx.trunk_only, '--trunk-only',
810 options.force_branch, '--force-branch')
812 not_both(ctx.trunk_only, '--trunk-only',
813 options.force_tag, '--force-tag')
815 if (
816 options.fs_type
817 and options.fs_type != 'bdb'
818 and options.bdb_txn_nosync
820 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
821 % options.fs_type)
823 if options.svnrepos:
824 if options.existing_svnrepos:
825 ctx.output_option = ExistingRepositoryOutputOption(options.svnrepos)
826 else:
827 ctx.output_option = NewRepositoryOutputOption(
828 options.svnrepos,
829 fs_type=options.fs_type, bdb_txn_nosync=options.bdb_txn_nosync,
830 create_options=options.create_options)
831 else:
832 ctx.output_option = DumpfileOutputOption(options.dumpfile)
834 if options.use_rcs:
835 ctx.revision_recorder = NullRevisionRecorder()
836 ctx.revision_excluder = NullRevisionExcluder()
837 ctx.revision_reader = RCSRevisionReader(options.co_executable)
838 elif options.use_cvs:
839 ctx.revision_recorder = NullRevisionRecorder()
840 ctx.revision_excluder = NullRevisionExcluder()
841 ctx.revision_reader = CVSRevisionReader(options.cvs_executable)
842 else:
843 # --use-internal-co is the default:
844 ctx.revision_recorder = InternalRevisionRecorder(compress=True)
845 ctx.revision_excluder = InternalRevisionExcluder()
846 ctx.revision_reader = InternalRevisionReader(compress=True)
848 try:
849 ctx.cvs_author_decoder = CVSTextDecoder(
850 options.encodings, options.fallback_encoding
852 ctx.cvs_log_decoder = CVSTextDecoder(
853 options.encodings, options.fallback_encoding
855 # Don't use fallback_encoding for filenames:
856 ctx.cvs_filename_decoder = CVSTextDecoder(options.encodings)
857 except LookupError, e:
858 raise FatalError(str(e))
860 # Add the standard symbol name cleanup rules:
861 options.symbol_transforms.extend([
862 ReplaceSubstringsSymbolTransform('\\','/'),
863 # Remove leading, trailing, and repeated slashes:
864 NormalizePathsSymbolTransform(),
867 if not options.keep_trivial_imports:
868 options.symbol_strategy_rules.append(ExcludeTrivialImportBranchRule())
870 options.symbol_strategy_rules.append(UnambiguousUsageRule())
871 if options.symbol_strategy_default == 'strict':
872 pass
873 elif options.symbol_strategy_default == 'branch':
874 options.symbol_strategy_rules.append(AllBranchRule())
875 elif options.symbol_strategy_default == 'tag':
876 options.symbol_strategy_rules.append(AllTagRule())
877 elif options.symbol_strategy_default == 'heuristic':
878 options.symbol_strategy_rules.append(BranchIfCommitsRule())
879 options.symbol_strategy_rules.append(HeuristicStrategyRule())
880 else:
881 assert False
883 # Now add a rule whose job it is to pick the preferred parents of
884 # branches and tags:
885 options.symbol_strategy_rules.append(HeuristicPreferredParentRule())
887 if options.auto_props_file:
888 ctx.svn_property_setters.append(AutoPropsPropertySetter(
889 options.auto_props_file, options.auto_props_ignore_case))
891 if options.mime_types_file:
892 ctx.svn_property_setters.append(MimeMapper(options.mime_types_file))
894 ctx.svn_property_setters.append(CVSBinaryFileEOLStyleSetter())
896 ctx.svn_property_setters.append(CVSBinaryFileDefaultMimeTypeSetter())
898 if options.eol_from_mime_type:
899 ctx.svn_property_setters.append(EOLStyleFromMimeTypeSetter())
901 ctx.svn_property_setters.append(
902 DefaultEOLStyleSetter(options.default_eol)
905 ctx.svn_property_setters.append(SVNBinaryFileKeywordsPropertySetter())
907 if not options.keywords_off:
908 ctx.svn_property_setters.append(
909 KeywordsPropertySetter(config.SVN_KEYWORDS_VALUE))
911 ctx.svn_property_setters.append(ExecutablePropertySetter())
913 # Create the default project (using ctx.trunk, ctx.branches, and
914 # ctx.tags):
915 self.add_project(
916 cvsroot,
917 trunk_path=options.trunk_base,
918 branches_path=options.branches_base,
919 tags_path=options.tags_base,
920 symbol_transforms=options.symbol_transforms,
921 symbol_strategy_rules=options.symbol_strategy_rules,
924 def check_options(self):
925 """Check the the run options are OK.
927 This should only be called after all options have been processed."""
929 # Convenience var, so we don't have to keep instantiating this Borg.
930 ctx = Ctx()
932 if not self.start_pass <= self.end_pass:
933 raise InvalidPassError(
934 'Ending pass must not come before starting pass.')
936 if not ctx.dry_run and ctx.output_option is None:
937 raise FatalError('No output option specified.')
939 if ctx.output_option is not None:
940 ctx.output_option.check()
942 if not self.projects:
943 raise FatalError('No project specified.')
945 def get_options(self, *names):
946 """Return a list of (option,value) pairs for options in NAMES.
948 Return a list containing any (opt, value) pairs from self.opts
949 where opt is in NAMES. The matching options are removed from
950 self.opts."""
952 retval = []
953 i = 0
954 while i < len(self.opts):
955 (opt, value) = self.opts[i]
956 if opt in names:
957 del self.opts[i]
958 retval.append( (opt, value) )
959 else:
960 i += 1
961 return retval
963 def verify_options_consumed(self):
964 """Verify that all command line options and arguments have been used.
966 The --options option was specified, and all options that are
967 compatible with that option have already been consumed. Verify
968 that there are no remaining (i.e., incompatible) options or
969 arguments."""
971 if self.opts or self.args:
972 if self.opts:
973 Log().error(
974 '%s: The following options cannot be used in combination with '
975 'the --options\n'
976 'option:\n'
977 ' %s\n'
979 error_prefix,
980 '\n '.join([opt for (opt,value) in self.opts])
983 if self.args:
984 Log().error(
985 '%s: No cvs-repos-path arguments are allowed with the --options '
986 'option.\n'
987 % (error_prefix,)
989 sys.exit(1)
991 def process_options_file(self, options_filename):
992 """Read options from the file named OPTIONS_FILENAME.
994 Store the run options to SELF."""
996 g = {}
997 l = {
998 'ctx' : Ctx(),
999 'run_options' : self,
1001 execfile(options_filename, g, l)
1003 def usage(self):
1004 self.parser.print_help()