• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <unistd.h>
18 
19 #include <cerrno>
20 #include <cstdio>
21 #include <cstring>
22 #include <filesystem>
23 #include <fstream>
24 #include <iomanip>
25 #include <iostream>
26 #include <queue>
27 #include <regex>
28 #include <sstream>
29 #include <vector>
30 
31 #include "declarations.h"
32 #include "language_y.h"
33 #include "struct_parser_generator.h"
34 
35 int yylex_init(void**);
36 int yylex_destroy(void*);
37 void yyset_debug(int, void*);
38 void yyset_in(FILE*, void*);
39 
40 bool generate_cpp_headers_one_file(
41     const Declarations& decls,
42     bool generate_fuzzing,
43     bool generate_tests,
44     const std::filesystem::path& input_file,
45     const std::filesystem::path& include_dir,
46     const std::filesystem::path& out_dir,
47     const std::string& root_namespace);
48 
parse_declarations_one_file(const std::filesystem::path & input_file,Declarations * declarations)49 bool parse_declarations_one_file(const std::filesystem::path& input_file, Declarations* declarations) {
50   void* scanner;
51   yylex_init(&scanner);
52 
53   FILE* in_file = fopen(input_file.string().c_str(), "r");
54   if (in_file == nullptr) {
55     std::cerr << "can't open " << input_file << ": " << strerror(errno) << std::endl;
56     return false;
57   }
58 
59   yyset_in(in_file, scanner);
60 
61   int ret = yy::parser(scanner, declarations).parse();
62   if (ret != 0) {
63     std::cerr << "yylex parsing failed: returned " << ret << std::endl;
64     return false;
65   }
66 
67   yylex_destroy(scanner);
68 
69   fclose(in_file);
70 
71   // Set endianess before returning
72   for (auto& s : declarations->type_defs_queue_) {
73     if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
74       auto* struct_def = static_cast<StructDef*>(s.second);
75       struct_def->SetEndianness(declarations->is_little_endian);
76     }
77   }
78 
79   for (auto& packet_def : declarations->packet_defs_queue_) {
80     packet_def.second->SetEndianness(declarations->is_little_endian);
81     if (packet_def.second->parent_ != nullptr) {
82       packet_def.second->parent_->children_.push_back(packet_def.second);
83     }
84   }
85 
86   return true;
87 }
88 
89 // TODO(b/141583809): stop leaks
__asan_default_options()90 extern "C" const char* __asan_default_options() {
91   return "detect_leaks=0";
92 }
93 
usage(const char * prog)94 void usage(const char* prog) {
95   auto& ofs = std::cerr;
96 
97   ofs << "Usage: " << prog << " [OPTIONS] file1 file2..." << std::endl;
98 
99   ofs << std::setw(24) << "--out= ";
100   ofs << "Root directory for generated output (relative to cwd)." << std::endl;
101 
102   ofs << std::setw(24) << "--include= ";
103   ofs << "Generate namespaces relative to this path per file." << std::endl;
104 
105   ofs << std::setw(24) << "--root_namespace= ";
106   ofs << "Change root namespace (default = bluetooth)." << std::endl;
107 
108   ofs << std::setw(24) << "--source_root= ";
109   ofs << "Root path to the source directory. Find input files relative to this." << std::endl;
110 }
111 
main(int argc,const char ** argv)112 int main(int argc, const char** argv) {
113   std::filesystem::path out_dir;
114   std::filesystem::path include_dir;
115   std::filesystem::path cwd = std::filesystem::current_path();
116   std::filesystem::path source_root = cwd;
117   std::string root_namespace = "bluetooth";
118   bool generate_fuzzing = false;
119   bool generate_tests = false;
120   std::queue<std::filesystem::path> input_files;
121 
122   const std::string arg_out = "--out=";
123   const std::string arg_include = "--include=";
124   const std::string arg_namespace = "--root_namespace=";
125   const std::string arg_fuzzing = "--fuzzing";
126   const std::string arg_testing = "--testing";
127   const std::string arg_source_root = "--source_root=";
128 
129   // Parse the source root first (if it exists) since it will be used for other
130   // paths.
131   for (int i = 1; i < argc; i++) {
132     std::string arg = argv[i];
133     if (arg.find(arg_source_root) == 0) {
134       source_root = std::filesystem::path(arg.substr(arg_source_root.size()));
135     }
136   }
137 
138   for (int i = 1; i < argc; i++) {
139     std::string arg = argv[i];
140     if (arg.find(arg_out) == 0) {
141       out_dir = cwd / std::filesystem::path(arg.substr(arg_out.size()));
142     } else if (arg.find(arg_include) == 0) {
143       include_dir = source_root / std::filesystem::path(arg.substr(arg_include.size()));
144     } else if (arg.find(arg_namespace) == 0) {
145       root_namespace = arg.substr(arg_namespace.size());
146     } else if (arg.find(arg_fuzzing) == 0) {
147       generate_fuzzing = true;
148     } else if (arg.find(arg_testing) == 0) {
149       generate_tests = true;
150     } else if (arg.find(arg_source_root) == 0) {
151       // Do nothing (just don't treat it as input_files)
152     } else {
153       input_files.emplace(source_root / std::filesystem::path(arg));
154     }
155   }
156   if (out_dir == std::filesystem::path() || include_dir == std::filesystem::path()) {
157     usage(argv[0]);
158     return 1;
159   }
160 
161   std::cout << "out dir: " << out_dir << std::endl;
162 
163   while (!input_files.empty()) {
164     Declarations declarations;
165     std::cout << "parsing: " << input_files.front() << std::endl;
166     if (!parse_declarations_one_file(input_files.front(), &declarations)) {
167       std::cerr << "Cannot parse " << input_files.front() << " correctly" << std::endl;
168       return 2;
169     }
170     std::cout << "generating c++" << std::endl;
171     if (!generate_cpp_headers_one_file(
172             declarations,
173             generate_fuzzing,
174             generate_tests,
175             input_files.front(),
176             include_dir,
177             out_dir,
178             root_namespace)) {
179       std::cerr << "Didn't generate cpp headers for " << input_files.front() << std::endl;
180       return 3;
181     }
182     input_files.pop();
183   }
184 
185   return 0;
186 }
187