1
+ // Spec here https://github.com/roc-lang/roc/issues/7517
2
+ const builtin = @import ("builtin" );
1
3
const std = @import ("std" );
4
+ const testing = std .testing ;
5
+ const Allocator = std .mem .Allocator ;
2
6
const base = @import ("base.zig" );
3
7
const canonicalize = @import ("check/canonicalize.zig" );
4
8
@@ -18,3 +22,195 @@ pub fn getPackageRootAbsDir(url_data: Package.Url, gpa: std.mem.Allocator) []con
18
22
19
23
@panic ("not implemented" );
20
24
}
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