Separate the mobile promo action from the message
[chromium-blink-merge.git] / courgette / courgette_tool.cc
blob2714e0fe8fe40c4bd447c0c30be5413a06edf784
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <vector>
6 #include <string>
8 #include "base/at_exit.h"
9 #include "base/basictypes.h"
10 #include "base/command_line.h"
11 #include "base/file_path.h"
12 #include "base/file_util.h"
13 #include "base/logging.h"
14 #include "base/string_number_conversions.h"
15 #include "base/string_util.h"
16 #include "base/utf_string_conversions.h"
17 #include "courgette/third_party/bsdiff.h"
18 #include "courgette/courgette.h"
19 #include "courgette/streams.h"
22 void PrintHelp() {
23 fprintf(stderr,
24 "Usage:\n"
25 " courgette -supported <executable_file>\n"
26 " courgette -dis <executable_file> <binary_assembly_file>\n"
27 " courgette -asm <binary_assembly_file> <executable_file>\n"
28 " courgette -disadj <executable_file> <reference> <binary_assembly_file>\n"
29 " courgette -gen <v1> <v2> <patch>\n"
30 " courgette -apply <v1> <patch> <v2>\n"
31 "\n");
34 void UsageProblem(const char* message) {
35 fprintf(stderr, "%s", message);
36 fprintf(stderr, "\n");
37 PrintHelp();
38 exit(1);
41 void Problem(const char* format, ...) {
42 va_list args;
43 va_start(args, format);
44 vfprintf(stderr, format, args);
45 fprintf(stderr, "\n");
46 va_end(args);
47 exit(1);
50 std::string ReadOrFail(const FilePath& file_name, const char* kind) {
51 int64 file_size = 0;
52 if (!file_util::GetFileSize(file_name, &file_size))
53 Problem("Can't read %s file.", kind);
54 std::string buffer;
55 buffer.reserve(static_cast<size_t>(file_size));
56 if (!file_util::ReadFileToString(file_name, &buffer))
57 Problem("Can't read %s file.", kind);
58 return buffer;
61 void WriteSinkToFile(const courgette::SinkStream *sink,
62 const FilePath& output_file) {
63 int count =
64 file_util::WriteFile(output_file,
65 reinterpret_cast<const char*>(sink->Buffer()),
66 static_cast<int>(sink->Length()));
67 if (count == -1)
68 Problem("Can't write output.");
69 if (static_cast<size_t>(count) != sink->Length())
70 Problem("Incomplete write.");
73 void Disassemble(const FilePath& input_file,
74 const FilePath& output_file) {
75 std::string buffer = ReadOrFail(input_file, "input");
77 courgette::AssemblyProgram* program = NULL;
78 const courgette::Status parse_status =
79 courgette::ParseDetectedExecutable(buffer.c_str(), buffer.length(),
80 &program);
82 if (parse_status != courgette::C_OK)
83 Problem("Can't parse input.");
85 courgette::EncodedProgram* encoded = NULL;
86 const courgette::Status encode_status = Encode(program, &encoded);
88 courgette::DeleteAssemblyProgram(program);
90 if (encode_status != courgette::C_OK)
91 Problem("Can't encode program.");
93 courgette::SinkStreamSet sinks;
95 const courgette::Status write_status =
96 courgette::WriteEncodedProgram(encoded, &sinks);
97 if (write_status != courgette::C_OK)
98 Problem("Can't serialize encoded program.");
100 courgette::DeleteEncodedProgram(encoded);
102 courgette::SinkStream sink;
103 if (!sinks.CopyTo(&sink))
104 Problem("Can't combine serialized encoded program streams.");
106 WriteSinkToFile(&sink, output_file);
109 bool Supported(const FilePath& input_file) {
110 bool result = false;
112 std::string buffer = ReadOrFail(input_file, "input");
114 courgette::ExecutableType type;
115 size_t detected_length;
117 DetectExecutableType(buffer.c_str(), buffer.length(),
118 &type,
119 &detected_length);
121 // If the detection fails, we just fall back on UNKNOWN
122 std::string format = "Unsupported";
124 switch (type)
126 case courgette::EXE_UNKNOWN:
127 break;
129 case courgette::EXE_WIN_32_X86:
130 format = "Windows 32 PE";
131 result = true;
132 break;
134 case courgette::EXE_ELF_32_X86:
135 format = "ELF 32 X86";
136 result = true;
137 break;
140 printf("%s Executable\n", format.c_str());
141 return result;
144 void DisassembleAndAdjust(const FilePath& program_file,
145 const FilePath& model_file,
146 const FilePath& output_file) {
147 std::string program_buffer = ReadOrFail(program_file, "program");
148 std::string model_buffer = ReadOrFail(model_file, "reference");
150 courgette::AssemblyProgram* program = NULL;
151 const courgette::Status parse_program_status =
152 courgette::ParseDetectedExecutable(program_buffer.c_str(),
153 program_buffer.length(),
154 &program);
155 if (parse_program_status != courgette::C_OK)
156 Problem("Can't parse program input.");
158 courgette::AssemblyProgram* model = NULL;
159 const courgette::Status parse_model_status =
160 courgette::ParseDetectedExecutable(model_buffer.c_str(),
161 model_buffer.length(),
162 &model);
163 if (parse_model_status != courgette::C_OK)
164 Problem("Can't parse model input.");
166 const courgette::Status adjust_status = Adjust(*model, program);
167 if (adjust_status != courgette::C_OK)
168 Problem("Can't adjust program.");
170 courgette::EncodedProgram* encoded = NULL;
171 const courgette::Status encode_status = Encode(program, &encoded);
173 courgette::DeleteAssemblyProgram(program);
175 if (encode_status != courgette::C_OK)
176 Problem("Can't encode program.");
178 courgette::SinkStreamSet sinks;
180 const courgette::Status write_status =
181 courgette::WriteEncodedProgram(encoded, &sinks);
182 if (write_status != courgette::C_OK)
183 Problem("Can't serialize encoded program.");
185 courgette::DeleteEncodedProgram(encoded);
187 courgette::SinkStream sink;
188 if (!sinks.CopyTo(&sink))
189 Problem("Can't combine serialized encoded program streams.");
191 WriteSinkToFile(&sink, output_file);
194 // Diffs two executable files, write a set of files for the diff, one file per
195 // stream of the EncodedProgram format. Each file is the bsdiff between the
196 // original file's stream and the new file's stream. This is completely
197 // uninteresting to users, but it is handy for seeing how much each which
198 // streams are contributing to the final file size. Adjustment is optional.
199 void DisassembleAdjustDiff(const FilePath& model_file,
200 const FilePath& program_file,
201 const FilePath& output_file_root,
202 bool adjust) {
203 std::string model_buffer = ReadOrFail(model_file, "'old'");
204 std::string program_buffer = ReadOrFail(program_file, "'new'");
206 courgette::AssemblyProgram* model = NULL;
207 const courgette::Status parse_model_status =
208 courgette::ParseDetectedExecutable(model_buffer.c_str(),
209 model_buffer.length(),
210 &model);
211 if (parse_model_status != courgette::C_OK)
212 Problem("Can't parse model input.");
214 courgette::AssemblyProgram* program = NULL;
215 const courgette::Status parse_program_status =
216 courgette::ParseDetectedExecutable(program_buffer.c_str(),
217 program_buffer.length(),
218 &program);
219 if (parse_program_status != courgette::C_OK)
220 Problem("Can't parse program input.");
222 if (adjust) {
223 const courgette::Status adjust_status = Adjust(*model, program);
224 if (adjust_status != courgette::C_OK)
225 Problem("Can't adjust program.");
228 courgette::EncodedProgram* encoded_program = NULL;
229 const courgette::Status encode_program_status =
230 Encode(program, &encoded_program);
231 courgette::DeleteAssemblyProgram(program);
232 if (encode_program_status != courgette::C_OK)
233 Problem("Can't encode program.");
235 courgette::EncodedProgram* encoded_model = NULL;
236 const courgette::Status encode_model_status = Encode(model, &encoded_model);
237 courgette::DeleteAssemblyProgram(model);
238 if (encode_model_status != courgette::C_OK)
239 Problem("Can't encode model.");
241 courgette::SinkStreamSet program_sinks;
242 const courgette::Status write_program_status =
243 courgette::WriteEncodedProgram(encoded_program, &program_sinks);
244 if (write_program_status != courgette::C_OK)
245 Problem("Can't serialize encoded program.");
246 courgette::DeleteEncodedProgram(encoded_program);
248 courgette::SinkStreamSet model_sinks;
249 const courgette::Status write_model_status =
250 courgette::WriteEncodedProgram(encoded_model, &model_sinks);
251 if (write_model_status != courgette::C_OK)
252 Problem("Can't serialize encoded model.");
253 courgette::DeleteEncodedProgram(encoded_model);
255 courgette::SinkStream empty_sink;
256 for (int i = 0; ; ++i) {
257 courgette::SinkStream* old_stream = model_sinks.stream(i);
258 courgette::SinkStream* new_stream = program_sinks.stream(i);
259 if (old_stream == NULL && new_stream == NULL)
260 break;
262 courgette::SourceStream old_source;
263 courgette::SourceStream new_source;
264 old_source.Init(old_stream ? *old_stream : empty_sink);
265 new_source.Init(new_stream ? *new_stream : empty_sink);
266 courgette::SinkStream patch_stream;
267 courgette::BSDiffStatus status =
268 courgette::CreateBinaryPatch(&old_source, &new_source, &patch_stream);
269 if (status != courgette::OK) Problem("-xxx failed.");
271 std::string append = std::string("-") + base::IntToString(i);
273 WriteSinkToFile(&patch_stream,
274 output_file_root.InsertBeforeExtensionASCII(append));
278 void Assemble(const FilePath& input_file,
279 const FilePath& output_file) {
280 std::string buffer = ReadOrFail(input_file, "input");
282 courgette::SourceStreamSet sources;
283 if (!sources.Init(buffer.c_str(), buffer.length()))
284 Problem("Bad input file.");
286 courgette::EncodedProgram* encoded = NULL;
287 const courgette::Status read_status = ReadEncodedProgram(&sources, &encoded);
288 if (read_status != courgette::C_OK)
289 Problem("Bad encoded program.");
291 courgette::SinkStream sink;
293 const courgette::Status assemble_status = courgette::Assemble(encoded, &sink);
294 if (assemble_status != courgette::C_OK)
295 Problem("Can't assemble.");
297 WriteSinkToFile(&sink, output_file);
300 void GenerateEnsemblePatch(const FilePath& old_file,
301 const FilePath& new_file,
302 const FilePath& patch_file) {
303 std::string old_buffer = ReadOrFail(old_file, "'old' input");
304 std::string new_buffer = ReadOrFail(new_file, "'new' input");
306 courgette::SourceStream old_stream;
307 courgette::SourceStream new_stream;
308 old_stream.Init(old_buffer);
309 new_stream.Init(new_buffer);
311 courgette::SinkStream patch_stream;
312 courgette::Status status =
313 courgette::GenerateEnsemblePatch(&old_stream, &new_stream, &patch_stream);
315 if (status != courgette::C_OK) Problem("-gen failed.");
317 WriteSinkToFile(&patch_stream, patch_file);
320 void ApplyEnsemblePatch(const FilePath& old_file,
321 const FilePath& patch_file,
322 const FilePath& new_file) {
323 // We do things a little differently here in order to call the same Courgette
324 // entry point as the installer. That entry point point takes file names and
325 // returns an status code but does not output any diagnostics.
327 courgette::Status status =
328 courgette::ApplyEnsemblePatch(old_file.value().c_str(),
329 patch_file.value().c_str(),
330 new_file.value().c_str());
332 if (status == courgette::C_OK)
333 return;
335 // Diagnose the error.
336 switch (status) {
337 case courgette::C_BAD_ENSEMBLE_MAGIC:
338 Problem("Not a courgette patch");
339 break;
341 case courgette::C_BAD_ENSEMBLE_VERSION:
342 Problem("Wrong version patch");
343 break;
345 case courgette::C_BAD_ENSEMBLE_HEADER:
346 Problem("Corrupt patch");
347 break;
349 case courgette::C_DISASSEMBLY_FAILED:
350 Problem("Disassembly failed (could be because of memory issues)");
351 break;
353 case courgette::C_STREAM_ERROR:
354 Problem("Stream error (likely out of memory or disk space)");
355 break;
357 default:
358 break;
361 // If we failed due to a missing input file, this will
362 // print the message.
363 std::string old_buffer = ReadOrFail(old_file, "'old' input");
364 old_buffer.clear();
365 std::string patch_buffer = ReadOrFail(patch_file, "'patch' input");
366 patch_buffer.clear();
368 // Non-input related errors:
369 if (status == courgette::C_WRITE_OPEN_ERROR)
370 Problem("Can't open output");
371 if (status == courgette::C_WRITE_ERROR)
372 Problem("Can't write output");
374 Problem("-apply failed.");
377 void GenerateBSDiffPatch(const FilePath& old_file,
378 const FilePath& new_file,
379 const FilePath& patch_file) {
380 std::string old_buffer = ReadOrFail(old_file, "'old' input");
381 std::string new_buffer = ReadOrFail(new_file, "'new' input");
383 courgette::SourceStream old_stream;
384 courgette::SourceStream new_stream;
385 old_stream.Init(old_buffer);
386 new_stream.Init(new_buffer);
388 courgette::SinkStream patch_stream;
389 courgette::BSDiffStatus status =
390 courgette::CreateBinaryPatch(&old_stream, &new_stream, &patch_stream);
392 if (status != courgette::OK) Problem("-genbsdiff failed.");
394 WriteSinkToFile(&patch_stream, patch_file);
397 void ApplyBSDiffPatch(const FilePath& old_file,
398 const FilePath& patch_file,
399 const FilePath& new_file) {
400 std::string old_buffer = ReadOrFail(old_file, "'old' input");
401 std::string patch_buffer = ReadOrFail(patch_file, "'patch' input");
403 courgette::SourceStream old_stream;
404 courgette::SourceStream patch_stream;
405 old_stream.Init(old_buffer);
406 patch_stream.Init(patch_buffer);
408 courgette::SinkStream new_stream;
409 courgette::BSDiffStatus status =
410 courgette::ApplyBinaryPatch(&old_stream, &patch_stream, &new_stream);
412 if (status != courgette::OK) Problem("-applybsdiff failed.");
414 WriteSinkToFile(&new_stream, new_file);
417 int main(int argc, const char* argv[]) {
418 base::AtExitManager at_exit_manager;
419 CommandLine::Init(argc, argv);
420 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
422 (void)logging::InitLogging(
423 FILE_PATH_LITERAL("courgette.log"),
424 logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
425 logging::LOCK_LOG_FILE,
426 logging::APPEND_TO_OLD_LOG_FILE,
427 logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
428 logging::SetMinLogLevel(logging::LOG_VERBOSE);
430 bool cmd_sup = command_line.HasSwitch("supported");
431 bool cmd_dis = command_line.HasSwitch("dis");
432 bool cmd_asm = command_line.HasSwitch("asm");
433 bool cmd_disadj = command_line.HasSwitch("disadj");
434 bool cmd_make_patch = command_line.HasSwitch("gen");
435 bool cmd_apply_patch = command_line.HasSwitch("apply");
436 bool cmd_make_bsdiff_patch = command_line.HasSwitch("genbsdiff");
437 bool cmd_apply_bsdiff_patch = command_line.HasSwitch("applybsdiff");
438 bool cmd_spread_1_adjusted = command_line.HasSwitch("gen1a");
439 bool cmd_spread_1_unadjusted = command_line.HasSwitch("gen1u");
441 std::vector<FilePath> values;
442 const CommandLine::StringVector& args = command_line.GetArgs();
443 for (size_t i = 0; i < args.size(); ++i) {
444 values.push_back(FilePath(args[i]));
447 // '-repeat=N' is for debugging. Running many iterations can reveal leaks and
448 // bugs in cleanup.
449 int repeat_count = 1;
450 std::string repeat_switch = command_line.GetSwitchValueASCII("repeat");
451 if (!repeat_switch.empty())
452 if (!base::StringToInt(repeat_switch, &repeat_count))
453 repeat_count = 1;
455 if (cmd_sup + cmd_dis + cmd_asm + cmd_disadj + cmd_make_patch +
456 cmd_apply_patch + cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch +
457 cmd_spread_1_adjusted + cmd_spread_1_unadjusted
458 != 1)
459 UsageProblem(
460 "Must have exactly one of:\n"
461 " -supported -asm, -dis, -disadj, -gen or -apply, -genbsdiff"
462 " or -applybsdiff.");
464 while (repeat_count-- > 0) {
465 if (cmd_sup) {
466 if (values.size() != 1)
467 UsageProblem("-supported <executable_file>");
468 return !Supported(values[0]);
469 } else if (cmd_dis) {
470 if (values.size() != 2)
471 UsageProblem("-dis <executable_file> <courgette_file>");
472 Disassemble(values[0], values[1]);
473 } else if (cmd_asm) {
474 if (values.size() != 2)
475 UsageProblem("-asm <courgette_file_input> <executable_file_output>");
476 Assemble(values[0], values[1]);
477 } else if (cmd_disadj) {
478 if (values.size() != 3)
479 UsageProblem("-disadj <executable_file> <model> <courgette_file>");
480 DisassembleAndAdjust(values[0], values[1], values[2]);
481 } else if (cmd_make_patch) {
482 if (values.size() != 3)
483 UsageProblem("-gen <old_file> <new_file> <patch_file>");
484 GenerateEnsemblePatch(values[0], values[1], values[2]);
485 } else if (cmd_apply_patch) {
486 if (values.size() != 3)
487 UsageProblem("-apply <old_file> <patch_file> <new_file>");
488 ApplyEnsemblePatch(values[0], values[1], values[2]);
489 } else if (cmd_make_bsdiff_patch) {
490 if (values.size() != 3)
491 UsageProblem("-genbsdiff <old_file> <new_file> <patch_file>");
492 GenerateBSDiffPatch(values[0], values[1], values[2]);
493 } else if (cmd_apply_bsdiff_patch) {
494 if (values.size() != 3)
495 UsageProblem("-applybsdiff <old_file> <patch_file> <new_file>");
496 ApplyBSDiffPatch(values[0], values[1], values[2]);
497 } else if (cmd_spread_1_adjusted || cmd_spread_1_unadjusted) {
498 if (values.size() != 3)
499 UsageProblem("-gen1[au] <old_file> <new_file> <patch_files_root>");
500 DisassembleAdjustDiff(values[0], values[1], values[2],
501 cmd_spread_1_adjusted);
502 } else {
503 UsageProblem("No operation specified");
507 return 0;