~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/mergetools.py

  • Committer: Gordon Tyler
  • Date: 2011-01-20 04:44:14 UTC
  • mto: This revision was merged to the branch mainline in revision 5632.
  • Revision ID: gordon@doxxx.net-20110120044414-x8vislng3ukcfr1d
Simplified mergetools module down to functions which deal with command lines -- no MergeTool class.

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
lazy_import(globals(), """
27
27
from bzrlib import (
28
28
    cmdline,
29
 
    config,
30
 
    errors,
31
29
    osutils,
32
30
    trace,
33
 
    ui,
34
 
    workingtree,
35
31
)
36
32
""")
37
33
 
38
34
 
39
 
def subprocess_invoker(executable, args, cleanup):
40
 
    retcode = subprocess.call([executable] + args)
41
 
    cleanup(retcode)
42
 
    return retcode
43
 
 
44
 
 
45
 
_WIN32_PATH_EXT = [unicode(ext.lower())
46
 
                   for ext in os.getenv('PATHEXT', '').split(os.pathsep)]
47
 
 
48
 
 
49
 
class MergeTool(object):
50
 
 
51
 
    def __init__(self, name, command_line):
52
 
        """Initializes the merge tool with a name and a command-line."""
53
 
        self.name = name
54
 
        self.command_line = command_line
55
 
 
56
 
    def __repr__(self):
57
 
        return '<%s(%s, %s)>' % (self.__class__, self.name, self.command_line)
58
 
        
59
 
    def _get_command_line(self):
60
 
        return self._command_line
61
 
    
62
 
    def _set_command_line(self, command_line):
63
 
        self._command_line = command_line
64
 
        self._cmd_list = cmdline.split(self.command_line)
65
 
        
66
 
    command_line = property(_get_command_line, _set_command_line)
67
 
 
68
 
    def is_available(self):
69
 
        exe = self._cmd_list[0]
70
 
        if sys.platform == 'win32':
71
 
            if os.path.isabs(exe):
72
 
                base, ext = os.path.splitext(exe)
73
 
                return os.path.exists(exe) and ext in _WIN32_PATH_EXT
74
 
            else:
75
 
                return osutils.find_executable_on_path(exe) is not None
76
 
        else:
77
 
            return (os.access(exe, os.X_OK)
78
 
                    or osutils.find_executable_on_path(exe) is not None)
79
 
 
80
 
    def invoke(self, filename, invoker=None):
81
 
        if invoker is None:
82
 
            invoker = subprocess_invoker
83
 
        args, tmp_file = self._subst_filename(self._cmd_list, filename)
84
 
        def cleanup(retcode):
85
 
            if tmp_file is not None:
86
 
                if retcode == 0: # on success, replace file with temp file
87
 
                    shutil.move(tmp_file, filename)
88
 
                else: # otherwise, delete temp file
89
 
                    os.remove(tmp_file)
90
 
        return invoker(args[0], args[1:], cleanup)
91
 
 
92
 
    def _subst_filename(self, args, filename):
93
 
        subst_names = {
94
 
            u'base': filename + u'.BASE',
95
 
            u'this': filename + u'.THIS',
96
 
            u'other': filename + u'.OTHER',
97
 
            u'result': filename,
98
 
        }
99
 
        tmp_file = None
100
 
        subst_args = []
101
 
        for arg in args:
102
 
            if u'{this_temp}' in arg and not 'this_temp' in subst_names:
103
 
                tmp_file = tempfile.mktemp(u"_bzr_mergetools_%s.THIS" %
104
 
                                           os.path.basename(filename))
105
 
                shutil.copy(filename + u".THIS", tmp_file)
106
 
                subst_names['this_temp'] = tmp_file
107
 
            arg = arg.format(**subst_names)
108
 
            subst_args.append(arg)
109
 
        return subst_args, tmp_file
110
 
 
111
 
 
112
35
known_merge_tools = {
113
36
    'bcompare': 'bcompare {this} {other} {base} {result}',
114
37
    'kdiff3': 'kdiff3 {base} {this} {other} -o {result}',
117
40
    'opendiff': 'opendiff {this} {other} -ancestor {base} -merge {result}',
118
41
    'winmergeu': 'winmergeu {result}',
119
42
}
 
43
 
 
44
 
 
45
def check_availability(command_line):
 
46
    cmd_list = cmdline.split(command_line)
 
47
    exe = cmd_list[0]
 
48
    if sys.platform == 'win32':
 
49
        if os.path.isabs(exe):
 
50
            base, ext = os.path.splitext(exe)
 
51
            path_ext = [unicode(s.lower())
 
52
                        for s in os.getenv('PATHEXT', '').split(os.pathsep)]
 
53
            return os.path.exists(exe) and ext in path_ext
 
54
        else:
 
55
            return osutils.find_executable_on_path(exe) is not None
 
56
    else:
 
57
        return (os.access(exe, os.X_OK)
 
58
                or osutils.find_executable_on_path(exe) is not None)
 
59
 
 
60
 
 
61
def invoke(command_line, filename, invoker=None):
 
62
    """Invokes the given merge tool command line, substituting the given
 
63
    filename according to the embedded substitution markers. Optionally, it
 
64
    will use the given invoker function instead of the default
 
65
    subprocess_invoker.
 
66
    """
 
67
    if invoker is None:
 
68
        invoker = subprocess_invoker
 
69
    cmd_list = cmdline.split(command_line)
 
70
    args, tmp_file = _subst_filename(cmd_list, filename)
 
71
    def cleanup(retcode):
 
72
        if tmp_file is not None:
 
73
            if retcode == 0: # on success, replace file with temp file
 
74
                shutil.move(tmp_file, filename)
 
75
            else: # otherwise, delete temp file
 
76
                os.remove(tmp_file)
 
77
    return invoker(args[0], args[1:], cleanup)
 
78
 
 
79
 
 
80
def _subst_filename(args, filename):
 
81
    subst_names = {
 
82
        u'base': filename + u'.BASE',
 
83
        u'this': filename + u'.THIS',
 
84
        u'other': filename + u'.OTHER',
 
85
        u'result': filename,
 
86
    }
 
87
    tmp_file = None
 
88
    subst_args = []
 
89
    for arg in args:
 
90
        if u'{this_temp}' in arg and not 'this_temp' in subst_names:
 
91
            fh, tmp_file = tempfile.mkstemp(u"_bzr_mergetools_%s.THIS" %
 
92
                                            os.path.basename(filename))
 
93
            trace.mutter('fh=%r, tmp_file=%r', fh, tmp_file)
 
94
            os.close(fh)
 
95
            shutil.copy(filename + u".THIS", tmp_file)
 
96
            subst_names['this_temp'] = tmp_file
 
97
        arg = arg.format(**subst_names)
 
98
        subst_args.append(arg)
 
99
    return subst_args, tmp_file
 
100
 
 
101
 
 
102
def subprocess_invoker(executable, args, cleanup):
 
103
    retcode = subprocess.call([executable] + args)
 
104
    cleanup(retcode)
 
105
    return retcode