check if recompiling is needed by preprocessing
This commit is contained in:
+19
-15
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user