libutil: return a program handle from runProgram2
this will let us also return a source for the program output later,
which will in turn make sinkToSource unnecessary for program output
processing. this may also reopen a path for provigin program input,
but that still needs a proper async io framework to avoid problems.
Change-Id: Iaf93f47db99c38cfaf134bd60ed6a804d7ddf688
This commit is contained in:
parent
f4f6d1d8e2
commit
b6a08a2fed
|
@ -251,7 +251,7 @@ void runNix(Path program, const Strings & args)
|
||||||
.program = settings.nixBinDir+ "/" + program,
|
.program = settings.nixBinDir+ "/" + program,
|
||||||
.args = args,
|
.args = args,
|
||||||
.environment = subprocessEnv,
|
.environment = subprocessEnv,
|
||||||
});
|
}).wait();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -650,7 +650,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||||
|
|
||||||
// runProgram redirects stdout to a StringSink,
|
// runProgram redirects stdout to a StringSink,
|
||||||
// using runProgram2 to allow editors to display their UI
|
// using runProgram2 to allow editors to display their UI
|
||||||
runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args });
|
runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args }).wait();
|
||||||
|
|
||||||
// Reload right after exiting the editor
|
// Reload right after exiting the editor
|
||||||
state->resetFileCache();
|
state->resetFileCache();
|
||||||
|
|
|
@ -695,7 +695,7 @@ struct GitInputScheme : InputScheme
|
||||||
.program = "git",
|
.program = "git",
|
||||||
.args = { "-C", repoDir, "--git-dir", gitDir, "archive", input.getRev()->gitRev() },
|
.args = { "-C", repoDir, "--git-dir", gitDir, "archive", input.getRev()->gitRev() },
|
||||||
.standardOut = &sink
|
.standardOut = &sink
|
||||||
});
|
}).wait();
|
||||||
});
|
});
|
||||||
|
|
||||||
unpackTarfile(*source, tmpDir);
|
unpackTarfile(*source, tmpDir);
|
||||||
|
|
|
@ -928,7 +928,7 @@ void runPostBuildHook(
|
||||||
.environment = hookEnvironment,
|
.environment = hookEnvironment,
|
||||||
.standardOut = &sink,
|
.standardOut = &sink,
|
||||||
.mergeStderrToStdout = true,
|
.mergeStderrToStdout = true,
|
||||||
});
|
}).wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationGoal::buildDone()
|
void DerivationGoal::buildDone()
|
||||||
|
|
|
@ -249,7 +249,7 @@ std::pair<int, std::string> runProgram(RunOptions && options)
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
runProgram2(options);
|
runProgram2(options).wait();
|
||||||
} catch (ExecError & e) {
|
} catch (ExecError & e) {
|
||||||
status = e.status;
|
status = e.status;
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,25 @@ std::pair<int, std::string> runProgram(RunOptions && options)
|
||||||
return {status, std::move(sink.s)};
|
return {status, std::move(sink.s)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void runProgram2(const RunOptions & options)
|
RunningProgram::~RunningProgram()
|
||||||
|
{
|
||||||
|
if (pid) {
|
||||||
|
// we will not kill a subprocess because we *can't* kill a subprocess
|
||||||
|
// reliably without placing it in its own process group, and cleaning
|
||||||
|
// up a subprocess only when `separatePG` is set is a loaded footgun.
|
||||||
|
assert(false && "destroying un-wait()ed running process");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunningProgram::wait()
|
||||||
|
{
|
||||||
|
int status = pid.wait();
|
||||||
|
if (status)
|
||||||
|
throw ExecError(status, "program '%1%' %2%", program, statusToString(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
RunningProgram runProgram2(const RunOptions & options)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
@ -317,11 +335,7 @@ void runProgram2(const RunOptions & options)
|
||||||
if (options.standardOut)
|
if (options.standardOut)
|
||||||
*options.standardOut << drainFDSource(out.readSide.get());
|
*options.standardOut << drainFDSource(out.readSide.get());
|
||||||
|
|
||||||
/* Wait for the child to finish. */
|
return RunningProgram{options.program, std::move(pid)};
|
||||||
int status = pid.wait();
|
|
||||||
|
|
||||||
if (status)
|
|
||||||
throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string statusToString(int status)
|
std::string statusToString(int status)
|
||||||
|
|
|
@ -87,9 +87,26 @@ struct RunOptions
|
||||||
bool isInteractive = false;
|
bool isInteractive = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct [[nodiscard("you must call RunningProgram::wait()")]] RunningProgram
|
||||||
|
{
|
||||||
|
friend RunningProgram runProgram2(const RunOptions & options);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Path program;
|
||||||
|
Pid pid;
|
||||||
|
|
||||||
|
RunningProgram(Path program, Pid pid) : program(std::move(program)), pid(std::move(pid)) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
RunningProgram() = default;
|
||||||
|
~RunningProgram();
|
||||||
|
|
||||||
|
void wait();
|
||||||
|
};
|
||||||
|
|
||||||
std::pair<int, std::string> runProgram(RunOptions && options);
|
std::pair<int, std::string> runProgram(RunOptions && options);
|
||||||
|
|
||||||
void runProgram2(const RunOptions & options);
|
RunningProgram runProgram2(const RunOptions & options);
|
||||||
|
|
||||||
class ExecError : public Error
|
class ExecError : public Error
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue