~bzr-pqm/bzr/bzr.dev

5321.1.69 by Gordon Tyler
Fixed line-endings to be Unix.
1
# Copyright (C) 2010 Canonical Ltd.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
5321.1.104 by Gordon Tyler
Tweaked docstring for mergetools module.
17
"""Utility functions for managing external merge tools such as kdiff3."""
5321.1.69 by Gordon Tyler
Fixed line-endings to be Unix.
18
19
import os
20
import shutil
21
import subprocess
22
import sys
23
import tempfile
24
25
from bzrlib.lazy_import import lazy_import
26
lazy_import(globals(), """
27
from bzrlib import (
28
    cmdline,
5321.1.83 by Gordon Tyler
Use osutils.find_executable_on_path in is_available instead.
29
    osutils,
5321.1.69 by Gordon Tyler
Fixed line-endings to be Unix.
30
    trace,
31
)
32
""")
33
34
5321.1.108 by Gordon Tyler
Changed known merge tools into a default set of merge tools that are always defined but can be overridden by user-defined merge tools.
35
known_merge_tools = {
5321.2.8 by Vincent Ladeuil
_KNOWN_MERGE_TOOLS should be a dict (there is an hidden assumption that the merg tool is unique anyway).
36
    'bcompare': 'bcompare {this} {other} {base} {result}',
37
    'kdiff3': 'kdiff3 {base} {this} {other} -o {result}',
38
    'xdiff': 'xxdiff -m -O -M {result} {this} {base} {other}',
39
    'meld': 'meld {base} {this_temp} {other}',
40
    'opendiff': 'opendiff {this} {other} -ancestor {base} -merge {result}',
41
    'winmergeu': 'winmergeu {result}',
42
}
5321.1.116 by Gordon Tyler
Simplified mergetools module down to functions which deal with command lines -- no MergeTool class.
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 = {
5321.1.119 by Gordon Tyler
Replace usage of format function from python 2.6 with our own very simple formatting function.
82
        'base': filename + u'.BASE',
83
        'this': filename + u'.THIS',
84
        'other': filename + u'.OTHER',
85
        'result': filename,
5321.1.116 by Gordon Tyler
Simplified mergetools module down to functions which deal with command lines -- no MergeTool class.
86
    }
87
    tmp_file = None
88
    subst_args = []
89
    for arg in args:
5321.1.119 by Gordon Tyler
Replace usage of format function from python 2.6 with our own very simple formatting function.
90
        if '{this_temp}' in arg and not 'this_temp' in subst_names:
5321.1.116 by Gordon Tyler
Simplified mergetools module down to functions which deal with command lines -- no MergeTool class.
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
5321.1.119 by Gordon Tyler
Replace usage of format function from python 2.6 with our own very simple formatting function.
97
        arg = _format_arg(arg, subst_names)
5321.1.116 by Gordon Tyler
Simplified mergetools module down to functions which deal with command lines -- no MergeTool class.
98
        subst_args.append(arg)
99
    return subst_args, tmp_file
100
101
5321.1.119 by Gordon Tyler
Replace usage of format function from python 2.6 with our own very simple formatting function.
102
# This would be better implemented using format() from python 2.6
103
def _format_arg(arg, subst_names):
104
    arg = arg.replace('{base}', subst_names['base'])
105
    arg = arg.replace('{this}', subst_names['this'])
106
    arg = arg.replace('{other}', subst_names['other'])
107
    arg = arg.replace('{result}', subst_names['result'])
108
    if subst_names.has_key('this_temp'):
109
        arg = arg.replace('{this_temp}', subst_names['this_temp'])
110
    return arg
111
112
5321.1.116 by Gordon Tyler
Simplified mergetools module down to functions which deal with command lines -- no MergeTool class.
113
def subprocess_invoker(executable, args, cleanup):
114
    retcode = subprocess.call([executable] + args)
115
    cleanup(retcode)
116
    return retcode