1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/compiler/kotlin/generator.h"
9
10 #include <cstdint>
11 #include <memory>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "absl/strings/str_cat.h"
17 #include "google/protobuf/compiler/code_generator.h"
18 #include "google/protobuf/compiler/java/helpers.h"
19 #include "google/protobuf/compiler/java/options.h"
20 #include "google/protobuf/compiler/kotlin/file.h"
21 #include "google/protobuf/io/printer.h"
22
23 namespace google {
24 namespace protobuf {
25 namespace compiler {
26 namespace kotlin {
27
28 using google::protobuf::compiler::java::Options;
29
KotlinGenerator()30 KotlinGenerator::KotlinGenerator() {}
~KotlinGenerator()31 KotlinGenerator::~KotlinGenerator() {}
32
GetSupportedFeatures() const33 uint64_t KotlinGenerator::GetSupportedFeatures() const {
34 return CodeGenerator::Feature::FEATURE_PROTO3_OPTIONAL |
35 CodeGenerator::Feature::FEATURE_SUPPORTS_EDITIONS;
36 }
37
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * context,std::string * error) const38 bool KotlinGenerator::Generate(const FileDescriptor* file,
39 const std::string& parameter,
40 GeneratorContext* context,
41 std::string* error) const {
42 // -----------------------------------------------------------------
43 // parse generator options
44
45 std::vector<std::pair<std::string, std::string> > options;
46 ParseGeneratorParameter(parameter, &options);
47 Options file_options;
48
49 for (auto& option : options) {
50 if (option.first == "output_list_file") {
51 file_options.output_list_file = option.second;
52 } else if (option.first == "immutable") {
53 // Note: the option is considered always set regardless of the input.
54 file_options.generate_immutable_code = true;
55 } else if (option.first == "mutable") {
56 *error = "Mutable not supported by Kotlin generator";
57 return false;
58 } else if (option.first == "shared") {
59 // Note: the option is considered always set regardless of the input.
60 file_options.generate_shared_code = true;
61 } else if (option.first == "lite") {
62 file_options.enforce_lite = true;
63 } else if (option.first == "annotate_code") {
64 file_options.annotate_code = true;
65 } else if (option.first == "annotation_list_file") {
66 file_options.annotation_list_file = option.second;
67 } else if (option.first == "experimental_strip_nonfunctional_codegen") {
68 file_options.strip_nonfunctional_codegen = true;
69 } else if (option.first == "no_jvm_dsl") {
70 file_options.jvm_dsl = false;
71 } else {
72 *error = absl::StrCat("Unknown generator option: ", option.first);
73 return false;
74 }
75 }
76
77 // We only support generation of immutable code so we do it.
78 file_options.generate_immutable_code = true;
79 file_options.generate_shared_code = true;
80
81 std::vector<std::string> all_files;
82 std::vector<std::string> all_annotations;
83
84 std::unique_ptr<FileGenerator> file_generator(
85 new FileGenerator(file, file_options));
86
87 if (!file_generator) return false;
88
89 auto open_file = [context](const std::string& filename) {
90 return std::unique_ptr<io::ZeroCopyOutputStream>(context->Open(filename));
91 };
92 std::string package_dir =
93 java::JavaPackageToDir(file_generator->java_package());
94 std::string kotlin_filename = absl::StrCat(
95 package_dir, file_generator->GetKotlinClassname(), ".proto.kt");
96 all_files.push_back(kotlin_filename);
97 std::string info_full_path = absl::StrCat(kotlin_filename, ".pb.meta");
98 if (file_options.annotate_code) {
99 all_annotations.push_back(info_full_path);
100 }
101
102 // Generate main kotlin file.
103 auto output = open_file(kotlin_filename);
104 GeneratedCodeInfo annotations;
105 io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
106 &annotations);
107 io::Printer printer(
108 output.get(), '$',
109 file_options.annotate_code ? &annotation_collector : nullptr);
110
111 file_generator->Generate(&printer);
112 file_generator->GenerateSiblings(package_dir, context, &all_files,
113 &all_annotations);
114
115 if (file_options.annotate_code) {
116 auto info_output = open_file(info_full_path);
117 annotations.SerializeToZeroCopyStream(info_output.get());
118 }
119
120 // Generate output list if requested.
121 if (!file_options.output_list_file.empty()) {
122 // Generate output list. This is just a simple text file placed in a
123 // deterministic location which lists the .kt files being generated.
124 auto srclist_raw_output = open_file(file_options.output_list_file);
125 io::Printer srclist_printer(srclist_raw_output.get(), '$');
126 for (auto& all_file : all_files) {
127 srclist_printer.Print("$filename$\n", "filename", all_file);
128 }
129 }
130
131 if (!file_options.annotation_list_file.empty()) {
132 // Generate output list. This is just a simple text file placed in a
133 // deterministic location which lists the .kt files being generated.
134 auto annotation_list_raw_output =
135 open_file(file_options.annotation_list_file);
136 io::Printer annotation_list_printer(annotation_list_raw_output.get(), '$');
137 for (auto& all_annotation : all_annotations) {
138 annotation_list_printer.Print("$filename$\n", "filename", all_annotation);
139 }
140 }
141
142 return true;
143 }
144
145 } // namespace kotlin
146 } // namespace compiler
147 } // namespace protobuf
148 } // namespace google
149