Import svntest.main.run_tests explicitly.
[cvs2svn.git] / cvs2svn_lib / run_options.py
blobf82cfd9adf07e67f020a1b5d4ad5cd1261b63b58
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 getopt
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 usage_message_template = """\
76 Usage: %(progname)s --options OPTIONFILE
77 %(progname)s [OPTION...] OUTPUT-OPTION CVS-REPOS-PATH
78 %(progname)s converts a CVS repository into a Subversion repository, including
79 history.
81 Configuration via options file:
83 --options=PATH read the conversion options from PATH. This
84 method allows more flexibility than using
85 command-line options. See documentation for info
87 Output options:
89 -s, --svnrepos=PATH path where SVN repos should be created
90 --existing-svnrepos load into existing SVN repository (for use with
91 --svnrepos)
92 --fs-type=TYPE pass --fs-type=TYPE to "svnadmin create" (for use
93 with --svnrepos)
94 --bdb-txn-nosync pass --bdb-txn-nosync to "svnadmin create" (for
95 use with --svnrepos)
96 --create-option=OPT pass OPT to "svnadmin create" (for use with
97 --svnrepos)
98 --dumpfile=PATH just produce a dumpfile; don't commit to a repos
99 --dry-run do not create a repository or a dumpfile;
100 just print what would happen.
102 Conversion options:
104 --trunk-only convert only trunk commits, not tags nor branches
105 --trunk=PATH path for trunk (default: %(trunk_base)s)
106 --branches=PATH path for branches (default: %(branches_base)s)
107 --tags=PATH path for tags (default: %(tags_base)s)
108 --no-prune don't prune empty directories
109 --encoding=ENC encoding for paths and log messages in CVS repos.
110 If option is specified multiple times, encoders
111 are tried in order until one succeeds. See
112 http://docs.python.org/lib/standard-encodings.html
113 for a list of standard Python encodings.
114 --fallback-encoding=ENC If all --encodings fail, use lossy encoding
115 with ENC
116 --symbol-transform=P:S transform symbol names from P to S, where P and S
117 use Python regexp and reference syntax
118 respectively. P must match the whole symbol name
119 --symbol-hints=PATH read symbol conversion hints from PATH
120 --force-branch=REGEXP force symbols matching REGEXP to be branches
121 --force-tag=REGEXP force symbols matching REGEXP to be tags
122 --exclude=REGEXP exclude branches and tags matching REGEXP
123 --keep-trivial-imports do not exclude branches that were only used for
124 a single import (usually these are unneeded)
125 --symbol-default=OPT specify how ambiguous symbols are converted.
126 OPT is "heuristic" (default), "strict", "branch",
127 or "tag"
128 --keep-cvsignore keep .cvsignore files (in addition to creating
129 the analogous svn:ignore properties)
130 --no-cross-branch-commits prevent the creation of cross-branch commits
131 --retain-conflicting-attic-files if a file appears both in and out of
132 the CVS Attic, then leave the attic version in a
133 SVN directory called "Attic"
134 --username=NAME username for cvs2svn-synthesized commits
135 --cvs-revnums record CVS revision numbers as file properties
136 --mime-types=FILE specify an apache-style mime.types file for
137 setting svn:mime-type
138 --eol-from-mime-type set svn:eol-style from mime type if known
139 --auto-props=FILE set file properties from the auto-props section
140 of a file in svn config format
141 --default-eol=VALUE default svn:eol-style for non-binary files with
142 undetermined mime types. VALUE is "binary"
143 (default), "native", "CRLF", "LF", or "CR"
144 --keywords-off don't set svn:keywords on any files (by default,
145 cvs2svn sets svn:keywords on non-binary files to
146 "%(svn_keywords_value)s")
148 Extraction options:
150 --use-rcs use RCS to extract revision contents
151 --use-cvs use CVS to extract revision contents
152 (only use this if having problems with RCS)
153 --use-internal-co use internal code to extract revision contents
154 (very fast but disk space intensive) (default)
156 Environment options:
158 --tmpdir=PATH directory to use for temporary data files
159 (default "cvs2svn-tmp")
160 --svnadmin=PATH path to the "svnadmin" program
161 --co=PATH path to the "co" program (required if --use-rcs)
162 --cvs=PATH path to the "cvs" program (required if --use-cvs)
163 --sort=PATH path to the GNU "sort" program
165 Partial conversions:
167 -p, --pass PASS execute only specified PASS of conversion
168 -p, --passes [START]:[END] execute passes START through END, inclusive (PASS,
169 START, and END can be pass names or numbers)
171 Information options:
173 --version print the version number
174 -h, --help print this usage message and exit with success
175 --help-passes list the available passes and their numbers
176 -v, --verbose verbose (may be specified twice for debug output)
177 -q, --quiet quiet (may be specified twice for very quiet)
178 --write-symbol-info=PATH write information and statistics about CVS
179 symbols to PATH.
180 --skip-cleanup prevent the deletion of intermediate files
181 --profile profile with 'hotshot' (into file cvs2svn.hotshot)
184 class RunOptions:
185 """A place to store meta-options that are used to start the conversion."""
187 def __init__(self, progname, cmd_args, pass_manager):
188 """Process the command-line options, storing run options to SELF.
190 PROGNAME is the name of the program, used in the usage string.
191 CMD_ARGS is the list of command-line arguments passed to the
192 program. PASS_MANAGER is an instance of PassManager, needed to
193 help process the -p and --help-passes options."""
195 self.pass_manager = pass_manager
196 self.start_pass = 1
197 self.end_pass = self.pass_manager.num_passes
198 self.profiling = False
199 self.progname = progname
201 self.projects = []
203 # A list of one list of SymbolStrategyRules for each project:
204 self.project_symbol_strategy_rules = []
206 try:
207 self.opts, self.args = getopt.gnu_getopt(cmd_args, 'hvqs:p:', [
208 "options=",
210 "svnrepos=", "existing-svnrepos",
211 "fs-type=", "bdb-txn-nosync", "create-option=",
212 "dumpfile=",
213 "dry-run",
215 "trunk-only",
216 "trunk=", "branches=", "tags=",
217 "no-prune",
218 "encoding=", "fallback-encoding=",
219 "symbol-transform=",
220 "symbol-hints=",
221 "force-branch=", "force-tag=", "exclude=",
222 "keep-trivial-imports", "symbol-default=",
223 "keep-cvsignore",
224 "no-cross-branch-commits",
225 "retain-conflicting-attic-files",
226 "username=",
227 "cvs-revnums",
228 "mime-types=",
229 "auto-props=",
230 "eol-from-mime-type", "default-eol=",
231 "keywords-off",
233 "use-rcs", "use-cvs", "use-internal-co",
235 "tmpdir=",
236 "svnadmin=", "co=", "cvs=", "sort=",
238 "pass=", "passes=",
240 "version", "help", "help-passes",
241 "verbose", "quiet",
242 "write-symbol-info=",
243 "skip-cleanup",
244 "profile",
246 # These options are deprecated and are only included for
247 # backwards compatibility:
248 "dump-only", "create", "no-default-eol", "auto-props-ignore-case",
250 except getopt.GetoptError, e:
251 Log().error('%s: %s\n\n' % (error_prefix, e))
252 self.usage()
253 sys.exit(1)
255 # First look for any 'help'-type options, as they just cause the
256 # program to print help and ignore any other options:
257 self.process_help_options()
259 # Next look for any --options options, process them, and remove
260 # them from the list, as they affect the processing of other
261 # options:
262 options_file_found = False
263 for (opt, value) in self.get_options('--options'):
264 self.process_options_file(value)
265 options_file_found = True
267 # Now process options that can be used either with or without
268 # --options:
269 self.process_common_options()
271 # Now the log level has been set; log the time when the run started:
272 Log().verbose(
273 time.strftime(
274 'Conversion start time: %Y-%m-%d %I:%M:%S %Z',
275 time.localtime(Log().start_time)
279 if options_file_found:
280 # All of the options that are compatible with --options have
281 # been consumed above. It is an error if any other options or
282 # arguments are left:
283 self.verify_options_consumed()
284 else:
285 # --options was not specified. So we can process other options
286 # that are not compatible with --options.
287 self.process_remaining_options()
289 # Check for problems with the options:
290 self.check_options()
292 def add_project(
293 self,
294 project_cvs_repos_path,
295 trunk_path=None, branches_path=None, tags_path=None,
296 initial_directories=[],
297 symbol_transforms=None,
298 symbol_strategy_rules=[],
300 """Add a project to be converted.
302 Most arguments are passed straight through to the Project
303 constructor. SYMBOL_STRATEGY_RULES is an iterable of
304 SymbolStrategyRules that will be applied to symbols in this
305 project."""
307 initial_directories = [
308 path
309 for path in [trunk_path, branches_path, tags_path]
310 if path
311 ] + list(initial_directories)
313 symbol_strategy_rules = list(symbol_strategy_rules)
315 # Add rules to set the SVN paths for LODs depending on whether
316 # they are the trunk, tags, or branches:
317 if trunk_path is not None:
318 symbol_strategy_rules.append(TrunkPathRule(trunk_path))
319 if branches_path is not None:
320 symbol_strategy_rules.append(BranchesPathRule(branches_path))
321 if tags_path is not None:
322 symbol_strategy_rules.append(TagsPathRule(tags_path))
324 id = len(self.projects)
325 project = Project(
327 project_cvs_repos_path,
328 initial_directories=initial_directories,
329 symbol_transforms=symbol_transforms,
332 self.projects.append(project)
333 self.project_symbol_strategy_rules.append(list(symbol_strategy_rules))
335 def clear_projects(self):
336 """Clear the list of projects to be converted.
338 This method is for the convenience of options files, which may
339 want to import one another."""
341 del self.projects[:]
342 del self.project_symbol_strategy_rules[:]
344 def process_help_options(self):
345 """Process any help-type options."""
347 if self.get_options('-h', '--help'):
348 self.usage()
349 sys.exit(0)
350 elif self.get_options('--help-passes'):
351 self.pass_manager.help_passes()
352 sys.exit(0)
353 elif self.get_options('--version'):
354 print '%s version %s' % (os.path.basename(self.progname), VERSION)
355 sys.exit(0)
357 def process_common_options(self):
358 """Process the options that are compatible with --options."""
360 # Adjust level of verbosity:
361 for (opt, value) in self.get_options('--verbose', '-v'):
362 Log().increase_verbosity()
364 for (opt, value) in self.get_options('--quiet', '-q'):
365 Log().decrease_verbosity()
367 for (opt, value) in self.get_options('--pass', '--passes', '-p'):
368 if value.find(':') >= 0:
369 start_pass, end_pass = value.split(':')
370 self.start_pass = self.pass_manager.get_pass_number(
371 start_pass, 1)
372 self.end_pass = self.pass_manager.get_pass_number(
373 end_pass, self.pass_manager.num_passes)
374 else:
375 self.end_pass = \
376 self.start_pass = \
377 self.pass_manager.get_pass_number(value)
379 if self.get_options('--dry-run'):
380 Ctx().dry_run = True
382 if self.get_options('--profile'):
383 self.profiling = True
385 def process_remaining_options(self):
386 """Process the options that are not compatible with --options."""
388 # Convenience var, so we don't have to keep instantiating this Borg.
389 ctx = Ctx()
391 target = None
392 existing_svnrepos = False
393 fs_type = None
394 bdb_txn_nosync = False
395 create_options = []
396 dump_only = False
397 dumpfile = None
398 use_rcs = False
399 use_cvs = False
400 use_internal_co = False
401 keep_trivial_imports = False
402 symbol_strategy_default = 'heuristic'
403 mime_types_file = None
404 auto_props_file = None
405 auto_props_ignore_case = True
406 eol_from_mime_type = False
407 default_eol = None
408 keywords_off = False
409 co_executable = config.CO_EXECUTABLE
410 cvs_executable = config.CVS_EXECUTABLE
411 trunk_base = config.DEFAULT_TRUNK_BASE
412 branches_base = config.DEFAULT_BRANCHES_BASE
413 tags_base = config.DEFAULT_TAGS_BASE
414 encodings = ['ascii']
415 fallback_encoding = None
416 force_branch = False
417 force_tag = False
418 symbol_transforms = []
419 symbol_strategy_rules = []
421 for opt, value in self.opts:
422 if opt in ['-s', '--svnrepos']:
423 target = value
424 elif opt == '--existing-svnrepos':
425 existing_svnrepos = True
426 elif opt == '--dumpfile':
427 dumpfile = value
428 elif opt == '--use-rcs':
429 use_rcs = True
430 elif opt == '--use-cvs':
431 use_cvs = True
432 elif opt == '--use-internal-co':
433 use_internal_co = True
434 elif opt == '--trunk-only':
435 ctx.trunk_only = True
436 elif opt == '--trunk':
437 trunk_base = value
438 elif opt == '--branches':
439 branches_base = value
440 elif opt == '--tags':
441 tags_base = value
442 elif opt == '--no-prune':
443 ctx.prune = False
444 elif opt == '--encoding':
445 encodings.insert(-1, value)
446 elif opt == '--fallback-encoding':
447 fallback_encoding = value
448 elif opt == '--symbol-hints':
449 symbol_strategy_rules.append(SymbolHintsFileRule(value))
450 elif opt == '--force-branch':
451 symbol_strategy_rules.append(ForceBranchRegexpStrategyRule(value))
452 force_branch = True
453 elif opt == '--force-tag':
454 symbol_strategy_rules.append(ForceTagRegexpStrategyRule(value))
455 force_tag = True
456 elif opt == '--exclude':
457 symbol_strategy_rules.append(ExcludeRegexpStrategyRule(value))
458 elif opt == '--keep-trivial-imports':
459 keep_trivial_imports = True
460 elif opt == '--symbol-default':
461 if value not in ['branch', 'tag', 'heuristic', 'strict']:
462 raise FatalError(
463 '%r is not a valid option for --symbol_default.' % (value,))
464 symbol_strategy_default = value
465 elif opt == '--keep-cvsignore':
466 ctx.keep_cvsignore = True
467 elif opt == '--no-cross-branch-commits':
468 ctx.cross_branch_commits = False
469 elif opt == '--retain-conflicting-attic-files':
470 ctx.retain_conflicting_attic_files = True
471 elif opt == '--symbol-transform':
472 [pattern, replacement] = value.split(":")
473 try:
474 symbol_transforms.append(
475 RegexpSymbolTransform(pattern, replacement))
476 except re.error:
477 raise FatalError("'%s' is not a valid regexp." % (pattern,))
478 elif opt == '--username':
479 ctx.username = value
480 elif opt == '--fs-type':
481 fs_type = value
482 elif opt == '--bdb-txn-nosync':
483 bdb_txn_nosync = True
484 elif opt == '--create-option':
485 create_options.append(value)
486 elif opt == '--cvs-revnums':
487 ctx.svn_property_setters.append(CVSRevisionNumberSetter())
488 elif opt == '--mime-types':
489 mime_types_file = value
490 elif opt == '--auto-props':
491 auto_props_file = value
492 elif opt == '--auto-props-ignore-case':
493 # "ignore case" is now the default, so this option doesn't
494 # affect anything.
495 auto_props_ignore_case = True
496 elif opt == '--eol-from-mime-type':
497 eol_from_mime_type = True
498 elif opt == '--default-eol':
499 try:
500 # Check that value is valid, and translate it to the proper case
501 default_eol = {
502 'binary' : None, 'native' : 'native',
503 'crlf' : 'CRLF', 'lf' : 'LF', 'cr' : 'CR',
504 }[value.lower()]
505 except KeyError:
506 raise FatalError(
507 'Illegal value specified for --default-eol: %s' % (value,)
509 elif opt == '--no-default-eol':
510 # For backwards compatibility:
511 default_eol = None
512 elif opt == '--keywords-off':
513 keywords_off = True
514 elif opt == '--tmpdir':
515 ctx.tmpdir = value
516 elif opt == '--write-symbol-info':
517 ctx.symbol_info_filename = value
518 elif opt == '--skip-cleanup':
519 ctx.skip_cleanup = True
520 elif opt == '--svnadmin':
521 ctx.svnadmin_executable = value
522 elif opt == '--co':
523 co_executable = value
524 elif opt == '--cvs':
525 cvs_executable = value
526 elif opt == '--sort':
527 ctx.sort_executable = value
528 elif opt == '--dump-only':
529 dump_only = True
530 Log().error(
531 warning_prefix +
532 ': The --dump-only option is deprecated (it is implied\n'
533 'by --dumpfile).\n'
535 elif opt == '--create':
536 Log().error(
537 warning_prefix +
538 ': The behaviour produced by the --create option is now the '
539 'default,\nand passing the option is deprecated.\n'
542 # Consistency check for options and arguments.
543 if len(self.args) == 0:
544 self.usage()
545 sys.exit(1)
547 if len(self.args) > 1:
548 Log().error(error_prefix + ": must pass only one CVS repository.\n")
549 self.usage()
550 sys.exit(1)
552 cvsroot = self.args[0]
554 if dump_only and not dumpfile:
555 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
557 if (not target) and (not dumpfile) and (not ctx.dry_run):
558 raise FatalError("must pass one of '-s' or '--dumpfile'.")
560 def not_both(opt1val, opt1name, opt2val, opt2name):
561 if opt1val and opt2val:
562 raise FatalError("cannot pass both '%s' and '%s'."
563 % (opt1name, opt2name,))
565 not_both(target, '-s',
566 dumpfile, '--dumpfile')
568 not_both(dumpfile, '--dumpfile',
569 existing_svnrepos, '--existing-svnrepos')
571 not_both(bdb_txn_nosync, '--bdb-txn-nosync',
572 existing_svnrepos, '--existing-svnrepos')
574 not_both(dumpfile, '--dumpfile',
575 bdb_txn_nosync, '--bdb-txn-nosync')
577 not_both(fs_type, '--fs-type',
578 existing_svnrepos, '--existing-svnrepos')
580 not_both(use_rcs, '--use-rcs',
581 use_cvs, '--use-cvs')
583 not_both(use_rcs, '--use-rcs',
584 use_internal_co, '--use-internal-co')
586 not_both(use_cvs, '--use-cvs',
587 use_internal_co, '--use-internal-co')
589 not_both(ctx.trunk_only, '--trunk-only',
590 force_branch, '--force-branch')
592 not_both(ctx.trunk_only, '--trunk-only',
593 force_tag, '--force-tag')
595 if fs_type and fs_type != 'bdb' and bdb_txn_nosync:
596 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
597 % fs_type)
599 if target:
600 if existing_svnrepos:
601 ctx.output_option = ExistingRepositoryOutputOption(target)
602 else:
603 ctx.output_option = NewRepositoryOutputOption(
604 target, fs_type=fs_type, bdb_txn_nosync=bdb_txn_nosync,
605 create_options=create_options)
606 else:
607 ctx.output_option = DumpfileOutputOption(dumpfile)
609 if use_rcs:
610 ctx.revision_recorder = NullRevisionRecorder()
611 ctx.revision_excluder = NullRevisionExcluder()
612 ctx.revision_reader = RCSRevisionReader(co_executable)
613 elif use_cvs:
614 ctx.revision_recorder = NullRevisionRecorder()
615 ctx.revision_excluder = NullRevisionExcluder()
616 ctx.revision_reader = CVSRevisionReader(cvs_executable)
617 else:
618 # --use-internal-co is the default:
619 ctx.revision_recorder = InternalRevisionRecorder(compress=True)
620 ctx.revision_excluder = InternalRevisionExcluder()
621 ctx.revision_reader = InternalRevisionReader(compress=True)
623 try:
624 ctx.cvs_author_decoder = CVSTextDecoder(encodings, fallback_encoding)
625 ctx.cvs_log_decoder = CVSTextDecoder(encodings, fallback_encoding)
626 # Don't use fallback_encoding for filenames:
627 ctx.cvs_filename_decoder = CVSTextDecoder(encodings)
628 except LookupError, e:
629 raise FatalError(str(e))
631 # Add the standard symbol name cleanup rules:
632 symbol_transforms.extend([
633 ReplaceSubstringsSymbolTransform('\\','/'),
634 # Remove leading, trailing, and repeated slashes:
635 NormalizePathsSymbolTransform(),
638 if not keep_trivial_imports:
639 symbol_strategy_rules.append(ExcludeTrivialImportBranchRule())
641 symbol_strategy_rules.append(UnambiguousUsageRule())
642 if symbol_strategy_default == 'strict':
643 pass
644 elif symbol_strategy_default == 'branch':
645 symbol_strategy_rules.append(AllBranchRule())
646 elif symbol_strategy_default == 'tag':
647 symbol_strategy_rules.append(AllTagRule())
648 elif symbol_strategy_default == 'heuristic':
649 symbol_strategy_rules.append(BranchIfCommitsRule())
650 symbol_strategy_rules.append(HeuristicStrategyRule())
651 else:
652 assert False
654 # Now add a rule whose job it is to pick the preferred parents of
655 # branches and tags:
656 symbol_strategy_rules.append(HeuristicPreferredParentRule())
658 if auto_props_file:
659 ctx.svn_property_setters.append(AutoPropsPropertySetter(
660 auto_props_file, auto_props_ignore_case))
662 if mime_types_file:
663 ctx.svn_property_setters.append(MimeMapper(mime_types_file))
665 ctx.svn_property_setters.append(CVSBinaryFileEOLStyleSetter())
667 ctx.svn_property_setters.append(CVSBinaryFileDefaultMimeTypeSetter())
669 if eol_from_mime_type:
670 ctx.svn_property_setters.append(EOLStyleFromMimeTypeSetter())
672 ctx.svn_property_setters.append(DefaultEOLStyleSetter(default_eol))
674 ctx.svn_property_setters.append(SVNBinaryFileKeywordsPropertySetter())
676 if not keywords_off:
677 ctx.svn_property_setters.append(
678 KeywordsPropertySetter(config.SVN_KEYWORDS_VALUE))
680 ctx.svn_property_setters.append(ExecutablePropertySetter())
682 # Create the default project (using ctx.trunk, ctx.branches, and
683 # ctx.tags):
684 self.add_project(
685 cvsroot,
686 trunk_path=trunk_base,
687 branches_path=branches_base,
688 tags_path=tags_base,
689 symbol_transforms=symbol_transforms,
690 symbol_strategy_rules=symbol_strategy_rules,
693 def check_options(self):
694 """Check the the run options are OK.
696 This should only be called after all options have been processed."""
698 # Convenience var, so we don't have to keep instantiating this Borg.
699 ctx = Ctx()
701 if not self.start_pass <= self.end_pass:
702 raise InvalidPassError(
703 'Ending pass must not come before starting pass.')
705 if not ctx.dry_run and ctx.output_option is None:
706 raise FatalError('No output option specified.')
708 if ctx.output_option is not None:
709 ctx.output_option.check()
711 if not self.projects:
712 raise FatalError('No project specified.')
714 def get_options(self, *names):
715 """Return a list of (option,value) pairs for options in NAMES.
717 Return a list containing any (opt, value) pairs from self.opts
718 where opt is in NAMES. The matching options are removed from
719 self.opts."""
721 retval = []
722 i = 0
723 while i < len(self.opts):
724 (opt, value) = self.opts[i]
725 if opt in names:
726 del self.opts[i]
727 retval.append( (opt, value) )
728 else:
729 i += 1
730 return retval
732 def verify_options_consumed(self):
733 """Verify that all command line options and arguments have been used.
735 The --options option was specified, and all options that are
736 compatible with that option have already been consumed. Verify
737 that there are no remaining (i.e., incompatible) options or
738 arguments."""
740 if self.opts or self.args:
741 if self.opts:
742 Log().error(
743 '%s: The following options cannot be used in combination with '
744 'the --options\n'
745 'option:\n'
746 ' %s\n'
748 error_prefix,
749 '\n '.join([opt for (opt,value) in self.opts])
752 if self.args:
753 Log().error(
754 '%s: No cvs-repos-path arguments are allowed with the --options '
755 'option.\n'
756 % (error_prefix,)
758 sys.exit(1)
760 def process_options_file(self, options_filename):
761 """Read options from the file named OPTIONS_FILENAME.
763 Store the run options to SELF."""
765 g = {}
766 l = {
767 'ctx' : Ctx(),
768 'run_options' : self,
770 execfile(options_filename, g, l)
772 def usage(self):
773 Log().write(usage_message_template % {
774 'progname' : self.progname,
775 'trunk_base' : config.DEFAULT_TRUNK_BASE,
776 'branches_base' : config.DEFAULT_BRANCHES_BASE,
777 'tags_base' : config.DEFAULT_TAGS_BASE,
778 'svn_keywords_value' : config.SVN_KEYWORDS_VALUE,