1//===- FileUpdate.cpp - Conditionally update a file -----------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// FileUpdate is a utility for conditionally updating a file from its input
11// based on whether the input differs from the output. It is used to avoid
12// unnecessary modifications in a build system.
13//
14//===----------------------------------------------------------------------===//
15
16#include "llvm/Support/CommandLine.h"
17#include "llvm/Support/MemoryBuffer.h"
18#include "llvm/ADT/OwningPtr.h"
19#include "llvm/Support/PrettyStackTrace.h"
20#include "llvm/Support/ToolOutputFile.h"
21#include "llvm/Support/Signals.h"
22#include "llvm/Support/system_error.h"
23using namespace llvm;
24
25static cl::opt<bool>
26Quiet("quiet", cl::desc("Don't print unnecessary status information"),
27      cl::init(false));
28
29static cl::opt<std::string>
30InputFilename("input-file", cl::desc("Input file (defaults to stdin)"),
31              cl::init("-"), cl::value_desc("filename"));
32
33static cl::opt<std::string>
34OutputFilename(cl::Positional, cl::desc("<output-file>"), cl::Required);
35
36int main(int argc, char **argv) {
37  sys::PrintStackTraceOnErrorSignal();
38  PrettyStackTraceProgram X(argc, argv);
39  cl::ParseCommandLineOptions(argc, argv);
40
41  if (OutputFilename == "-") {
42    errs() << argv[0] << ": error: Can't update standard output\n";
43    return 1;
44  }
45
46  // Get the input data.
47  OwningPtr<MemoryBuffer> In;
48  if (error_code ec = MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), In)) {
49    errs() << argv[0] << ": error: Unable to get input '"
50           << InputFilename << "': " << ec.message() << '\n';
51    return 1;
52  }
53
54  // Get the output data.
55  OwningPtr<MemoryBuffer> Out;
56  MemoryBuffer::getFile(OutputFilename.c_str(), Out);
57
58  // If the output exists and the contents match, we are done.
59  if (Out && In->getBufferSize() == Out->getBufferSize() &&
60      memcmp(In->getBufferStart(), Out->getBufferStart(),
61             Out->getBufferSize()) == 0) {
62    if (!Quiet)
63      errs() << argv[0] << ": Not updating '" << OutputFilename
64             << "', contents match input.\n";
65    return 0;
66  }
67
68  // Otherwise, overwrite the output.
69  if (!Quiet)
70    errs() << argv[0] << ": Updating '" << OutputFilename
71           << "', contents changed.\n";
72  std::string ErrorStr;
73  tool_output_file OutStream(OutputFilename.c_str(), ErrorStr,
74                             raw_fd_ostream::F_Binary);
75  if (!ErrorStr.empty()) {
76    errs() << argv[0] << ": Unable to write output '"
77           << OutputFilename << "': " << ErrorStr << '\n';
78    return 1;
79  }
80
81  OutStream.os().write(In->getBufferStart(), In->getBufferSize());
82
83  // Declare success.
84  OutStream.keep();
85
86  return 0;
87}
88