From d88212f6e71926cbb7829378563df1d17793d40c Mon Sep 17 00:00:00 2001 From: Venelin Date: Sun, 17 Mar 2024 23:12:32 +0200 Subject: [PATCH] check if recompiling is needed by preprocessing --- commands.cpp | 34 +++++----- commands.hpp | 2 - cppipe.cpp | 172 +++++++++++++++++++++++++++++++++++++++------------ install.sh | 4 ++ test.cpp | 4 ++ todo.txt | 4 ++ 6 files changed, 162 insertions(+), 58 deletions(-) diff --git a/commands.cpp b/commands.cpp index e71977c..e3b7f50 100644 --- a/commands.cpp +++ b/commands.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -95,16 +96,6 @@ RetProc PendingCmd::operator()() return cmd(in, out, err); } -RetProc PendingCmd::runRedir() -{ - assert(!execed_ && "executed command twice"); assert(out==1 && "already redirected"); - assert(out==1 && "capturing redirected proccess"); - execed_ = true; - - Proc p = createCapProcess(cmd.argv.data(), in, err); - return wait(p); -} - Proc PendingCmd::detach() { assert(!execed_ && "Executed command twice"); @@ -115,6 +106,8 @@ Proc PendingCmd::detach() Proc PendingCmd::detachRedirOut() { assert(!execed_ && "Executed command twice"); assert(out==1 && "already redirected"); + assert(out==1 && "capturing redirected proccess"); + execed_ = true; return createCapProcess(cmd.argv.data(), in, err); } @@ -126,17 +119,28 @@ void PendingCmd::cancel() std::string $(const PendingCmd& cmd) { - RetProc p = const_cast(cmd).runRedir(); + Proc p = const_cast(cmd).detachRedirOut(); // check output size (not portable?) - int pipe_size; + // int pipe_size; // int rc = ioctl(p.out, FIONREAD, &pipe_size); assert(rc==0); - ioctl(p.out, FIONREAD, &pipe_size); + // ioctl(p.out, FIONREAD, &pipe_size); // write to the string directly, todo: find a better way std::string output; - output.resize(pipe_size); - read(p.out, (char*)output.c_str(), output.size()); + + int read_count; + int i = 0; + do + { + output.resize(output.size() + PIPE_BUF); // todo: check if we are overallocating + + read_count = read(p.out, &output[i], PIPE_BUF); // todo: check errno + i += read_count; + } + while(read_count > 0); + // p finished? + output.erase(i, output.size() - i); return output; } diff --git a/commands.hpp b/commands.hpp index b42a636..db2ab0e 100644 --- a/commands.hpp +++ b/commands.hpp @@ -58,8 +58,6 @@ public: /* Run the command */ RetProc operator()(); - /* Run the command, capturing the output in a new pipe */ - RetProc runRedir(); /* Run the command async shell: cmd & diff --git a/cppipe.cpp b/cppipe.cpp index 2a329d0..0f24498 100644 --- a/cppipe.cpp +++ b/cppipe.cpp @@ -2,15 +2,43 @@ #include #include #include +#include + #include +#include +#include #include "commands.hpp" using namespace std; namespace fs = std::filesystem; -static fs::path find_path_to_cpp(string_view file); // find the cpp to run, if it doesn't exist, exit program -static fs::path get_cache_path(const fs::path& file); // find the path of the cache for the given file path -static bool compile_file(const fs::path& file, const fs::path& cache, bool debug); // return true on success +struct MappedFile // todo: move to utils +{ + // ~MappedFile() + // { + // munmap(data, len); + // } + char* data; + unsigned len; +}; + +// find the cpp to run, if it doesn't exist, exit program +static fs::path find_path_to_cpp(string_view src_file); + +// find the path of the cache for the given src_file path +static fs::path get_cache_dir_path(const fs::path& src_file); + +// map file in memory with write persmissions +static MappedFile mapfile_for_writing(const fs::path& file); + +// check weather a src file or it's headers have changed +static bool is_src_file_changed(const fs::path& src_file, const fs::path& cache_dir); + +// returns bin path +static fs::path compile_file(const fs::path& src_file, const fs::path& cache_dir); + +static const char DEBUG_PREFIX[] = "__DBG"; +static bool debug = false; int main(int argc, char* argv[]) { @@ -23,14 +51,13 @@ int main(int argc, char* argv[]) { cout << "Usage: cppipe [-g] FILE [ARGUMENTS]...\n" << "Compile and run C++ source FILE that uses the cppipe library.\n" - "Pass the ARGUMENTS to he compiled binary.\n" - "The binaries are cached and recompiled only if the source is newer.\n" + "Pass the ARGUMENTS to the compiled binary.\n" + "The binaries are cached and recompiled only if the source or it's headers have changed.\n" "-g debug the binary, asserts are also enabled\n"; return 0; } - bool debug = false; int file_arg = 1; if( !strcmp(argv[1], "-g") ) { @@ -44,18 +71,14 @@ int main(int argc, char* argv[]) file_arg = 2; } - const fs::path file = find_path_to_cpp( argv[file_arg] ); + const fs::path src_file = find_path_to_cpp( argv[file_arg] ); - fs::path cache( get_cache_path(file) ); // cache bins to avoid recompiles + fs::path cache_dir( get_cache_dir_path(src_file) ); // cache bins to avoid recompiles - if(debug) - cache += "_dbg"; + // Compile the src + fs::path bin = compile_file(src_file, cache_dir); - // Compile the cpp - if( !compile_file(file, cache, debug) ) - return 1; - - // Run the file without forking + // Run the src file without forking Cmd run; if(debug) // debug it with gdb { @@ -63,17 +86,17 @@ int main(int argc, char* argv[]) run += "--args"; } - run += cache.c_str(); + run += bin.c_str(); for(int i = file_arg+1; i < argc; ++i) run += argv[i]; exec(run); } -static fs::path find_path_to_cpp(string_view file) +static fs::path find_path_to_cpp(string_view src_file) { - if(fs::exists(file)) // found relative to CWD + if(fs::exists(src_file)) // found relative to CWD { - return file; + return src_file; } // search files on CPPIPEPATH @@ -90,7 +113,7 @@ static fs::path find_path_to_cpp(string_view file) { for(const fs::directory_entry& e: fs::directory_iterator(path_entry)) { - if(fs::is_regular_file(e) && e.path().filename() == file) + if(fs::is_regular_file(e) && e.path().filename() == src_file) { return e; } @@ -103,60 +126,127 @@ static fs::path find_path_to_cpp(string_view file) } // Couln't find the cpp - cerr << "File: " << file << " doesn't exist\n"; + cerr << "File: " << src_file << " doesn't exist\n"; exit(1); } -static fs::path get_cache_path(const fs::path& file) +static fs::path get_cache_dir_path(const fs::path& src_file) { - fs::path cache; + fs::path cache_dir; if(char* xdg_cache = getenv("XDG_CACHE_HOME")) { - cache = xdg_cache; - cache /= "cppipe"; + cache_dir = xdg_cache; + cache_dir /= "cppipe"; } else { char* home( getenv("HOME") ); - cache = home; - cache /= ".cache/cppipe"; + cache_dir = home; + cache_dir /= ".cache/cppipe"; } - cache += fs::absolute(file); - fs::create_directories(cache.parent_path()); - return cache; + cache_dir += fs::canonical( src_file ).parent_path(); + fs::create_directories(cache_dir); + return cache_dir; } -static bool compile_file(const fs::path& file, const fs::path& cache, bool debug) +static MappedFile mapfile_for_writing(const fs::path& file) +{ + fd_t fd = open(file.c_str(), O_RDWR); + + MappedFile res; + res.len = lseek(fd, 0, SEEK_END); + res.data = (char*)mmap(nullptr, res.len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + close(fd); + return res; +} + +// preprocess the file and comapre it to the old pped version +static bool is_src_file_changed(const fs::path& src_file, const fs::path& cache_dir) +{ + Cmd preprocess( + "g++", + "-E", // preprocess only + "-P", // don't generate linemarkers in the output to reduce file size + src_file.c_str() + ); + if(!debug) + preprocess += "-DNDEBUG"; + + fs::path old_pp_path = cache_dir / (debug ? DEBUG_PREFIX : "") += src_file.stem() += ".ii"; + + string new_pp = $(preprocess); + + if( fs::exists(old_pp_path) ) // todo: clean up if else blocks + { + if( fs::file_size(old_pp_path) == new_pp.size() ) + { + MappedFile old_pp = mapfile_for_writing(old_pp_path); + if( !memcmp(old_pp.data, &new_pp[0], old_pp.len) ) // unchanged + { + return false; + } + else + { + memcpy(old_pp.data, &new_pp[0], old_pp.len); + munmap(old_pp.data, old_pp.len); + return true; + } + // todo + // munmap(data, len); + } + else + { + ofstream pp_file(old_pp_path); + pp_file << new_pp; + return true; + } + + + } + else + { + ofstream pp_file(old_pp_path); + pp_file << new_pp; + return true; + } + + return true; +} + +static fs::path compile_file(const fs::path& src_file, const fs::path& cache_dir) { // Only compile if the source is newer then the bin - error_code ec; - if(last_write_time(file) > last_write_time(cache, ec)) + fs::path cache_bin = cache_dir / (debug ? DEBUG_PREFIX : "") += src_file.filename(); + + if(is_src_file_changed(src_file, cache_dir) || !fs::exists(cache_bin)) { + string preprocessed_file = cache_dir / src_file.stem() += ".ii"; // todo: duplicates with old_pp_path // todo: support C Cmd compile( "g++", - "-o", cache.c_str(), + preprocessed_file.c_str(), + "-o", cache_bin.c_str(), "-pipe", "-std=c++17", "-march=native", - // "-Wall", "-Wextra", "-Wno-parentheses", - file.c_str(), + "-Wall", "-Wextra", "-Wno-parentheses", "-lcppipe" ); if(debug) { compile.append_args({ - "-g", - "-Wall", "-Wextra", "-Wno-parentheses" + "-g" + // "-Wall", "-Wextra", "-Wno-parentheses" }); } else { compile.append_args({ - "-Ofast", "-flto", "-DNDEBUG", "-s" + "-Ofast", "-flto", "-s" }); } if( !compile() ) // if failed to compile - return false; + exit(1); } - return true; + return cache_bin; } diff --git a/install.sh b/install.sh index f02a5cf..e540645 100755 --- a/install.sh +++ b/install.sh @@ -6,6 +6,7 @@ PREFIX=/usr/local # Compile lib options="-std=c++17 -Ofast -flto -march=native -DNDEBUG -Wall -Wextra -Wno-parentheses -Wno-unused-result -s -pipe" +# options="-std=c++17 -g" g++ -c $options commands.cpp childProcess.cpp # Create lib @@ -21,3 +22,6 @@ mv cppipe ${PREFIX}/bin # Install headers mkdir -p ${PREFIX}/include/cppipe cp basicTypes.h commands.hpp childProcess.hpp ${PREFIX}/include/cppipe + +# Clear old cache +rm -rf ~/.cache/cppipe ${XDG_CACHE_HOME}/cppipe diff --git a/test.cpp b/test.cpp index 07414de..03c03c7 100644 --- a/test.cpp +++ b/test.cpp @@ -24,6 +24,10 @@ int main(int argc, char* argv[]) if(out1.find("childProcess.cpp") != string::npos) cout << "OK 0/11" << endl; + string out2 = $(echo + "abc" + "def"); + if(auto len = out2.size(); len != 8) + cerr << "FAILURE: unexpected output length " << len << endl; + Cmd success("echo", "OK 1/11"); Cmd fail("mkdir", "."); Cmd unexpected("echo", "FAILURE"); diff --git a/todo.txt b/todo.txt index 8f296b2..0f347e6 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,11 @@ +config +support C man ? +header only lib -Wno-unused-result in install.sh +file operations, lack of uniformity (C++ vs POSIX) split Check all return codes and report errors close files on destruction