@@ -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; | |||
} | |||
} |