1 // Copyright 2018 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.
4
5 #include <fcntl.h>
6 #include <string.h>
7 #include <unistd.h>
8
9 #include <fstream>
10 #include <iostream>
11 #include <string>
12 #include <vector>
13
14 #include "tools/cddl/codegen.h"
15 #include "tools/cddl/logging.h"
16 #include "tools/cddl/parse.h"
17 #include "tools/cddl/sema.h"
18
ReadEntireFile(const std::string & filename)19 std::string ReadEntireFile(const std::string& filename) {
20 std::ifstream input(filename);
21 if (!input) {
22 return {};
23 }
24
25 input.seekg(0, std::ios_base::end);
26 size_t length = input.tellg();
27 std::string input_data(length + 1, 0);
28
29 input.seekg(0, std::ios_base::beg);
30 input.read(const_cast<char*>(input_data.data()), length);
31 input_data[length] = 0;
32
33 return input_data;
34 }
35
36 struct CommandLineArguments {
37 std::string header_filename;
38 std::string cc_filename;
39 std::string gen_dir;
40 std::string cddl_filename;
41 };
42
ParseCommandLineArguments(int argc,char ** argv)43 CommandLineArguments ParseCommandLineArguments(int argc, char** argv) {
44 --argc;
45 ++argv;
46 CommandLineArguments result;
47 while (argc) {
48 if (strcmp(*argv, "--header") == 0) {
49 // Parse the filename of the output header file. This is also the name
50 // that will be used for the include guard and as the include path in the
51 // source file.
52 if (!result.header_filename.empty()) {
53 return {};
54 }
55 if (!argc) {
56 return {};
57 }
58 --argc;
59 ++argv;
60 result.header_filename = *argv;
61 } else if (strcmp(*argv, "--cc") == 0) {
62 // Parse the filename of the output source file.
63 if (!result.cc_filename.empty()) {
64 return {};
65 }
66 if (!argc) {
67 return {};
68 }
69 --argc;
70 ++argv;
71 result.cc_filename = *argv;
72 } else if (strcmp(*argv, "--gen-dir") == 0) {
73 // Parse the directory prefix that should be added to the output header.
74 // and source file
75 if (!result.gen_dir.empty()) {
76 return {};
77 }
78 if (!argc) {
79 return {};
80 }
81 --argc;
82 ++argv;
83 result.gen_dir = *argv;
84 } else if (!result.cddl_filename.empty()) {
85 return {};
86 } else {
87 // The input file which contains the CDDL spec.
88 result.cddl_filename = *argv;
89 }
90 --argc;
91 ++argv;
92 }
93
94 // If one of the required properties is missed, return empty. Else, return
95 // generated struct.
96 if (result.header_filename.empty() || result.cc_filename.empty() ||
97 result.gen_dir.empty() || result.cddl_filename.empty()) {
98 return {};
99 }
100 return result;
101 }
102
main(int argc,char ** argv)103 int main(int argc, char** argv) {
104 // Parse and validate all cmdline arguments.
105 CommandLineArguments args = ParseCommandLineArguments(argc, argv);
106 if (args.cddl_filename.empty()) {
107 std::cerr << "Usage: " << std::endl
108 << "cddl --header parsed.h --cc parsed.cc --gen-dir "
109 "output/generated input.cddl"
110 << std::endl
111 << "All flags are required." << std::endl
112 << "Example: " << std::endl
113 << "./cddl --header osp_messages.h --cc osp_messages.cc "
114 "--gen-dir gen/msgs ../../msgs/osp_messages.cddl"
115 << std::endl;
116 return 1;
117 }
118
119 size_t pos = args.cddl_filename.find_last_of('.');
120 if (pos == std::string::npos) {
121 return 1;
122 }
123
124 // Validate and open the provided header file.
125 std::string header_filename = args.gen_dir + "/" + args.header_filename;
126 int header_fd = open(header_filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
127 S_IRUSR | S_IWUSR | S_IRGRP);
128 if (header_fd == -1) {
129 std::cerr << "failed to open " << args.header_filename << std::endl;
130 return 1;
131 }
132
133 // Validate and open the provided output source file.
134 std::string cc_filename = args.gen_dir + "/" + args.cc_filename;
135 int cc_fd = open(cc_filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
136 S_IRUSR | S_IWUSR | S_IRGRP);
137 if (cc_fd == -1) {
138 std::cerr << "failed to open " << args.cc_filename << std::endl;
139 return 1;
140 }
141
142 // Read and parse the CDDL spec file.
143 std::string data = ReadEntireFile(args.cddl_filename);
144 if (data.empty()) {
145 return 1;
146 }
147
148 Logger::Log("Successfully initialized CDDL Code generator!");
149
150 // Parse the full CDDL into a graph structure.
151 Logger::Log("Parsing CDDL input file...");
152 ParseResult parse_result = ParseCddl(data);
153 if (!parse_result.root) {
154 Logger::Error("Failed to parse CDDL input file");
155 return 1;
156 }
157 Logger::Log("Successfully parsed CDDL input file!");
158
159 // Build the Symbol table from this graph structure.
160 Logger::Log("Generating CDDL Symbol Table...");
161 std::pair<bool, CddlSymbolTable> cddl_result =
162 BuildSymbolTable(*parse_result.root);
163 if (!cddl_result.first) {
164 Logger::Error("Failed to generate CDDL symbol table");
165 return 1;
166 }
167 Logger::Log("Successfully generated CDDL symbol table!");
168
169 Logger::Log("Generating CPP symbol table...");
170 std::pair<bool, CppSymbolTable> cpp_result =
171 BuildCppTypes(cddl_result.second);
172 if (!cpp_result.first) {
173 Logger::Error("Failed to generate CPP symbol table");
174 return 1;
175 }
176 Logger::Log("Successfully generated CPP symbol table!");
177
178 // Validate that the provided CDDL doesnt have duplicated indices.
179 if (!ValidateCppTypes(cpp_result.second)) {
180 return 1;
181 }
182
183 // Create the C++ files from the Symbol table.
184
185 Logger::Log("Writing Header prologue...");
186 if (!WriteHeaderPrologue(header_fd, args.header_filename)) {
187 Logger::Error("WriteHeaderPrologue failed");
188 return 1;
189 }
190 Logger::Log("Successfully wrote header prologue!");
191
192 Logger::Log("Writing type definitions...");
193 if (!WriteTypeDefinitions(header_fd, &cpp_result.second)) {
194 Logger::Error("WriteTypeDefinitions failed");
195 return 1;
196 }
197 Logger::Log("Successfully wrote type definitions!");
198
199 Logger::Log("Writing function declaration...");
200 if (!WriteFunctionDeclarations(header_fd, &cpp_result.second)) {
201 Logger::Error("WriteFunctionDeclarations failed");
202 return 1;
203 }
204 Logger::Log("Successfully wrote function declarations!");
205
206 Logger::Log("Writing header epilogue...");
207 if (!WriteHeaderEpilogue(header_fd, args.header_filename)) {
208 Logger::Error("WriteHeaderEpilogue failed");
209 return 1;
210 }
211 Logger::Log("Successfully wrote header epilogue!");
212
213 Logger::Log("Writing source prologue...");
214 if (!WriteSourcePrologue(cc_fd, args.header_filename)) {
215 Logger::Error("WriteSourcePrologue failed");
216 return 1;
217 }
218 Logger::Log("Successfully wrote source prologue!");
219
220 Logger::Log("Writing encoders...");
221 if (!WriteEncoders(cc_fd, &cpp_result.second)) {
222 Logger::Error("WriteEncoders failed");
223 return 1;
224 }
225 Logger::Log("Successfully wrote encoders!");
226
227 Logger::Log("Writing decoders...");
228 if (!WriteDecoders(cc_fd, &cpp_result.second)) {
229 Logger::Error("WriteDecoders failed");
230 return 1;
231 }
232 Logger::Log("Successfully wrote decoders!");
233
234 Logger::Log("Writing equality operators...");
235 if (!WriteEqualityOperators(cc_fd, &cpp_result.second)) {
236 Logger::Error("WriteStructEqualityOperators failed");
237 return 1;
238 }
239 Logger::Log("Successfully wrote equality operators!");
240
241 Logger::Log("Writing source epilogue...");
242 if (!WriteSourceEpilogue(cc_fd)) {
243 Logger::Error("WriteSourceEpilogue failed");
244 return 1;
245 }
246 Logger::Log("Successfully wrote source epilogue!");
247
248 close(header_fd);
249 close(cc_fd);
250 Logger::Log("SUCCESSFULLY COMPLETED ALL OPERATIONS");
251
252 return 0;
253 }
254