• 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 #include <cerrno>
19 #include <cstdio>
20 #include <filesystem>
21 #include <fstream>
22 #include <iomanip>
23 #include <iostream>
24 #include <queue>
25 #include <regex>
26 #include <sstream>
27 #include <vector>
28 
29 #include "declarations.h"
30 #include "struct_parser_generator.h"
31 
32 #include "language_y.h"
33 
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     const std::filesystem::path& input_file,
43     const std::filesystem::path& include_dir,
44     const std::filesystem::path& out_dir,
45     const std::string& root_namespace);
46 
47 bool generate_pybind11_sources_one_file(
48     const Declarations& decls,
49     const std::filesystem::path& input_file,
50     const std::filesystem::path& include_dir,
51     const std::filesystem::path& out_dir,
52     const std::string& root_namespace,
53     size_t num_shards);
54 
55 bool generate_rust_source_one_file(
56     const Declarations& decls,
57     const std::filesystem::path& input_file,
58     const std::filesystem::path& include_dir,
59     const std::filesystem::path& out_dir,
60     const std::string& root_namespace);
61 
parse_declarations_one_file(const std::filesystem::path & input_file,Declarations * declarations)62 bool parse_declarations_one_file(const std::filesystem::path& input_file, Declarations* declarations) {
63   void* scanner;
64   yylex_init(&scanner);
65 
66   FILE* in_file = fopen(input_file.string().c_str(), "r");
67   if (in_file == nullptr) {
68     std::cerr << "can't open " << input_file << ": " << strerror(errno) << std::endl;
69     return false;
70   }
71 
72   yyset_in(in_file, scanner);
73 
74   int ret = yy::parser(scanner, declarations).parse();
75   if (ret != 0) {
76     std::cerr << "yylex parsing failed: returned " << ret << std::endl;
77     return false;
78   }
79 
80   yylex_destroy(scanner);
81 
82   fclose(in_file);
83 
84   // Set endianess before returning
85   for (auto& s : declarations->type_defs_queue_) {
86     if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
87       auto* struct_def = static_cast<StructDef*>(s.second);
88       struct_def->SetEndianness(declarations->is_little_endian);
89     }
90   }
91 
92   for (auto& packet_def : declarations->packet_defs_queue_) {
93     packet_def.second->SetEndianness(declarations->is_little_endian);
94     if (packet_def.second->parent_ != nullptr) {
95       packet_def.second->parent_->children_.push_back(packet_def.second);
96     }
97   }
98 
99   return true;
100 }
101 
102 // TODO(b/141583809): stop leaks
__asan_default_options()103 extern "C" const char* __asan_default_options() {
104   return "detect_leaks=0";
105 }
106 
usage(const char * prog)107 void usage(const char* prog) {
108   auto& ofs = std::cerr;
109 
110   ofs << "Usage: " << prog << " [OPTIONS] file1 file2..." << std::endl;
111 
112   ofs << std::setw(24) << "--out= ";
113   ofs << "Root directory for generated output (relative to cwd)." << std::endl;
114 
115   ofs << std::setw(24) << "--include= ";
116   ofs << "Generate namespaces relative to this path per file." << std::endl;
117 
118   ofs << std::setw(24) << "--root_namespace= ";
119   ofs << "Change root namespace (default = bluetooth)." << std::endl;
120 
121   ofs << std::setw(24) << "--source_root= ";
122   ofs << "Root path to the source directory. Find input files relative to this." << std::endl;
123 
124   ofs << std::setw(24) << "--num_shards= ";
125   ofs << "Number of shards per output pybind11 cc file." << std::endl;
126 }
127 
main(int argc,const char ** argv)128 int main(int argc, const char** argv) {
129   std::filesystem::path out_dir;
130   std::filesystem::path include_dir;
131   std::filesystem::path cwd = std::filesystem::current_path();
132   std::filesystem::path source_root = cwd;
133   std::string root_namespace = "bluetooth";
134   // Number of shards per output pybind11 cc file
135   size_t num_shards = 1;
136   bool generate_rust = false;
137   std::queue<std::filesystem::path> input_files;
138 
139   const std::string arg_out = "--out=";
140   const std::string arg_include = "--include=";
141   const std::string arg_namespace = "--root_namespace=";
142   const std::string arg_num_shards = "--num_shards=";
143   const std::string arg_rust = "--rust";
144   const std::string arg_source_root = "--source_root=";
145 
146   // Parse the source root first (if it exists) since it will be used for other
147   // paths.
148   for (int i = 1; i < argc; i++) {
149     std::string arg = argv[i];
150     if (arg.find(arg_source_root) == 0) {
151       source_root = std::filesystem::path(arg.substr(arg_source_root.size()));
152     }
153   }
154 
155   for (int i = 1; i < argc; i++) {
156     std::string arg = argv[i];
157     if (arg.find(arg_out) == 0) {
158       out_dir = cwd / std::filesystem::path(arg.substr(arg_out.size()));
159     } else if (arg.find(arg_include) == 0) {
160       include_dir = source_root / std::filesystem::path(arg.substr(arg_include.size()));
161     } else if (arg.find(arg_namespace) == 0) {
162       root_namespace = arg.substr(arg_namespace.size());
163     } else if (arg.find(arg_num_shards) == 0) {
164       num_shards = std::stoul(arg.substr(arg_num_shards.size()));
165     } else if (arg.find(arg_rust) == 0) {
166       generate_rust = true;
167     } else if (arg.find(arg_source_root) == 0) {
168       // Do nothing (just don't treat it as input_files)
169     } else {
170       input_files.emplace(source_root / std::filesystem::path(arg));
171     }
172   }
173   if (out_dir == std::filesystem::path() || include_dir == std::filesystem::path() || num_shards == 0) {
174     usage(argv[0]);
175     return 1;
176   }
177 
178   std::cout << "out dir: " << out_dir << std::endl;
179 
180   while (!input_files.empty()) {
181     Declarations declarations;
182     std::cout << "parsing: " << input_files.front() << std::endl;
183     if (!parse_declarations_one_file(input_files.front(), &declarations)) {
184       std::cerr << "Cannot parse " << input_files.front() << " correctly" << std::endl;
185       return 2;
186     }
187     if (generate_rust) {
188       std::cout << "generating rust" << std::endl;
189       if (!generate_rust_source_one_file(declarations, input_files.front(), include_dir, out_dir, root_namespace)) {
190         std::cerr << "Didn't generate rust source for " << input_files.front() << std::endl;
191         return 5;
192       }
193     } else {
194       std::cout << "generating c++ and pybind11" << std::endl;
195       if (!generate_cpp_headers_one_file(declarations, input_files.front(), include_dir, out_dir, root_namespace)) {
196         std::cerr << "Didn't generate cpp headers for " << input_files.front() << std::endl;
197         return 3;
198       }
199       if (!generate_pybind11_sources_one_file(
200               declarations, input_files.front(), include_dir, out_dir, root_namespace, num_shards)) {
201         std::cerr << "Didn't generate pybind11 sources for " << input_files.front() << std::endl;
202         return 4;
203       }
204     }
205     input_files.pop();
206   }
207 
208   return 0;
209 }
210