Browse Source

compiling, dependency tracking, etc

feature/dependency-graph
Martin Dørum 3 years ago
parent
commit
5a062a3d43
11 changed files with 274 additions and 64 deletions
  1. 2
    2
      Makefile
  2. 1
    0
      example-project/hello world.c
  3. 3
    0
      example-project/hello world.h
  4. 3
    2
      example-project/main.c
  5. 90
    4
      src/SourceFile.cc
  6. 18
    2
      src/SourceFile.h
  7. 23
    44
      src/main.cc
  8. 39
    0
      src/sys.cc
  9. 16
    0
      src/sys.h
  10. 72
    10
      src/toolchain.cc
  11. 7
    0
      src/toolchain.h

+ 2
- 2
Makefile View File

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

+ 1
- 0
example-project/hello world.c View File

@@ -0,0 +1 @@
int x = 10;

+ 3
- 0
example-project/hello world.h View File

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

extern int x;

+ 3
- 2
example-project/main.c View File

@@ -1,9 +1,10 @@
#include <math.h>
#include <stdio.h>

#include "hello world.h"

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

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

+ 90
- 4
src/SourceFile.cc View File

@@ -5,6 +5,8 @@
#include <sstream>
#include <string.h>

#include "toolchain.h"

static bool startsWith(BBBParser &parser, const char *str) {
for (size_t i = 0; str[i] != '\0'; ++i) {
if (parser.peek() != str[i])
@@ -18,7 +20,6 @@ static bool startsWith(BBBParser &parser, const char *str) {
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;
}

@@ -38,8 +39,11 @@ SourceFile::FileType SourceFile::fileTypeFrom(const std::string &name) {
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)) {
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);
@@ -47,7 +51,6 @@ SourceFile::SourceFile(std::string dir, std::string name, FileType type, BBBPars
while (file.good()) {
if (startsWith(parser, "//#bb")) {
parser.parse(vars_);
contains_bbb_ = true;
} else {
while (file.good() && parser.get() != '\n');
}
@@ -62,3 +65,86 @@ const std::vector<std::string> *SourceFile::variable(const std::string &name) co

return &it->second;
}

const std::vector<std::string> &SourceFile::compileFlags() const {
if (hasCompileFlags_) {
return compileFlags_;
}

const std::vector<std::string> *pkgs = variable("pkgs");
if (pkgs != nullptr) {
toolchain::pkgFlags(*pkgs, compileFlags_);
}

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

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

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

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

return compileFlags_;
}

const std::vector<std::string> &SourceFile::ldLibs() const {
const std::vector<std::string> *pkgs = variable("pkgs");
if (pkgs != nullptr) {
toolchain::pkgLDLibs(*pkgs, ldLibs_);
}

const std::vector<std::string> *libs = variable("ldlibs");
if (libs != nullptr) {
for (const std::string &flag: *libs) {
ldLibs_.push_back(flag);
}
}

hasLDLibs_ = true;
return ldLibs_;
}

std::vector<std::string> SourceFile::dependencies() const {
std::vector<std::string> deps;
toolchain::getDependencies(compileFlags(), type_, dir_, name_, deps);
return deps;
}

bool SourceFile::needsRecompile(const std::string &outDir) const {
std::string outPath = outDir + "/" + name_ + ".o";
if (!sys::fileExists(outPath)) {
return true;
}

sys::FileInfo outInfo = sys::fileInfo(outPath);

std::vector<std::string> deps = dependencies();
for (std::string &dep: deps) {
sys::FileInfo depInfo = sys::fileInfo(dep);

if (depInfo.mTimeSec > outInfo.mTimeSec || (
depInfo.mTimeSec == outInfo.mTimeSec &&
depInfo.mTimeNsec > outInfo.mTimeNsec)) {
return true;
}
}

return false;
}

void SourceFile::compile(const std::string &outDir) const {
std::cerr << "Compile " << dir_ << '/' << name_ << '\n';
toolchain::compile(compileFlags(), type_, dir_, name_, outDir);
}

+ 18
- 2
src/SourceFile.h View File

@@ -2,8 +2,10 @@

#include <vector>
#include <string>
#include <optional>

#include "BBBParser.h"
#include "sys.h"

class SourceFile {
public:
@@ -13,7 +15,9 @@ public:

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

SourceFile(std::string dir, std::string name, FileType type, BBBParser::Variables vars);
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_; }
@@ -21,10 +25,22 @@ public:
const BBBParser::Variables &vars() const { return vars_; }

const std::vector<std::string> *variable(const std::string &name) const;

const std::vector<std::string> &compileFlags() const;
const std::vector<std::string> &ldLibs() const;

std::vector<std::string> dependencies() const;
bool needsRecompile(const std::string &outDir) const;
void compile(const std::string &outDir) const;

private:
std::string dir_;
std::string name_;
FileType type_;
BBBParser::Variables vars_;
bool contains_bbb_ = false;

mutable std::vector<std::string> compileFlags_;
mutable bool hasCompileFlags_ = false;
mutable std::vector<std::string> ldLibs_;
mutable bool hasLDLibs_ = false;
};

+ 23
- 44
src/main.cc View File

@@ -1,5 +1,3 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <getopt.h>
#include <dirent.h>
@@ -8,6 +6,7 @@
#include <errno.h>
#include <fstream>
#include <iostream>
#include <mutex>

#include "SourceFile.h"
#include "BBBParser.h"
@@ -21,12 +20,9 @@ static void readDir(std::string dir, std::vector<SourceFile> &sources,
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));
}
sys::FileInfo finfo = sys::fileInfo(path);

if (S_ISDIR(st.st_mode)) {
if (finfo.isDir) {
// We don't want to send 'files'
BBBParser::Variables subvars = vars;
subvars.erase("files");
@@ -95,46 +91,24 @@ static 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 bool compile(const std::vector<SourceFile> &sources, const std::string &outDir, int jobs) {
bool compiled = false;
std::mutex mut;
parallel::parallel(jobs, sources, [&](const SourceFile &source) {
if (source.needsRecompile(outDir)) {
source.compile(outDir);
mut.lock();
compiled = true;
mut.unlock();
}
}
}

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);
return compiled;
}

static void compile(const std::vector<SourceFile> &sources, const std::string &outDir, int jobs) {
parallel::parallel(jobs, sources, [&](const SourceFile &source) {
compileFile(source, outDir);
});
static void link(const std::vector<SourceFile> &sources, const std::string &outDir) {
// TODO: Actually link
printf("Linking\n");
}

int main(int argc, char **argv) {
@@ -249,7 +223,12 @@ int main(int argc, char **argv) {

switch (action) {
case Action::BUILD:
compile(sources, outDir, jobs);
if (compile(sources, outDir, jobs)) {
link(sources, outDir);
} else {
std::cerr << "Nothing to do.\n";
}

break;

case Action::PRINT_STATE:

+ 39
- 0
src/sys.cc View File

@@ -0,0 +1,39 @@
#include "sys.h"

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

namespace sys {

FileInfo fileInfo(const std::string &path) {
FileInfo finfo;

struct stat st;
if (stat(path.c_str(), &st) < 0) {
throw std::runtime_error("stat '" + path + "': " + strerror(errno));
}

finfo.mTimeSec = st.st_mtim.tv_sec;
finfo.mTimeNsec = st.st_mtim.tv_nsec;
finfo.isDir = S_ISDIR(st.st_mode);
return finfo;
}

bool fileExists(const std::string &path) {
struct stat st;
if (stat(path.c_str(), &st) < 0) {
if (errno == ENOENT) {
return false;
} else {
throw std::runtime_error("stat '" + path + "': " + strerror(errno));
}
}

return true;
}

}

+ 16
- 0
src/sys.h View File

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

#include <string>

namespace sys {

struct FileInfo {
long mTimeSec;
long mTimeNsec;
bool isDir;
};

FileInfo fileInfo(const std::string &path);
bool fileExists(const std::string &path);

}

+ 72
- 10
src/toolchain.cc View File

@@ -161,16 +161,39 @@ static void execute(std::vector<const char *> &args, std::string *output) {
}
}

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;
}
static bool isWhitespace(char ch) {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
}

static void parseWhitespaceSeparated(
const std::string &input,
std::vector<std::string> &output,
size_t pos = 0) {
size_t i = pos;
while (true) {
// Skip leading whitespace
char ch = input[i];
while (isWhitespace(ch) || (ch == '\\' && isWhitespace(input[i + 1]))) {
ch = input[i += 1];
}

// Read non-whitespace
std::string str;
while (!isWhitespace(ch) && ch != '\0') {
if (ch == '\\') {
str += input[i + 1];
ch = input[i += 2];
} else {
str += input[i];
ch = input[i += 1];
}
}

if (prevPos >= 0) {
output.push_back(input.substr(prevPos + 1));
output.push_back(std::move(str));

if (ch == '\0') {
break;
}
}
}

@@ -184,7 +207,46 @@ void pkgFlags(const std::vector<std::string> &pkgs, std::vector<std::string> &fl

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

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

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

void getDependencies(
const std::vector<std::string> &flags,
SourceFile::FileType type,
const std::string &srcDir,
const std::string &name,
std::vector<std::string> &deps) {
std::string sourcePath = srcDir + "/" + name;

std::vector<const char *> argv;
argv.push_back(getCompilerFor(type));
for (auto &flag: flags) {
argv.push_back(flag.c_str());
}

argv.push_back("-MM");
argv.push_back("-c");
argv.push_back(sourcePath.c_str());
std::string output;
execute(argv, &output);

size_t idx = output.find(':');
if (idx != std::string::npos) {
parseWhitespaceSeparated(output, deps, idx + 1);
}
}

void compile(

+ 7
- 0
src/toolchain.h View File

@@ -10,6 +10,13 @@ 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 getDependencies(
const std::vector<std::string> &flags,
SourceFile::FileType type,
const std::string &srcDir,
const std::string &name,
std::vector<std::string> &deps);

void compile(
const std::vector<std::string> &flags,
SourceFile::FileType type,

Loading…
Cancel
Save