]> git.lyx.org Git - lyx.git/blob - lib/scripts/docbook_copy.py
DocBook: redirect LilyPond output to main LyX output to ease debugging.
[lyx.git] / lib / scripts / docbook_copy.py
1 # -*- coding: utf-8 -*-
2
3 # file docbook_copy.py
4 # This file is part of LyX, the document processor.
5 # Licence details can be found in the file COPYING.
6 #
7 # \author Thibaut Cuvelier
8 #
9 # Full author contact details are available in file CREDITS
10
11 # Usage:
12 #   python docbook_copy.py lilypond_book_command in.docbook out.docbook
13 # This script copies the original DocBook file (directly produced by LyX) to the output DocBook file,
14 # potentially applying a post-processing step. For now, the only implemented post-processing step is
15 # LilyPond.
16 # lilypond_book_command is either directly the binary to call OR the equivalent Python script that is
17 # not directly executable.
18 # /!\ The original file may be modified by this script!
19
20
21 import subprocess
22 import os
23 import os.path
24 import re
25 import shutil
26 import sys
27
28
29 def need_lilypond(file):
30     # Really tailored to the kind of output lilypond.module makes (in lib/layouts).
31     with open(file, 'r') as f:
32         return "language='lilypond'" in f.read()
33
34
35 def copy_docbook(args):
36     print(args)
37     if len(args) != 4:
38         print('Exactly four arguments are expected, only %s found: %s.' % (len(args), args))
39         sys.exit(1)
40
41     # Parse the command line.
42     lilypond_command = args[1]
43     in_file = args[2]
44     out_file = args[3]
45
46     has_lilypond = lilypond_command != "" and lilypond_command != "none"
47
48     # Guess the path for LilyPond.
49     lilypond_folder = os.path.split(lilypond_command)[0] if has_lilypond else ''
50
51     # Help debugging.
52     print(">> Given arguments:")
53     print(">> LilyPond: " + ("present" if has_lilypond else "not found") + " " + lilypond_command)
54     print(">> LilyPond path: " + lilypond_folder)
55     print(">> Input file: " + in_file)
56     print(">> Output file: " + out_file)
57
58     # Apply LilyPond to the original file if available and needed.
59     if has_lilypond and need_lilypond(in_file):
60         in_lily_file = in_file.replace(".xml", ".lyxml")
61         print(">> The input file needs a LilyPond pass and LilyPond is available.")
62         print(">> Rewriting " + in_file + " as " + in_lily_file)
63
64         # LilyPond requires that its input file has the .lyxml extension. Due to a bug in LilyPond,
65         # use " instead of ' to encode XML attributes.
66         # https://lists.gnu.org/archive/html/bug-lilypond/2021-09/msg00039.html
67         # Typical transformation:
68         #     FROM:  language='lilypond' role='fragment verbatim staffsize=16 ragged-right relative=2'
69         #     TO:    language="lilypond" role="fragment verbatim staffsize=16 ragged-right relative=2"
70         with open(in_file, 'r', encoding='utf-8') as f, open(in_lily_file, 'w', encoding='utf-8') as f_lily:
71             for line in f:
72                 if "language='lilypond'" in line:
73                     line = re.sub(
74                         '<programlisting\\s+language=\'lilypond\'.*?(role=\'(?P<options>.*?)\')?>',
75                         '<programlisting language="lilypond" role="\\g<options>">',
76                         line
77                     )
78                 f_lily.write(line)
79         os.unlink(in_file)
80
81         # Add LilyPond to the PATH.
82         if os.path.isdir(lilypond_folder):
83             os.environ['PATH'] += os.pathsep + lilypond_folder
84
85         # Start LilyPond on the copied file. First test the binary, then check if adding Python helps.
86         command_raw = [lilypond_command, '--format=docbook', in_lily_file]
87         command_python = ['python', lilypond_command, '--format=docbook', in_lily_file]
88
89         failed = False
90         try:
91             subprocess.check_call(command_raw, stdout=sys.stdout.fileno(), stderr=sys.stdout.fileno())
92             print(">> Success running LilyPond with " + str(command_raw))
93         except (subprocess.CalledProcessError, OSError) as e1:
94             try:
95                 subprocess.check_call(command_python, stdout=sys.stdout.fileno(), stderr=sys.stdout.fileno())
96                 print(">> Success running LilyPond with " + str(command_python))
97             except (subprocess.CalledProcessError, OSError) as e2:
98                 print('>> Error from LilyPond')
99                 print('>> Error from trying ' + str(command_raw) + ':')
100                 print(e1)
101                 print('>> Error from trying ' + str(command_python) + ':')
102                 print(e2)
103                 failed = True
104
105         if failed:
106             sys.exit(1)
107
108         # Now, in_file should have the LilyPond-processed contents.
109
110     # Perform the final copy.
111     shutil.copyfile(in_file, out_file, follow_symlinks=False)
112
113
114 if __name__ == '__main__':
115     copy_docbook(sys.argv)