1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

os: implement Process.set_work_folder/0 to set the initial working folder of the new child process (#17946)

This commit is contained in:
Delyan Angelov 2023-04-13 14:48:32 +03:00 committed by GitHub
parent 489ac892b9
commit c6947fde57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 7 deletions

View File

@ -28,6 +28,8 @@ mut:
target Target
omode Target
is_verbose bool
show_wd bool
show_env bool
}
fn (mut ctx Context) println(s string) {
@ -53,7 +55,7 @@ fn main() {
args := os.args[1..]
if '-h' in args || '--help' in args {
println("Usage:
test_os_process [-v] [-h] [-target stderr/stdout/both/alternate] [-exitcode 0] [-timeout_ms 200] [-period_ms 50]
test_os_process [-v] [-h] [-target stderr/stdout/both/alternate] [-show_wd] [-show_env] [-exitcode 0] [-timeout_ms 200] [-period_ms 50]
Prints lines periodically (-period_ms), to stdout/stderr (-target).
After a while (-timeout_ms), exit with (-exitcode).
This program is useful for platform independent testing
@ -63,6 +65,8 @@ fn main() {
return
}
ctx.is_verbose = '-v' in args
ctx.show_wd = '-show_wd' in args
ctx.show_env = '-show_env' in args
ctx.target = s2target(cmdline.option(args, '-target', 'both'))
ctx.exitcode = cmdline.option(args, '-exitcode', '0').int()
ctx.timeout_ms = cmdline.option(args, '-timeout_ms', '200').int()
@ -73,6 +77,15 @@ fn main() {
if ctx.is_verbose {
eprintln('> args: ${args} | context: ${ctx}')
}
if ctx.show_wd {
ctx.println('WORK_DIR=${os.getwd()}')
}
if ctx.show_env {
all := os.environ()
for k, v in all {
ctx.println('${k}=${v}')
}
}
spawn do_timeout(&ctx)
for i := 1; true; i++ {
ctx.println('${i}')

View File

@ -17,16 +17,16 @@ pub enum ProcessState {
[heap]
pub struct Process {
pub:
filename string // the process's command file path
pub mut:
pid int // the PID of the process
code int = -1
filename string // the process's command file path
pid int // the PID of the process
code int = -1
// the exit code of the process, != -1 *only* when status is .exited *and* the process was not aborted
status ProcessState = .not_started
// the current status of the process
err string // if the process fails, contains the reason why
args []string // the arguments that the command takes
work_folder string // the initial working folder of the process. When '', reuse the same folder as the parent process.
env_is_custom bool // true, when the environment was customized with .set_environment
env []string // the environment with which the process was started (list of 'var=val')
use_stdio_ctl bool // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp()
@ -57,6 +57,16 @@ pub fn (mut p Process) set_args(pargs []string) {
return
}
// set_work_folder - set the initial working folder for the new process
// If you do not set it, it will reuse the current working folder of the parent process.
pub fn (mut p Process) set_work_folder(path string) {
if p.status != .not_started {
return
}
p.work_folder = real_path(path)
return
}
// set_environment - set a custom environment variable mapping for the new process
pub fn (mut p Process) set_environment(envs map[string]string) {
if p.status != .not_started {

View File

@ -50,6 +50,15 @@ fn (mut p Process) unix_spawn_process() int {
fd_close(pipeset[3])
fd_close(pipeset[5])
}
if p.work_folder != '' {
if !is_abs_path(p.filename) {
// Ensure p.filename contains an absolute path, so it
// can be located reliably, even after changing the
// current folder in the child process:
p.filename = abs_path(p.filename)
}
chdir(p.work_folder) or {}
}
execve(p.filename, p.args, p.env) or {
eprintln(err)
exit(1)

View File

@ -36,6 +36,39 @@ fn test_getpid() {
assert pid != 0
}
fn test_set_work_folder() {
new_work_folder := os.real_path(os.temp_dir())
parent_working_folder := os.getwd()
dump(new_work_folder)
dump(parent_working_folder)
if new_work_folder == parent_working_folder {
eprintln('... skipping ${@METHOD} because the working folder is the temporary one')
return
}
mut p := os.new_process(test_os_process)
p.set_args(['-show_wd', '-target', 'stdout'])
p.set_work_folder(new_work_folder)
p.set_redirect_stdio()
p.wait()
assert p.code == 0
output := p.stdout_slurp().trim_space()
p.close()
$if trace_process_output ? {
eprintln('p output: "${output}"')
}
child_work_folder := output.find_between('stdout, WORK_DIR=', '\n').trim_space()
dump(child_work_folder)
assert child_work_folder == new_work_folder
new_parent_work_folder := os.getwd()
dump(new_parent_work_folder)
assert new_parent_work_folder == parent_working_folder
assert new_parent_work_folder != child_work_folder
}
fn test_done() {
exit(0)
}
fn test_run() {
mut p := os.new_process(test_os_process)
p.set_args(['-timeout_ms', '150', '-period_ms', '50'])

View File

@ -57,7 +57,16 @@ pub mut:
child_stderr_write &u32 = unsafe { nil }
}
[manualfree]
fn (mut p Process) win_spawn_process() int {
mut to_be_freed := []voidptr{cap: 5}
defer {
for idx := to_be_freed.len - 1; idx >= 0; idx-- {
unsafe { free(to_be_freed[idx]) }
}
unsafe { to_be_freed.free() }
}
p.filename = abs_path(p.filename) // expand the path to an absolute one, in case we later change the working folder
mut wdata := &WProcess{
child_stdin: 0
child_stdout_read: 0
@ -95,7 +104,9 @@ fn (mut p Process) win_spawn_process() int {
start_info.dw_flags = u32(C.STARTF_USESTDHANDLES)
}
cmd := '${p.filename} ' + p.args.join(' ')
C.ExpandEnvironmentStringsW(cmd.to_wide(), voidptr(&wdata.command_line[0]), 32768)
cmd_wide_ptr := cmd.to_wide()
to_be_freed << cmd_wide_ptr
C.ExpandEnvironmentStringsW(cmd_wide_ptr, voidptr(&wdata.command_line[0]), 32768)
mut creation_flags := if p.create_no_window {
int(C.CREATE_NO_WINDOW)
@ -105,8 +116,15 @@ fn (mut p Process) win_spawn_process() int {
if p.use_pgroup {
creation_flags |= C.CREATE_NEW_PROCESS_GROUP
}
mut work_folder_ptr := voidptr(unsafe { nil })
if p.work_folder != '' {
work_folder_ptr = p.work_folder.to_wide()
to_be_freed << work_folder_ptr
}
create_process_ok := C.CreateProcessW(0, &wdata.command_line[0], 0, 0, C.TRUE, creation_flags,
0, 0, voidptr(&start_info), voidptr(&wdata.proc_info))
0, work_folder_ptr, voidptr(&start_info), voidptr(&wdata.proc_info))
failed_cfn_report_error(create_process_ok, 'CreateProcess')
if p.use_stdio_ctl {
close_valid_handle(&wdata.child_stdout_write)