diff --git a/CMakeLists.txt b/CMakeLists.txt index 98b3a60e8e..dee46470cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,45 @@ else() endif() target_include_directories(libninja-re2c PRIVATE src) +# --- Create shared library --- +add_library(libninja_shared SHARED + src/build_log.cc + src/build.cc + src/clean.cc + src/clparser.cc + src/dyndep.cc + src/dyndep_parser.cc + src/debug_flags.cc + src/deps_log.cc + src/disk_interface.cc + src/edit_distance.cc + src/elide_middle.cc + src/eval_env.cc + src/graph.cc + src/graphviz.cc + src/json.cc + src/line_printer.cc + src/manifest_parser.cc + src/metrics.cc + src/missing_deps.cc + src/parser.cc + src/state.cc + src/status_printer.cc + src/string_piece_util.cc + src/util.cc + src/version.cc +) + +# Link re2c library if required +target_link_libraries(libninja_shared PRIVATE libninja-re2c) + +# Include directories for the shared library +target_include_directories(libninja_shared PUBLIC ${CMAKE_SOURCE_DIR}/src) + +# Set shared library properties +set_target_properties(libninja_shared PROPERTIES OUTPUT_NAME "ninja_shared") + + # --- Check for 'browse' mode support function(check_platform_supports_browse_mode RESULT) # Make sure the inline.sh script works on this platform. @@ -320,6 +359,7 @@ if(BUILD_TESTING) add_test(NAME NinjaTest COMMAND ninja_test) + endif() if(NINJA_BUILD_BINARY) diff --git a/README.md b/README.md index 7df4cfd20a..b03a06225d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Or your can do it manually: a. Before building simulate-ninja-graph, you need to compile and generate a shared object that includes partial functionality (functions) of Ninja. You can refer to lines 41 to 67 in `new-graph/easybuild.sh` to find the .cc files that need to be included. b. After generating the shared object, compile the `new-graph/simulate-ninja-graph.cc` file and link it with the previously built shared object. c. Once completed, run ./simulate-ninja-graph and pass hello_world as an argument (./simulate-ninja-graph hello_world). This program will look for a `new-graph/hello_world.cc` file in the current folder and compile it. - 4. After the compilation, an executable file named `new-graph/hello_world` will be created, which you can run. +e. After the compilation, an executable file named `new-graph/hello_world` will be created, which you can run. (You can modify easybuild.sh based on your OS and compiler to simplify this process.) ## CMake diff --git a/commands.md b/commands.md new file mode 100644 index 0000000000..19f2ddf3e0 --- /dev/null +++ b/commands.md @@ -0,0 +1,23 @@ +mkdir build && cd build +cmake -S .. +make -j $(nproc) +ls +ctest +sudo cp ninja /usr/local/bin/ (move nin to local/bin) +ninja --version + +(cd ..) +git clone https://github.com/llvm/llvm-project.git +cd llvm-project +mkdir build +cd build +cmake -G "Ninja" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_BUILD_TYPE=Release ../llvm +ninja clang-tidy +export PATH=$PATH:/home/yuwei/Documents/llvm-project/build/bin +clang-tidy --version + + +cmake -S .. -B build-cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +cmake --build build-cmake --target run-clang-tidy + + diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index c4b2980164..8655c0d565 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -29,8 +29,7 @@ using namespace std; ManifestParser::ManifestParser(State* state, FileReader* file_reader, ManifestParserOptions options) - : Parser(state, file_reader), - options_(options), quiet_(false) { + : Parser(state, file_reader), options_(options), quiet_(false) { env_ = &state->bindings_; } @@ -87,14 +86,12 @@ bool ManifestParser::Parse(const string& filename, const string& input, case Lexer::NEWLINE: break; default: - return lexer_.Error(string("unexpected ") + Lexer::TokenName(token), - err); + return lexer_.Error(string("unexpected ") + Lexer::TokenName(token), err); } } return false; // not reached } - bool ManifestParser::ParsePool(string* err) { string name; if (!lexer_.ReadIdent(&name)) @@ -131,7 +128,6 @@ bool ManifestParser::ParsePool(string* err) { return true; } - bool ManifestParser::ParseRule(string* err) { string name; if (!lexer_.ReadIdent(&name)) @@ -162,8 +158,10 @@ bool ManifestParser::ParseRule(string* err) { if (rule->bindings_["rspfile"].empty() != rule->bindings_["rspfile_content"].empty()) { - return lexer_.Error("rspfile and rspfile_content need to be " - "both specified", err); + return lexer_.Error( + "rspfile and rspfile_content need to be " + "both specified", + err); } if (rule->bindings_["command"].empty()) @@ -365,7 +363,7 @@ bool ManifestParser::ParseEdge(string* err) { edge->validations_.reserve(validations.size()); for (std::vector::iterator v = validations.begin(); - v != validations.end(); ++v) { + v != validations.end(); ++v) { string path = v->Evaluate(env); if (path.empty()) return lexer_.Error("empty path", err); @@ -386,9 +384,10 @@ bool ManifestParser::ParseEdge(string* err) { if (new_end != edge->inputs_.end()) { edge->inputs_.erase(new_end, edge->inputs_.end()); if (!quiet_) { - Warning("phony target '%s' names itself as an input; " - "ignoring [-w phonycycle=warn]", - out->path().c_str()); + Warning( + "phony target '%s' names itself as an input; " + "ignoring [-w phonycycle=warn]", + out->path().c_str()); } } } @@ -403,7 +402,7 @@ bool ManifestParser::ParseEdge(string* err) { edge->dyndep_ = state_->GetNode(dyndep, slash_bits); edge->dyndep_->set_dyndep_pending(true); vector::iterator dgi = - std::find(edge->inputs_.begin(), edge->inputs_.end(), edge->dyndep_); + std::find(edge->inputs_.begin(), edge->inputs_.end(), edge->dyndep_); if (dgi == edge->inputs_.end()) { return lexer_.Error("dyndep '" + dyndep + "' is not an input", err); } diff --git a/src/manifest_parser.h b/src/manifest_parser.h index db6812dce4..98f81fe45f 100644 --- a/src/manifest_parser.h +++ b/src/manifest_parser.h @@ -51,7 +51,7 @@ struct ManifestParser : public Parser { std::string* err); /// Parse various statement types. - bool ParsePool(std::string* err); + bool ParsePool(std::string* err); // wokers bool ParseRule(std::string* err); bool ParseLet(std::string* key, EvalString* val, std::string* err); bool ParseEdge(std::string* err); diff --git a/src/ninja.cc b/src/ninja.cc index 5d9cd55a6f..860f87d656 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -31,4 +31,4 @@ int main(int argc, char** argv) { #else real_main(argc, argv); #endif -} +} \ No newline at end of file diff --git a/src/test1.cc b/src/test1.cc new file mode 100644 index 0000000000..fcd3142f47 --- /dev/null +++ b/src/test1.cc @@ -0,0 +1,127 @@ +#include +#include + +#include "build.h" +#include "graph.h" +#include "state.h" +#include "util.h" +using namespace std; + +void CreateHelloWorldGraph(State* state) { + BindingEnv env; + env.AddBinding("in", "hello_world.cpp"); + env.AddBinding("out", "hello_world"); + env.AddBinding("DEP_FILE", "hello_world.d"); + + // Create rules + Rule* cxx_compiler = new Rule("CXX_COMPILER__hello_world_"); + + // Create command + EvalString command; + command.AddText("g++ -std=gnu++11 "); + command.AddSpecial("$in"); + command.AddText(" -o "); + command.AddSpecial("$out"); + cxx_compiler->AddBinding("command", command); + + // Create description + EvalString description; + description.AddText("Building CXX object "); + description.AddSpecial("$out"); + cxx_compiler->AddBinding("description", description); + + // Add dependency file + EvalString depfile; + depfile.AddSpecial("$DEP_FILE"); + cxx_compiler->AddBinding("depfile", depfile); + + // Add dependency format + EvalString deps; + deps.AddText("gcc"); + cxx_compiler->AddBinding("deps", deps); + + // Add rule to state + state->bindings_.AddRule(cxx_compiler); + + Rule* cxx_linker = new Rule("CXX_EXECUTABLE_LINKER__hello_world_"); + EvalString link; + link.AddText("g++ "); + link.AddSpecial("$in"); + link.AddText(" -o "); + link.AddSpecial("$out"); + cxx_linker->AddBinding("command", link); + EvalString linkDescription; + description.AddText("Linking CXX object "); + description.AddSpecial("$out"); + cxx_linker->AddBinding("description", linkDescription); + state->bindings_.AddRule(cxx_linker); + + // Create nodes + Node* source_file = state->GetNode("hello_world.cpp", 0); + Node* object_file = + state->GetNode("CMakeFiles/hello_world.dir/hello_world.cpp.o", 0); + Node* executable = state->GetNode("hello_world", 0); + + // Create edges + string error_message = ""; + string* err_ptr = &error_message; + Edge* compile_edge = state->AddEdge(cxx_compiler); + if (!state->AddOut(compile_edge, object_file->path(), 0, err_ptr)) { + // Handle error + *err_ptr = "Failed to add output to compile edge\n"; + Error(error_message.c_str()); + return; + } + compile_edge->inputs_.push_back(source_file); + + Edge* link_edge = state->AddEdge(cxx_linker); + if (!state->AddOut(link_edge, executable->path(), 0, err_ptr)) { + // Handle error + *err_ptr = "Failed to add output to link edge\n"; + Error(error_message.c_str()); + return; + } + link_edge->inputs_.push_back(object_file); + + // Set up environment for compile edge + if (compile_edge->env_ == nullptr) { + compile_edge->env_ = new BindingEnv(&state->bindings_); + } + compile_edge->env_->AddBinding( + "DEP_FILE", "CMakeFiles/hello_world.dir/hello_world.cpp.o.d"); + + // Add default target + if (!state->AddDefault(executable->path(), err_ptr)) { + // Handle error + *err_ptr = "Failed to add default target: \n"; + Error(error_message.c_str()); + return; + } +} + +void Error(const string& message) { + cerr << "Error: " << message << endl; +} + +int main() { + State state; + // Create Graph + CreateHelloWorldGraph(&state); + // Create the Builder object + Builder builder(&state); + + string err; + if (!builder.AddTarget("hello_world", &err)) { + Error("Failed to add build target: " + err); + return 1; + } + + if (!builder.Build(&err)) { + Error("Build failed: " + err); + return 1; + } + + cout << "Build completed successfully!" << std::endl; + return 0; + return 0; +} \ No newline at end of file diff --git a/steps/hello-world-graph/CMakeLists.txt b/steps/hello-world-graph/CMakeLists.txt new file mode 100644 index 0000000000..7172433d3d --- /dev/null +++ b/steps/hello-world-graph/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.10) + +# Set the project name +project(HelloWorld) + +# Specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Add the executable +add_executable(hello_world hello_world.cpp) + +# Optional: You can set the value of N here +# target_compile_definitions(hello_world PRIVATE N="CMake") \ No newline at end of file diff --git a/steps/hello-world-graph/README.md b/steps/hello-world-graph/README.md new file mode 100644 index 0000000000..bbc80bb76d --- /dev/null +++ b/steps/hello-world-graph/README.md @@ -0,0 +1,4 @@ +### Command for Hello World Graph +cmake -G "Ninja" -S .. +ninja +ninja -t graph | dot -Tpng -ograph.png \ No newline at end of file diff --git a/steps/hello-world-graph/commands.md b/steps/hello-world-graph/commands.md new file mode 100644 index 0000000000..8290665fe3 --- /dev/null +++ b/steps/hello-world-graph/commands.md @@ -0,0 +1,48 @@ +/home/yuwei/Documents/ShadowDash/src + +export LD_LIBRARY_PATH=/home/yuwei/Documents/ShadowDash/build:$LD_LIBRARY_PATH + +g++ -I /home/yuwei/Documents/ShadowDash/src -L /home/yuwei/Documents/ShadowDash/build -lninja_shared -o test1 test1.cc +or +g++ -I /home/yuwei/Documents/ShadowDash/src -L /home/yuwei/Documents/ShadowDash/build -lninja_shared -Wl,-rpath,/home/yuwei/Documents/ShadowDash/build -o test1 test1.cc + +g++ eval_env.cc test1.cc state.cc graph.cc -o exe.out + +### SO +g++ -fPIC -shared -I../../src ../../src/build_log.cc \ + ../../src/build.cc \ + ../../src/clean.cc \ + ../../src/clparser.cc \ + ../../src/dyndep.cc \ + ../../src/dyndep_parser.cc \ + ../../src/debug_flags.cc \ + ../../src/deps_log.cc \ + ../../src/disk_interface.cc \ + ../../src/edit_distance.cc \ + ../../src/elide_middle.cc \ + ../../src/eval_env.cc \ + ../../src/graph.cc \ + ../../src/graphviz.cc \ + ../../src/json.cc \ + ../../src/line_printer.cc \ + ../../src/manifest_parser.cc \ + ../../src/metrics.cc \ + ../../src/missing_deps.cc \ + ../../src/parser.cc \ + ../../src/state.cc \ + ../../src/status_printer.cc \ + ../../src/string_piece_util.cc \ + ../../src/util.cc \ + ../../src/subprocess-posix.cc \ + ../../src/lexer.cc \ + ../../src/depfile_parser.cc \ + ../../src/version.cc -o libninja.so +g++ -fPIC test1.cc -I../../src -L./ -lninja -Wl,-rpath=. -o test1 + +### For debug +g++ -g test1.cc -I../../src -L./ -lninja -Wl,-rpath=. -o test1 +gdb ./test1 +sharedlibrary libninja.so +run +backtrace +break xx \ No newline at end of file diff --git a/steps/hello-world-graph/graph.png b/steps/hello-world-graph/graph.png new file mode 100644 index 0000000000..47bfc5783c Binary files /dev/null and b/steps/hello-world-graph/graph.png differ diff --git a/steps/hello-world-graph/hello_world.cpp b/steps/hello-world-graph/hello_world.cpp new file mode 100644 index 0000000000..27bacf1f8f --- /dev/null +++ b/steps/hello-world-graph/hello_world.cpp @@ -0,0 +1,10 @@ +#include + +#ifndef N +#define N "World" +#endif + +int main() { + printf("Hello %s! \n", N); + return 0; +} \ No newline at end of file diff --git a/steps/hello-world-graph/libninja.so b/steps/hello-world-graph/libninja.so new file mode 100755 index 0000000000..6e32c4a7b1 Binary files /dev/null and b/steps/hello-world-graph/libninja.so differ diff --git a/steps/hello-world-graph/test1.cc b/steps/hello-world-graph/test1.cc new file mode 100644 index 0000000000..a5d73d60e4 --- /dev/null +++ b/steps/hello-world-graph/test1.cc @@ -0,0 +1,194 @@ +#include +#include +#include +#include + +#include "build.h" +#include "build_log.h" +#include "deps_log.h" +#include "disk_interface.h" +#include "graph.h" +#include "state.h" +#include "status.h" +#include "util.h" +using namespace std; + +void CreateHelloWorldGraph(State* state) { + BindingEnv env; + env.AddBinding("in", "hello_world.cpp"); + env.AddBinding("out", "hello_world"); + env.AddBinding("DEP_FILE", "hello_world.d"); + + // Create rules + Rule* cxx_compiler = new Rule("CXX_COMPILER__hello_world_"); + + // Create command + EvalString command; + command.AddText("g++ -std=gnu++11 -MMD -MF "); + command.AddSpecial("$in"); + command.AddText(" -o "); + command.AddSpecial("$out"); + cxx_compiler->AddBinding("command", command); + + // Create description + EvalString description; + description.AddText("Building CXX object "); + description.AddSpecial("$out"); + cxx_compiler->AddBinding("description", description); + + // Add dependency file + EvalString depfile; + depfile.AddSpecial("$DEP_FILE"); + cxx_compiler->AddBinding("depfile", depfile); + + // Add dependency format + EvalString deps; + deps.AddText("g++"); + cxx_compiler->AddBinding("deps", deps); + + // Add rule to state + state->bindings_.AddRule(cxx_compiler); + + Rule* cxx_linker = new Rule("CXX_EXECUTABLE_LINKER__hello_world_"); + EvalString link; + link.AddText("g++ "); + link.AddSpecial("$in"); + link.AddText(" -o "); + link.AddSpecial("$out"); + cxx_linker->AddBinding("command", link); + EvalString linkDescription; + description.AddText("Linking CXX object "); + description.AddSpecial("$out"); + cxx_linker->AddBinding("description", linkDescription); + state->bindings_.AddRule(cxx_linker); + + // Create nodes + Node* source_file = state->GetNode("hello_world.cpp", 0); + Node* object_file = + state->GetNode("CMakeFiles/hello_world.dir/hello_world.cpp.o", 0); + Node* executable = state->GetNode("hello_world", 0); + + // Create edges + // cout << "Binding 'in': " << env.LookupVariable("in") << endl; + // cout << "Binding 'out': " << env.LookupVariable("out") << endl; + // cout << "Binding 'DEP_FILE': " << env.LookupVariable("DEP_FILE") << endl; + string error_message; + string* err_ptr = &error_message; + Edge* compile_edge = state->AddEdge(cxx_compiler); + compile_edge->env_ = &env; + if (!state->AddOut(compile_edge, object_file->path(), 0, err_ptr)) { + // Handle error + *err_ptr = "Failed to add output to compile edge\n"; + Error(error_message.c_str()); + return; + } + compile_edge->inputs_.push_back(source_file); + + Edge* link_edge = state->AddEdge(cxx_linker); + if (!state->AddOut(link_edge, executable->path(), 0, err_ptr)) { + // Handle error + *err_ptr = "Failed to add output to link edge\n"; + Error(error_message.c_str()); + return; + } + link_edge->inputs_.push_back(object_file); + + // Set up environment for compile edge + if (compile_edge->env_ == nullptr) { + compile_edge->env_ = new BindingEnv(&state->bindings_); + } + compile_edge->env_->AddBinding( + "DEP_FILE", "CMakeFiles/hello_world.dir/hello_world.cpp.o.d"); + + // Add default target + if (!state->AddDefault(executable->path(), err_ptr)) { + // Handle error + *err_ptr = "Failed to add default target: \n"; + Error(error_message.c_str()); + return; + } +} + +void CreateLogFile(const string& path) { + ifstream infile(path); + if (!infile.good()) { + ofstream outfile(path); + if (outfile.is_open()) { + cout << "Log file created: " << path << endl; + } else { + cerr << "Failed to create log file: " << path << endl; + } + } else { + cout << "Log file already exists: " << path << endl; + } +} + +void Error(const string& message) { + cerr << "Error: " << message << endl; +} + +int main() { + cout << "Build start" << endl; + + State state; + // Create Graph + CreateHelloWorldGraph(&state); + + // Config with Verbose output and 1 process + BuildConfig config; + config.verbosity = config.VERBOSE; + config.parallelism = 1; // 1 process + + BuildLog build_log; + DepsLog deps_log; + RealDiskInterface disk_interface; + Status* status = Status::factory(config); + + // Create log file + const string log_path = "build_log"; + string build_message; + CreateLogFile(log_path); + const string deps_path = "deps_log"; + string deps_message; + CreateLogFile(deps_path); + + if (!build_log.Load(log_path, &build_message)) { + Error("Failed to load build log"); + delete status; + return 1; + } + + if (!deps_log.Load(deps_path, &state, &deps_message)) { + Error("Failed to load dependencies log"); + delete status; + return 1; + } + + // Get currrent timestamp(ms) + int64_t start_time_millis = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + // Create the Builder object + Builder builder(&state, config, &build_log, &deps_log, &disk_interface, + status, start_time_millis); + + string err; + if (!builder.AddTarget("hello_world", &err)) { + Error("Failed to add build target: " + err); + delete status; + return 1; + } + + if (!builder.Build(&err)) { + Error("Build failed: " + err); + delete status; + return 1; + } + + cout << "Build completed" << endl; + + delete status; + return 0; +} diff --git a/test1 b/test1 new file mode 100755 index 0000000000..5c10a4122e Binary files /dev/null and b/test1 differ