SRCS = \ | SRCS = \ | ||||
src/BBParser.cc src/build.cc src/compdb.cc src/CompileStep.cc src/DepNode.cc \ | |||||
src/BXParser.cc src/build.cc src/compdb.cc src/CompileStep.cc src/DepNode.cc \ | |||||
src/globals.cc src/LinkStep.cc src/logger.cc src/parallel.cc src/sys.cc \ | src/globals.cc src/LinkStep.cc src/logger.cc src/parallel.cc src/sys.cc \ | ||||
src/toolchain.cc src/main.cc | src/toolchain.cc src/main.cc | ||||
HDRS = \ | HDRS = \ | ||||
src/BBParser.h src/build.h src/compdb.h src/CompileStep.h src/DepNode.h \ | |||||
src/BXParser.h src/build.h src/compdb.h src/CompileStep.h src/DepNode.h \ | |||||
src/globals.h src/LinkStep.h src/logger.h src/parallel.h src/sys.h \ | src/globals.h src/LinkStep.h src/logger.h src/parallel.h src/sys.h \ | ||||
src/toolchain.h | src/toolchain.h | ||||
BUILD = build | BUILD = build | ||||
@mkdir -p $(@D) | @mkdir -p $(@D) | ||||
$(CXX) $(CFLAGS) -c $< -o $@ | $(CXX) $(CFLAGS) -c $< -o $@ | ||||
$(BUILD)/bbbuild: $(OBJS) | |||||
$(BUILD)/box: $(OBJS) | |||||
@mkdir -p $(@D) | @mkdir -p $(@D) | ||||
$(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) | ||||
# BBBuild: A build system which scales down | |||||
# Boxberry: A build system which scales down | |||||
BBBuild is a build system which aims to be usable for tiny projects, | |||||
Boxberry is a build system which aims to be usable for tiny projects, | |||||
all the way down to single source files. | all the way down to single source files. | ||||
Not all projects need a big Makefile or a huge CMakeLists.txt. | Not all projects need a big Makefile or a huge CMakeLists.txt. | ||||
However, almost all projects grow to the size where they need some library, | However, almost all projects grow to the size where they need some library, | ||||
or some compiler flag, maybe an optimization option. | or some compiler flag, maybe an optimization option. | ||||
BBBuild aims to make the path from running `cc file.c` to running | |||||
Boxberry aims to make the path from running `cc file.c` to running | |||||
`cc -lpthread file.c` to `cc file.c $(pkg-config --libs --cflags libfoo) -lpthread` | `cc -lpthread file.c` to `cc file.c $(pkg-config --libs --cflags libfoo) -lpthread` | ||||
as easy as possible. | as easy as possible. | ||||
* Source files are automatically discovered (by default). | * Source files are automatically discovered (by default). | ||||
* Dependencies can be specified in the source file: `//#bb pkgs := libfoo` | |||||
* Dependencies can be specified in the source file: `//#bx pkgs := libfoo` | |||||
* Linker and compiler flags can be specified in the source file: | * Linker and compiler flags can be specified in the source file: | ||||
`//#bb ldlibs := -lpthread ldflags := -fsanitize=address cflags := -fsanitize=address` | |||||
`//#bx ldlibs := -lpthread ldflags := -fsanitize=address cflags := -fsanitize=address` | |||||
* A source file is automatically recompiled if it has changed, or if any | * A source file is automatically recompiled if it has changed, or if any | ||||
included header files have changed. | included header files have changed. | ||||
* If your project grows, you can put global configs in a `build.bb` file. | |||||
Each directory can have a build.bb file, and each directory inherits the | |||||
* If your project grows, you can put global configs in a `build.bx` file. | |||||
Each directory can have a build.bx file, and each directory inherits the | |||||
configuration of its parent. | configuration of its parent. | ||||
* Files can be specified instead of discovered automatically by setting the | * Files can be specified instead of discovered automatically by setting the | ||||
`files` variable in a `build.bb` file. | |||||
`files` variable in a `build.bx` file. | |||||
## Building | ## Building | ||||
This project can be built by running either `make` or `bbbuild`. | |||||
This project can be built by running either `make` or `box`. | |||||
The only dependency is `pthreads`. | The only dependency is `pthreads`. | ||||
## TODO | ## TODO | ||||
* ~~Add `--print-compile-commands`~~ | * ~~Add `--print-compile-commands`~~ | ||||
* ~~Consider changing the config file format name from BBB to BB~~ | * ~~Consider changing the config file format name from BBB to BB~~ | ||||
* Support different build configurations (requires conditionals in the BBB parser) | |||||
* Support different build configurations (requires conditionals in the parser) | |||||
* Re-work the command-line interface | * Re-work the command-line interface | ||||
* ~~Save build configuration in the output dir~~ | * ~~Save build configuration in the output dir~~ | ||||
* ~~Support one "main" build.bb file which specifies things like the target name~~ | |||||
* ~~Support one "main" build.bx file which specifies things like the target name~~ | |||||
* Support creating shared/static libraries too, not just binaries | * Support creating shared/static libraries too, not just binaries | ||||
* Implement recursive mkdir in C++ instead of shelling out to mkdir | * Implement recursive mkdir in C++ instead of shelling out to mkdir | ||||
* Support subprojects | * Support subprojects |
target := bbbuild | |||||
target := box | |||||
files := src | files := src | ||||
warnings := all extra no-unused-parameter | warnings := all extra no-unused-parameter | ||||
std := c++14 | std := c++14 |
#include "BBParser.h" | |||||
#include "BXParser.h" | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
int BBParser::get() { | |||||
int BXParser::get() { | |||||
int ch = stream_.get(); | int ch = stream_.get(); | ||||
if (ch == '\n') { | if (ch == '\n') { | ||||
line_ += 1; | line_ += 1; | ||||
return ch; | return ch; | ||||
} | } | ||||
BBParser::Operator BBParser::readOperator() { | |||||
BXParser::Operator BXParser::readOperator() { | |||||
int ch2 = peek2(); | int ch2 = peek2(); | ||||
if (peek() == ':' && ch2 == '=') { | if (peek() == ':' && ch2 == '=') { | ||||
skip(); // ':' | skip(); // ':' | ||||
return Operator::NONE; | return Operator::NONE; | ||||
} | } | ||||
void BBParser::skip(char expected) { | |||||
void BXParser::skip(char expected) { | |||||
int ch = get(); | int ch = get(); | ||||
if (ch == EOF) { | if (ch == EOF) { | ||||
error(std::string("Expected '") + expected + "', got EOF"); | error(std::string("Expected '") + expected + "', got EOF"); | ||||
} | } | ||||
} | } | ||||
[[noreturn]] void BBParser::error(std::string msg) { | |||||
throw BBParseError(std::to_string(line_) + ":" + std::to_string(ch_) + ": " + msg); | |||||
[[noreturn]] void BXParser::error(std::string msg) { | |||||
throw BXParseError(std::to_string(line_) + ":" + std::to_string(ch_) + ": " + msg); | |||||
} | } | ||||
static bool isWhitespace(int ch) { | static bool isWhitespace(int ch) { | ||||
return false; | return false; | ||||
} | } | ||||
void BBParser::skipWhitespace() { | |||||
void BXParser::skipWhitespace() { | |||||
if (flags_ & FLAG_ONE_LINE) { | if (flags_ & FLAG_ONE_LINE) { | ||||
int ch; | int ch; | ||||
while (isWhitespace(ch = peek()) && ch != '\r' && ch != '\n') | while (isWhitespace(ch = peek()) && ch != '\r' && ch != '\n') | ||||
} | } | ||||
} | } | ||||
char BBParser::parseEscape() { | |||||
char BXParser::parseEscape() { | |||||
skip(); // '\' | skip(); // '\' | ||||
int ch; | int ch; | ||||
switch (ch = get()) { | switch (ch = get()) { | ||||
} | } | ||||
static void appendVariableToString( | static void appendVariableToString( | ||||
const BBVariables &vars, std::string &name, | |||||
const BXVariables &vars, std::string &name, | |||||
std::string &value) { | std::string &value) { | ||||
if (name.size() == 0) | if (name.size() == 0) | ||||
return; | return; | ||||
} | } | ||||
static void appendVariableToArray( | static void appendVariableToArray( | ||||
const BBVariables &vars, const std::string &name, | |||||
const BXVariables &vars, const std::string &name, | |||||
std::vector<std::string> &values) { | std::vector<std::string> &values) { | ||||
if (name.size() == 0) | if (name.size() == 0) | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
void BBParser::parseExpansion(const BBVariables &vars, std::vector<std::string> &values) { | |||||
void BXParser::parseExpansion(const BXVariables &vars, std::vector<std::string> &values) { | |||||
skip(); // '$' | skip(); // '$' | ||||
std::string str; | std::string str; | ||||
} | } | ||||
} | } | ||||
void BBParser::parseQuotedExpansion(const BBVariables &vars, std::string &content) { | |||||
void BXParser::parseQuotedExpansion(const BXVariables &vars, std::string &content) { | |||||
skip(); // '$' | skip(); // '$' | ||||
std::string str; | std::string str; | ||||
} | } | ||||
} | } | ||||
void BBParser::parseQuotedString(const BBVariables &vars, std::string &content) { | |||||
void BXParser::parseQuotedString(const BXVariables &vars, std::string &content) { | |||||
skip(); // '"' | skip(); // '"' | ||||
int ch; | int ch; | ||||
} | } | ||||
} | } | ||||
bool BBParser::parseString(const BBVariables &vars, std::string &content, int sep) { | |||||
bool BXParser::parseString(const BXVariables &vars, std::string &content, int sep) { | |||||
bool success = false; | bool success = false; | ||||
int ch; | int ch; | ||||
while (1) { | while (1) { | ||||
} | } | ||||
} | } | ||||
bool BBParser::parseIdentifier(std::string &content) { | |||||
bool BXParser::parseIdentifier(std::string &content) { | |||||
int ch = peek(); | int ch = peek(); | ||||
if (!( | if (!( | ||||
(ch >= 'a' && ch <= 'z') || | (ch >= 'a' && ch <= 'z') || | ||||
} | } | ||||
} | } | ||||
void BBParser::parse(BBVariables &vars) { | |||||
void BXParser::parse(BXVariables &vars) { | |||||
std::string key, value; | std::string key, value; | ||||
std::vector<std::string> values; | std::vector<std::string> values; | ||||
doAssignment(); | doAssignment(); | ||||
} | } | ||||
void BBParser::parseList(const BBVariables &vars, std::vector<std::string> &values) { | |||||
void BXParser::parseList(const BXVariables &vars, std::vector<std::string> &values) { | |||||
while (true) { | while (true) { | ||||
skipWhitespace(); | skipWhitespace(); | ||||
std::string value; | std::string value; | ||||
} | } | ||||
} | } | ||||
void BBWriter::put(char ch) { | |||||
void BXWriter::put(char ch) { | |||||
ch_ += 1; | ch_ += 1; | ||||
stream_ << ch; | stream_ << ch; | ||||
} | } | ||||
void BBWriter::put(const std::string &str) { | |||||
void BXWriter::put(const std::string &str) { | |||||
ch_ += str.size(); | ch_ += str.size(); | ||||
stream_ << str; | stream_ << str; | ||||
} | } | ||||
void BBWriter::newline() { | |||||
void BXWriter::newline() { | |||||
ch_ = 1; | ch_ = 1; | ||||
line_ += 1; | line_ += 1; | ||||
stream_ << '\n'; | stream_ << '\n'; | ||||
} | } | ||||
void BBWriter::escape(const std::string &str) { | |||||
void BXWriter::escape(const std::string &str) { | |||||
put('"'); | put('"'); | ||||
for (char ch: str) { | for (char ch: str) { | ||||
if (ch == '$' || ch == '"' || ch == '\\') { | if (ch == '$' || ch == '"' || ch == '\\') { | ||||
put('"'); | put('"'); | ||||
} | } | ||||
void BBWriter::write(const BBVariables &vars) { | |||||
void BXWriter::write(const BXVariables &vars) { | |||||
for (const auto &pair: vars) { | for (const auto &pair: vars) { | ||||
put(pair.first); | put(pair.first); | ||||
put(" :="); | put(" :="); |
#include <iostream> | #include <iostream> | ||||
#include <exception> | #include <exception> | ||||
struct BBParseError: std::exception { | |||||
BBParseError(std::string msg): message(msg) {} | |||||
struct BXParseError: std::exception { | |||||
BXParseError(std::string msg): message(msg) {} | |||||
std::string message; | std::string message; | ||||
const char *what() const noexcept override { | const char *what() const noexcept override { | ||||
} | } | ||||
}; | }; | ||||
using BBVariables = std::unordered_map<std::string, std::vector<std::string>>; | |||||
using BXVariables = std::unordered_map<std::string, std::vector<std::string>>; | |||||
class BBParser { | |||||
class BXParser { | |||||
public: | public: | ||||
static const int FLAG_NONE = 0; | static const int FLAG_NONE = 0; | ||||
static const int FLAG_ONE_LINE = 1 << 0; | static const int FLAG_ONE_LINE = 1 << 0; | ||||
BBParser(std::istream &stream, int flags, int line = 1, int ch = 1): | |||||
BXParser(std::istream &stream, int flags, int line = 1, int ch = 1): | |||||
flags_(flags), line_(line), ch_(ch), stream_(stream) {} | flags_(flags), line_(line), ch_(ch), stream_(stream) {} | ||||
void parse(BBVariables &vars); | |||||
void parseList(const BBVariables &vars, std::vector<std::string> &values); | |||||
void parse(BXVariables &vars); | |||||
void parseList(const BXVariables &vars, std::vector<std::string> &values); | |||||
int peek() { return stream_.peek(); } | int peek() { return stream_.peek(); } | ||||
int peek2() { stream_.get(); int ch = peek(); stream_.unget(); return ch; } | int peek2() { stream_.get(); int ch = peek(); stream_.unget(); return ch; } | ||||
void skipWhitespace(); | void skipWhitespace(); | ||||
char parseEscape(); | char parseEscape(); | ||||
void parseExpansion(const BBVariables &vars, std::vector<std::string> &values); | |||||
void parseQuotedExpansion(const BBVariables &vars, std::string &content); | |||||
void parseQuotedString(const BBVariables &vars, std::string &content); | |||||
bool parseString(const BBVariables &vars, std::string &content, int sep = -1); | |||||
void parseExpansion(const BXVariables &vars, std::vector<std::string> &values); | |||||
void parseQuotedExpansion(const BXVariables &vars, std::string &content); | |||||
void parseQuotedString(const BXVariables &vars, std::string &content); | |||||
bool parseString(const BXVariables &vars, std::string &content, int sep = -1); | |||||
bool parseIdentifier(std::string &content); | bool parseIdentifier(std::string &content); | ||||
int flags_; | int flags_; | ||||
std::istream &stream_; | std::istream &stream_; | ||||
}; | }; | ||||
class BBWriter { | |||||
class BXWriter { | |||||
public: | public: | ||||
BBWriter(std::ostream &stream, int line = 1, int ch = 1): | |||||
BXWriter(std::ostream &stream, int line = 1, int ch = 1): | |||||
line_(line), ch_(ch), stream_(stream) {} | line_(line), ch_(ch), stream_(stream) {} | ||||
void write(const BBVariables &vars); | |||||
void write(const BXVariables &vars); | |||||
private: | private: | ||||
void put(char ch); | void put(char ch); |
#include "logger.h" | #include "logger.h" | ||||
#include "globals.h" | #include "globals.h" | ||||
static bool startsWith(BBParser &parser, const char *str) { | |||||
static bool startsWith(BXParser &parser, const char *str) { | |||||
for (size_t i = 0; str[i] != '\0'; ++i) { | for (size_t i = 0; str[i] != '\0'; ++i) { | ||||
if (parser.peek() != str[i]) | if (parser.peek() != str[i]) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
} | } | ||||
std::string bbPath = this->bbPath(outDir); | |||||
if (!sys::fileExists(bbPath)) { | |||||
std::string confPath = this->confPath(outDir); | |||||
if (!sys::fileExists(confPath)) { | |||||
return true; | return true; | ||||
} | } | ||||
BBVariables cachedVariables; | |||||
BXVariables cachedVariables; | |||||
try { | try { | ||||
std::ifstream f = sys::ifstream(bbPath); | |||||
BBParser parser(f, BBParser::FLAG_NONE); | |||||
std::ifstream f = sys::ifstream(confPath); | |||||
BXParser parser(f, BXParser::FLAG_NONE); | |||||
parser.parse(cachedVariables); | parser.parse(cachedVariables); | ||||
} catch (BBParseError &err) { | |||||
logger::log(bbPath + ": " + err.what()); | |||||
} catch (BXParseError &err) { | |||||
logger::log(confPath + ": " + err.what()); | |||||
return true; | return true; | ||||
} | } | ||||
// the object file hasn't actually changed. | // the object file hasn't actually changed. | ||||
auto depsIt = cachedVariables.find("deps"); | auto depsIt = cachedVariables.find("deps"); | ||||
if (depsIt == cachedVariables.end()) { | if (depsIt == cachedVariables.end()) { | ||||
logger::log(bbPath + ": Missing 'deps' field"); | |||||
logger::log(confPath + ": Missing 'deps' field"); | |||||
return true; | return true; | ||||
} | } | ||||
// Maybe the build command has changed? | // Maybe the build command has changed? | ||||
auto commandIt = cachedVariables.find("command"); | auto commandIt = cachedVariables.find("command"); | ||||
if (commandIt == cachedVariables.end()) { | if (commandIt == cachedVariables.end()) { | ||||
logger::log(bbPath + ": Missing 'command' field"); | |||||
logger::log(confPath + ": Missing 'command' field"); | |||||
return true; | return true; | ||||
} | } | ||||
std::string dirPath = sys::dirname(objPath); | std::string dirPath = sys::dirname(objPath); | ||||
std::vector<std::string> command = compileCommand(outDir); | std::vector<std::string> command = compileCommand(outDir); | ||||
BBVariables newCachedVars = { | |||||
BXVariables newCachedVars = { | |||||
{ "deps", toolchain::getDependencies(flags(), type_, path_) }, | { "deps", toolchain::getDependencies(flags(), type_, path_) }, | ||||
{ "command", command}, | { "command", command}, | ||||
}; | }; | ||||
sys::mkdirp(dirPath); | sys::mkdirp(dirPath); | ||||
std::ofstream f = sys::ofstream(bbPath(outDir)); | |||||
BBWriter writer(f); | |||||
std::ofstream f = sys::ofstream(confPath(outDir)); | |||||
BXWriter writer(f); | |||||
writer.write(newCachedVars); | writer.write(newCachedVars); | ||||
sys::execute(command, nullptr, verboseCommand); | sys::execute(command, nullptr, verboseCommand); | ||||
} | } | ||||
std::vector<std::string> CompileStep::getPublicLDFlags(const std::string &outDir) { | std::vector<std::string> CompileStep::getPublicLDFlags(const std::string &outDir) { | ||||
BBVariables &vars = variables(); | |||||
BXVariables &vars = variables(); | |||||
std::vector<std::string> flags; | std::vector<std::string> flags; | ||||
toolchain::getLDFlags(vars, flags); | toolchain::getLDFlags(vars, flags); | ||||
return flags; | return flags; | ||||
} | } | ||||
std::vector<std::string> CompileStep::getPublicLDLibs(const std::string &outDir) { | std::vector<std::string> CompileStep::getPublicLDLibs(const std::string &outDir) { | ||||
BBVariables &vars = variables(); | |||||
BXVariables &vars = variables(); | |||||
std::vector<std::string> libs; | std::vector<std::string> libs; | ||||
toolchain::getLDLibs(vars, libs); | toolchain::getLDLibs(vars, libs); | ||||
return libs; | return libs; | ||||
return std::vector<std::string>{ toolchain::objectFilePath(path_, outDir) }; | return std::vector<std::string>{ toolchain::objectFilePath(path_, outDir) }; | ||||
} | } | ||||
BBVariables &CompileStep::variables() { | |||||
BXVariables &CompileStep::variables() { | |||||
if (hasVariables_) { | if (hasVariables_) { | ||||
return variables_; | return variables_; | ||||
} | } | ||||
std::ifstream f = sys::ifstream(path_); | std::ifstream f = sys::ifstream(path_); | ||||
BBParser parser(f, BBParser::FLAG_ONE_LINE); | |||||
BXParser parser(f, BXParser::FLAG_ONE_LINE); | |||||
while (f.good()) { | while (f.good()) { | ||||
if (startsWith(parser, "//#bb")) { | |||||
if (startsWith(parser, "//#bx")) { | |||||
parser.parse(variables_); | parser.parse(variables_); | ||||
} else { | } else { | ||||
while (f.good() && parser.get() != '\n'); | while (f.good() && parser.get() != '\n'); | ||||
return flags_; | return flags_; | ||||
} | } | ||||
BBVariables &vars = variables(); | |||||
BXVariables &vars = variables(); | |||||
toolchain::getFlags(vars, type_, flags_); | toolchain::getFlags(vars, type_, flags_); | ||||
hasFlags_ = true; | hasFlags_ = true; |
#pragma once | #pragma once | ||||
#include "DepNode.h" | #include "DepNode.h" | ||||
#include "BBParser.h" | |||||
#include "BXParser.h" | |||||
#include "toolchain.h" | #include "toolchain.h" | ||||
class CompileStep: public DepNode { | class CompileStep: public DepNode { | ||||
public: | public: | ||||
CompileStep(std::string path, toolchain::FileType type, BBVariables vars): | |||||
CompileStep(std::string path, toolchain::FileType type, BXVariables vars): | |||||
path_(std::move(path)), type_(type), variables_(std::move(vars)) {} | path_(std::move(path)), type_(type), variables_(std::move(vars)) {} | ||||
private: | private: | ||||
std::vector<std::string> getPublicObjects(const std::string &outDir) override; | std::vector<std::string> getPublicObjects(const std::string &outDir) override; | ||||
bool hasVariables_ = false; | bool hasVariables_ = false; | ||||
BBVariables variables_; | |||||
BBVariables &variables(); | |||||
BXVariables variables_; | |||||
BXVariables &variables(); | |||||
bool hasFlags_ = false; | bool hasFlags_ = false; | ||||
std::vector<std::string> flags_; | std::vector<std::string> flags_; | ||||
std::vector<std::string> compileCommand_; | std::vector<std::string> compileCommand_; | ||||
std::vector<std::string> &compileCommand(const std::string &outDir); | std::vector<std::string> &compileCommand(const std::string &outDir); | ||||
std::string bbPath(const std::string &outDir) { return outDir + '/' + path_ + ".bb"; } | |||||
std::string confPath(const std::string &outDir) { return outDir + '/' + path_ + ".bx"; } | |||||
}; | }; |
#include "sys.h" | #include "sys.h" | ||||
#include "logger.h" | #include "logger.h" | ||||
#include "globals.h" | #include "globals.h" | ||||
#include "BBParser.h" | |||||
#include "BXParser.h" | |||||
bool LinkStep::checkHasChanged(const std::string &outDir) { | bool LinkStep::checkHasChanged(const std::string &outDir) { | ||||
std::string targetPath = toolchain::targetFilePath(type_, path_, outDir); | std::string targetPath = toolchain::targetFilePath(type_, path_, outDir); | ||||
return true; | return true; | ||||
} | } | ||||
std::string bbPath = this->bbPath(outDir); | |||||
if (!sys::fileExists(bbPath)) { | |||||
std::string confPath = this->confPath(outDir); | |||||
if (!sys::fileExists(confPath)) { | |||||
return true; | return true; | ||||
} | } | ||||
BBVariables cachedVariables; | |||||
BXVariables cachedVariables; | |||||
try { | try { | ||||
std::ifstream f = sys::ifstream(bbPath); | |||||
BBParser parser(f, BBParser::FLAG_NONE); | |||||
std::ifstream f = sys::ifstream(confPath); | |||||
BXParser parser(f, BXParser::FLAG_NONE); | |||||
parser.parse(cachedVariables); | parser.parse(cachedVariables); | ||||
} catch (BBParseError &err) { | |||||
logger::log(bbPath + ": " + err.what()); | |||||
} catch (BXParseError &err) { | |||||
logger::log(confPath + ": " + err.what()); | |||||
return true; | return true; | ||||
} | } | ||||
auto commandIt = cachedVariables.find("command"); | auto commandIt = cachedVariables.find("command"); | ||||
if (commandIt == cachedVariables.end()) { | if (commandIt == cachedVariables.end()) { | ||||
logger::log(bbPath + ": Missing 'command' field"); | |||||
logger::log(confPath + ": Missing 'command' field"); | |||||
return true; | return true; | ||||
} | } | ||||
std::vector<std::string> command = linkCommand(outDir); | std::vector<std::string> command = linkCommand(outDir); | ||||
std::string dirPath = sys::dirname(outDir + '/' + path_); | std::string dirPath = sys::dirname(outDir + '/' + path_); | ||||
BBVariables newCachedVars = { | |||||
BXVariables newCachedVars = { | |||||
{ "command", command}, | { "command", command}, | ||||
}; | }; | ||||
std::ofstream f = sys::ofstream(bbPath(outDir)); | |||||
BBWriter writer(f); | |||||
std::ofstream f = sys::ofstream(confPath(outDir)); | |||||
BXWriter writer(f); | |||||
writer.write(newCachedVars); | writer.write(newCachedVars); | ||||
sys::mkdirp(dirPath); | sys::mkdirp(dirPath); |
std::vector<std::string> linkCommand_; | std::vector<std::string> linkCommand_; | ||||
std::vector<std::string> &linkCommand(const std::string &outDir); | std::vector<std::string> &linkCommand(const std::string &outDir); | ||||
std::string bbPath(const std::string &outDir) { return outDir + '/' + path_ + ".bb"; } | |||||
std::string confPath(const std::string &outDir) { return outDir + '/' + path_ + ".bx"; } | |||||
}; | }; |
#include <string.h> | #include <string.h> | ||||
#include "sys.h" | #include "sys.h" | ||||
#include "BBParser.h" | |||||
#include "BXParser.h" | |||||
#include "CompileStep.h" | #include "CompileStep.h" | ||||
#include "LinkStep.h" | #include "LinkStep.h" | ||||
#include "logger.h" | #include "logger.h" | ||||
} | } | ||||
static void addFile( | static void addFile( | ||||
std::string path, BBVariables variables, | |||||
std::string path, BXVariables variables, | |||||
std::vector<std::unique_ptr<DepNode>> &deps) { | std::vector<std::unique_ptr<DepNode>> &deps) { | ||||
toolchain::FileType type; | toolchain::FileType type; | ||||
std::string ext = extension(path); | std::string ext = extension(path); | ||||
static void findDeps( | static void findDeps( | ||||
const std::string &dir, const std::string &name, | const std::string &dir, const std::string &name, | ||||
const BBVariables &variables, std::vector<std::unique_ptr<DepNode>> &deps) { | |||||
const BXVariables &variables, std::vector<std::unique_ptr<DepNode>> &deps) { | |||||
std::string path = dir + "/" + name; | std::string path = dir + "/" + name; | ||||
sys::FileInfo info; | sys::FileInfo info; | ||||
try { | try { | ||||
if (info.isDir) { | if (info.isDir) { | ||||
// May or may not need to copy variables | // May or may not need to copy variables | ||||
BBVariables subvars; | |||||
const BBVariables *varsptr = &variables; | |||||
BXVariables subvars; | |||||
const BXVariables *varsptr = &variables; | |||||
std::vector<std::string> subpaths; | std::vector<std::string> subpaths; | ||||
// There might be a $path/build.bb. If there is, files in that dir | |||||
// needs a new copy of variables, and that $path/build.bb might contain | |||||
// There might be a $path/build.bx. If there is, files in that dir | |||||
// needs a new copy of variables, and that $path/build.bx might contain | |||||
// a files property. | // a files property. | ||||
if (sys::fileExists(path + "/build.bb")) { | |||||
if (sys::fileExists(path + "/build.bx")) { | |||||
subvars = variables; | subvars = variables; | ||||
varsptr = &subvars; | varsptr = &subvars; | ||||
std::ifstream stream = sys::ifstream("build.bb"); | |||||
BBParser parser(stream, BBParser::FLAG_NONE); | |||||
std::ifstream stream = sys::ifstream("build.bx"); | |||||
BXParser parser(stream, BXParser::FLAG_NONE); | |||||
parser.parse(subvars); | parser.parse(subvars); | ||||
auto it = subvars.find("files"); | auto it = subvars.find("files"); | ||||
} | } | ||||
} | } | ||||
static std::string findTargetName(const BBVariables &variables) { | |||||
static std::string findTargetName(const BXVariables &variables) { | |||||
auto it = variables.find("target"); | auto it = variables.find("target"); | ||||
if (it != variables.end() && it->second.size() != 0) { | if (it != variables.end() && it->second.size() != 0) { | ||||
return it->second[0]; | return it->second[0]; | ||||
return "target"; | return "target"; | ||||
} | } | ||||
std::unique_ptr<DepNode> buildDepTree(const std::string &outDir, BBVariables variables) { | |||||
std::unique_ptr<DepNode> buildDepTree(const std::string &outDir, BXVariables variables) { | |||||
// Read config from file | // Read config from file | ||||
if (sys::fileExists("build.bb")) { | |||||
std::ifstream stream = sys::ifstream("build.bb"); | |||||
BBParser parser(stream, BBParser::FLAG_NONE); | |||||
if (sys::fileExists("build.bx")) { | |||||
std::ifstream stream = sys::ifstream("build.bx"); | |||||
BXParser parser(stream, BXParser::FLAG_NONE); | |||||
parser.parse(variables); | parser.parse(variables); | ||||
} | } | ||||
#include <memory> | #include <memory> | ||||
#include "DepNode.h" | #include "DepNode.h" | ||||
#include "BBParser.h" | |||||
#include "BXParser.h" | |||||
std::unique_ptr<DepNode> buildDepTree(const std::string &outDir, BBVariables variables); | |||||
std::unique_ptr<DepNode> buildDepTree(const std::string &outDir, BXVariables variables); |
//#bb ldlibs := pthread | |||||
//#bx ldlibs := pthread | |||||
#include <getopt.h> | #include <getopt.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <utility> | #include <utility> | ||||
#include <stdexcept> | #include <stdexcept> | ||||
#include "BBParser.h" | |||||
#include "BXParser.h" | |||||
#include "parallel.h" | #include "parallel.h" | ||||
#include "toolchain.h" | #include "toolchain.h" | ||||
#include "globals.h" | #include "globals.h" | ||||
if (args.size() == 0) { | if (args.size() == 0) { | ||||
op = "build"; | op = "build"; | ||||
path = "./bb-out"; | |||||
path = "./bx-out"; | |||||
} else if (args[0][0] == '.' || args[0][0] == '/') { | } else if (args[0][0] == '.' || args[0][0] == '/') { | ||||
op = "build"; | op = "build"; | ||||
path = args[0]; | path = args[0]; | ||||
} else if (args.size() == 1) { | } else if (args.size() == 1) { | ||||
op = args[0]; | op = args[0]; | ||||
path = "./bb-out"; | |||||
path = "./bx-out"; | |||||
} else if (args.size() == 2) { | } else if (args.size() == 2) { | ||||
op = args[0]; | op = args[0]; | ||||
path = args[1]; | path = args[1]; | ||||
sys::mkdirp(path); | sys::mkdirp(path); | ||||
auto buildVariables = [&]() -> BBVariables { | |||||
BBVariables variables; | |||||
if (sys::fileExists(path + "/.config.bb")) { | |||||
std::ifstream f = sys::ifstream(path + "/.config.bb"); | |||||
BBParser parser(f, BBParser::FLAG_NONE); | |||||
auto buildVariables = [&]() -> BXVariables { | |||||
BXVariables variables; | |||||
if (sys::fileExists(path + "/.config.bx")) { | |||||
std::ifstream f = sys::ifstream(path + "/.config.bx"); | |||||
BXParser parser(f, BXParser::FLAG_NONE); | |||||
parser.parse(variables); | parser.parse(variables); | ||||
} | } | ||||
for (auto &pair: kwargs) { | for (auto &pair: kwargs) { | ||||
std::stringstream ss(pair.second); | std::stringstream ss(pair.second); | ||||
BBParser parser(ss, BBParser::FLAG_NONE); | |||||
BXParser parser(ss, BXParser::FLAG_NONE); | |||||
auto &list = variables[pair.first]; | auto &list = variables[pair.first]; | ||||
list.clear(); | list.clear(); | ||||
parser.parseList(variables, list); | parser.parseList(variables, list); | ||||
} | } | ||||
if (kwargs.size() > 0) { | if (kwargs.size() > 0) { | ||||
std::ofstream f = sys::ofstream(path + "/.config.bb"); | |||||
BBWriter w(f); | |||||
std::ofstream f = sys::ofstream(path + "/.config.bx"); | |||||
BXWriter w(f); | |||||
w.write(variables); | w.write(variables); | ||||
} | } | ||||
return variables; | return variables; | ||||
}; | }; | ||||
auto buildTree = [&](BBVariables vars) -> std::unique_ptr<DepNode> { | |||||
auto buildTree = [&](BXVariables vars) -> std::unique_ptr<DepNode> { | |||||
return buildDepTree(path, std::move(vars)); | return buildDepTree(path, std::move(vars)); | ||||
}; | }; | ||||
} | } | ||||
} else if (op == "config") { | } else if (op == "config") { | ||||
BBVariables variables = buildVariables(); | |||||
BXVariables variables = buildVariables(); | |||||
printf("%s:\n", (path + "/.config.bb").c_str()); | |||||
printf("%s:\n", (path + "/.config.bx").c_str()); | |||||
if (variables.size() == 0) { | if (variables.size() == 0) { | ||||
printf("No config options set.\n"); | printf("No config options set.\n"); | ||||
} | } | ||||
} else if (op == "clean") { | } else if (op == "clean") { | ||||
// TODO: Remove what's needed, instead of removing everything and | // TODO: Remove what's needed, instead of removing everything and | ||||
// re-creating .config.bb | |||||
BBVariables variables = buildVariables(); | |||||
// re-creating .config.bx | |||||
BXVariables variables = buildVariables(); | |||||
sys::rmrf(path); | sys::rmrf(path); | ||||
sys::rmrf("compile_commands.json"); | sys::rmrf("compile_commands.json"); | ||||
sys::mkdirp(path); | sys::mkdirp(path); | ||||
std::ofstream f = sys::ofstream(path + "/.config.bb"); | |||||
BBWriter w(f); | |||||
std::ofstream f = sys::ofstream(path + "/.config.bx"); | |||||
BXWriter w(f); | |||||
w.write(variables); | w.write(variables); | ||||
} else { | } else { | ||||
} | } | ||||
int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||
std::string outDir = "bbbuild"; | |||||
int jobs = parallel::coreCount() * 1.2 + 2; | int jobs = parallel::coreCount() * 1.2 + 2; | ||||
std::string workDir = ""; | std::string workDir = ""; | ||||
std::string target = ""; | std::string target = ""; | ||||
}; | }; | ||||
const char usage[] = | const char usage[] = | ||||
"Usage: bbbuild [options...] [build | config | compile-commands | clean] [path]\n" | |||||
"Usage: %s [options...] [build | config | compile-commands | clean] [path]\n" | |||||
"\n" | "\n" | ||||
" -h, --help " | " -h, --help " | ||||
"Show this help text.\n" | "Show this help text.\n" | ||||
switch (c) { | switch (c) { | ||||
case 'h': | case 'h': | ||||
puts(usage); | |||||
printf(usage, argv[0]); | |||||
return 0; | return 0; | ||||
case 'v': | case 'v': | ||||
break; | break; | ||||
default: | default: | ||||
printf("Unknown option: '%c'.\n%s", (char)c, usage); | |||||
printf("Unknown option: '%c'.\n", (char)c); | |||||
printf(usage, argv[0]); | |||||
return 1; | return 1; | ||||
} | } | ||||
} | } |
abort(); | abort(); | ||||
} | } | ||||
void getFlags(const BBVariables &vars, FileType type, std::vector<std::string> &flags) { | |||||
void getFlags(const BXVariables &vars, FileType type, std::vector<std::string> &flags) { | |||||
auto stdver = vars.find("std"); | auto stdver = vars.find("std"); | ||||
if (stdver != vars.end() && stdver->second.size() > 0) { | if (stdver != vars.end() && stdver->second.size() > 0) { | ||||
flags.push_back("-std=" + stdver->second[0]); | flags.push_back("-std=" + stdver->second[0]); | ||||
} | } | ||||
} | } | ||||
void getLDLibs(const BBVariables &vars, std::vector<std::string> &flags) { | |||||
void getLDLibs(const BXVariables &vars, std::vector<std::string> &flags) { | |||||
auto pkgs = vars.find("pkgs"); | auto pkgs = vars.find("pkgs"); | ||||
if (pkgs != vars.end()) { | if (pkgs != vars.end()) { | ||||
std::vector<std::string> argv; | std::vector<std::string> argv; | ||||
} | } | ||||
} | } | ||||
void getLDFlags(const BBVariables &vars, std::vector<std::string> &flags) { | |||||
void getLDFlags(const BXVariables &vars, std::vector<std::string> &flags) { | |||||
auto sanitize = vars.find("sanitizers"); | auto sanitize = vars.find("sanitizers"); | ||||
if (sanitize != vars.end()) { | if (sanitize != vars.end()) { | ||||
for (auto &s: sanitize->second) { | for (auto &s: sanitize->second) { |
#include <vector> | #include <vector> | ||||
#include <string> | #include <string> | ||||
#include "BBParser.h" | |||||
#include "BXParser.h" | |||||
namespace toolchain { | namespace toolchain { | ||||
const std::string &path, | const std::string &path, | ||||
const std::string &outDir); | const std::string &outDir); | ||||
void getFlags(const BBVariables &vars, FileType type, std::vector<std::string> &flags); | |||||
void getLDLibs(const BBVariables &vars, std::vector<std::string> &flags); | |||||
void getLDFlags(const BBVariables &vars, std::vector<std::string> &flags); | |||||
void getFlags(const BXVariables &vars, FileType type, std::vector<std::string> &flags); | |||||
void getLDLibs(const BXVariables &vars, std::vector<std::string> &flags); | |||||
void getLDFlags(const BXVariables &vars, std::vector<std::string> &flags); | |||||
std::vector<std::string> getDependencies( | std::vector<std::string> getDependencies( | ||||
const std::vector<std::string> &flags, | const std::vector<std::string> &flags, |