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