xref: /aosp_15_r20/external/openscreen/tools/cddl/main.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
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