Browse Source

toolchain stuff

feature/dependency-graph
Martin Dørum 3 years ago
parent
commit
70718f4af9
13 changed files with 433 additions and 21 deletions
  1. 2
    2
      Makefile
  2. 1
    0
      example-project/.gitignore
  3. 1
    0
      example-project/build.bbb
  4. 1
    1
      example-project/main.c
  5. 36
    2
      src/SourceFile.cc
  6. 11
    1
      src/SourceFile.h
  7. 7
    0
      src/globals.cc
  8. 7
    0
      src/globals.h
  9. 102
    15
      src/main.cc
  10. 7
    0
      src/parallel.cc
  11. 18
    0
      src/parallel.h
  12. 220
    0
      src/toolchain.cc
  13. 20
    0
      src/toolchain.h

+ 2
- 2
Makefile View File

@@ -1,5 +1,5 @@
SRCS = src/BBBParser.cc src/SourceFile.cc src/main.cc
HDRS = src/BBBParser.h src/SourceFile.h
SRCS = src/BBBParser.cc src/SourceFile.cc src/toolchain.cc src/globals.cc src/main.cc
HDRS = src/BBBParser.h src/SourceFile.h src/toolchain.h src/globals.h src/parallel.h
BUILD = build
OBJS = $(patsubst %,$(BUILD)/%.o,$(SRCS))
CFLAGS = -g -Wall -Wextra -Wno-unused-parameter

+ 1
- 0
example-project/.gitignore View File

@@ -0,0 +1 @@
/bbbuild

+ 1
- 0
example-project/build.bbb View File

@@ -0,0 +1 @@
cflags := -O3

+ 1
- 1
example-project/main.c View File

@@ -2,7 +2,7 @@
#include <stdio.h>

//#bb ldflags := -lm
//#bb pkgs := libsdl cairo
//#bb pkgs := sdl2 cairo

int main() {
printf("sin(PI) is %f\n", sin(M_PI));

+ 36
- 2
src/SourceFile.cc View File

@@ -3,6 +3,7 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <string.h>

static bool startsWith(BBBParser &parser, const char *str) {
for (size_t i = 0; str[i] != '\0'; ++i) {
@@ -14,8 +15,31 @@ static bool startsWith(BBBParser &parser, const char *str) {
return true;
}

SourceFile::SourceFile(std::string dir, std::string name, BBBParser::Variables vars):
dir_(std::move(dir)), name_(std::move(name)), vars_(std::move(vars)) {
SourceFile::FileType SourceFile::fileTypeFrom(const std::string &name) {
size_t idx = name.find_last_of('.');
if (idx >= std::string::npos) {
printf("%s: no ftype\n", name.c_str());
return FileType::UNKNOWN;
}

char ext[16] = { 0 };
strncpy(ext, name.c_str() + idx, sizeof(ext) - 1);

if (strcmp(ext, ".c") == 0) {
return FileType::C;
} else if (
(strcmp(ext, ".C") == 0) ||
(strcmp(ext, ".cc") == 0) ||
(strcmp(ext, ".cpp") == 0) ||
(strcmp(ext, ".cxx") == 0)) {
return FileType::CXX;
}

return FileType::UNKNOWN;
}

SourceFile::SourceFile(std::string dir, std::string name, FileType type, BBBParser::Variables vars):
dir_(std::move(dir)), name_(std::move(name)), type_(type), vars_(std::move(vars)) {

std::ifstream file(dir_ + "/" + name_);
BBBParser parser(file, BBBParser::FLAG_ONE_LINE);
@@ -23,8 +47,18 @@ SourceFile::SourceFile(std::string dir, std::string name, BBBParser::Variables v
while (file.good()) {
if (startsWith(parser, "//#bb")) {
parser.parse(vars_);
contains_bbb_ = true;
} else {
while (file.good() && parser.get() != '\n');
}
}
}

const std::vector<std::string> *SourceFile::variable(const std::string &name) const {
auto it = vars_.find(name);
if (it == vars_.end()) {
return nullptr;
}

return &it->second;
}

+ 11
- 1
src/SourceFile.h View File

@@ -7,14 +7,24 @@

class SourceFile {
public:
SourceFile(std::string dir, std::string name, BBBParser::Variables vars);
enum FileType {
UNKNOWN, C, CXX,
};

static FileType fileTypeFrom(const std::string &name);

SourceFile(std::string dir, std::string name, FileType type, BBBParser::Variables vars);

const std::string &dir() const { return dir_; }
const std::string &name() const { return name_; }
FileType type() const { return type_; }
const BBBParser::Variables &vars() const { return vars_; }

const std::vector<std::string> *variable(const std::string &name) const;
private:
std::string dir_;
std::string name_;
FileType type_;
BBBParser::Variables vars_;
bool contains_bbb_ = false;
};

+ 7
- 0
src/globals.cc View File

@@ -0,0 +1,7 @@
#include "globals.h"

namespace global {

bool verbose = false;

}

+ 7
- 0
src/globals.h View File

@@ -0,0 +1,7 @@
#pragma once

namespace global {

extern bool verbose;

}

+ 102
- 15
src/main.cc View File

@@ -11,6 +11,9 @@

#include "SourceFile.h"
#include "BBBParser.h"
#include "parallel.h"
#include "toolchain.h"
#include "globals.h"

static void readDir(std::string dir, std::vector<SourceFile> &sources,
BBBParser::Variables vars);
@@ -29,7 +32,10 @@ static void readPath(std::string dir, std::string name,
subvars.erase("files");
readDir(path, sources, std::move(subvars));
} else {
sources.emplace_back(dir, name, vars);
SourceFile::FileType type = SourceFile::fileTypeFrom(name);
if (type != SourceFile::FileType::UNKNOWN) {
sources.emplace_back(dir, name, type, vars);
}
}
}

@@ -77,7 +83,7 @@ static void readDir(std::string dir, std::vector<SourceFile> &sources,
}
}

void printState(const std::vector<SourceFile> &sources) {
static void printState(const std::vector<SourceFile> &sources) {
for (auto &source: sources) {
std::cout << source.dir() << '/' << source.name() << ":\n";
for (auto &kv: source.vars()) {
@@ -89,31 +95,85 @@ void printState(const std::vector<SourceFile> &sources) {
}
}

static void getCompileFlags(const SourceFile &source, std::vector<std::string> &flags) {
const std::vector<std::string> *pkgs = source.variable("pkgs");

// TODO: Decide whether to put this before or after cflags/cxxflags
if (pkgs != nullptr) {
toolchain::pkgFlags(*pkgs, flags);
}

const std::vector<std::string> *cflags;
switch (source.type()) {
case SourceFile::FileType::C:
cflags = source.variable("cflags");
break;

case SourceFile::FileType::CXX:
cflags = source.variable("cxxflags");
break;

case SourceFile::FileType::UNKNOWN:
break;
}

if (cflags != nullptr) {
for (const std::string &flag: *cflags) {
flags.push_back(flag.c_str());
}
}
}

static void compileFile(const SourceFile &source, const std::string &outDir) {
std::vector<std::string> flags;
getCompileFlags(source, flags);

toolchain::compile(flags, source.type(), source.dir(), source.name(), outDir);
}

static void compile(const std::vector<SourceFile> &sources, const std::string &outDir, int jobs) {
parallel::parallel(jobs, sources, [&](const SourceFile &source) {
compileFile(source, outDir);
});
}

int main(int argc, char **argv) {
std::vector<std::string> srcDirs;
std::string outdir = "bbbuild";
std::string outDir = "bbbuild";
int jobs = parallel::coreCount();
std::string workDir = "";

enum class Action {
BUILD, PRINT_STATE,
};
Action action = Action::BUILD;

const char *shortopts = "o:hp";
const char *shortopts = "hvo:j:C:p";
const struct option opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "output", required_argument, NULL, 'o' },
{ "print-state", no_argument, NULL, 'p' },
{ "help", no_argument, NULL, 'h' },
{ "verbose", no_argument, NULL, 'v' },
{ "output", required_argument, NULL, 'o' },
{ "jobs", required_argument, NULL, 'j' },
{ "directory", required_argument, NULL, 'C' },
{ "print-state", no_argument, NULL, 'p' },
{},
};

const char usage[] =
"Usage: bbbuild [options...] [sources]\n"
"\n"
" -h, --help "
" -h, --help "
"Show this help text.\n"
" -o, --output <dir> "
"Set output directory.\n"
" -p, --print-state "
" -v, --verbose "
"Show every command as it's executing."
" -o, --output <dir> "
"Set output directory. Default: bbbuild\n"
" -j, --jobs <count> "
"Set the number of jobs run simultaneously. "
"Default: the number of cores in the machine.\n"
" -C, --directory <dir> "
"Change directory before doing anything else.\n"
" -p, --print-state "
"Print the state instead of building.\n";

// Parse options from argv
@@ -130,8 +190,24 @@ int main(int argc, char **argv) {
puts(usage);
return 0;

case 'v':
global::verbose = true;
break;

case 'o':
outdir = optarg;
outDir = optarg;
break;

case 'j':
jobs = atoi(optarg);
if (jobs <= 0) {
fprintf(stderr, "Can't run %i jobs.\n", jobs);
return 1;
}
break;

case 'C':
workDir = optarg;
break;

case 'p':
@@ -140,11 +216,22 @@ int main(int argc, char **argv) {

default:
printf("Unknown option: '%c'.\n%s", (char)c, usage);
break;
return 1;
}
}

// Change directory?
if (workDir.size() > 0) {
if (global::verbose) {
fprintf(stderr, "Entering directory '%s'\n", workDir.c_str());
}

if (chdir(workDir.c_str()) < 0) {
perror(workDir.c_str());
return 1;
}
}

// Parse non-opt argv as source dirs
if (optind < argc) {
while (optind < argc) {
@@ -162,8 +249,8 @@ int main(int argc, char **argv) {

switch (action) {
case Action::BUILD:
std::cerr << "Building is not implemented yet.\n";
abort();
compile(sources, outDir, jobs);
break;

case Action::PRINT_STATE:
printState(sources);

+ 7
- 0
src/parallel.cc View File

@@ -0,0 +1,7 @@
#include "parallel.h"

#include <thread>

int coreCount() {
return std::thread::hardware_concurrency();
}

+ 18
- 0
src/parallel.h View File

@@ -0,0 +1,18 @@
#pragma once

#include <thread>

namespace parallel {

template<typename Container, typename Func>
void parallel(int jobs, Container &cont, Func func) {
for (auto &elem: cont) {
func(elem);
}
}

int coreCount() {
return std::thread::hardware_concurrency();
}

}

+ 220
- 0
src/toolchain.cc View File

@@ -0,0 +1,220 @@
#include "toolchain.h"

#include <stdexcept>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include "globals.h"

namespace toolchain {

// TODO: Decide whether it would be better to use $SHELL
static const char *getShell() {
return "/bin/sh";
}

static const char *getPkgConfig() {
static const char *pkgConfig = nullptr;
if (pkgConfig != nullptr) {
return pkgConfig;
}

pkgConfig = getenv("PKG_CONFIG");
if (pkgConfig != nullptr) {
return pkgConfig;
}

return (pkgConfig = "pkg-config");
}

static const char *getCCompiler() {
static const char *cCompiler = nullptr;
if (cCompiler != nullptr) {
return cCompiler;
}

cCompiler = getenv("CC");
if (cCompiler != nullptr) {
return cCompiler;
}

return (cCompiler = "cc");
}

static const char *getCXXCompiler() {
static const char *cxxCompiler = nullptr;
if (cxxCompiler != nullptr) {
return cxxCompiler;
}

cxxCompiler = getenv("CC");
if (cxxCompiler != nullptr) {
return cxxCompiler;
}

return (cxxCompiler = "g++");
}

static const char *getCompilerFor(SourceFile::FileType type) {
switch (type) {
case SourceFile::FileType::C:
return getCCompiler();

case SourceFile::FileType::CXX:
return getCXXCompiler();

case SourceFile::FileType::UNKNOWN:
abort();
}

abort();
}

static void execute(std::vector<const char *> &args, std::string *output) {
if (global::verbose) {
for (size_t i = 0; i < args.size(); ++i) {
if (i == 0) {
fprintf(stderr, "%s", args[i]);
} else {
fprintf(stderr, " %s", args[i]);
}
}
fprintf(stderr, "\n");
}

// argv[0] should be interpreted as a shell command, because being able to run
// CC='gcc --sysroot=/blah' is used by some systems.
std::string command = std::string(args[0]) + " \"$@\"";
std::vector<const char *> argv;
argv.push_back(getShell());
argv.push_back("-c");
argv.push_back(command.c_str());
argv.push_back("--");
for (size_t i = 1; i < args.size(); ++i) {
argv.push_back(args[i]);
}

argv.push_back(nullptr);

int fds[2];
if (pipe(fds) < 0) {
throw std::runtime_error(std::string("fork: ") + strerror(errno));
}

pid_t child = fork();
if (child == 0) {
close(fds[0]);
dup2(fds[1], 1);

// So, from what I've read, execvp should never modify its argv; so this should be fine?
if (execvp(argv[0], (char *const *)argv.data()) < 0) {
perror(argv[0]);
abort();
}
} else if (child < 0) {
throw std::runtime_error(std::string("fork: ") + strerror(errno));
}

close(fds[1]);

if (output == nullptr) {
close(fds[0]);
} else {
char buf[1025];
while (true) {
ssize_t num = read(fds[0], buf, sizeof(buf) - 1);
if (num < 0 && errno != EAGAIN) {
close(fds[0]);
throw std::runtime_error(std::string("read: ") + strerror(errno));
} else if (num < 0) {
continue;
}

if (num == 0) {
close(fds[1]);
break;
}

buf[num] = '\0';
*output += buf;
}
}

int wstatus;
waitpid(child, &wstatus, 0);
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) {
throw std::runtime_error(std::string(args[0]) +
" exited with code " + std::to_string(WEXITSTATUS(wstatus)));
}

if (WTERMSIG(wstatus)) {
throw std::runtime_error(std::string(args[0]) +
" terminated due to " + strsignal(WTERMSIG(wstatus)));
}

if (output != nullptr && output->back() == '\n') {
output->pop_back();
}
}

static void parseSpaceSeparated(const std::string &input, std::vector<std::string> &output) {
ssize_t prevPos = -1;
ssize_t pos = -1;
while ((pos = input.find(' ', pos + 1)) != (ssize_t)std::string::npos) {
output.push_back(input.substr(prevPos + 1, pos - prevPos));
prevPos = pos;
}

if (prevPos >= 0) {
output.push_back(input.substr(prevPos + 1));
}
}

void pkgFlags(const std::vector<std::string> &pkgs, std::vector<std::string> &flags) {
std::vector<const char *> argv;
argv.push_back(getPkgConfig());
argv.push_back("--cflags");
for (auto &pkg: pkgs) {
argv.push_back(pkg.c_str());
}

std::string output;
execute(argv, &output);
parseSpaceSeparated(output, flags);
}

void compile(
const std::vector<std::string> &flags,
SourceFile::FileType type,
const std::string &srcDir,
const std::string &name,
const std::string &outDir) {
std::string sourcePath = srcDir + "/" + name;
std::string destDir = outDir + "/" + srcDir;
std::string destPath = destDir + "/" + name + ".o";

// TODO: Change this to a C++ mkdirp function
std::vector<const char *> argv;
argv.push_back("mkdir");
argv.push_back("-p");
argv.push_back(destDir.c_str());
execute(argv, nullptr);

argv.clear();
argv.push_back(getCompilerFor(type));
for (auto &flag: flags) {
argv.push_back(flag.c_str());
}

argv.push_back("-o");
argv.push_back(destPath.c_str());
argv.push_back("-c");
argv.push_back(sourcePath.c_str());
execute(argv, nullptr);
}

}

+ 20
- 0
src/toolchain.h View File

@@ -0,0 +1,20 @@
#pragma once

#include <vector>
#include <string>

#include "SourceFile.h"

namespace toolchain {

void pkgFlags(const std::vector<std::string> &pkgs, std::vector<std::string> &flags);
void pkgLDLibs(const std::vector<std::string> &pkgs, std::vector<std::string> &flags);

void compile(
const std::vector<std::string> &flags,
SourceFile::FileType type,
const std::string &srcDir,
const std::string &name,
const std::string &outDir);

}

Loading…
Cancel
Save