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:
parent
489ac892b9
commit
c6947fde57
@ -28,6 +28,8 @@ mut:
|
|||||||
target Target
|
target Target
|
||||||
omode Target
|
omode Target
|
||||||
is_verbose bool
|
is_verbose bool
|
||||||
|
show_wd bool
|
||||||
|
show_env bool
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut ctx Context) println(s string) {
|
fn (mut ctx Context) println(s string) {
|
||||||
@ -53,7 +55,7 @@ fn main() {
|
|||||||
args := os.args[1..]
|
args := os.args[1..]
|
||||||
if '-h' in args || '--help' in args {
|
if '-h' in args || '--help' in args {
|
||||||
println("Usage:
|
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).
|
Prints lines periodically (-period_ms), to stdout/stderr (-target).
|
||||||
After a while (-timeout_ms), exit with (-exitcode).
|
After a while (-timeout_ms), exit with (-exitcode).
|
||||||
This program is useful for platform independent testing
|
This program is useful for platform independent testing
|
||||||
@ -63,6 +65,8 @@ fn main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.is_verbose = '-v' in args
|
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.target = s2target(cmdline.option(args, '-target', 'both'))
|
||||||
ctx.exitcode = cmdline.option(args, '-exitcode', '0').int()
|
ctx.exitcode = cmdline.option(args, '-exitcode', '0').int()
|
||||||
ctx.timeout_ms = cmdline.option(args, '-timeout_ms', '200').int()
|
ctx.timeout_ms = cmdline.option(args, '-timeout_ms', '200').int()
|
||||||
@ -73,6 +77,15 @@ fn main() {
|
|||||||
if ctx.is_verbose {
|
if ctx.is_verbose {
|
||||||
eprintln('> args: ${args} | context: ${ctx}')
|
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)
|
spawn do_timeout(&ctx)
|
||||||
for i := 1; true; i++ {
|
for i := 1; true; i++ {
|
||||||
ctx.println('${i}')
|
ctx.println('${i}')
|
||||||
|
@ -17,16 +17,16 @@ pub enum ProcessState {
|
|||||||
|
|
||||||
[heap]
|
[heap]
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
pub:
|
|
||||||
filename string // the process's command file path
|
|
||||||
pub mut:
|
pub mut:
|
||||||
pid int // the PID of the process
|
filename string // the process's command file path
|
||||||
code int = -1
|
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
|
// the exit code of the process, != -1 *only* when status is .exited *and* the process was not aborted
|
||||||
status ProcessState = .not_started
|
status ProcessState = .not_started
|
||||||
// the current status of the process
|
// the current status of the process
|
||||||
err string // if the process fails, contains the reason why
|
err string // if the process fails, contains the reason why
|
||||||
args []string // the arguments that the command takes
|
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_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')
|
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()
|
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
|
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
|
// set_environment - set a custom environment variable mapping for the new process
|
||||||
pub fn (mut p Process) set_environment(envs map[string]string) {
|
pub fn (mut p Process) set_environment(envs map[string]string) {
|
||||||
if p.status != .not_started {
|
if p.status != .not_started {
|
||||||
|
@ -50,6 +50,15 @@ fn (mut p Process) unix_spawn_process() int {
|
|||||||
fd_close(pipeset[3])
|
fd_close(pipeset[3])
|
||||||
fd_close(pipeset[5])
|
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 {
|
execve(p.filename, p.args, p.env) or {
|
||||||
eprintln(err)
|
eprintln(err)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -36,6 +36,39 @@ fn test_getpid() {
|
|||||||
assert pid != 0
|
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() {
|
fn test_run() {
|
||||||
mut p := os.new_process(test_os_process)
|
mut p := os.new_process(test_os_process)
|
||||||
p.set_args(['-timeout_ms', '150', '-period_ms', '50'])
|
p.set_args(['-timeout_ms', '150', '-period_ms', '50'])
|
||||||
|
@ -57,7 +57,16 @@ pub mut:
|
|||||||
child_stderr_write &u32 = unsafe { nil }
|
child_stderr_write &u32 = unsafe { nil }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[manualfree]
|
||||||
fn (mut p Process) win_spawn_process() int {
|
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{
|
mut wdata := &WProcess{
|
||||||
child_stdin: 0
|
child_stdin: 0
|
||||||
child_stdout_read: 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)
|
start_info.dw_flags = u32(C.STARTF_USESTDHANDLES)
|
||||||
}
|
}
|
||||||
cmd := '${p.filename} ' + p.args.join(' ')
|
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 {
|
mut creation_flags := if p.create_no_window {
|
||||||
int(C.CREATE_NO_WINDOW)
|
int(C.CREATE_NO_WINDOW)
|
||||||
@ -105,8 +116,15 @@ fn (mut p Process) win_spawn_process() int {
|
|||||||
if p.use_pgroup {
|
if p.use_pgroup {
|
||||||
creation_flags |= C.CREATE_NEW_PROCESS_GROUP
|
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,
|
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')
|
failed_cfn_report_error(create_process_ok, 'CreateProcess')
|
||||||
if p.use_stdio_ctl {
|
if p.use_stdio_ctl {
|
||||||
close_valid_handle(&wdata.child_stdout_write)
|
close_valid_handle(&wdata.child_stdout_write)
|
||||||
|
Loading…
Reference in New Issue
Block a user