diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..d9c58a9fed --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,76 @@ + +version: 2.1 +jobs: + build-test: + docker: + - image: cimg/base:current + steps: + # Checkout the code as the first step. + - checkout + - run: + name: InstallPreq + command: | + echo "Installing clang-tidy" + sudo apt-get update && sudo apt-get install -y clang-tidy + echo "Installed" + - run: + name: Build + command: | + echo "Building" + mkdir build && cd build + cmake -S .. + make -j $(nproc) + cd ../ + echo "Built" + - run: + name: ninjaTest + command: | + cd build + ./ninja_test --gtest_output=XML + echo "Testing Done" + - store_test_results: + path: build + - run: + name: ctest + command: | + echo "Start Testing" + cd build + ctest --output-junit results.xml + echo "Testing Done" + - store_test_results: + path: build/results.xml + - run: + name: Run Performance Tests + command: | + cd build + for test in build_log_perftest canon_perftest clparser_perftest elide_middle_perftest hash_collision_bench; do + echo "Running $test" + ./$test + done + - run: + name: Run Compile .o and .so Tests + command: | + check_success() { + if [ $? -eq 0 ]; then + echo "$1" + else + echo "$1" + exit 1 + fi + } + + cd src/shadowdash + + g++ -w -std=c++17 -fPIC -c manifest.cc manifest.h + check_success ".o compiled successfully" + + g++ -w -shared -o manifest.so manifest.o + check_success ".so compiled successfully" + + cd ../.. + + +workflows: + build-test-workflow: + jobs: + - build-test \ No newline at end of file diff --git a/.gitignore b/.gitignore index ca36ec8810..5e6932bbad 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,10 @@ # Visual Studio files /.vs/ /out/ + +commands.md + +# New Language files +src/shadowdash/manifest.o +src/shadowdash/manifest.so +src/shadowdash/manifest.h.gch \ No newline at end of file diff --git a/src/shadowdash/manifest.cc b/src/shadowdash/manifest.cc new file mode 100644 index 0000000000..8d39c75cfa --- /dev/null +++ b/src/shadowdash/manifest.cc @@ -0,0 +1,87 @@ +#include "manifest.h" + +using namespace shadowdash; + +BuildsReturn manifest() { + // example of defining a variable named "flags" + // the value "-03" is a literal token + let(flags, "-O3"); // original language: flags = -03 + let(pool_depth, "4"); // original language: pool_depth = 4 + // variable examples + + // examples of creating pool objects: + auto heavy_object_pool = pool_(bind(depth, "pool_depth"_v)); + // passing the argument via macro + auto heavy_object_pool2 = pool_(binding("depth", {"pool_depth"_v})); + // passing the argument via creating a binding object + /* + in original language: + pool heavy_object_pool: + depth = pool_depth + */ + // pool examples + + // examples of creating rules in the new language + make_rule(compile, + bind(command, "g++", "flags"_v, "-c", in, "-o", out), + bind(pool, "heavy_object_pool"_v) + ); + /* + in original language: + rule compile: + command = gcc $flags -c $in -o $out + pool = heavy_object_pool + */ + + make_rule(link, + bind(command, "g++", in, "-o", out) + ); + // rule examples + + // examples of creating builds in the new language: + auto build_c = build(list{ str{ "hello.o" } }, + {}, + compile, + list{ str{ "hello.cc" } }, + {}, + {}, + { bind(flags, "-O2"), + bind(pool, "console"_v) } + ); + /* + in original language: + build hello.o : compile hello.cc + flags = -02 + pool = console + */ + + auto build_l = build(list{ str{ "hello" } }, + {}, + link, + list{ str{ "hello.o" } }, + {}, + {}, + {} + ); + + auto build_p = build(list{ str{ "dummy" } }, + {}, + phony, + {}, + {}, + {}, + {} + ); + // build examples + + // examples of adding defaults in new language + default_(str{ "hello" }); + default_(list{ str{ "foo1" }, str{ "foo2" } }); + /* + in original language: + default hello + default foo1 foo2 + */ + // default examples + return {build_c, build_l, build_p}; // returns all the builds +} \ No newline at end of file diff --git a/src/shadowdash/manifest.h b/src/shadowdash/manifest.h new file mode 100644 index 0000000000..315fb88539 --- /dev/null +++ b/src/shadowdash/manifest.h @@ -0,0 +1,243 @@ +#pragma once + +#include +#include +#include +#include + +namespace shadowdash { + +// The atom "string" object in the new language with type identifying whether +// a string object is a "Literal" or a "Variable" +class Token { +public: + enum Type {LITERAL, VAR}; + // Token type: LITERAL (constant) or VAR (variable). + + // Constructor for a Token with a specified type and value. + constexpr Token(Type type, std::string_view value) : + type_(type), value_(value) {} + + // Constructor for a Token defaulting to LITERAL type. + constexpr Token(std::string_view value) : Token(Token::LITERAL, value) {} + + // Constructor for a Token from a C-style string, defaulting to LITERAL. + constexpr Token(const char* value) : Token(Token::LITERAL, value) {} + + Type type_; // The type of the token. + std::string_view value_; // The value of the token (string content). +}; + +// User-defined literal to create a LITERAL token. +constexpr Token operator"" _l(const char* value, std::size_t len) { + return Token(Token::Type::LITERAL, { value, len }); +} + +// User-defined literal to create a VAR token. +constexpr Token operator"" _v(const char* value, std::size_t len) { + return Token(Token::Type::VAR, { value, len }); +} + +// Represents a string as a collection of tokens. +class str { +public: + // Constructor accepting an initializer list of tokens. + str(std::initializer_list tokens) : tokens_(tokens) {} + + // Returns the number of tokens in the string. + constexpr size_t size() const { return tokens_.size(); } + + // Access a specific token by index. + constexpr const Token& operator[](size_t i) const + { return *(tokens_.begin() + i); } + + std::initializer_list tokens_; + // The list of tokens forming the string. +}; + +// Alias for a name-value binding, where the value is a `str`. +// typically used when passing in arguments for rules, builds, etc. +// example: depth = 4 -> binding("depth", {"4"}) in binding language +using binding = std::pair; + +// map: alias for an initializaiton_list of bindings +// represents a list of all the arguments +using map = std::initializer_list; + +// Represents a list of `str` objects. +class list { +public: + // Constructor accepting an initializer list of `str` objects. + list(std::initializer_list values) : values_(values) {} + + // Returns the size of the list. + constexpr size_t size() const { return values_.size(); } + + // Access a specific `str` by index. + constexpr const str& operator[](size_t i) const + { return *(values_.begin() + i); } + + std::initializer_list values_; // The list of strings. +}; + +// Ninja variables with attributes of name and value +class var { +public: + const char* name_; + str value_; + + var(const char* name, str value) : name_(name), value_(value) {} +}; + +// Ninja rule object +class rule { +public: + // Constructor with a name and bindings. + constexpr rule(std::string_view name, map bindings) : + name(name), bindings_(bindings) {} + + // Constructor with only a name. + constexpr rule(std::string_view name) : name(name) {} + + // Get the name of the rule. + constexpr std::string_view get_rule_name() const { return name; } + + // Returns the number of bindings in the rule. + constexpr size_t size() const { return bindings_.size(); } + + // Access a specific binding by index. + constexpr const binding& operator[](size_t i) const + { return *(bindings_.begin() + i); } + + std::string_view name; // Name of the rule. + map bindings_; + // + /* + bindings_: { + (type binding, {op1, {args}}), + (type binding, {op2, {args}}), + ... + } + records operations that will be executed within this rule + as well as arguments required for each operation in list + of tokens + */ + // +}; + +// Exception class for invalid pool arguments. +class PoolException : public std::exception { +public: + // Error message for invalid pool arguments. + const char* what() const noexcept override { + return "Pool has argument that is not depth!"; + } +}; + +// Represents a pool with a depth binding. +class pool_ { +public: + // Constructor that validates the binding is for "depth". + pool_(binding depth) : depth_(depth) { + if (depth.first != "depth") throw PoolException(); + // throw an excpetion when pool_ objects received arguments other than depth + } + + // Get the depth binding. + constexpr const binding& get_depth() const { return depth_; } + + binding depth_; // The depth argument of pool. +}; + +// Represents a build configuration with inputs, outputs, and rules. +class build { +public: + build( + list outputs, // List of output files. + list implicit_outputs, // Implicit output files. + rule& rule, // Associated build rule. + list inputs, // List of input files. + list implicit_inputs, // Implicit input files. + list order_only_inputs, // Order-only dependencies. + map bindings // Additional arguments. + ) : outputs_(outputs), + implicit_outputs_(implicit_outputs), + rule_(rule), + inputs_(inputs), + implicit_inputs_(implicit_inputs), + order_only_inputs_(order_only_inputs), + bindings_(bindings) {} + + list outputs_; // Outputs of the build. + list implicit_outputs_; // Implicit outputs. + rule& rule_; // Associated rule. + list inputs_; // Inputs of the build. + list implicit_inputs_; // Implicit inputs. + list order_only_inputs_; // Order-only dependencies. + map bindings_; // Additional arguments. +}; + +// Represents a collection of builds returned by a manifest. +struct BuildsReturn { + // Constructor accepting a list of builds. + BuildsReturn(std::initializer_list builds) : builds(builds) {} + + // Returns the number of builds. + constexpr size_t size() const { return builds.size(); } + + // Access a specific build by index. + constexpr const build& operator[](size_t i) const + { return *(builds.begin() + i); } + + std::initializer_list builds; // List of builds. +}; + +// Represents default build targets. +class default_ { +public: + // Add a list of targets to the defaults. + default_(list addedList) { + for (size_t i = 0; i < addedList.size(); i++) + targets_.push_back(addedList[i]); + } + + // Add a single target to the defaults. + default_(str addedTarget) { + targets_.push_back(addedTarget); + } + + static std::vector targets_; // List of default targets. +}; + +std::vector default_::targets_; // Definition of static targets list. + +// Predefined constants for common variables. +static constexpr auto in = "in"_v; // Input variable, used in rules +static constexpr auto out = "out"_v; // Output variable, used in rules +static auto phony = rule{{}}; // Default phony rule. +static auto console = pool_(binding("depth", str{ {"1"} })); +// Default console pool. + +} // namespace shadowdash + +// Macro to define a variable with a name and value(s). +#define let(name, ...) \ + var name { \ + #name, str { \ + __VA_ARGS__ \ + } \ + } + +// Macro to create a binding with a name and value(s). +#define bind(name, ...) \ + { \ + #name, str { \ + __VA_ARGS__ \ + } \ + } + +// Macro to define a rule with a name and bindings. +#define make_rule(name, ...) \ +rule name { \ + #name, {__VA_ARGS__} \ +} \ No newline at end of file