Skip to content

Commit dec5946

Browse files
committed
subprocess_run: correct stdin for Fortran
1 parent 6c830ad commit dec5946

File tree

8 files changed

+112
-46
lines changed

8 files changed

+112
-46
lines changed

+stdlib/subprocess_run.m

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@
8181
%% stdin pipe
8282
if strlength(opt.stdin) > 0
8383
writer = java.io.BufferedWriter(java.io.OutputStreamWriter(h.getOutputStream()));
84-
writer.write(opt.stdin);
84+
stdin_text = opt.stdin;
85+
if ~endsWith(stdin_text, newline)
86+
% Fortran (across compilers) needs a \n at the end of stdin.
87+
stdin_text = stdin_text + newline;
88+
end
89+
writer.write(stdin_text);
8590
writer.flush()
8691
writer.close()
8792
end

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ docs/
1010
*.mex*
1111
*.oct
1212

13-
test/printer_c.exe
14-
test/printer_fortran.exe
13+
test/stdout_stderr_c.exe
14+
test/stdout_stderr_fortran.exe
15+
test/stdin_cpp.exe
16+
test/stdin_fortran.exe
1517
test/sleep.exe

buildfile.m

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,23 @@ function publishTask(context)
9999
function subprocess_build_c(context)
100100

101101
td = context.Plan.RootFolder + "/test";
102-
src = td + "/main.c";
103-
exe = td + "/printer_c.exe";
102+
src = td + "/stdout_stderr_c.c";
104103

105-
cmd = get_build_cmd("c", src);
106-
if isempty(cmd), return, end
107-
cmd = join([cmd, exe]);
104+
for s = src
105+
[~, n] = fileparts(s);
106+
exe = fullfile(td, n + ".exe");
107+
if stdlib.get_modtime(s) < stdlib.get_modtime(exe)
108+
continue
109+
end
108110

109-
[r, m] = system(cmd);
110-
if r ~= 0
111-
warning("failed to build TestSubprocess printer_c.exe " + m)
111+
cmd = get_build_cmd("c++", s);
112+
if isempty(cmd), return, end
113+
cmd = cmd + exe;
114+
disp(cmd)
115+
[r, m] = system(cmd);
116+
if r ~= 0
117+
disp("failed to build TestSubprocess " + exe + " " + m)
118+
end
112119
end
113120

114121
end
@@ -117,16 +124,23 @@ function subprocess_build_c(context)
117124
function subprocess_build_cpp(context)
118125

119126
td = context.Plan.RootFolder + "/test";
120-
src = td + "/sleep.cpp";
121-
exe = td + "/sleep.exe";
127+
src = [td + "/sleep.cpp", td + "/stdin_cpp.cpp"];
122128

123-
cmd = get_build_cmd("c++", src);
124-
if isempty(cmd), return, end
125-
cmd = join([cmd, exe]);
129+
for s = src
130+
[~, n] = fileparts(s);
131+
exe = fullfile(td, n + ".exe");
132+
if stdlib.get_modtime(s) < stdlib.get_modtime(exe)
133+
continue
134+
end
126135

127-
[r, m] = system(cmd);
128-
if r ~= 0
129-
warning("failed to build TestSubprocess " + exe + " " + m)
136+
cmd = get_build_cmd("c++", s);
137+
if isempty(cmd), return, end
138+
cmd = cmd + exe;
139+
disp(cmd)
140+
[r, m] = system(cmd);
141+
if r ~= 0
142+
disp("failed to build TestSubprocess " + exe + " " + m)
143+
end
130144
end
131145

132146
end
@@ -135,16 +149,23 @@ function subprocess_build_cpp(context)
135149
function subprocess_build_fortran(context)
136150

137151
td = context.Plan.RootFolder + "/test";
138-
src = td + "/main.f90";
139-
exe = td + "/printer_fortran.exe";
152+
src = [td + "/stdout_stderr_fortran.f90", td + "/stdin_fortran.f90"];
140153

141-
cmd = get_build_cmd("Fortran", src);
142-
if isempty(cmd), return, end
143-
cmd = join([cmd, exe]);
154+
for s = src
155+
[~, n] = fileparts(s);
156+
exe = fullfile(td, n + ".exe");
157+
if stdlib.get_modtime(s) < stdlib.get_modtime(exe)
158+
continue
159+
end
144160

145-
[r, m] = system(cmd);
146-
if r ~= 0
147-
warning("failed to build TestSubprocess " + exe + " " + m)
161+
cmd = get_build_cmd("Fortran", s);
162+
if isempty(cmd), return, end
163+
cmd = cmd + exe;
164+
disp(cmd)
165+
[r, m] = system(cmd);
166+
if r ~= 0
167+
disp("failed to build TestSubprocess " + exe + " " + m)
168+
end
148169
end
149170

150171
end
@@ -159,28 +180,27 @@ function subprocess_build_fortran(context)
159180
if lang == "Fortran"
160181
fc = getenv("FC");
161182
if isempty(fc)
162-
warning("set FC environment variable to the Fortran compiler executable, or do 'mex -setup fortran' to configure the Fortran compiler")
183+
disp("set FC environment variable to the Fortran compiler executable, or do 'mex -setup fortran' to configure the Fortran compiler")
163184
end
164185
end
165-
warning(lang + " compiler not found")
186+
disp(lang + " compiler not found")
166187
return
167188
else
168189
comp = co.Details.CompilerExecutable;
169190
end
170191

171192
outFlag = "-o";
172-
shell = "";
173-
shell_arg = "";
174-
msvcLike = ispc && endsWith(comp, "cl");
193+
shell = string.empty;
194+
msvcLike = ispc && (contains(co.Name, "Visual Studio"));
175195
if msvcLike
176-
shell = strtrim(co.Details.CommandLineShell);
177-
shell_arg = co.Details.CommandLineShellArg;
178-
outFlag = "/link /out:";
196+
shell = join([strcat('"',string(co.Details.CommandLineShell),'"'), ...
197+
co.Details.CommandLineShellArg], " ");
198+
outFlag = "/Fo" + tempdir + " /link /out:";
179199
end
180200

181201
cmd = join([comp, src, outFlag]);
182-
if shell ~= ""
183-
cmd = join([shell, shell_arg, cmd]);
202+
if ~isempty(shell)
203+
cmd = join([shell, "&&", cmd]);
184204
end
185205

186206
end

test/TestSubprocess.m

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
classdef TestSubprocess < matlab.unittest.TestCase
22

3+
properties (TestParameter)
4+
lang_out = {"c", "fortran"}
5+
lang_in = {"cpp", "fortran"}
6+
end
7+
38
methods(TestClassSetup)
49
function java_required(tc)
510
tc.assumeTrue(stdlib.has_java())
@@ -9,33 +14,32 @@ function java_required(tc)
914

1015
methods (Test)
1116

12-
function test_exe_c(tc)
17+
function test_stdout_stderr(tc, lang_out)
1318
import matlab.unittest.constraints.IsFile
1419

1520
cwd = fileparts(mfilename('fullpath'));
16-
exe = cwd + "/printer_c.exe";
21+
exe = cwd + "/stdout_stderr_" + lang_out + ".exe";
1722
tc.assumeThat(exe, IsFile, exe + " not found")
1823

1924
[status, msg, err] = stdlib.subprocess_run(exe);
2025
tc.assertEqual(status, 0, err)
2126
tc.verifyEqual(msg, "stdout")
2227
tc.verifyEqual(err, "stderr")
23-
2428
end
2529

2630

27-
function test_exe_fortran(tc)
31+
function test_stdin(tc, lang_in)
2832
import matlab.unittest.constraints.IsFile
2933

3034
cwd = fileparts(mfilename('fullpath'));
31-
exe = cwd + "/printer_fortran.exe";
35+
exe = cwd + "/stdin_" + lang_in + ".exe";
3236
tc.assumeThat(exe, IsFile, exe + " not found")
3337

34-
[status, msg, err] = stdlib.subprocess_run(exe);
35-
tc.assertEqual(status, 0, err)
36-
tc.verifyEqual(msg, "stdout")
37-
tc.verifyEqual(err, "stderr")
38+
[status, msg, err] = stdlib.subprocess_run(exe, stdin="1 2");
3839

40+
tc.assertEqual(status, 0, err)
41+
tc.verifyEqual(msg, "3")
42+
tc.verifyEqual(err, "")
3943
end
4044

4145

test/stdin_cpp.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <iostream>
2+
#include <cstdlib>
3+
4+
int main() {
5+
6+
int a, b;
7+
8+
std::cin >> a >> b;
9+
10+
std::cout << a + b << "\n";
11+
12+
return EXIT_SUCCESS;
13+
}

test/stdin_fortran.f90

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
program stdin_pipe
2+
3+
use, intrinsic :: iso_fortran_env
4+
5+
implicit none
6+
7+
integer :: a, b, i
8+
9+
! Python and Matlab have the same issue with stdin truncation in Fortran across compilers.
10+
! >>> subprocess.run("stdin_fortran.exe", input="1 2", capture_output=True, text=True)
11+
! CompletedProcess(args='stdin_fortran.exe', returncode=0, stdout=' 0.000\n', stderr='')
12+
! >>> subprocess.run("stdin_fortran.exe", input="1 2\n", capture_output=True, text=True)
13+
! CompletedProcess(args='stdin_fortran.exe', returncode=0, stdout=' 3.000\n', stderr='')
14+
15+
read(input_unit, *, iostat=i) a, b
16+
if (i == iostat_end) error stop "stdin was truncated -- add a newline at the end of the stdin input"
17+
if (i /= 0) error stop "stdin read error"
18+
19+
20+
write(output_unit, '(i0)') a + b
21+
22+
end program
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)