Просмотр исходного кода

compiling, dependency tracking, etc

feature/dependency-graph
Martin Dørum 5 лет назад
Родитель
Сommit
5a062a3d43
11 измененных файлов: 274 добавлений и 64 удалений
  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 Просмотреть файл

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 BUILD = build
OBJS = $(patsubst %,$(BUILD)/%.o,$(SRCS)) OBJS = $(patsubst %,$(BUILD)/%.o,$(SRCS))
CFLAGS = -g -Wall -Wextra -Wno-unused-parameter CFLAGS = -g -Wall -Wextra -Wno-unused-parameter

+ 1
- 0
example-project/hello world.c Просмотреть файл

int x = 10;

+ 3
- 0
example-project/hello world.h Просмотреть файл

#pragma once

extern int x;

+ 3
- 2
example-project/main.c Просмотреть файл

#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>


#include "hello world.h"

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


int main() { 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 Просмотреть файл

#include <sstream> #include <sstream>
#include <string.h> #include <string.h>


#include "toolchain.h"

static bool startsWith(BBBParser &parser, const char *str) { static bool startsWith(BBBParser &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])
SourceFile::FileType SourceFile::fileTypeFrom(const std::string &name) { SourceFile::FileType SourceFile::fileTypeFrom(const std::string &name) {
size_t idx = name.find_last_of('.'); size_t idx = name.find_last_of('.');
if (idx >= std::string::npos) { if (idx >= std::string::npos) {
printf("%s: no ftype\n", name.c_str());
return FileType::UNKNOWN; return FileType::UNKNOWN;
} }


return FileType::UNKNOWN; 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_); std::ifstream file(dir_ + "/" + name_);
BBBParser parser(file, BBBParser::FLAG_ONE_LINE); BBBParser parser(file, BBBParser::FLAG_ONE_LINE);
while (file.good()) { while (file.good()) {
if (startsWith(parser, "//#bb")) { if (startsWith(parser, "//#bb")) {
parser.parse(vars_); parser.parse(vars_);
contains_bbb_ = true;
} else { } else {
while (file.good() && parser.get() != '\n'); while (file.good() && parser.get() != '\n');
} }


return &it->second; 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 Просмотреть файл



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


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


class SourceFile { class SourceFile {
public: public:


static FileType fileTypeFrom(const std::string &name); 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 &dir() const { return dir_; }
const std::string &name() const { return name_; } const std::string &name() const { return name_; }
const BBBParser::Variables &vars() const { return vars_; } const BBBParser::Variables &vars() const { return vars_; }


const std::vector<std::string> *variable(const std::string &name) const; 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: private:
std::string dir_; std::string dir_;
std::string name_; std::string name_;
FileType type_; FileType type_;
BBBParser::Variables vars_; 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 Просмотреть файл

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <getopt.h> #include <getopt.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <mutex>


#include "SourceFile.h" #include "SourceFile.h"
#include "BBBParser.h" #include "BBBParser.h"
static void readPath(std::string dir, std::string name, static void readPath(std::string dir, std::string name,
std::vector<SourceFile> &sources, BBBParser::Variables vars) { std::vector<SourceFile> &sources, BBBParser::Variables vars) {
std::string path = dir + "/" + name; 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' // We don't want to send 'files'
BBBParser::Variables subvars = vars; BBBParser::Variables subvars = vars;
subvars.erase("files"); subvars.erase("files");
} }
} }


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) { int main(int argc, char **argv) {


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

break; break;


case Action::PRINT_STATE: case Action::PRINT_STATE:

+ 39
- 0
src/sys.cc Просмотреть файл

#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 Просмотреть файл

#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 Просмотреть файл

} }
} }


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




std::string output; std::string output;
execute(argv, &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( void compile(

+ 7
- 0
src/toolchain.h Просмотреть файл

void pkgFlags(const std::vector<std::string> &pkgs, std::vector<std::string> &flags); 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 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( void compile(
const std::vector<std::string> &flags, const std::vector<std::string> &flags,
SourceFile::FileType type, SourceFile::FileType type,

Загрузка…
Отмена
Сохранить