check if recompiling is needed by preprocessing

This commit is contained in:
vrd
2024-03-18 09:50:04 +02:00
committed by Venelin
parent 097738cf64
commit d88212f6e7
6 changed files with 162 additions and 58 deletions
+19 -15
View File
@@ -1,6 +1,7 @@
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <assert.h> #include <assert.h>
#include <limits.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
@@ -95,16 +96,6 @@ RetProc PendingCmd::operator()()
return cmd(in, out, err); 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() Proc PendingCmd::detach()
{ {
assert(!execed_ && "Executed command twice"); assert(!execed_ && "Executed command twice");
@@ -115,6 +106,8 @@ Proc PendingCmd::detach()
Proc PendingCmd::detachRedirOut() Proc PendingCmd::detachRedirOut()
{ {
assert(!execed_ && "Executed command twice"); assert(out==1 && "already redirected"); assert(!execed_ && "Executed command twice"); assert(out==1 && "already redirected");
assert(out==1 && "capturing redirected proccess");
execed_ = true; execed_ = true;
return createCapProcess(cmd.argv.data(), in, err); return createCapProcess(cmd.argv.data(), in, err);
} }
@@ -126,17 +119,28 @@ void PendingCmd::cancel()
std::string $(const PendingCmd& cmd) std::string $(const PendingCmd& cmd)
{ {
RetProc p = const_cast<PendingCmd&>(cmd).runRedir(); Proc p = const_cast<PendingCmd&>(cmd).detachRedirOut();
// check output size (not portable?) // check output size (not portable?)
int pipe_size; // int pipe_size;
// int rc = ioctl(p.out, FIONREAD, &pipe_size); assert(rc==0); // 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 // write to the string directly, todo: find a better way
std::string output; 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; return output;
} }
-2
View File
@@ -58,8 +58,6 @@ public:
/* Run the command */ /* Run the command */
RetProc operator()(); RetProc operator()();
/* Run the command, capturing the output in a new pipe */
RetProc runRedir();
/* Run the command async /* Run the command async
shell: cmd & shell: cmd &
+131 -41
View File
@@ -2,15 +2,43 @@
#include <cstring> #include <cstring>
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
#include <fstream>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "commands.hpp" #include "commands.hpp"
using namespace std; using namespace std;
namespace fs = std::filesystem; 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 struct MappedFile // todo: move to utils
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 // ~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[]) int main(int argc, char* argv[])
{ {
@@ -23,14 +51,13 @@ int main(int argc, char* argv[])
{ {
cout << "Usage: cppipe [-g] FILE [ARGUMENTS]...\n" cout << "Usage: cppipe [-g] FILE [ARGUMENTS]...\n"
<< "Compile and run C++ source FILE that uses the cppipe library.\n" << "Compile and run C++ source FILE that uses the cppipe library.\n"
"Pass the ARGUMENTS to he compiled binary.\n" "Pass the ARGUMENTS to the compiled binary.\n"
"The binaries are cached and recompiled only if the source is newer.\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"; "-g debug the binary, asserts are also enabled\n";
return 0; return 0;
} }
bool debug = false;
int file_arg = 1; int file_arg = 1;
if( !strcmp(argv[1], "-g") ) if( !strcmp(argv[1], "-g") )
{ {
@@ -44,18 +71,14 @@ int main(int argc, char* argv[])
file_arg = 2; 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) // Compile the src
cache += "_dbg"; fs::path bin = compile_file(src_file, cache_dir);
// Compile the cpp // Run the src file without forking
if( !compile_file(file, cache, debug) )
return 1;
// Run the file without forking
Cmd run; Cmd run;
if(debug) // debug it with gdb if(debug) // debug it with gdb
{ {
@@ -63,17 +86,17 @@ int main(int argc, char* argv[])
run += "--args"; run += "--args";
} }
run += cache.c_str(); run += bin.c_str();
for(int i = file_arg+1; i < argc; ++i) for(int i = file_arg+1; i < argc; ++i)
run += argv[i]; run += argv[i];
exec(run); 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 // 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)) 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; return e;
} }
@@ -103,60 +126,127 @@ static fs::path find_path_to_cpp(string_view file)
} }
// Couln't find the cpp // Couln't find the cpp
cerr << "File: " << file << " doesn't exist\n"; cerr << "File: " << src_file << " doesn't exist\n";
exit(1); 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")) if(char* xdg_cache = getenv("XDG_CACHE_HOME"))
{ {
cache = xdg_cache; cache_dir = xdg_cache;
cache /= "cppipe"; cache_dir /= "cppipe";
} }
else else
{ {
char* home( getenv("HOME") ); char* home( getenv("HOME") );
cache = home; cache_dir = home;
cache /= ".cache/cppipe"; cache_dir /= ".cache/cppipe";
} }
cache += fs::absolute(file); cache_dir += fs::canonical( src_file ).parent_path();
fs::create_directories(cache.parent_path()); fs::create_directories(cache_dir);
return cache; 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 // Only compile if the source is newer then the bin
error_code ec; fs::path cache_bin = cache_dir / (debug ? DEBUG_PREFIX : "") += src_file.filename();
if(last_write_time(file) > last_write_time(cache, ec))
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( Cmd compile(
"g++", "g++",
"-o", cache.c_str(), preprocessed_file.c_str(),
"-o", cache_bin.c_str(),
"-pipe", "-std=c++17", "-march=native", "-pipe", "-std=c++17", "-march=native",
// "-Wall", "-Wextra", "-Wno-parentheses", "-Wall", "-Wextra", "-Wno-parentheses",
file.c_str(),
"-lcppipe" "-lcppipe"
); );
if(debug) if(debug)
{ {
compile.append_args({ compile.append_args({
"-g", "-g"
"-Wall", "-Wextra", "-Wno-parentheses" // "-Wall", "-Wextra", "-Wno-parentheses"
}); });
} }
else else
{ {
compile.append_args({ compile.append_args({
"-Ofast", "-flto", "-DNDEBUG", "-s" "-Ofast", "-flto", "-s"
}); });
} }
if( !compile() ) // if failed to compile if( !compile() ) // if failed to compile
return false; exit(1);
} }
return true; return cache_bin;
} }
+4
View File
@@ -6,6 +6,7 @@ PREFIX=/usr/local
# Compile lib # Compile lib
options="-std=c++17 -Ofast -flto -march=native -DNDEBUG -Wall -Wextra -Wno-parentheses -Wno-unused-result -s -pipe" 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 g++ -c $options commands.cpp childProcess.cpp
# Create lib # Create lib
@@ -21,3 +22,6 @@ mv cppipe ${PREFIX}/bin
# Install headers # Install headers
mkdir -p ${PREFIX}/include/cppipe mkdir -p ${PREFIX}/include/cppipe
cp basicTypes.h commands.hpp childProcess.hpp ${PREFIX}/include/cppipe cp basicTypes.h commands.hpp childProcess.hpp ${PREFIX}/include/cppipe
# Clear old cache
rm -rf ~/.cache/cppipe ${XDG_CACHE_HOME}/cppipe
+4
View File
@@ -24,6 +24,10 @@ int main(int argc, char* argv[])
if(out1.find("childProcess.cpp") != string::npos) if(out1.find("childProcess.cpp") != string::npos)
cout << "OK 0/11" << endl; 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 success("echo", "OK 1/11");
Cmd fail("mkdir", "."); Cmd fail("mkdir", ".");
Cmd unexpected("echo", "FAILURE"); Cmd unexpected("echo", "FAILURE");
+4
View File
@@ -1,7 +1,11 @@
config
support C
man man
? ?
header only lib
-Wno-unused-result in install.sh -Wno-unused-result in install.sh
file operations, lack of uniformity (C++ vs POSIX)
split split
Check all return codes and report errors Check all return codes and report errors
close files on destruction close files on destruction