1#!@PYTHON@
2#
3# This file is part of LilyPond, the GNU music typesetter.
4#
5# Copyright (C) 2008--2021 John Mandereau <john.mandereau@gmail.com>
6#
7# LilyPond is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# LilyPond is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19
20
21import subprocess
22import re
23import sys
24
25verbose = False
26
27
28def read_pipe(command):
29    child = subprocess.Popen(command,
30                             stdout=subprocess.PIPE,
31                             stderr=subprocess.PIPE,
32                             shell=True)
33    (output, error) = child.communicate()
34    code = str(child.wait())
35    if not child.stdout or child.stdout.close():
36        print("pipe failed: %(command)s" % locals())
37    (output, error) = (output.decode('utf-8'), error.decode('utf-8'))
38    if code != '0':
39        error = code + ' ' + error
40    return (output, error)
41
42
43# Renamed files map to ensure continuity of file history
44# Map of new_name: old_name
45# This is for handling 69f0ec47 ("Docs: reorganize documentation
46# directory structure"), which removed the user/ dir and the topdocs/NEWS.tely file
47renames_map = {
48    'usage.tely': 'user/lilypond-program.tely',
49    'notation.tely': 'user/lilypond.tely',
50    'learning.tely': 'user/lilypond-learning.tely',
51    'changes.tely': 'topdocs/NEWS.tely',
52}
53
54# FIXME: Hardcoded file names!?
55manuals_subdirectories_re = \
56    re.compile(
57        '(usage|automated-engraving|changes|essay|extending|web|learning|notation)/')
58
59
60def get_old_name(file_path):
61    for new_path in renames_map:
62        if file_path.endswith(new_path):
63            old_file_path = file_path.replace(new_path,
64                                              renames_map[new_path])
65            break
66    else:
67        if file_path.endswith('macros.itexi'):
68            old_file_path = file_path.replace('macros.itexi',
69                                              'user/macros.itexi')
70        elif file_path.endswith('.itely'):
71            old_file_path = manuals_subdirectories_re.sub('user/',
72                                                          file_path)
73        elif 'snippets/' in file_path:
74            old_file_path = file_path.replace('snippets/',
75                                              '../input/lsr/')
76        else:
77            return file_path
78    return old_file_path
79
80
81def file_exists_at(revision, name):
82    cmd = "git show %s:Documentation/%s" % (revision, name)
83    if verbose:
84        sys.stderr.write('running: %s\n' % cmd)
85
86    child = subprocess.run(cmd,
87                           stdout=subprocess.DEVNULL,
88                           stderr=subprocess.DEVNULL,
89                           shell=True)
90    return not child.returncode
91
92
93revision_re = re.compile(r'GIT [Cc]ommittish:\s+([a-f0-9]+)')
94
95no_committish_fatal_error = """error: %s: no 'GIT committish: <hash>' found.
96Please check the whole file against the original in English, then
97fill in HEAD committish in the header.
98"""
99
100def check_translated_doc(original, translated_file, translated_contents,
101                         color=False, upper_revision='HEAD'):
102    """Returns the diff of the original relative to the last translation"""
103    m = revision_re.search(translated_contents)
104    if not m:
105        sys.stderr.write(no_committish_fatal_error % translated_file)
106        sys.exit(1)
107    revision = m.group(1)
108    if revision == '0':
109        return '', 0
110
111    if color:
112        color_flag = '--color --color-words'
113    else:
114        color_flag = '--no-color'
115
116    current_name = original
117
118    if original.startswith("en/") and not file_exists_at(revision, original):
119        # this is to handle the rename in 82d72b7 ("Merge branch doc-build")
120        original = original[3:]
121    if not file_exists_at(revision, original):
122        original = get_old_name(original)
123
124    c = 'git diff -M %(color_flag)s %(revision)s:Documentation/%(original)s \
125%(upper_revision)s:Documentation/%(current_name)s' % vars()
126    if verbose:
127        sys.stderr.write('running: %s\n' % c)
128    return read_pipe(c)
129