Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
138 lines (133 sloc) 3.87 KB
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/;
/**
* Reads a file from disk. This returns a list containing each line of the file.
*/
let readLines = (file: string): list(string) => {
let lines = ref([]);
let chan = open_in(file);
let finished_lines =
try (
{
while (true) {
lines := [input_line(chan), ...lines^];
};
[];
}
) {
| End_of_file =>
close_in(chan);
lines^ |> List.rev;
};
finished_lines;
};
/**
* Reads a file from disk. This returns a single, new-line separated string
* containing the contents of the file.
*/
let readFile = (file: string): string =>
String.concat("\n", readLines(file));
/**
* Recursively makes sure a directory exists.
*/
let rec mkdirp = (dirPath: string): unit =>
if (Sys.file_exists(dirPath)) {
/* The directory exists, stop recursing but check error cases */
if (Sys.is_directory(dirPath)) {
();
/* All good */
} else {
/* This means something like 'foo/bar' is a file, but we are trying
to create the directory 'foo/bar/baz' since 'bar' is a file not a
directory this will obviously fail. Here we call out the case
explicitly and try to provide a helpful error message. */
failwith(
Printf.sprintf(
"'%s' exists but is not a directory, aborting",
dirPath,
),
);
};
} else {
mkdirp(Filename.dirname(dirPath));
/* 493 = 0o755 */
Unix.mkdir(dirPath, 493);
();
};
/**
* Implements `rm -rf <path>` behaviour.
*/
let rmrf = (path: string): unit => {
let rec visit = path =>
if (Sys.is_directory(path)) {
Array.iter(
name => visit(Filename.concat(path, name)),
Sys.readdir(path),
);
Unix.rmdir(path);
} else {
Unix.unlink(path);
};
if (Sys.file_exists(path)) {
visit(path);
};
};
/**
* Writes a file at the given path containing the given contents.
*/
let writeFile = (filePath: string, contents: string) => {
let outFile = open_out(filePath);
output_string(outFile, contents);
close_out(outFile);
};
/**
* TODO: Allow passing a serializer and file descriptor the child fn can write
* its return value to.
*/
let captureOutput = (fn: unit => unit): (string, string, Unix.process_status) => {
/* Capture normal stdout/stderr channels */
let oldstdout = Unix.dup(Unix.stdout);
let oldstderr = Unix.dup(Unix.stderr);
/* Create tmpOut and tmpErr files */
let (tmpOut, tmpOutChannel) =
Filename.open_temp_file("reason-", ".stdout");
let (tmpErr, tmpErrChannel) =
Filename.open_temp_file("reason-", ".stderr");
/* Redirect stdout/stderr channels to the tmp files */
Unix.dup2(Unix.descr_of_out_channel(tmpOutChannel), Unix.stdout);
Unix.dup2(Unix.descr_of_out_channel(tmpErrChannel), Unix.stderr);
/**
* Fork and call the function that may produce output. The fork is necessary
* in case the function fatals. We do not want to crash the parent program.
*/
let pid = Unix.fork();
if (pid === 0) {
fn();
/*
* Exit the child process, we don't care about the program anymore. We have
* already captured the output.
*/
exit(0);
} else {
/* Wait for the child process to finish */
let (_, processStatus) = Unix.waitpid([], pid);
/* Flush channels */
flush(tmpOutChannel);
flush(tmpErrChannel);
/* Restore old channels */
Unix.dup2(oldstdout, Unix.stdout);
Unix.dup2(oldstderr, Unix.stderr);
/* Read tmp files */
let stdOutStr = readFile(tmpOut);
let stdErrStr = readFile(tmpErr);
/* Cleanup tmp files */
Unix.unlink(tmpOut);
Unix.unlink(tmpErr);
/* Return outputs and exitStatus */
(stdOutStr, stdErrStr, processStatus);
};
};
You can’t perform that action at this time.