Skip to content

Commit 4bfe5af

Browse files
feat: add exercise runner module
- add exercise compilation to seperate module - add compiler stategy pattern; use different cpp compilers
1 parent d648ad4 commit 4bfe5af

1 file changed

Lines changed: 170 additions & 0 deletions

File tree

src/exercise_runner.zig

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
const STD = @import("std");
2+
3+
const STYLES = @import("styles.zig");
4+
5+
const EXERCISE_OUT_PATH = "./build/cpplings_exercise";
6+
7+
pub const ExerciseRunner = struct {
8+
const Self = @This();
9+
10+
pub const CompilerStrategy = enum {
11+
zig,
12+
gpp,
13+
clang,
14+
};
15+
16+
current_strategy: CompilerStrategy = .zig,
17+
current_exercise_stdout: STD.ArrayList(u8) = .empty,
18+
current_exercise_stderr: STD.ArrayList(u8) = .empty,
19+
did_current_exercise_compile: bool = false,
20+
21+
pub fn init() Self {
22+
return Self{};
23+
}
24+
25+
pub fn deinit(self: *Self, allocator: STD.mem.Allocator) void {
26+
self.current_exercise_stdout.deinit(allocator);
27+
self.current_exercise_stderr.deinit(allocator);
28+
}
29+
30+
pub fn setStrategy(self: *Self, strategy: CompilerStrategy) void {
31+
self.current_strategy = strategy;
32+
}
33+
34+
pub fn compile(
35+
self: *Self,
36+
allocator: STD.mem.Allocator,
37+
exercise_path: []const u8,
38+
support_files: []const []const u8,
39+
) !void {
40+
self.current_exercise_stdout.clearAndFree(allocator);
41+
self.current_exercise_stderr.clearAndFree(allocator);
42+
43+
var process_args = try self.buildCompilerArgs(allocator, exercise_path, support_files);
44+
defer process_args.deinit(allocator);
45+
46+
var process = STD.process.Child.init(process_args.items, allocator);
47+
48+
process.stderr_behavior = .Pipe;
49+
process.stdout_behavior = .Pipe;
50+
51+
process.spawn() catch {
52+
STD.debug.print("\n{s}Error compiling exercise...{s}\n", .{
53+
STYLES.ASCII_STYLES.red,
54+
STYLES.ASCII_STYLES.clear_style,
55+
});
56+
return;
57+
};
58+
59+
try process.collectOutput(
60+
allocator,
61+
&self.current_exercise_stdout,
62+
&self.current_exercise_stderr,
63+
1 << 16,
64+
);
65+
66+
const PROCESS_STATUS = try process.wait();
67+
68+
if (PROCESS_STATUS.Exited == 0) {
69+
self.did_current_exercise_compile = true;
70+
return;
71+
}
72+
73+
self.did_current_exercise_compile = false;
74+
}
75+
76+
fn buildCompilerArgs(
77+
self: *Self,
78+
allocator: STD.mem.Allocator,
79+
exercise_path: []const u8,
80+
support_files: []const []const u8,
81+
) !STD.ArrayList([]const u8) {
82+
var process_args: STD.ArrayList([]const u8) = .empty;
83+
84+
switch (self.current_strategy) {
85+
.zig => {
86+
try process_args.appendSlice(allocator, &[_][]const u8{
87+
"zig",
88+
"build",
89+
"exercises",
90+
"--",
91+
exercise_path,
92+
});
93+
},
94+
.gpp => {
95+
try process_args.append(allocator, "g++");
96+
try process_args.append(allocator, "-o");
97+
try process_args.append(allocator, EXERCISE_OUT_PATH);
98+
try process_args.append(allocator, exercise_path);
99+
},
100+
.clang => {
101+
try process_args.append(allocator, "clang++");
102+
try process_args.append(allocator, "-o");
103+
try process_args.append(allocator, EXERCISE_OUT_PATH);
104+
try process_args.append(allocator, exercise_path);
105+
},
106+
}
107+
108+
if (support_files.len > 0) {
109+
try process_args.appendSlice(allocator, support_files);
110+
}
111+
112+
return process_args;
113+
}
114+
115+
pub fn getChapterSupportFiles(
116+
self: *Self,
117+
allocator: STD.mem.Allocator,
118+
exercise_path: []const u8,
119+
) !STD.ArrayList([]const u8) {
120+
_ = self;
121+
122+
var path_parts = STD.mem.splitAny(u8, exercise_path, "/");
123+
var path_parts_list: STD.ArrayList([]const u8) = .empty;
124+
defer path_parts_list.deinit(allocator);
125+
126+
while (path_parts.next()) |part| {
127+
if (STD.mem.containsAtLeast(u8, part, 1, ".cpp")) {
128+
break;
129+
}
130+
try path_parts_list.append(allocator, part);
131+
}
132+
133+
const chapter_path = try STD.fs.path.join(allocator, path_parts_list.items);
134+
defer allocator.free(chapter_path);
135+
136+
var chapter_dir = try STD.fs.cwd().openDir(chapter_path, .{ .iterate = true });
137+
defer chapter_dir.close();
138+
139+
var chapter_contents = chapter_dir.iterate();
140+
141+
var support_files: STD.ArrayList([]const u8) = .empty;
142+
143+
while (try chapter_contents.next()) |content| {
144+
if (STD.mem.containsAtLeast(u8, content.name, 1, "src")) {
145+
const support_dir_path = try STD.fs.path.join(allocator, &[_][]const u8{
146+
chapter_path,
147+
content.name,
148+
});
149+
defer allocator.free(support_dir_path);
150+
151+
var support_dir = try STD.fs.cwd().openDir(support_dir_path, .{ .iterate = true });
152+
defer support_dir.close();
153+
154+
var support_contents = support_dir.iterate();
155+
156+
while (try support_contents.next()) |support_file| {
157+
if (STD.mem.containsAtLeast(u8, support_file.name, 1, ".cpp")) {
158+
const full_path = try STD.fs.path.join(allocator, &[_][]const u8{
159+
support_dir_path,
160+
support_file.name,
161+
});
162+
try support_files.append(allocator, full_path);
163+
}
164+
}
165+
}
166+
}
167+
168+
return support_files;
169+
}
170+
};

0 commit comments

Comments
 (0)