@@ -131,7 +131,7 @@ fn ignoreHttpCallback(request: []const u8) void {
131131 _ = request ;
132132}
133133
134- fn allocInstallDirStringXdg (allocator : Allocator ) ! []const u8 {
134+ fn allocInstallDirStringXdg (allocator : Allocator ) error { AlreadyReported } ! []const u8 {
135135 // see https://specifications.freedesktop.org/basedir-spec/latest/#variables
136136 // try $XDG_DATA_HOME/zigup first
137137 xdg_var : {
@@ -141,7 +141,7 @@ fn allocInstallDirStringXdg(allocator: Allocator) ![]const u8 {
141141 std .log .err ("$XDG_DATA_HOME environment variable '{s}' is not an absolute path" , .{xdg_data_home });
142142 return error .AlreadyReported ;
143143 }
144- return std .fs .path .join (allocator , &[_ ][]const u8 { xdg_data_home , "zigup" });
144+ return std .fs .path .join (allocator , &[_ ][]const u8 { xdg_data_home , "zigup" }) catch | e | oom ( e ) ;
145145 }
146146 // .. then fallback to $HOME/.local/share/zigup
147147 const home = std .posix .getenv ("HOME" ) orelse {
@@ -152,16 +152,77 @@ fn allocInstallDirStringXdg(allocator: Allocator) ![]const u8 {
152152 std .log .err ("$HOME environment variable '{s}' is not an absolute path" , .{home });
153153 return error .AlreadyReported ;
154154 }
155- return std .fs .path .join (allocator , &[_ ][]const u8 { home , ".local" , "share" , "zigup" });
155+ return std .fs .path .join (allocator , &[_ ][]const u8 { home , ".local" , "share" , "zigup" }) catch | e | oom ( e ) ;
156156}
157157
158- fn allocInstallDirString (allocator : Allocator ) ! []const u8 {
159- // TODO: maybe support ZIG_INSTALL_DIR environment variable?
160- // TODO: maybe support a file on the filesystem to configure install dir?
158+ fn getSettingsDir (allocator : Allocator ) ? []const u8 {
159+ return std .fs .getAppDataDir (allocator , "zigup" ) catch | err | switch (err ) {
160+ error .OutOfMemory = > | e | oom (e ),
161+ error .AppDataDirUnavailable = > return null ,
162+ };
163+ }
164+
165+ fn readInstallDir (allocator : Allocator ) ! ? []const u8 {
166+ const settings_dir_path = getSettingsDir (allocator ) orelse return null ;
167+ defer allocator .free (settings_dir_path );
168+ const setting_path = std .fs .path .join (allocator , &.{ settings_dir_path , "install-dir" }) catch | e | oom (e );
169+ defer allocator .free (setting_path );
170+ var file = std .fs .cwd ().openFile (setting_path , .{}) catch | err | switch (err ) {
171+ error .FileNotFound = > return null ,
172+ else = > | e | {
173+ std .log .err ("open '{s}' failed with {s}" , .{ setting_path , @errorName (e ) });
174+ return error .AlreadyReported ;
175+ },
176+ };
177+ defer file .close ();
178+ return file .readToEndAlloc (allocator , 9999 ) catch | err | {
179+ std .log .err ("read install dir from '{s}' failed with {s}" , .{ setting_path , @errorName (err ) });
180+ return error .AlreadyReported ;
181+ };
182+ }
183+
184+ fn saveInstallDir (allocator : Allocator , maybe_dir : ? []const u8 ) ! void {
185+ const settings_dir_path = getSettingsDir (allocator ) orelse {
186+ std .log .err ("cannot save install dir, unable to find a suitable settings directory" , .{});
187+ return error .AlreadyReported ;
188+ };
189+ defer allocator .free (settings_dir_path );
190+ const setting_path = std .fs .path .join (allocator , &.{ settings_dir_path , "install-dir" }) catch | e | oom (e );
191+ defer allocator .free (setting_path );
192+ if (maybe_dir ) | d | {
193+ {
194+ const file = try std .fs .cwd ().createFile (setting_path , .{});
195+ defer file .close ();
196+ try file .writer ().writeAll (d );
197+ }
198+
199+ // sanity check, read it back
200+ const readback = (try readInstallDir (allocator )) orelse {
201+ std .log .err ("unable to readback install-dir after saving it" , .{});
202+ return error .AlreadyReported ;
203+ };
204+ defer allocator .free (readback );
205+ if (! std .mem .eql (u8 , readback , d )) {
206+ std .log .err ("saved install dir readback mismatch\n wrote: '{s}'\n read : '{s}'\n " , .{ d , readback });
207+ return error .AlreadyReported ;
208+ }
209+ } else {
210+ std .fs .cwd ().deleteFile (setting_path ) catch | err | switch (err ) {
211+ error .FileNotFound = > {},
212+ else = > | e | return e ,
213+ };
214+ }
215+ }
216+
217+ fn allocInstallDirString (allocator : Allocator ) error {AlreadyReported }! []const u8 {
218+ if (try readInstallDir (allocator )) | d | return d ;
161219 if (builtin .os .tag == .windows ) {
162- const self_exe_dir = try std .fs .selfExeDirPathAlloc (allocator );
220+ const self_exe_dir = std .fs .selfExeDirPathAlloc (allocator ) catch | e | {
221+ std .log .err ("failed to get exe dir path with {s}" , .{@errorName (e )});
222+ return error .AlreadyReported ;
223+ };
163224 defer allocator .free (self_exe_dir );
164- return std .fs .path .join (allocator , &.{ self_exe_dir , "zig" });
225+ return std .fs .path .join (allocator , &.{ self_exe_dir , "zig" }) catch | e | oom ( e ) ;
165226 }
166227 return allocInstallDirStringXdg (allocator );
167228}
@@ -205,8 +266,12 @@ fn toAbsolute(allocator: Allocator, path: []const u8) ![]u8 {
205266 return std .fs .path .join (allocator , &[_ ][]const u8 { cwd , path });
206267}
207268
208- fn help () void {
209- std .io .getStdErr ().writeAll (
269+ fn help (allocator : Allocator ) ! void {
270+ const default_install_dir = allocInstallDirString (allocator ) catch | err | switch (err ) {
271+ error .AlreadyReported = > "unknown (see error printed above)" ,
272+ };
273+
274+ try std .io .getStdErr ().writer ().print (
210275 \\Download and manage zig compilers.
211276 \\
212277 \\Common Usage:
@@ -220,12 +285,16 @@ fn help() void {
220285 \\ zigup keep VERSION mark a compiler to be kept during clean
221286 \\ zigup run VERSION ARGS... run the given VERSION of the compiler with the given ARGS...
222287 \\
288+ \\ zigup get-install-dir prints the install directory to stdout
289+ \\ zigup set-install-dir PATH set the default install directory
290+ \\
223291 \\Uncommon Usage:
224292 \\
225293 \\ zigup fetch-index download and print the download index json
226294 \\
227295 \\Common Options:
228296 \\ --install-dir DIR override the default install location
297+ \\ default: {s}
229298 \\ --path-link PATH path to the `zig` symlink that points to the default compiler
230299 \\ this will typically be a file path within a PATH directory so
231300 \\ that the user can just run `zig`
@@ -234,7 +303,9 @@ fn help() void {
234303 ++ " " ++ default_index_url ++
235304 \\
236305 \\
237- ) catch unreachable ;
306+ ,
307+ .{default_install_dir },
308+ );
238309}
239310
240311fn getCmdOpt (args : [][:0 ]u8 , i : * usize ) ! []const u8 {
@@ -287,7 +358,7 @@ pub fn main2() !u8 {
287358 } else if (std .mem .eql (u8 , "--index" , arg )) {
288359 index_url = try getCmdOpt (args , & i );
289360 } else if (std .mem .eql (u8 , "-h" , arg ) or std .mem .eql (u8 , "--help" , arg )) {
290- help ();
361+ try help (allocator );
291362 return 0 ;
292363 } else {
293364 if (newlen == 0 and std .mem .eql (u8 , "run" , arg )) {
@@ -300,9 +371,35 @@ pub fn main2() !u8 {
300371 args = args [0.. newlen ];
301372 }
302373 if (args .len == 0 ) {
303- help ();
374+ try help (allocator );
304375 return 1 ;
305376 }
377+ if (std .mem .eql (u8 , "get-install-dir" , args [0 ])) {
378+ if (args .len != 1 ) {
379+ std .log .err ("get-install-dir does not accept any cmdline arguments" , .{});
380+ return 1 ;
381+ }
382+ const install_dir = getInstallDir (allocator , .{ .create = false }) catch | err | switch (err ) {
383+ error .AlreadyReported = > return 1 ,
384+ else = > | e | return e ,
385+ };
386+ try std .io .getStdOut ().writer ().writeAll (install_dir );
387+ return 0 ;
388+ }
389+ if (std .mem .eql (u8 , "set-install-dir" , args [0 ])) {
390+ const set_args = args [1.. ];
391+ if (set_args .len != 1 ) {
392+ std .log .err ("set-install-dir requires 1 cmdline arg but got {}" , .{set_args .len });
393+ return 1 ;
394+ }
395+ const path = set_args [0 ];
396+ if (! std .fs .path .isAbsolute (path )) {
397+ std .log .err ("set-install-dir requires an absolute path" , .{});
398+ return 1 ;
399+ }
400+ try saveInstallDir (allocator , path );
401+ return 0 ;
402+ }
306403 if (std .mem .eql (u8 , "fetch-index" , args [0 ])) {
307404 if (args .len != 1 ) {
308405 std .log .err ("'index' command requires 0 arguments but got {d}" , .{args .len - 1 });
0 commit comments