#[solved] Simplest possible app that calls another app with the arguments passed

1 messages · Page 1 of 1 (latest)

hollow pecan
#

I'm passing zig cc as the compiler to a transpiler that deletes the cc part of zig cc 😦
I want to create a cross-platform file that can call zig cc, so I can name it zigcc instead

I used to do this in nim by string interpolating a file with the absolute path to the child app,
and compiling that into a separate app.
But that turned to be brittle, and I needed to recompile the app if the file changed by any reason

The main catch is that it needs to take a path to a local binary (eg: ./bin/.zig/zig cc)
and not just a blanket zig cc, relying on PATH. Otherwise the problem would be very simple.

WIth bash I'd just do:

#!/bin/sh
path/to/zig cc $@
```But that's not cross platform, so I'm ruling that out
_I don't want to maintain code for every shell_

Could there be a way to do this with Zig
better than with string interpolation+compilation? 🤔
willow moss
#

std.process.argvAlloc + std.process.Child?
for taking a path to a local binary, you could make it launch the string "BINARY_FILE_NAME_BUFFER........................." and that will end up in the output file, so you can find and replace for it with the actual file path (and null terminate or something)

so you could make a program

./linkerprogram zigcc ./bin/.zig/zig cc
(reads itself, repaces the string, and writes to zigcc)
./zigcc --help
#

it won't work on macos though because of codesigning, you will have to have it remove the signature and re-add it

willow moss
#

you would make a zig program like

const file_to_launch = "__REPLACE_ME__........................................................";
pub fn main() !void {
    spawn(file_to_launch);
}

then find and replace the text "__REPLACE_ME__........................................................" in the emitted binary with the program to launch and args

hollow pecan
#

std.process.argvAlloc + std.process.Child solves the zig calling zig part
but... how do I solve the "maybe has moved" part?

willow moss
#

what do you mean "maybe has moved"?

hollow pecan
#

./bin/zig/zig when the user does cd thing and cwd does not have a ./bin folder at all....

willow moss
#

put an absolute path?

hollow pecan
#

I used to use absolute paths with interpolation

#

but that is damn brittle

#

the zigcc binary would need to be recompiled everytime

willow moss
#

if zig is in your $PATH, you can make it run "zig" instead if ./bin/zig/zig

hollow pecan
#

doable, and works... but was thinking of a maybe alternative solution

hollow pecan
#

I'm running a local zig, otherwise this is trivial

ornate flower
#

isn't the first argument to the program is the path of the program, which may or may not be an absolute path

#

imo if the script is literally just path/to/zig cc $@ i would just write the shell and windows batch script for these two and be done.

hollow pecan
#

also said cross platform

#

using PATH and platform scripts make this question irrelevant

#

which was stated in the question 😦

#

I'm looking for an alternative to that

ornate flower
hollow pecan
#

isn't the first argument to the program is the path of the program
I don't understand this grammar

ornate flower
#
const std = @import("std");

pub fn main() !void {
    var args = try std.process.argsWithAllocator(std.heap.smp_allocator);
    defer args.deinit();
    const path_to_this_bin = args.next() orelse @panic("no first argument");
    std.debug.print("path_to_this_bin: {s}\n", .{path_to_this_bin});
}
#

std.fs.path.dirname + join + spawn

willow moss
#

how does that help?

ornate flower
#

oh, relative to the current working directory?

hollow pecan
#
# I say
compiler ./path/to/zig cc this.file.c
# Compiler reads:
./path/to/zig this.file.c
# Compiler calls that
# Zig says
Incorrect command
#

gets worse if the binary is not there on that relative route
(which is solved by passing an absolute path, but the caller app needs to resolve that path)

hollow pecan
#

I think I'm looking for something like getCurrentAppPath() 🤔

#

is arg0 always absolute?

unkempt remnant
hollow pecan
#

I don't have a buildsystem instance in a running app, afaik

unkempt remnant
#

you pass it as build option or as argument, unless you need the custom binary to be used outside of zig build system then why not just call zig from $PATH

hollow pecan
#

it was mentioned twice in this conv

#

the goal is to get the absolute path to the binary itself from inside the binary,
in order to find another file that is stored right next to it

unkempt remnant
#

std.fs.selfExeDirPathAlloc

hollow pecan
#

ty

hollow pecan
#
const std = @import("std");

pub fn main () !u8 {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const A = arena.allocator();

    const dir = try std.fs.selfExeDirPathAlloc(A);
    defer A.free(dir);

    const zig = try std.mem.join(A, "", .{dir, "zig"});
    defer A.free(zig);

    const cmd = std.ArrayList([]u8).init(A);
    defer cmd.deinit();

    const args = try std.process.argsWithAllocator(A);
    try cmd.append(zig);
    try cmd.append("cc");
    for (args.next(), 0..) |arg, id| {
      if (id == 0) continue;
      cmd.append(arg);
    }

    var P = std.process.Child.init(cmd.items, A);
    const R = try std.process.Child.spawnAndWait(&P);
    return R.Exited;
}
```Sanity check
Does this look ok?
unkempt remnant
#

you can give cwd to std.process.Child which should allow you to skip joining paths

hollow pecan
#

the whole point of it is to avoid cwd?

unkempt remnant
#

in that case that's fine

#

though instead of std.mem.join, use std.fs.path.join

hollow pecan
#
const std = @import("std");

pub fn main() !u8 {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const A = arena.allocator();

    const dir = try std.fs.selfExeDirPathAlloc(A);
    defer A.free(dir);

    const zig = try std.mem.join(A, "/", &.{ dir, "zig" });
    defer A.free(zig);

    var cmd = std.ArrayList([]const u8).init(A);
    defer cmd.deinit();

    var args = try std.process.argsWithAllocator(A);
    defer args.deinit();
    try cmd.append(zig);
    try cmd.append("cc");
    _ = args.next(); // Discard arg0
    while (args.next()) |arg| try cmd.append(arg); // Passthrough all args

    std.debug.print("Running Command: {s}\n", .{cmd.items});
    var P = std.process.Child.init(cmd.items, A);
    const R = try std.process.Child.spawnAndWait(&P);
    return R.Exited;
}
hollow pecan
unkempt remnant
#

the mem join simply joins 2 slices, the path join will add the path separator in portable way (if needed)

hollow pecan
#

ah true, forgot about portability

#

ty, thats a good point

unkempt remnant
#

since you use arena, you can just remove all those defers too (except arena.deinit)

hollow pecan
#
       if (std.mem.endsWith(u8, self, "cc" )) { try cmd.append("cc" ); }
  else if (std.mem.endsWith(u8, self, "cpp")) { try cmd.append("c++"); }
  else if (std.mem.endsWith(u8, self, "ar" )) { try cmd.append("ar" ); }
```It actually needs no knowledge of the outside now! so amazing!
#

how did I not think of that before