diff --git a/editions/defaults.bzl b/editions/defaults.bzl index b1c3f8d894..7d83865b9a 100644 --- a/editions/defaults.bzl +++ b/editions/defaults.bzl @@ -64,32 +64,18 @@ compile_edition_defaults = rule( ) def _embed_edition_defaults_impl(ctx): - if ctx.attr.encoding == "base64": - args = "--encoding=base64" - elif ctx.attr.encoding == "octal": - args = "--encoding=octal" - else: - fail("Unknown encoding %s" % ctx.attr.encoding) - ctx.actions.run_shell( + args = ctx.actions.args() + args.add(ctx.attr.encoding, format = "--encoding=%s") + args.add(ctx.file.defaults, format = "--defaults_path=%s") + args.add(ctx.file.template, format = "--template_path=%s") + args.add(ctx.outputs.output, format = "--output_path=%s") + args.add(ctx.attr.placeholder, format = "--placeholder=%s") + + ctx.actions.run( + executable = ctx.executable._escape, + arguments = [args], outputs = [ctx.outputs.output], inputs = [ctx.file.defaults, ctx.file.template], - tools = [ctx.executable._escape], - command = """ - DEFAULTS_RAW=$({escape} {args} < {defaults}) - # Windows requires extra escaping. - DEFAULTS_ESCAPED=$(echo $DEFAULTS_RAW | sed 's/\\\\/\\\\\\\\/g' || - echo $DEFAULTS_RAW | sed 's/\\\\\\\\/\\\\\\\\\\\\\\\\/g') - cp -f {template} {output} - # MacOS requires a backup file. - sed -i.bak \"s|{placeholder}|$DEFAULTS_ESCAPED|g\" {output} - """.format( - escape = ctx.executable._escape.path, - args = args, - defaults = ctx.file.defaults.path, - template = ctx.file.template.path, - output = ctx.outputs.output.path, - placeholder = ctx.attr.placeholder, - ), ) embed_edition_defaults = rule( diff --git a/editions/internal_defaults_escape.cc b/editions/internal_defaults_escape.cc index 0919ceabd7..ec46a86491 100644 --- a/editions/internal_defaults_escape.cc +++ b/editions/internal_defaults_escape.cc @@ -1,4 +1,7 @@ +#include +#include #include +#include #include #ifdef _WIN32 @@ -16,34 +19,125 @@ #if defined(_WIN32) #include "google/protobuf/io/io_win32.h" -// DO NOT include , instead create functions in io_win32.{h,cc} and import -// them like we do below. +// DO NOT include , instead create functions in io_win32.{h,cc} and +// import them like we do below. using google::protobuf::io::win32::setmode; #endif ABSL_FLAG(std::string, encoding, "octal", "The encoding to use for the output."); +ABSL_FLAG(std::string, defaults_path, "defaults_path", + "The path to the compile_edition_defaults file to embed."); +ABSL_FLAG(std::string, template_path, "template_path", + "The template to use for generating the output file."); +ABSL_FLAG(std::string, output_path, "output_path", + "The path to the the output file."); +ABSL_FLAG( + std::string, placeholder, "placeholder", + "The placeholder to replace with a serialized string in the template."); + +int defaults_escape(const std::string& defaults_path, + const std::string& encoding, std::string& out_content) { + std::ifstream defaults_file(defaults_path); + if (!defaults_file.is_open()) { + std::cerr << "Could not open defaults file " << defaults_path << std::endl; + return 1; + } -int main(int argc, char *argv[]) { - absl::ParseCommandLine(argc, argv); -#ifdef _WIN32 - setmode(STDIN_FILENO, _O_BINARY); - setmode(STDOUT_FILENO, _O_BINARY); -#endif google::protobuf::FeatureSetDefaults defaults; - if (!defaults.ParseFromFileDescriptor(STDIN_FILENO)) { - std::cerr << argv[0] << ": unable to parse edition defaults." << std::endl; + if (!defaults.ParseFromIstream(&defaults_file)) { + std::cerr << "Unable to parse edition defaults " << defaults_path + << std::endl; + defaults_file.close(); return 1; } - std::string output; - defaults.SerializeToString(&output); - std::string encoding = absl::GetFlag(FLAGS_encoding); + + defaults_file.close(); + + std::string content = {}; + defaults.SerializeToString(&content); if (encoding == "base64") { - std::cout << absl::Base64Escape(output); + content = absl::Base64Escape(content); } else if (encoding == "octal") { - std::cout << absl::CEscape(output); + content = absl::CEscape(content); } else { ABSL_LOG(FATAL) << "Unknown encoding: " << encoding; + return 1; } + + out_content = content; + return 0; +} + +int read_to_string(const std::string& path, std::string& out_content) { + std::ifstream input_file(path); + if (!input_file.is_open()) { + std::cerr << "Could not open file " << path << std::endl; + return 1; + } + + std::ostringstream buffer; + buffer << input_file.rdbuf(); + out_content = buffer.str(); + input_file.close(); + + return 0; +} + +int replace_placeholder(std::string& out_content, + const std::string& placeholder, + const std::string& replacement) { + size_t pos = 0; + + while ((pos = out_content.find(placeholder, pos)) != std::string::npos) { + out_content.replace(pos, placeholder.length(), replacement); + pos += replacement.length(); + } + + return 0; +} + +int write(const std::string& path, const std::string& content) { + std::ofstream output_file(path); + if (!output_file.is_open()) { + std::cerr << "Could not write to file " << path << std::endl; + return 1; + } + + output_file << content; + output_file.close(); + + return 0; +} + +int main(int argc, char* argv[]) { + absl::ParseCommandLine(argc, argv); +#ifdef _WIN32 + setmode(STDOUT_FILENO, _O_BINARY); +#endif + std::string encoding = absl::GetFlag(FLAGS_encoding); + std::string defaults_path = absl::GetFlag(FLAGS_defaults_path); + std::string template_path = absl::GetFlag(FLAGS_template_path); + std::string output_path = absl::GetFlag(FLAGS_output_path); + std::string placeholder = absl::GetFlag(FLAGS_placeholder); + + std::string replacement = {}; + if (defaults_escape(defaults_path, encoding, replacement)) { + return 1; + } + + std::string content = {}; + if (read_to_string(template_path, content)) { + return 1; + } + + if (replace_placeholder(content, placeholder, replacement)) { + return 1; + } + + if (write(output_path, content)) { + return 1; + } + return 0; }