| @@ -0,0 +1,9 @@ | |||
| #include <math.h> | |||
| #include <stdio.h> | |||
| //#bb ldflags := -lm | |||
| //#bb pkgs := libsdl cairo | |||
| int main() { | |||
| printf("sin(PI) is %f\n", sin(M_PI)); | |||
| } | |||
| @@ -130,20 +130,20 @@ void BBBParser::parseExpansion(const Variables &vars, std::vector<std::string> & | |||
| std::string str; | |||
| switch (peek()) { | |||
| case '{': | |||
| skip(); | |||
| parseString(vars, str, '}'); | |||
| skip('}'); | |||
| appendVariableToArray(vars, str, values); | |||
| break; | |||
| default: | |||
| if (!parseIdentifier(str)) { | |||
| error("No identifier after $."); | |||
| } | |||
| case '{': | |||
| skip(); | |||
| parseString(vars, str, '}'); | |||
| skip('}'); | |||
| appendVariableToArray(vars, str, values); | |||
| break; | |||
| default: | |||
| if (!parseIdentifier(str)) { | |||
| error("No identifier after $."); | |||
| } | |||
| appendVariableToArray(vars, str, values); | |||
| break; | |||
| appendVariableToArray(vars, str, values); | |||
| break; | |||
| } | |||
| } | |||
| @@ -152,20 +152,20 @@ void BBBParser::parseQuotedExpansion(const Variables &vars, std::string &content | |||
| std::string str; | |||
| switch (peek()) { | |||
| case '{': | |||
| skip(); | |||
| parseString(vars, str, '}'); | |||
| skip('}'); | |||
| appendVariableToString(vars, str, content); | |||
| break; | |||
| default: | |||
| if (!parseIdentifier(str)) { | |||
| error("No identifier after $."); | |||
| } | |||
| case '{': | |||
| skip(); | |||
| parseString(vars, str, '}'); | |||
| skip('}'); | |||
| appendVariableToString(vars, str, content); | |||
| break; | |||
| appendVariableToString(vars, str, content); | |||
| break; | |||
| default: | |||
| if (!parseIdentifier(str)) { | |||
| error("No identifier after $."); | |||
| } | |||
| appendVariableToString(vars, str, content); | |||
| break; | |||
| } | |||
| } | |||
| @@ -175,24 +175,24 @@ void BBBParser::parseQuotedString(const Variables &vars, std::string &content) { | |||
| int ch; | |||
| while ((ch = peek()) != EOF) { | |||
| switch (ch) { | |||
| case EOF: | |||
| error("Unexpected EOF"); | |||
| case EOF: | |||
| error("Unexpected EOF"); | |||
| case '\\': | |||
| content.push_back(parseEscape()); | |||
| break; | |||
| case '\\': | |||
| content.push_back(parseEscape()); | |||
| break; | |||
| case '$': | |||
| parseQuotedExpansion(vars, content); | |||
| break; | |||
| case '$': | |||
| parseQuotedExpansion(vars, content); | |||
| break; | |||
| case '"': | |||
| skip(); | |||
| return; | |||
| case '"': | |||
| skip(); | |||
| return; | |||
| default: | |||
| content.push_back(get()); | |||
| break; | |||
| default: | |||
| content.push_back(get()); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| @@ -207,31 +207,31 @@ bool BBBParser::parseString(const Variables &vars, std::string &content, int sep | |||
| } | |||
| switch (ch) { | |||
| case EOF: | |||
| return success; | |||
| case EOF: | |||
| return success; | |||
| case '\\': | |||
| content.push_back(parseEscape()); | |||
| success = true; | |||
| break; | |||
| case '\\': | |||
| content.push_back(parseEscape()); | |||
| success = true; | |||
| break; | |||
| case '$': | |||
| parseQuotedExpansion(vars, content); | |||
| success = true; | |||
| break; | |||
| case '$': | |||
| parseQuotedExpansion(vars, content); | |||
| success = true; | |||
| break; | |||
| case '"': | |||
| parseQuotedString(vars, content); | |||
| success = true; | |||
| break; | |||
| case '"': | |||
| parseQuotedString(vars, content); | |||
| success = true; | |||
| break; | |||
| default: | |||
| if (ch == ':' && peek2() == '=') | |||
| return success; | |||
| default: | |||
| if (ch == ':' && peek2() == '=') | |||
| return success; | |||
| content.push_back(get()); | |||
| success = true; | |||
| break; | |||
| content.push_back(get()); | |||
| success = true; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| @@ -239,9 +239,9 @@ bool BBBParser::parseString(const Variables &vars, std::string &content, int sep | |||
| bool BBBParser::parseIdentifier(std::string &content) { | |||
| int ch = peek(); | |||
| if (!( | |||
| (ch >= 'a' && ch <= 'z') || | |||
| (ch >= 'A' && ch <= 'Z') || | |||
| (ch == '_'))) { | |||
| (ch >= 'a' && ch <= 'z') || | |||
| (ch >= 'A' && ch <= 'Z') || | |||
| (ch == '_'))) { | |||
| return false; | |||
| } | |||
| @@ -250,10 +250,10 @@ bool BBBParser::parseIdentifier(std::string &content) { | |||
| while (1) { | |||
| ch = peek(); | |||
| if (!( | |||
| (ch >= '0' && ch <= '9') || | |||
| (ch >= 'a' && ch <= 'z') || | |||
| (ch >= 'A' && ch <= 'Z') || | |||
| (ch == '_'))) { | |||
| (ch >= '0' && ch <= '9') || | |||
| (ch >= 'a' && ch <= 'z') || | |||
| (ch >= 'A' && ch <= 'Z') || | |||
| (ch == '_'))) { | |||
| return true; | |||
| } | |||
| @@ -270,6 +270,7 @@ void BBBParser::parse(Variables &vars) { | |||
| return; | |||
| } | |||
| skipWhitespace(); | |||
| Operator prevOper = readOperator(); | |||
| if (prevOper == Operator::NONE) { | |||
| error("Expected operator."); | |||
| @@ -33,9 +33,8 @@ public: | |||
| void skip(char); | |||
| void skip() { get(); } | |||
| int flags_; | |||
| int line_; | |||
| int ch_; | |||
| int line() const { return line_; } | |||
| int ch() const { return ch_; } | |||
| private: | |||
| enum class Operator { | |||
| @@ -57,5 +56,9 @@ private: | |||
| bool parseString(const Variables &vars, std::string &content, int sep = -1); | |||
| bool parseIdentifier(std::string &content); | |||
| int flags_; | |||
| int line_; | |||
| int ch_; | |||
| std::istream &stream_; | |||
| }; | |||
| @@ -6,8 +6,9 @@ | |||
| static bool startsWith(BBBParser &parser, const char *str) { | |||
| for (size_t i = 0; str[i] != '\0'; ++i) { | |||
| if (parser.get() != str[i]) | |||
| if (parser.peek() != str[i]) | |||
| return false; | |||
| parser.skip(); | |||
| } | |||
| return true; | |||
| @@ -19,13 +20,11 @@ SourceFile::SourceFile(std::string dir, std::string name, BBBParser::Variables v | |||
| std::ifstream file(dir_ + "/" + name_); | |||
| BBBParser parser(file, BBBParser::FLAG_ONE_LINE); | |||
| bool freshLine = true; | |||
| while (file.good()) { | |||
| if (freshLine && startsWith(parser, "//#bb")) { | |||
| if (startsWith(parser, "//#bb")) { | |||
| parser.parse(vars_); | |||
| } else { | |||
| while (file.good() && parser.get() != '\n'); | |||
| freshLine = true; | |||
| } | |||
| } | |||
| } | |||
| @@ -9,6 +9,10 @@ class SourceFile { | |||
| public: | |||
| SourceFile(std::string dir, std::string name, BBBParser::Variables vars); | |||
| const std::string &dir() const { return dir_; } | |||
| const std::string &name() const { return name_; } | |||
| const BBBParser::Variables &vars() const { return vars_; } | |||
| private: | |||
| std::string dir_; | |||
| std::string name_; | |||
| @@ -1,16 +1,40 @@ | |||
| #include <sys/types.h> | |||
| #include <sys/stat.h> | |||
| #include <unistd.h> | |||
| #include <getopt.h> | |||
| #include <dirent.h> | |||
| #include <stdexcept> | |||
| #include <string.h> | |||
| #include <errno.h> | |||
| #include <fstream> | |||
| #include <iostream> | |||
| #include "SourceFile.h" | |||
| #include "BBBParser.h" | |||
| void readDir(std::string dir, std::vector<SourceFile> &sources, BBBParser::Variables vars) { | |||
| static void readDir(std::string dir, std::vector<SourceFile> &sources, | |||
| BBBParser::Variables vars); | |||
| static void readPath(std::string dir, std::string name, | |||
| std::vector<SourceFile> &sources, BBBParser::Variables vars) { | |||
| std::string path = dir + "/" + name; | |||
| struct stat st; | |||
| if (stat(path.c_str(), &st) < 0) { | |||
| throw std::runtime_error("stat '" + path + "': " + strerror(errno)); | |||
| } | |||
| if (S_ISDIR(st.st_mode)) { | |||
| // We don't want to send 'files' | |||
| BBBParser::Variables subvars = vars; | |||
| subvars.erase("files"); | |||
| readDir(path, sources, std::move(subvars)); | |||
| } else { | |||
| sources.emplace_back(dir, name, vars); | |||
| } | |||
| } | |||
| static void readDir(std::string dir, std::vector<SourceFile> &sources, | |||
| BBBParser::Variables vars) { | |||
| std::ifstream buildFile(dir + "/build.bbb"); | |||
| std::vector<std::string> files; | |||
| bool hasFiles = false; | |||
| @@ -37,6 +61,8 @@ void readDir(std::string dir, std::vector<SourceFile> &sources, BBBParser::Varia | |||
| struct dirent *ent; | |||
| while ((ent = readdir(d)) != NULL) { | |||
| if (ent->d_name[0] == '.') | |||
| continue; | |||
| files.emplace_back(ent->d_name); | |||
| } | |||
| @@ -46,31 +72,101 @@ void readDir(std::string dir, std::vector<SourceFile> &sources, BBBParser::Varia | |||
| // Go through files | |||
| for (auto &ent: files) { | |||
| std::string path = dir + "/" + ent; | |||
| struct stat st; | |||
| if (stat(path.c_str(), &st) < 0) { | |||
| throw std::runtime_error("stat '" + path + "': " + strerror(errno)); | |||
| } | |||
| BBBParser::Variables subvars = vars; | |||
| readPath(dir, ent, sources, vars); | |||
| } | |||
| } | |||
| if (S_ISDIR(st.st_mode)) { | |||
| readDir(path, sources, vars); | |||
| } else { | |||
| sources.emplace_back(dir, ent, vars); | |||
| void printState(const std::vector<SourceFile> &sources) { | |||
| for (auto &source: sources) { | |||
| std::cout << source.dir() << '/' << source.name() << ":\n"; | |||
| for (auto &kv: source.vars()) { | |||
| std::cout << " " << kv.first << ":\n"; | |||
| for (auto &val: kv.second) { | |||
| std::cout << " " << val << '\n'; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| int main(int argc, char **argv) { | |||
| std::ifstream file("sample.bbb"); | |||
| BBBParser parser(file, BBBParser::FLAG_NONE); | |||
| std::vector<std::string> srcDirs; | |||
| std::string outdir = "bbbuild"; | |||
| enum class Action { | |||
| BUILD, PRINT_STATE, | |||
| }; | |||
| Action action = Action::BUILD; | |||
| BBBParser::Variables vars; | |||
| parser.parse(vars); | |||
| const char *shortopts = "o:hp"; | |||
| const struct option opts[] = { | |||
| { "help", no_argument, NULL, 'h' }, | |||
| { "output", required_argument, NULL, 'o' }, | |||
| { "print-state", no_argument, NULL, 'p' }, | |||
| {}, | |||
| }; | |||
| std::cout << "\nVariables found:\n"; | |||
| for (auto &kv: vars) { | |||
| std::cout << kv.first << ":\n"; | |||
| for (auto &val: kv.second) { | |||
| std::cout << "\t'" << val << "'\n"; | |||
| const char usage[] = | |||
| "Usage: bbbuild [options...] [sources]\n" | |||
| "\n" | |||
| " -h, --help " | |||
| "Show this help text.\n" | |||
| " -o, --output <dir> " | |||
| "Set output directory.\n" | |||
| " -p, --print-state " | |||
| "Print the state instead of building.\n"; | |||
| // Parse options from argv | |||
| while (1) { | |||
| int optidx; | |||
| int c = getopt_long(argc, argv, shortopts, opts, &optidx); | |||
| if (c < 0) { | |||
| break; | |||
| } | |||
| switch (c) { | |||
| case 'h': | |||
| puts(usage); | |||
| return 0; | |||
| case 'o': | |||
| outdir = optarg; | |||
| break; | |||
| case 'p': | |||
| action = Action::PRINT_STATE; | |||
| break; | |||
| default: | |||
| printf("Unknown option: '%c'.\n%s", (char)c, usage); | |||
| break; | |||
| return 1; | |||
| } | |||
| } | |||
| // Parse non-opt argv as source dirs | |||
| if (optind < argc) { | |||
| while (optind < argc) { | |||
| srcDirs.push_back(argv[optind++]); | |||
| } | |||
| } else { | |||
| srcDirs.push_back("."); | |||
| } | |||
| // Read configs from source dirs | |||
| std::vector<SourceFile> sources; | |||
| for (std::string &dir: srcDirs) { | |||
| readDir(dir, sources, {}); | |||
| } | |||
| switch (action) { | |||
| case Action::BUILD: | |||
| std::cerr << "Building is not implemented yet.\n"; | |||
| abort(); | |||
| case Action::PRINT_STATE: | |||
| printState(sources); | |||
| break; | |||
| } | |||
| } | |||