1
|
|
|
"""Utilities to call shell programs.""" |
2
|
|
|
|
3
|
1 |
|
import os |
4
|
1 |
|
import subprocess |
5
|
1 |
|
|
6
|
|
|
import log |
7
|
1 |
|
|
8
|
1 |
|
from . import common |
9
|
|
|
from .exceptions import ShellError |
10
|
1 |
|
|
11
|
1 |
|
|
12
|
|
|
CMD_PREFIX = "$ " |
13
|
1 |
|
OUT_PREFIX = "> " |
14
|
|
|
|
15
|
|
|
|
16
|
1 |
|
def call(name, *args, _show=True, _show_stdout=True, _shell=False, _ignore=False): |
17
|
|
|
"""Call a program with arguments. |
18
|
|
|
|
19
|
|
|
:param name: name of program to call |
20
|
|
|
:param args: list of command-line arguments |
21
|
|
|
:param _show: display the call on stdout |
22
|
|
|
:param _show_stdout: display stdout of the call on stdout |
23
|
|
|
:param _shell: force executing the program into a real shell |
24
|
|
|
a Windows shell command (i.e: dir, echo) needs a real shell |
25
|
|
|
but not a regular program (i.e: calc, git) |
26
|
|
|
:param _ignore: ignore non-zero return codes |
27
|
1 |
|
""" |
28
|
|
|
program = show(name, *args, stdout=_show) |
29
|
1 |
|
|
30
|
|
|
command = subprocess.Popen( # pylint: disable=subprocess-run-check |
31
|
|
|
name if _shell else [name, *args], |
32
|
|
|
universal_newlines=True, |
33
|
|
|
stdout=subprocess.PIPE, |
34
|
|
|
stderr=subprocess.STDOUT, |
35
|
1 |
|
shell=_shell, |
36
|
1 |
|
) |
37
|
1 |
|
|
38
|
|
|
# Poll process.stdout to show stdout live |
39
|
1 |
|
complete_output = [] |
40
|
1 |
|
while True: |
41
|
|
|
output = command.stdout.readline() |
42
|
1 |
|
if output == '' and command.poll() is not None: |
43
|
1 |
|
break |
44
|
1 |
|
|
45
|
|
|
if output != '': |
46
|
|
|
output = output.strip() |
47
|
1 |
|
else: |
48
|
|
|
continue |
49
|
|
|
|
50
|
|
|
complete_output.append(output) |
51
|
|
|
if _show_stdout: |
52
|
|
|
common.show(output, color='shell_output') |
53
|
|
|
else: |
54
|
1 |
|
log.debug(OUT_PREFIX + output) |
55
|
|
|
|
56
|
|
|
if command.returncode == 0: |
57
|
1 |
|
return complete_output |
58
|
1 |
|
|
59
|
1 |
|
if _ignore: |
60
|
|
|
log.debug("Ignored error from call to '%s'", name) |
61
|
|
|
return complete_output |
62
|
1 |
|
|
63
|
|
|
message = ( |
64
|
|
|
"An external program call failed." + "\n\n" |
65
|
1 |
|
"In working directory: " + os.getcwd() + "\n\n" |
66
|
1 |
|
"The following command produced a non-zero return code:" |
67
|
|
|
+ "\n\n" |
68
|
|
|
+ CMD_PREFIX |
69
|
1 |
|
+ program |
70
|
1 |
|
+ "\n".join(complete_output) |
71
|
|
|
) |
72
|
|
|
raise ShellError(message, program=program, output=complete_output) |
73
|
1 |
|
|
74
|
1 |
|
|
75
|
|
|
def mkdir(path): |
76
|
|
|
if not os.path.exists(path): |
77
|
1 |
|
if os.name == 'nt': |
78
|
1 |
|
call("mkdir " + path, _shell=True) |
79
|
1 |
|
else: |
80
|
1 |
|
call('mkdir', '-p', path) |
81
|
|
|
|
82
|
|
|
|
83
|
1 |
|
def cd(path, _show=True): |
84
|
1 |
|
if os.name == 'nt': |
85
|
|
|
show('cd', '/D', path, stdout=_show) |
86
|
|
|
else: |
87
|
|
|
show('cd', path, stdout=_show) |
88
|
|
|
os.chdir(path) |
89
|
|
|
|
90
|
1 |
|
|
91
|
|
|
def pwd(_show=True): |
92
|
|
|
cwd = os.getcwd() |
93
|
1 |
|
if os.name == 'nt': |
94
|
1 |
|
cwd = cwd.replace(os.sep, '/') |
95
|
1 |
|
show('cwd', cwd, stdout=_show) |
96
|
1 |
|
return cwd |
97
|
|
|
|
98
|
1 |
|
|
99
|
1 |
|
def ln(source, target): |
100
|
|
|
dirpath = os.path.dirname(target) |
101
|
|
|
if not os.path.isdir(dirpath): |
102
|
|
|
mkdir(dirpath) |
103
|
|
|
os.symlink(source, target) |
104
|
|
|
|
105
|
|
|
|
106
|
|
|
def rm(path): |
107
|
|
|
if os.name == 'nt': |
108
|
|
|
if os.path.isfile(path): |
109
|
|
|
call("del /Q /F " + path, _shell=True) |
110
|
|
|
elif os.path.isdir(path): |
111
|
|
|
call("rmdir /Q /S " + path, _shell=True) |
112
|
|
|
else: |
113
|
|
|
call('rm', '-rf', path) |
114
|
|
|
|
115
|
|
|
|
116
|
|
|
def show(name, *args, stdout=True): |
117
|
|
|
program = ' '.join([name, *args]) |
118
|
|
|
if stdout: |
119
|
|
|
common.show(CMD_PREFIX + program, color='shell') |
120
|
|
|
else: |
121
|
|
|
log.debug(CMD_PREFIX + program) |
122
|
|
|
return program |
123
|
|
|
|