Browse Source

Adding the first pass of a C++-based build system concept, as inspired
by reddit.

This pass contains the following features:
- Add sourcefiles
- Name executable
- Select C++ version from {C++11, C++14, C++17}
- Use named types to make the implementation details of things like '-c'
or '-o' hidden from the project.
- build.cxx builds the executable

Sam Jaffe 5 năm trước cách đây
mục cha
commit
f2b3aaa016
7 tập tin đã thay đổi với 5722 bổ sung0 xóa
  1. 9 0
      build.cxx
  2. 5 0
      build.h
  3. 94 0
      command_builder.h
  4. 5456 0
      ghc/filesystem.hpp
  5. 39 0
      main.cxx
  6. 83 0
      project.h
  7. 36 0
      types.h

+ 9 - 0
build.cxx

@@ -0,0 +1,9 @@
+#include "build.h"
+
+int main(int argc, char const * const argv[]) {
+  auto p = build::project(argc, argv);
+  p.set_name("make-build")
+      .set_version(build::version::cxx17)
+      .set_source_file("main.cxx");
+  return p.generate();
+}

+ 5 - 0
build.h

@@ -0,0 +1,5 @@
+#pragma once
+
+#include "command_builder.h"
+#include "project.h"
+#include "types.h"

+ 94 - 0
command_builder.h

@@ -0,0 +1,94 @@
+#pragma once
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "types.h"
+
+namespace build {
+
+  class command_builder {
+  private:
+    indent_t indent_;
+    std::stringstream buffer_;
+
+  public:
+    command_builder(std::string const & cmd, int indent = 0);
+    template <typename T> command_builder && operator<<(T const & value) &&;
+    // TODO(samjaffe): Use the type system for things here...
+    command_builder & operator<<(version const & vers) &;
+    command_builder & operator<<(source_file const & file) &;
+    command_builder & operator<<(output_file const & file) &;
+    template <typename T> command_builder & operator<<(T const & value) &;
+    template <typename T>
+    command_builder & operator<<(std::vector<T> const & value) &;
+    int execute() const;
+  };
+
+  command_builder::command_builder(std::string const & cmd, int indent)
+      : indent_{indent}, buffer_() {
+    buffer_ << cmd;
+  }
+
+  template <typename T>
+  command_builder && command_builder::operator<<(T const & value) && {
+    return std::move((*this) << value);
+  }
+
+  template <typename T>
+  command_builder & command_builder::operator<<(T const & value) & {
+    buffer_ << ' ' << value;
+    return *this;
+  }
+
+  command_builder & command_builder::operator<<(version const & vers) & {
+    switch (vers) {
+    case version::cxx11:
+      buffer_ << ' ' << "-std=c++11";
+      break;
+    case version::cxx14:
+      buffer_ << ' ' << "-std=c++14";
+      break;
+    case version::cxx17:
+      buffer_ << ' ' << "-std=c++17";
+      break;
+    case none:
+      break;
+    }
+    return *this;
+  }
+
+  command_builder & command_builder::operator<<(source_file const & file) & {
+    buffer_ << " -c " << file.get();
+    return *this;
+  }
+
+  command_builder & command_builder::operator<<(output_file const & file) & {
+    buffer_ << " -o " << file.get();
+    return *this;
+  }
+
+  template <typename T>
+  command_builder &
+  command_builder::operator<<(std::vector<T> const & value) & {
+    for (T const & v : value) {
+      (*this) << v;
+    }
+    return *this;
+  }
+
+  std::ostream & operator<<(std::ostream & os, indent_t const & value) {
+    for (int i = 0; i < value.value; ++i) {
+      os << "  ";
+    }
+    return os;
+  }
+
+  int command_builder::execute() const {
+    std::string cmdstr = buffer_.str();
+    std::cout << indent_ << cmdstr << std::endl;
+    return std::system(cmdstr.c_str());
+  }
+
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 5456 - 0
ghc/filesystem.hpp


+ 39 - 0
main.cxx

@@ -0,0 +1,39 @@
+#include "command_builder.h"
+
+// TODO(samjaffe): This function should perform a check to see if the
+// the buildfile actually needs to be recompiled.
+int compile_build_system() {
+  std::cout << "Generating Build System..." << std::endl;
+  // TODO(samjaffe): Magic constant for compiler
+  auto cmd = build::command_builder("clang++", 1)
+             // TODO(samjaffe): Detect the correct build source file
+             << build::version::cxx17 << "build.cxx"
+             << "-o"
+             << "./build";
+  int const rc = cmd.execute();
+  if (rc) {
+    std::cout << "Completed with errors, aborting" << std::endl;
+  } else {
+    std::cout << "Done!" << std::endl;
+  }
+  return rc;
+}
+
+int run_build_system(int const argc, char const * const argv[]) {
+  std::vector<char const *> arguments(argv + 1, argv + argc);
+  std::cout << "Building... ";
+  // TODO(samjaffe): Magic constant
+  auto cmd = build::command_builder("./build") << arguments;
+  int const rc = cmd.execute();
+  if (rc) {
+    std::cout << "Build failed!" << std::endl;
+  } else {
+    std::cout << "Success!" << std::endl;
+  }
+  return rc;
+}
+
+int main(int argc, char const * const argv[]) {
+  if (int rc = compile_build_system()) { return rc; }
+  return run_build_system(argc, argv);
+}

+ 83 - 0
project.h

@@ -0,0 +1,83 @@
+#pragma once
+
+// Because Apple refuses to update libc++ in a timely manner
+// In exchange, this in C++11 compatible...
+#include <string>
+#include <vector>
+
+#include "command_builder.h"
+#include "types.h"
+
+#define CXX "clang++"
+
+namespace build {
+
+  class project {
+  private:
+    version vers_{version::none};
+    std::string name_;
+    std::vector<source_file> source_files_;
+
+  public:
+    project(int argc, char const * const * const argv);
+
+    project & set_version(version vers);
+    project & set_name(std::string const & name);
+    project & set_source_file(fs::path const & file);
+
+    int generate() const;
+
+  private:
+    std::pair<std::vector<object_file>, int> generate_objects() const;
+  };
+
+  // TODO(samjaffe): Are there actually any arguments that belong here?
+  project::project(int argc, char const * const * const argv) {}
+
+  project & project::set_version(version vers) {
+    vers_ = vers;
+    return *this;
+  }
+
+  project & project::set_name(std::string const & name) {
+    name_ = name;
+    return *this;
+  }
+
+  project & project::set_source_file(fs::path const & file) {
+    if (!fs::is_regular_file(file)) {
+      throw std::logic_error(file.string() + " is not a regular file");
+    }
+    source_files_.emplace_back(file);
+    return *this;
+  }
+
+  std::pair<std::vector<object_file>, int> project::generate_objects() const {
+    fs::path obj_dir(".obj");
+    fs::create_directory(obj_dir);
+    std::vector<object_file> objects;
+    for (auto const & file : source_files_) {
+      output_file const outfile =
+          obj_dir / file.get().filename().replace_extension(".o");
+      auto cmd = command_builder(CXX, 1) << vers_ << file << outfile;
+      if (int rc = cmd.execute()) { return {{}, rc}; }
+      objects.emplace_back(outfile);
+    }
+    return {objects, 0};
+  }
+
+  int project::generate() const {
+    std::cout << "Generating Project: " << name_ << std::endl;
+    // Step 1: Compile Object files
+    auto [objects, rc] = generate_objects();
+    if (rc) { return rc; }
+
+    // Step 2: Generate Executable
+    fs::path bin_dir(".bin");
+    fs::create_directory(bin_dir);
+    output_file const outfile = bin_dir / name_;
+    auto cmd = command_builder(CXX, 1) << vers_ << outfile << objects;
+    return cmd.execute();
+  }
+
+}

+ 36 - 0
types.h

@@ -0,0 +1,36 @@
+#pragma once
+
+#include "ghc/filesystem.hpp"
+
+namespace build {
+
+  namespace fs = ghc::filesystem;
+
+  /**
+   * A simple opaque typedef which allows us to perform type-sensitive
+   * output while creating the command string to be invoked.
+   * For example, command_builder << output_file would automagically
+   * know to output the tokens: ["-o", output_file.get()].
+   */
+  template <typename T, typename> class named_type {
+  private:
+    T value_;
+
+  public:
+    named_type(T const & value) : value_(value) {}
+    named_type(T && value) : value_(std::move(value)) {}
+    T const & get() const { return value_; }
+    operator T const &() const { return value_; }
+  };
+
+  struct indent_t {
+    int value;
+  };
+
+  using source_file = named_type<fs::path, struct source_file_tag>;
+  using object_file = named_type<fs::path, struct object_file_tag>;
+  using output_file = named_type<fs::path, struct output_file_tag>;
+
+  enum version { none, cxx11, cxx14, cxx17 };
+
+}