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 <iostream>
#include <assert.h>
#include <limits.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
@@ -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<PendingCmd&>(cmd).runRedir();
Proc p = const_cast<PendingCmd&>(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;
}
-2
View File
@@ -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 &
+131 -41
View File
@@ -2,15 +2,43 @@
#include <cstring>
#include <filesystem>
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#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;
}
+4
View File
@@ -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
+4
View File
@@ -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");
+4
View File
@@ -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