/*
* Copyright 2016-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.rage;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.util.ProcessExecutor;
import com.facebook.buck.util.ProcessExecutorParams;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
/** Runs an optional user-specified command to get extra info for the rage report. */
public class DefaultExtraInfoCollector implements ExtraInfoCollector {
private static final long PROCESS_TIMEOUT_MS = 5 * 60 * 1000; // 5min.
private final RageConfig rageConfig;
private final ProjectFilesystem projectFilesystem;
private final ProcessExecutor processExecutor;
public DefaultExtraInfoCollector(
RageConfig rageConfig, ProjectFilesystem projectFilesystem, ProcessExecutor processExecutor) {
this.rageConfig = rageConfig;
this.projectFilesystem = projectFilesystem;
this.processExecutor = processExecutor;
}
@Override
public Optional<ExtraInfoResult> run()
throws IOException, InterruptedException, ExtraInfoExecutionException {
ImmutableList<String> extraInfoCommand = rageConfig.getExtraInfoCommand();
if (extraInfoCommand.isEmpty()) {
return Optional.empty();
}
// TODO(ruibm): Potentially add the initial static launch dir here as well as any launch-*
// logs buck is currently generating.
Path rageExtraFilesDir =
projectFilesystem.getBuckPaths().getLogDir().resolve("rage-extra-info");
projectFilesystem.deleteRecursivelyIfExists(rageExtraFilesDir);
projectFilesystem.mkdirs(rageExtraFilesDir);
String extraInfoCommandOutput =
runCommandAndGetStdout(
Iterables.concat(
extraInfoCommand,
ImmutableList.of(
"--output-dir", projectFilesystem.resolve(rageExtraFilesDir).toString())),
projectFilesystem,
processExecutor);
ImmutableSet<Path> rageExtraFiles = projectFilesystem.getFilesUnderPath(rageExtraFilesDir);
return Optional.of(
ExtraInfoResult.builder()
.setExtraFiles(rageExtraFiles)
.setOutput(extraInfoCommandOutput)
.build());
}
public static String runCommandAndGetStdout(
Iterable<String> command,
ProjectFilesystem projectFilesystem,
ProcessExecutor processExecutor)
throws IOException, InterruptedException, ExtraInfoExecutionException {
ProcessExecutor.Result extraInfoResult;
try {
extraInfoResult =
processExecutor.launchAndExecute(
ProcessExecutorParams.builder()
.addAllCommand(command)
.setDirectory(projectFilesystem.getRootPath())
.build(),
ImmutableSet.of(
ProcessExecutor.Option.EXPECTING_STD_OUT, ProcessExecutor.Option.PRINT_STD_ERR),
Optional.empty(),
Optional.of(PROCESS_TIMEOUT_MS),
Optional.empty());
} catch (IOException e) {
throw new ExtraInfoExecutionException("Could not invoke extra report command.", e);
}
if (extraInfoResult.isTimedOut()) {
throw new ExtraInfoExecutionException(
String.format(
"Gathering extra information for rage report from %s timed out after %d ms",
command, PROCESS_TIMEOUT_MS));
}
if (extraInfoResult.getExitCode() != 0) {
throw new ExtraInfoExecutionException(
extraInfoResult.getMessageForResult(
"Could not get extra info for report from " + command));
}
return extraInfoResult.getStdout().orElse("");
}
}