blob: aad443920690108371f6e52d01d63b77a162194f [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include "tools/cddl/codegen.h"
#include "tools/cddl/logging.h"
#include "tools/cddl/parse.h"
#include "tools/cddl/sema.h"
std::string ReadEntireFile(const std::string& filename) {
std::ifstream input(filename);
if (!input) {
return {};
}
input.seekg(0, std::ios_base::end);
size_t length = input.tellg();
std::string input_data(length + 1, 0);
input.seekg(0, std::ios_base::beg);
input.read(const_cast<char*>(input_data.data()), length);
input_data[length] = 0;
return input_data;
}
struct CommandLineArguments {
std::string header_filename;
std::string cc_filename;
std::string gen_dir;
std::string cddl_filename;
};
CommandLineArguments ParseCommandLineArguments(int argc, char** argv) {
--argc;
++argv;
CommandLineArguments result;
while (argc) {
if (strcmp(*argv, "--header") == 0) {
// Parse the filename of the output header file. This is also the name
// that will be used for the include guard and as the include path in the
// source file.
if (!result.header_filename.empty()) {
return {};
}
if (!argc) {
return {};
}
--argc;
++argv;
result.header_filename = *argv;
} else if (strcmp(*argv, "--cc") == 0) {
// Parse the filename of the output source file.
if (!result.cc_filename.empty()) {
return {};
}
if (!argc) {
return {};
}
--argc;
++argv;
result.cc_filename = *argv;
} else if (strcmp(*argv, "--gen-dir") == 0) {
// Parse the directory prefix that should be added to the output header.
// and source file
if (!result.gen_dir.empty()) {
return {};
}
if (!argc) {
return {};
}
--argc;
++argv;
result.gen_dir = *argv;
} else if (!result.cddl_filename.empty()) {
return {};
} else {
// The input file which contains the CDDL spec.
result.cddl_filename = *argv;
}
--argc;
++argv;
}
// If one of the required properties is missed, return empty. Else, return
// generated struct.
if (result.header_filename.empty() || result.cc_filename.empty() ||
result.gen_dir.empty() || result.cddl_filename.empty()) {
return {};
}
return result;
}
int main(int argc, char** argv) {
// Parse and validate all cmdline arguments.
CommandLineArguments args = ParseCommandLineArguments(argc, argv);
if (args.cddl_filename.empty()) {
std::cerr << "Usage: " << std::endl
<< "cddl --header parsed.h --cc parsed.cc --gen-dir "
"output/generated input.cddl"
<< std::endl
<< "All flags are required." << std::endl
<< "Example: " << std::endl
<< "./cddl --header osp_messages.h --cc osp_messages.cc "
"--gen-dir gen/msgs ../../msgs/osp_messages.cddl"
<< std::endl;
return 1;
}
size_t pos = args.cddl_filename.find_last_of('.');
if (pos == std::string::npos) {
return 1;
}
// Validate and open the provided header file.
std::string header_filename = args.gen_dir + "/" + args.header_filename;
int header_fd = open(header_filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
S_IRUSR | S_IWUSR | S_IRGRP);
if (header_fd == -1) {
std::cerr << "failed to open " << args.header_filename << std::endl;
return 1;
}
// Validate and open the provided output source file.
std::string cc_filename = args.gen_dir + "/" + args.cc_filename;
int cc_fd = open(cc_filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
S_IRUSR | S_IWUSR | S_IRGRP);
if (cc_fd == -1) {
std::cerr << "failed to open " << args.cc_filename << std::endl;
return 1;
}
// Read and parse the CDDL spec file.
std::string data = ReadEntireFile(args.cddl_filename);
if (data.empty()) {
return 1;
}
Logger::Log("Successfully initialized CDDL Code generator!");
// Parse the full CDDL into a graph structure.
Logger::Log("Parsing CDDL input file...");
ParseResult parse_result = ParseCddl(data);
if (!parse_result.root) {
Logger::Error("Failed to parse CDDL input file");
return 1;
}
Logger::Log("Successfully parsed CDDL input file!");
// Build the Symbol table from this graph structure.
Logger::Log("Generating CDDL Symbol Table...");
std::pair<bool, CddlSymbolTable> cddl_result =
BuildSymbolTable(*parse_result.root);
if (!cddl_result.first) {
Logger::Error("Failed to generate CDDL symbol table");
return 1;
}
Logger::Log("Successfully generated CDDL symbol table!");
Logger::Log("Generating CPP symbol table...");
std::pair<bool, CppSymbolTable> cpp_result =
BuildCppTypes(cddl_result.second);
if (!cpp_result.first) {
Logger::Error("Failed to generate CPP symbol table");
return 1;
}
Logger::Log("Successfully generated CPP symbol table!");
// Validate that the provided CDDL doesnt have duplicated indices.
if (!ValidateCppTypes(cpp_result.second)) {
return 1;
}
// Create the C++ files from the Symbol table.
Logger::Log("Writing Header prologue...");
if (!WriteHeaderPrologue(header_fd, args.header_filename)) {
Logger::Error("WriteHeaderPrologue failed");
return 1;
}
Logger::Log("Successfully wrote header prologue!");
Logger::Log("Writing type definitions...");
if (!WriteTypeDefinitions(header_fd, &cpp_result.second)) {
Logger::Error("WriteTypeDefinitions failed");
return 1;
}
Logger::Log("Successfully wrote type definitions!");
Logger::Log("Writing function declaration...");
if (!WriteFunctionDeclarations(header_fd, &cpp_result.second)) {
Logger::Error("WriteFunctionDeclarations failed");
return 1;
}
Logger::Log("Successfully wrote function declarations!");
Logger::Log("Writing header epilogue...");
if (!WriteHeaderEpilogue(header_fd, args.header_filename)) {
Logger::Error("WriteHeaderEpilogue failed");
return 1;
}
Logger::Log("Successfully wrote header epilogue!");
Logger::Log("Writing source prologue...");
if (!WriteSourcePrologue(cc_fd, args.header_filename)) {
Logger::Error("WriteSourcePrologue failed");
return 1;
}
Logger::Log("Successfully wrote source prologue!");
Logger::Log("Writing encoders...");
if (!WriteEncoders(cc_fd, &cpp_result.second)) {
Logger::Error("WriteEncoders failed");
return 1;
}
Logger::Log("Successfully wrote encoders!");
Logger::Log("Writing decoders...");
if (!WriteDecoders(cc_fd, &cpp_result.second)) {
Logger::Error("WriteDecoders failed");
return 1;
}
Logger::Log("Successfully wrote decoders!");
Logger::Log("Writing equality operators...");
if (!WriteEqualityOperators(cc_fd, &cpp_result.second)) {
Logger::Error("WriteStructEqualityOperators failed");
return 1;
}
Logger::Log("Successfully wrote equality operators!");
Logger::Log("Writing source epilogue...");
if (!WriteSourceEpilogue(cc_fd)) {
Logger::Error("WriteSourceEpilogue failed");
return 1;
}
Logger::Log("Successfully wrote source epilogue!");
close(header_fd);
close(cc_fd);
Logger::Log("SUCCESSFULLY COMPLETED ALL OPERATIONS");
return 0;
}