Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 14 additions & 95 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,100 +1,19 @@
# Ninja

Ninja is a small build system with a focus on speed.
https://ninja-build.org/

See [the manual](https://ninja-build.org/manual.html) or
`doc/manual.asciidoc` included in the distribution for background
and more details.

Binaries for Linux, Mac and Windows are available on
[GitHub](https://github.com/ninja-build/ninja/releases).
Run `./ninja -h` for Ninja help.

Installation is not necessary because the only required file is the
resulting ninja binary. However, to enable features like Bash
completion and Emacs and Vim editing modes, some files in misc/ must be
copied to appropriate locations.

If you're interested in making changes to Ninja, read
[CONTRIBUTING.md](CONTRIBUTING.md) first.

## Building Ninja itself

You can either build Ninja via the custom generator script written in Python or
via CMake. For more details see
[the wiki](https://github.com/ninja-build/ninja/wiki).

### Python

```
./configure.py --bootstrap
# Shadowdash Converter

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this replacing the stock ninja readme!?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that is a part of the ninja like here, I believe rewriting the stock ninja readme is kinda wrong in this case

## Build
```sh
./build.sh
```
This will create the converter in the `debug-build` directory with debug symbols.

This will generate the `ninja` binary and a `build.ninja` file you can now use
to build Ninja with itself.

If you have a GoogleTest source directory, you can build the tests
by passing its path with `--gtest-source-dir=PATH` option, or the
`GTEST_SOURCE_DIR` environment variable, e.g.:

```
./configure.py --bootstrap --gtest-source-dir=/path/to/googletest
./ninja all # build ninja_test and other auxiliary binaries
./ninja_test` # run the unit-test suite.
```

Use the CMake build below if you want to use a preinstalled binary
version of the library.

### CMake

```
cmake -Bbuild-cmake
cmake --build build-cmake
## Run
```sh
./debug-build/ninja
```
Run the above in a directory where a `build.ninja` file is present

The `ninja` binary will now be inside the `build-cmake` directory (you can
choose any other name you like).

To run the unit tests:

## Testing
```sh
cd converter/testing/
../../debug-build/ninja
```
./build-cmake/ninja_test
```

## Generating documentation

### Ninja Manual

You must have `asciidoc` and `xsltproc` in your PATH, then do:

```
./configure.py
ninja manual doc/manual.pdf
```

Which will generate `doc/manual.html`.

To generate the PDF version of the manual, you must have `dblatext` in your PATH then do:

```
./configure.py # only if you didn't do it previously.
ninja doc/manual.pdf
```

Which will generate `doc/manual.pdf`.

### Doxygen documentation

If you have `doxygen` installed, you can build documentation extracted from C++
declarations and comments to help you navigate the code. Note that Ninja is a standalone
executable, not a library, so there is no public API, all details exposed here are
internal.

```
./configure.py # if needed
ninja doxygen
```

Then open `doc/doxygen/html/index.html` in a browser to look at it.
This will run the converter on the hello world example `build.ninja` file and produce `output.cc` as a result.
2 changes: 2 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cmake -DCMAKE_BUILD_TYPE=Debug -B debug-build
cmake --build debug-build --parallel --config Debug --target ninja
98 changes: 98 additions & 0 deletions converter/archive/convert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import argparse

def parse_ninja_file(ninja_file_content):
rules = {}
builds = []
current_rule = None
default_target = None

lines = ninja_file_content.splitlines()

for line in lines:
line = line.strip()
if line.startswith("rule"):
current_rule = line.split()[1]
rules[current_rule] = {"command": None, "flags": "-O3"}
elif line.startswith("command ="):
if current_rule:
rules[current_rule]["command"] = line.split("=", 1)[1].strip()
elif line.startswith("flags ="):
if current_rule:
rules[current_rule]["flags"] = line.split("=", 1)[1].strip()
elif line.startswith("build"):
parts = line.split()
output, rule, inputs = parts[1], parts[2], parts[3:]
builds.append((output, rule, inputs))
elif line.startswith("default"):
default_target = line.split()[1]

return rules, builds, default_target

def generate_manifest_cc(rules, builds, default_target):
cc_code = '#include "../manifest.h"\n\nusing namespace shadowdash;\n\nvoid manifest() {\n'
cc_code += f' let(flags, "{rules["compile"]["flags"]}");\n\n'

for rule, details in rules.items():
cc_code += f' auto {rule} = rule{{ {{\n'
command = details["command"].replace("$flags", '"flags"_v').replace("$in", "in").replace("$out", "out")
cc_code += f' bind(command, {command}), //\n'
cc_code += f' }} }};\n\n'

for build in builds:
output, rule, inputs = build
inputs_str = ", ".join([f'str{{ "{inp}" }}' for inp in inputs])
if rule == "compile" and "flags" in rules["compile"] and rules["compile"]["flags"] != "-O3":
cc_code += f' build(list{{ str{{ "{output}" }} }}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' {rule}, //\n'
cc_code += f' list{{ {inputs_str} }}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' {{ bind(flags, "{rules["compile"]["flags"]}") }} //\n'
cc_code += f' );\n\n'
else:
cc_code += f' build(list{{ str{{ "{output}" }} }}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' {rule}, //\n'
cc_code += f' list{{ {inputs_str} }}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' {{}} //\n'
cc_code += f' );\n\n'

cc_code += f' build(list{{ str{{ "{default_target}" }} }}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' link, //\n'
cc_code += f' list{{ str{{ "{default_target}.o" }} }}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' {{}}, //\n'
cc_code += f' {{}} //\n'
cc_code += f' );\n'

cc_code += '}\n'
return cc_code

def main():
# argument parser for input and output file names
parser = argparse.ArgumentParser(description="Convert a build.ninja file to a build.ninja.cc file.")
parser.add_argument('input_file', help="Path to the input build.ninja file")
parser.add_argument('output_file', help="Path to the output build.ninja.cc file")

args = parser.parse_args()

# read input file
with open(args.input_file, 'r') as f:
ninja_file_content = f.read()

# parse the ninja file
rules, builds, default_target = parse_ninja_file(ninja_file_content)

# generate the manifest.cc content
manifest_cc_content = generate_manifest_cc(rules, builds, default_target)

# write the output to the file
with open(args.output_file, 'w') as f:
f.write(manifest_cc_content)

if __name__ == "__main__":
main()
21 changes: 21 additions & 0 deletions converter/src/hooks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

void HandleRule(Rule* rule)
{
// output string
std::string output = "auto " + rule.name_ + " = rule{{bind(command, \"g++\", \"flags\"_v, \"-c\", ";

// Iterate over the parsed_ vector in the "command" binding
for (const auto& entry : rule.bindings_["command"].parsed_) {
if (entry.second == RAW) {
output += "\"" + entry.first + "\", "; // Add quotes for raw strings
} else if (entry.second == SPECIAL) {
output += entry.first + ", "; // No quotes for special variables
}
}

// Finish the string
output += ")}};";

// Print the result
std::cout << output << std::endl;
}
55 changes: 55 additions & 0 deletions converter/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <iostream>
#include <string>
#include <map>
#include <vector>

// Assuming you have these constants
enum EvalStringType { RAW, SPECIAL };

// Example structures based on your description
struct EvalString {
std::string first;
EvalStringType second;
};

struct Binding {
std::vector<EvalString> parsed_;
};

struct Rule {
std::string name_;
std::map<std::string, Binding> bindings_;
};

int main() {
// Simulate your object
Rule rule;
rule.name_ = "compile";
rule.bindings_["command"].parsed_ = {
{"g++ -c ", RAW},
{"in", SPECIAL},
{" -o ", RAW},
{"out", SPECIAL},
{" ", RAW}
};

// Begin the output string
std::string output = "auto " + rule.name_ + " = rule{{bind(command, \"g++\", \"flags\"_v, \"-c\", ";

// Iterate over the parsed_ vector in the "command" binding
for (const auto& entry : rule.bindings_["command"].parsed_) {
if (entry.second == RAW) {
output += "\"" + entry.first + "\", "; // Add quotes for raw strings
} else if (entry.second == SPECIAL) {
output += entry.first + ", "; // No quotes for special variables
}
}

// Finish the string
output += ")}};";

// Print the result
std::cout << output << std::endl;

return 0;
}
86 changes: 86 additions & 0 deletions converter/src/manifest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#pragma once

#include <initializer_list>
#include <string_view>

namespace shadowdash {

class Token {
public:
enum Type { LITERAL, VAR };

constexpr Token(Type type, std::string_view value)
: type_(type), value_(value) {}

constexpr Token(std::string_view value) : Token(Token::LITERAL, value) {}

constexpr Token(const char* value) : Token(Token::LITERAL, value) {}

Type type_;
std::string_view value_;
};

constexpr Token operator"" _l(const char* value, std::size_t len) {
return Token(Token::Type::LITERAL, { value, len });
}

constexpr Token operator"" _v(const char* value, std::size_t len) {
return Token(Token::Type::VAR, { value, len });
}

class str {
public:
str(std::initializer_list<Token> tokens) : tokens_(tokens) {}
std::initializer_list<Token> tokens_;
};

using binding = std::pair<std::string_view, str>;
using map = std::initializer_list<binding>;

class list {
public:
list(std::initializer_list<str> values) : values_(values) {}
std::initializer_list<str> values_;
};

class var {
public:
var(const char* name, str value) {}
};

class rule {
public:
rule(map bindings) {}
};

class build {
public:
build( //
list outputs, //
list implicit_outputs, //
rule& rule, //
list inputs, //
list implicit_inputs, //
list order_only_inputs, //
map bindings) //
{}
};

static constexpr auto in = "in"_v;
static constexpr auto out = "out"_v;

} // namespace shadowdash

#define let(name, ...) \
var name { \
#name, str { \
__VA_ARGS__ \
} \
}

#define bind(name, ...) \
{ \
#name, str { \
__VA_ARGS__ \
} \
}
Loading