Skip to content

Commit 2753c0d

Browse files
committed
buildtool: functionalize, use incremental build
1 parent 39a77e8 commit 2753c0d

File tree

2 files changed

+73
-87
lines changed

2 files changed

+73
-87
lines changed

.github/workflows/ci-nojvm.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,8 @@ jobs:
3434

3535
- uses: ./.github/workflows/composite-install-matlab
3636

37+
- name: Fortran FC
38+
if: runner.os == 'macOS'
39+
run: echo "FC=gfortran-14" >> $GITHUB_ENV
40+
3741
- uses: ./.github/workflows/composite-buildtool

buildfile.m

Lines changed: 69 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@
2323
plan("clean") = matlab.buildtool.tasks.CleanTask;
2424
end
2525

26-
plan("build_c") = matlab.buildtool.Task(Actions=@subprocess_build_c);
27-
plan("build_cpp") = matlab.buildtool.Task(Actions=@subprocess_build_cpp);
28-
plan("build_fortran") = matlab.buildtool.Task(Actions=@subprocess_build_fortran);
29-
plan("test").Dependencies = ["build_c", "build_cpp", "build_fortran"];
30-
3126
if ~isMATLABReleaseOlderThan("R2024a")
3227
plan("check") = matlab.buildtool.tasks.CodeIssuesTask(pkg_name, IncludeSubfolders=true, ...
3328
WarningThreshold=0, Results="CodeIssues.sarif");
@@ -42,6 +37,19 @@
4237
% dummy task to allow "buildtool mex" to build all MEX targets
4338
plan("mex") = matlab.buildtool.Task();
4439
mex_deps = string.empty;
40+
else
41+
td = plan.RootFolder + "/test";
42+
43+
plan("exe:c") = matlab.buildtool.Task(Inputs=td+"/stdout_stderr_c.c", ...
44+
Outputs=td+"/stdout_stderr_c.exe", Actions=@build_exe);
45+
46+
plan("exe:cpp") = matlab.buildtool.Task(Inputs=td+"/stdin_cpp.cpp",...
47+
Outputs=td+"/stdin_cpp.exe", Actions=@build_exe);
48+
49+
plan("exe:fortran") = matlab.buildtool.Task(Inputs=[td + "/stdout_stderr_fortran.f90", td + "/stdin_fortran.f90"],...
50+
Outputs=[td+"/stdout_stderr_fortran.exe", td+"/stdin_fortran.exe"], Actions=@build_exe);
51+
52+
plan("test").Dependencies = "exe";
4553
end
4654

4755
for s = get_mex_sources()
@@ -51,11 +59,8 @@
5159
% name of MEX target function is name of first source file
5260
if isMATLABReleaseOlderThan("R2024b")
5361
mex_name = "mex_" + name;
54-
% specifying .Inputs and .Outputs enables incremental builds
55-
% https://www.mathworks.com/help/matlab/matlab_prog/improve-performance-with-incremental-builds.html
56-
plan(mex_name) = matlab.buildtool.Task(Actions=@(context) legacy_mex(context, compiler_opt, linker_opt));
57-
plan(mex_name).Inputs = src;
58-
plan(mex_name).Outputs = fullfile(bindir, name + "." + mexext());
62+
plan(mex_name) = matlab.buildtool.Task(Inputs=src, Outputs=fullfile(bindir, name + "." + mexext()), ...
63+
Actions=@(context) legacy_mex(context, compiler_opt, linker_opt));
5964
mex_deps(end+1) = mex_name; %#ok<AGROW>
6065
else
6166
plan("mex:" + name) = matlab.buildtool.tasks.MexTask(src, bindir, ...
@@ -96,112 +101,89 @@ function publishTask(context)
96101
end
97102

98103

99-
function subprocess_build_c(context)
104+
function build_exe(context)
100105

101-
td = context.Plan.RootFolder + "/test";
102-
src = td + "/stdout_stderr_c.c";
106+
for i = 1:length(context.Task.Inputs)
107+
src = context.Task.Inputs(i);
108+
exe = context.Task.Outputs(i).paths;
109+
exe = exe(1);
103110

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
111+
[~,~,ext] = fileparts(src.paths);
112+
switch ext
113+
case ".c", lang = "c";
114+
case ".cpp", lang = "c++";
115+
case ".f90", lang = "fortran";
116+
otherwise, error("unknown code suffix " + ext)
109117
end
110118

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
119-
end
119+
[comp, shell, outFlag] = get_build_cmd(lang);
120+
if isempty(comp), return, end
120121

121-
end
122-
123-
124-
function subprocess_build_cpp(context)
125-
126-
td = context.Plan.RootFolder + "/test";
127-
src = [td + "/sleep.cpp", td + "/stdin_cpp.cpp"];
128-
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
122+
cmd = join([comp, src.paths, outFlag + exe]);
123+
if ~isempty(shell)
124+
cmd = join([shell, "&&", cmd]);
134125
end
135126

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

146131
end
147132

148133

149-
function subprocess_build_fortran(context)
134+
function [comp, shell] = get_compiler(lang)
150135

151-
td = context.Plan.RootFolder + "/test";
152-
src = [td + "/stdout_stderr_fortran.f90", td + "/stdin_fortran.f90"];
136+
lang = lower(lang);
153137

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
138+
co = mex.getCompilerConfigurations(lang);
139+
140+
if isempty(co)
141+
switch lang
142+
case "fortran"
143+
comp = getenv("FC");
144+
if isempty(comp)
145+
error("set FC environment variable to the Fortran compiler path, or do 'mex -setup fortran'")
146+
end
147+
case "c++"
148+
comp = getenv("CXX");
149+
if isempty(comp)
150+
error("set CXX environment variable to the C++ compiler path, or do 'mex -setup c++")
151+
end
152+
case "c"
153+
comp = getenv("CC");
154+
if isempty(comp)
155+
error("set CC environment variable to the C compiler path, or do 'mex -setup c'")
156+
end
157+
otherwise, error("language not known " + lang)
159158
end
159+
else
160+
comp = co.Details.CompilerExecutable;
161+
disp(lang + " compiler: " + co.Name + " " + co.Version + " " + comp)
162+
end
160163

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)
164+
165+
shell = string.empty;
166+
if ispc
167+
disp("Shell: " + co.Details.CommandLineShell)
168+
if contains(co.Name, "Visual Studio")
169+
shell = join([strcat('"',string(co.Details.CommandLineShell),'"'), ...
170+
co.Details.CommandLineShellArg], " ");
168171
end
169172
end
170173

171174
end
172175

173176

174-
function cmd = get_build_cmd(lang, src)
177+
function [comp, shell, outFlag] = get_build_cmd(lang)
175178

176-
cmd = string.empty;
179+
outFlag = "-o";
177180

178-
co = mex.getCompilerConfigurations(lang);
179-
if isempty(co)
180-
if lang == "Fortran"
181-
fc = getenv("FC");
182-
if isempty(fc)
183-
disp("set FC environment variable to the Fortran compiler executable, or do 'mex -setup fortran' to configure the Fortran compiler")
184-
end
185-
end
186-
disp(lang + " compiler not found")
187-
return
188-
else
189-
comp = co.Details.CompilerExecutable;
190-
end
181+
[comp, shell] = get_compiler(lang);
191182

192-
outFlag = "-o";
193-
shell = string.empty;
194-
msvcLike = ispc && (contains(co.Name, "Visual Studio"));
195-
if msvcLike
196-
shell = join([strcat('"',string(co.Details.CommandLineShell),'"'), ...
197-
co.Details.CommandLineShellArg], " ");
183+
if ~isempty(shell)
198184
outFlag = "/Fo" + tempdir + " /link /out:";
199185
end
200186

201-
cmd = join([comp, src, outFlag]);
202-
if ~isempty(shell)
203-
cmd = join([shell, "&&", cmd]);
204-
end
205187

206188
end
207189

0 commit comments

Comments
 (0)