/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "declarations.h" #include "language_y.h" #include "struct_parser_generator.h" int yylex_init(void**); int yylex_destroy(void*); void yyset_debug(int, void*); void yyset_in(FILE*, void*); bool generate_cpp_headers_one_file( const Declarations& decls, bool generate_fuzzing, bool generate_tests, const std::filesystem::path& input_file, const std::filesystem::path& include_dir, const std::filesystem::path& out_dir, const std::string& root_namespace); bool parse_declarations_one_file(const std::filesystem::path& input_file, Declarations* declarations) { void* scanner; yylex_init(&scanner); FILE* in_file = fopen(input_file.string().c_str(), "r"); if (in_file == nullptr) { std::cerr << "can't open " << input_file << ": " << strerror(errno) << std::endl; return false; } yyset_in(in_file, scanner); int ret = yy::parser(scanner, declarations).parse(); if (ret != 0) { std::cerr << "yylex parsing failed: returned " << ret << std::endl; return false; } yylex_destroy(scanner); fclose(in_file); // Set endianess before returning for (auto& s : declarations->type_defs_queue_) { if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) { auto* struct_def = static_cast(s.second); struct_def->SetEndianness(declarations->is_little_endian); } } for (auto& packet_def : declarations->packet_defs_queue_) { packet_def.second->SetEndianness(declarations->is_little_endian); if (packet_def.second->parent_ != nullptr) { packet_def.second->parent_->children_.push_back(packet_def.second); } } return true; } // TODO(b/141583809): stop leaks extern "C" const char* __asan_default_options() { return "detect_leaks=0"; } void usage(const char* prog) { auto& ofs = std::cerr; ofs << "Usage: " << prog << " [OPTIONS] file1 file2..." << std::endl; ofs << std::setw(24) << "--out= "; ofs << "Root directory for generated output (relative to cwd)." << std::endl; ofs << std::setw(24) << "--include= "; ofs << "Generate namespaces relative to this path per file." << std::endl; ofs << std::setw(24) << "--root_namespace= "; ofs << "Change root namespace (default = bluetooth)." << std::endl; ofs << std::setw(24) << "--source_root= "; ofs << "Root path to the source directory. Find input files relative to this." << std::endl; } int main(int argc, const char** argv) { std::filesystem::path out_dir; std::filesystem::path include_dir; std::filesystem::path cwd = std::filesystem::current_path(); std::filesystem::path source_root = cwd; std::string root_namespace = "bluetooth"; bool generate_fuzzing = false; bool generate_tests = false; std::queue input_files; const std::string arg_out = "--out="; const std::string arg_include = "--include="; const std::string arg_namespace = "--root_namespace="; const std::string arg_fuzzing = "--fuzzing"; const std::string arg_testing = "--testing"; const std::string arg_source_root = "--source_root="; // Parse the source root first (if it exists) since it will be used for other // paths. for (int i = 1; i < argc; i++) { std::string arg = argv[i]; if (arg.find(arg_source_root) == 0) { source_root = std::filesystem::path(arg.substr(arg_source_root.size())); } } for (int i = 1; i < argc; i++) { std::string arg = argv[i]; if (arg.find(arg_out) == 0) { out_dir = cwd / std::filesystem::path(arg.substr(arg_out.size())); } else if (arg.find(arg_include) == 0) { include_dir = source_root / std::filesystem::path(arg.substr(arg_include.size())); } else if (arg.find(arg_namespace) == 0) { root_namespace = arg.substr(arg_namespace.size()); } else if (arg.find(arg_fuzzing) == 0) { generate_fuzzing = true; } else if (arg.find(arg_testing) == 0) { generate_tests = true; } else if (arg.find(arg_source_root) == 0) { // Do nothing (just don't treat it as input_files) } else { input_files.emplace(source_root / std::filesystem::path(arg)); } } if (out_dir == std::filesystem::path() || include_dir == std::filesystem::path()) { usage(argv[0]); return 1; } std::cout << "out dir: " << out_dir << std::endl; while (!input_files.empty()) { Declarations declarations; std::cout << "parsing: " << input_files.front() << std::endl; if (!parse_declarations_one_file(input_files.front(), &declarations)) { std::cerr << "Cannot parse " << input_files.front() << " correctly" << std::endl; return 2; } std::cout << "generating c++" << std::endl; if (!generate_cpp_headers_one_file( declarations, generate_fuzzing, generate_tests, input_files.front(), include_dir, out_dir, root_namespace)) { std::cerr << "Didn't generate cpp headers for " << input_files.front() << std::endl; return 3; } input_files.pop(); } return 0; }