Skip to content

Commit 637f032

Browse files
committed
Added cache logic
1 parent 12a635d commit 637f032

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed

src/cache.zig

+196
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
// Spec here https://github.com/roc-lang/roc/issues/7517
2+
const builtin = @import("builtin");
13
const std = @import("std");
4+
const testing = std.testing;
5+
const Allocator = std.mem.Allocator;
26
const base = @import("base.zig");
37
const canonicalize = @import("check/canonicalize.zig");
48

@@ -18,3 +22,195 @@ pub fn getPackageRootAbsDir(url_data: Package.Url, gpa: std.mem.Allocator) []con
1822

1923
@panic("not implemented");
2024
}
25+
26+
pub const CacheFile = union(enum) {
27+
compiler: Compiler,
28+
build,
29+
packages_src,
30+
packages_build,
31+
32+
pub const Compiler = struct {
33+
version_name: []const u8,
34+
binary: []const u8,
35+
};
36+
};
37+
38+
const target_os = builtin.target.os.tag;
39+
pub fn setEnvVar(name: []const u8, value: []const u8) !void {
40+
// SKILL_ISSUE: it's required to use -lc to run the tests because of the
41+
// c library calls. Is there a better way to run a single test file?
42+
// zig test ./src/cache.zig -lc
43+
return {
44+
switch (target_os) {
45+
.macos, .linux => {
46+
const c = @cImport({
47+
@cInclude("stdlib.h");
48+
});
49+
if (c.setenv(name.ptr, value.ptr, 1) != 0) {
50+
return error.SetEnvFailed;
51+
}
52+
},
53+
.windows => {
54+
// SKILL_ISSUE: my zig language server is complaining about
55+
// windows.h not existing. Is there a better way to conditionally
56+
// import c libraries that will make my language server happy?
57+
const c = @cImport({
58+
@cInclude("windows.h");
59+
});
60+
if (c.SetEnvironmentVariableA(name.ptr, value.ptr) == 0) {
61+
return error.SetEnvFailed;
62+
}
63+
},
64+
else => {
65+
@compileError("\"" ++ @tagName(target_os) ++ "\" is not supported in cache.zig");
66+
},
67+
}
68+
};
69+
}
70+
71+
fn getDefaultCacheFolder(allocator: Allocator) ![]u8 {
72+
return {
73+
switch (target_os) {
74+
.macos, .linux => {
75+
const home_dir = try std.process.getEnvVarOwned(std.testing.allocator, "HOME");
76+
defer allocator.free(home_dir);
77+
const default_cache_folder_parts = [_][]const u8{
78+
home_dir,
79+
".cache",
80+
};
81+
const default_cache_folder = try std.fs.path.join(allocator, &default_cache_folder_parts);
82+
return default_cache_folder;
83+
},
84+
.windows => {
85+
const default_cache_folder_parts = [_][]const u8{"%APPDATA%"};
86+
const default_cache_folder = try std.fs.path.join(allocator, &default_cache_folder_parts);
87+
return default_cache_folder;
88+
},
89+
else => {
90+
@compileError("\"" ++ @tagName(target_os) ++ "\" is not supported in cache.zig");
91+
},
92+
}
93+
};
94+
}
95+
96+
const xdg_cache_home_var_name = "XDG_CACHE_HOME";
97+
fn getBaseRocCacheFolder(allocator: Allocator, sub_path: []const u8) ![]u8 {
98+
const xdg_cache_home = std.process.getEnvVarOwned(allocator, xdg_cache_home_var_name) catch null;
99+
defer {
100+
if (xdg_cache_home) |x| {
101+
allocator.free(x);
102+
}
103+
}
104+
const base_dir = if (xdg_cache_home) |x| try allocator.dupe(u8, x) else try getDefaultCacheFolder(allocator);
105+
defer allocator.free(base_dir);
106+
const roc_cache_parts = [_][]const u8{
107+
base_dir,
108+
"roc",
109+
sub_path,
110+
};
111+
return try std.fs.path.join(allocator, &roc_cache_parts);
112+
}
113+
114+
fn getCompilerPath(allocator: Allocator, version_name: []const u8) ![]u8 {
115+
const sub_path_parts = [_][]const u8{ "compiler", version_name };
116+
const sub_path = try std.fs.path.join(allocator, &sub_path_parts);
117+
defer allocator.free(sub_path);
118+
return try getBaseRocCacheFolder(allocator, sub_path);
119+
}
120+
121+
const SaveCacheFileError = error{
122+
FailedToMakeCacheFolder,
123+
FailedToMakeCacheFile,
124+
FailedToWriteToCacheFile,
125+
};
126+
127+
fn saveCacheFile(file_cache_path: []const u8, file_binary: []const u8) SaveCacheFileError!void {
128+
const cwd = std.fs.cwd();
129+
const roc_cache_folder = std.fs.path.dirname(file_cache_path);
130+
if (roc_cache_folder) |x| {
131+
cwd.makePath(x) catch {
132+
return error.FailedToMakeCacheFolder;
133+
};
134+
}
135+
const file = cwd.createFile(file_cache_path, .{}) catch {
136+
return SaveCacheFileError.FailedToMakeCacheFile;
137+
};
138+
file.writeAll(file_binary) catch {
139+
return SaveCacheFileError.FailedToWriteToCacheFile;
140+
};
141+
}
142+
143+
fn saveToCacheInternal(
144+
// SKILL_ISSUE: force my formatter to put the arguments in a vertical line
145+
allocator: Allocator,
146+
roc_cache_file: CacheFile,
147+
comptime save: fn (file_cache_path: []const u8, file_binary: []const u8) SaveCacheFileError!void,
148+
) !void {
149+
const file_cache_path = switch (roc_cache_file) {
150+
.compiler => |value| try getCompilerPath(allocator, value.version_name),
151+
else => @panic("FIX ME!!!!!!"),
152+
};
153+
154+
defer allocator.free(file_cache_path);
155+
156+
const file_binary = switch (roc_cache_file) {
157+
.compiler => |value| value.binary,
158+
else => @panic("FIX ME!!!!!!"),
159+
};
160+
161+
try save(file_cache_path, file_binary);
162+
}
163+
164+
pub fn saveToCache(allocator: Allocator, roc_cache_file: CacheFile) !void {
165+
try saveToCacheInternal(allocator, roc_cache_file, saveCacheFile);
166+
}
167+
168+
//pub fn setRocCompilerInternal(allocator: Allocator, version_name: []const u8) !void {
169+
//}
170+
//pub fn setRocCompiler(allocator: Allocator, version_name: []const u8) !void {
171+
//const xdg_cache_home = getXdgCacheHome(allocator);
172+
//defer allocator.free(xdg_cache_home);
173+
//try setRocCompilerInternal(allocator, roc_cache_file, xdg_cache_home, saveCacheFile);
174+
//}
175+
176+
const test_cache_folder = "./test-cache";
177+
fn create_test_cache_folder() !void {
178+
try setEnvVar(xdg_cache_home_var_name, test_cache_folder);
179+
const cwd = std.fs.cwd();
180+
// remove folders temp cache folder in case a old test fail to clean it up
181+
delete_test_cache_folder();
182+
try cwd.makePath(test_cache_folder);
183+
}
184+
185+
fn delete_test_cache_folder() void {
186+
const cwd = std.fs.cwd();
187+
cwd.deleteTree(test_cache_folder) catch @panic("Failed to delete test cache folder!");
188+
}
189+
190+
fn make_test_cache_file_path(allocator: Allocator, relative_path: []const u8) ![]u8 {
191+
const parts = [_][]const u8{ test_cache_folder, "roc", relative_path };
192+
return try std.fs.path.join(allocator, &parts);
193+
}
194+
195+
test "Compiler cache should save in the correct place with the correct data" {
196+
const allocator = testing.allocator;
197+
try create_test_cache_folder();
198+
defer delete_test_cache_folder();
199+
const cache_file = CacheFile.Compiler{
200+
.version_name = "1.0.0",
201+
.binary = "A roc compiler binary",
202+
};
203+
try saveToCacheInternal(allocator, CacheFile{ .compiler = cache_file }, saveCacheFile);
204+
const expected_cache_file_local = try make_test_cache_file_path(allocator, "compiler/1.0.0");
205+
defer allocator.free(expected_cache_file_local);
206+
207+
const file = try std.fs.cwd().openFile(expected_cache_file_local, .{ .mode = .read_only });
208+
defer file.close();
209+
210+
const file_size = try file.getEndPos();
211+
const buf = try allocator.alloc(u8, file_size);
212+
defer allocator.free(buf);
213+
_ = try file.readAll(buf);
214+
try testing.expectEqual(cache_file.binary.len, buf.len);
215+
try testing.expectEqualStrings(cache_file.binary, buf);
216+
}

0 commit comments

Comments
 (0)