File nptool.cxx
File List > src > utility > nptool.cxx
Go to the documentation of this file
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <string>
#include "NPApplication.h"
#include "NPException.h"
#include "NPInputParser.h"
#include "NPTerminalColor.h"
// function
// env
void new_env(std::string);
void remove_env(std::string);
bool check_env(std::string);
// package management
void install(std::string, bool allow_branch = true);
void reinstall(std::string, bool allow_branch = true);
void update(std::string);
void uninstall(std::string);
bool check_install(std::string);
bool update_remote();
// project
std::string check_project(std::string);
void new_project(std::string);
void remove_project(std::string);
void mv_project(std::string);
void duplicate(std::vector<std::string>);
std::string PWD;
std::string Path;
std::string Home;
std::string Env;
std::string Log;
std::string LogShell;
std::string check;
std::string cross;
// Platform dependent sed command
#ifdef __linux__
std::string sed = "sed -i ";
std::string lib_extention = ".so";
#endif
#ifdef __FreeBSD__
std::string sed = "sed -i \'\' -e ";
std::string lib_extention = ".so"
#endif
#ifdef __APPLE__
std::string sed = "sed -i \'\' -e ";
std::string lib_extention = ".dylib";
#endif
int execute_command(std::string& command, bool login = true, bool output = true) {
std::string cmd = command;
if (login) {
cmd = "echo \"**** executing command: " + command + "\" " + LogShell;
system(cmd.c_str());
if (output) {
cmd = command + LogShell;
}
else
cmd = command;
}
return system(cmd.c_str());
}
int main(int argc, char** argv) {
std::cout << nptool::cli_blue << nptool::cli_horizontal;
std::cout << " nptool v0.1 environment utility "
" \n";
std::cout << nptool::cli_horizontal << nptool::cli_restore_color << std::endl;
PWD = getenv("PWD");
Path = getenv("NPTOOL");
Home = getenv("NPTOOL_HOME");
if (Home.back() != '/')
Home += "/";
Env = Home + getenv("NPTOOL_ENV");
Log = Env + "/nptool.log";
LogShell = " >> " + Log + " 2>&1";
check = nptool::cli_green + " \u2713 " + nptool::cli_blue;
cross = nptool::cli_red + " \u2718 " + nptool::cli_blue;
try {
// instantiate an application
auto app = nptool::Application::InitApplication(argc, argv, 1);
app->Start(false);
// Checking the correct number of argument is given:
if (app->NumberOfFlags() == 0) {
std::string msg = "No flags provided";
nptool::Error ex("nptool", msg);
throw ex;
}
if (app->HasFlag("-h")) {
system("man nptool");
return 0;
}
// If new-env flag is present: //
else if (app->HasFlag("--new-env")) {
std::string env = app->GetArg("--new-env");
if (!check_env(env))
new_env(env);
else
throw nptool::Error("nptool", "Environment \"" + env + "\" already exist.",
"Remove with nptool --remove-env " + env);
}
// If remove-env flag is present: //
else if (app->HasFlag("--remove-env")) {
std::string env = app->GetArg("--remove-env");
if (check_env(env))
remove_env(env);
else
throw nptool::Warning("nptool", "Environment \"" + env + "\" does not exist.");
}
// If update-remote flag is present: //
else if (app->HasFlag("--update-remote")) {
if (!update_remote())
throw nptool::Warning("nptool", "Fail to update remote package list");
}
// If install flag is present: //
else if (app->HasFlag("--install")) {
std::vector<std::string> List = app->GetVectorArg("--install");
std::cout << nptool::cli_blue << "**** Package installation: \n";
for (unsigned int i = 0; i < List.size(); i++) {
install(List[i]);
}
std::cout << std::flush;
}
// If update flag is present: //
else if (app->HasFlag("--update")) {
std::vector<std::string> List = app->GetVectorArg("--update");
std::cout << nptool::cli_blue << "**** Package update of " << List.size() << " packages: \n";
for (unsigned int i = 0; i < List.size(); i++) {
update(List[i]);
}
std::cout << std::flush;
}
// If uninstall flag is present: //
else if (app->HasFlag("--uninstall")) {
std::vector<std::string> List = app->GetVectorArg("--uninstall");
std::cout << nptool::cli_blue << "**** Package uninstallation: \n";
for (unsigned int i = 0; i < List.size(); i++) {
uninstall(List[i]);
}
std::cout << std::flush;
}
// If reinstall flag is present: //
else if (app->HasFlag("--reinstall")) {
std::vector<std::string> List = app->GetVectorArg("--reinstall");
std::cout << nptool::cli_blue << "**** Package reinstallation: \n";
for (unsigned int i = 0; i < List.size(); i++) {
reinstall(List[i]);
}
std::cout << std::flush;
}
// If new project flag is present: //
else if (app->HasFlag("--new-project")) {
std::string List = app->GetArg("--new-project");
std::cout << nptool::cli_blue << "**** Project creation: \n";
new_project(List);
std::cout << std::flush;
}
// If remove-project flag is present: //
else if (app->HasFlag("--remove-project")) {
std::string List = app->GetArg("--remove-project");
std::cout << nptool::cli_blue << "**** Project deletion: \n";
remove_project(List);
std::cout << std::flush;
}
else if (app->HasFlag("--duplicate")) {
std::vector<std::string> List = app->GetVectorArg("--duplicate");
std::cout << nptool::cli_blue << "**** Plugin duplicate: \n";
duplicate(List);
std::cout << std::flush;
}
std::cout << nptool::cli_blue << nptool::cli_horizontal;
app->Stop();
}
// catch any exception that was not deal with before
catch (nptool::Exception& ex) {
nptool::DisplayException(ex);
return 1;
}
// to do list:
// - duplicate -> local version of the package
// - switch-env -> use existing env in home
return 0;
}
void uninstall(std::string package) {
std::cout << nptool::cli_blue;
std::cout << "** " << package << std::endl;
// Userful variable
// path
std::string Package = Env + "/" + package;
std::string Build = Package + "/build";
std::string Sources = Package + "/sources";
std::string Manifest = Build + "/install_manifest.txt";
std::ifstream manifest(Manifest);
if (!manifest.is_open()) {
std::string msg = "install_manifest.txt not found for package " + package;
Manifest = "expecting file \"" + Manifest + "\"";
throw nptool::Error("nptool", msg, Manifest);
}
// remove installed file
std::string file;
while (manifest >> file) {
std::string command = "rm " + file;
if (execute_command(command) != 0) {
std::string msg = " fail to execute \"" + command + "\"";
throw nptool::Error("nptool", msg);
}
else
std::cout << check << " Removing file " << file << std::endl;
}
// remove installed lib
std::ifstream pl(Env + "/plugin_lib.list");
if (!pl.is_open()) {
std::string msg = "plugin_lib.list not found ";
throw nptool::Error("nptool", msg);
}
std::string stoken, spackage, slib;
while (pl >> stoken >> spackage >> slib) {
if (spackage == package) {
std::string command = "rm " + Env + "/lib/" + slib;
if (execute_command(command) != 0) {
std::string msg = " fail to execute \"" + command + "\"";
throw nptool::Error("nptool", msg);
}
else
std::cout << check << " Removing file " << slib << std::endl;
}
}
// remove folder
std::string command = "rm -Rf " + Package;
if (execute_command(command) != 0) {
std::string msg = " fail to execute \"" + command + "\"";
throw nptool::Error("nptool", msg);
}
else
std::cout << check << " Removing folder " << Package << std::endl;
// remove from installed
command = sed + "\'/^" + package + "/d\' " + Env + "/installed_package.list";
if (execute_command(command) != 0) {
std::string msg = " fail to execute \"" + command + "\"";
throw nptool::Error("nptool", msg);
}
else
std::cout << check << " Package removed from installed list " << std::endl;
// remove from package_lib
command = sed + "\'/^" + package + "/d\' " + Env + "/plugin_lib.list";
if (execute_command(command) != 0) {
std::string msg = " fail to execute \"" + command + "\"";
throw nptool::Error("nptool", msg);
}
else
std::cout << check << " Package removed from library list " << std::endl;
}
void install(std::string package, bool allow_branch) {
update_remote();
std::cout << nptool::cli_blue;
std::cout << "** " << package << std::endl;
// Userful varialbe
// path
std::string Package = Env + "/" + package;
std::string Build = Package + "/build";
std::string Sources = Package + "/sources";
// check if package is installed
if (check_install(package)) {
std::cout << check << " " << package << " already installed" << std::endl;
return;
}
// check if branch and/or commit option are given
std::string branch_or_commit = "xx_void_xx";
if (allow_branch) {
auto app = nptool::Application::GetApplication();
if (app->HasFlag("--branch")) {
auto arg = app->GetVectorArg("--branch");
if (arg.size() == 1)
branch_or_commit = arg[0];
else {
throw nptool::Error("nptool", "branch flag take one value only");
}
}
if (app->HasFlag("--commit")) {
auto arg = app->GetVectorArg("--commit");
if (arg.size() == 1)
branch_or_commit = arg[0];
else
throw nptool::Error("nptool", "commit flag take one value only");
}
}
// exe and cmake option
std::string cmake_generator = getenv("NPTOOL_GENERATOR");
std::string cmake, make_bin;
if (cmake_generator.compare("Ninja") == 0) {
make_bin = "ninja ";
cmake = "cmake -GNinja -Dnptool=1";
}
else {
make_bin = "make ";
cmake = "cmake -Dnptool=1";
}
// find the remote package list in the nptool folder
std::string remote = Env + "/remote_package.list";
std::ifstream remote_list(remote.c_str());
if (remote_list.is_open()) {
std::string name, url, command;
while (remote_list >> name >> url) {
if (name.compare(package) == 0) {
// get the package file
command = "curl -s " + url + " > /tmp/nptool_package ";
if (execute_command(command, true, false) == 0)
std::cout << " " << check << " package file checkout -> ";
else
throw nptool::Error("nptool", "fail to checkout package file at " + url);
// read the package file
nptool::InputParser parser("/tmp/nptool_package", true);
system("rm /tmp/nptool_package > /dev/null");
auto blocks = parser.GetAllBlocksWithToken("Package");
if (blocks.size() > 0) {
// checking dependencies:
auto dependencies = blocks[0]->GetVectorString("dependencies");
if (dependencies[0] != "null") {
std::cout << "** installing dependencies " << std::endl;
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; it++) {
install(*it, false); // false to ignore commit or branch flag on
// dependencies
}
std::cout << "** dependencies done " << std::endl;
}
// repository url
url = blocks[0]->GetString("url", true);
// git clone
command = "git clone " + url + " " + Sources;
if (execute_command(command) == 0)
std::cout << " " << check << " cloning repository from " << url << std::endl;
else
throw nptool::Error("nptool", "fail to clone repository at " + url);
// checkout branch or commit if specified
if (branch_or_commit.compare("xx_void_xx") != 0) {
command = "git --git-dir=" + Sources + "/.git checkout -f " + branch_or_commit;
if (execute_command(command) == 0) {
std::cout << " " << check << " switching to " << branch_or_commit << std::endl;
}
else {
std::string msg = "fail to checkout " + branch_or_commit + " from " + url;
throw nptool::Error("nptool", msg);
}
}
// cmake configuration
command = cmake + " -S" + Sources + " -B" + Build + " -DCMAKE_INSTALL_PREFIX=" + Env;
if (execute_command(command) == 0)
std::cout << " " << check << " cmake config " << std::endl;
else {
// remove folder
std::string command = "rm -Rf " + Package;
if (execute_command(command) != 0) {
std::string msg = " fail to clean up with \"" + command + "\"";
throw nptool::Error("nptool", msg);
}
throw nptool::Error("nptool", "fail to run cmake for package ");
}
// compile
command = make_bin + " -C " + Build;
if (execute_command(command) == 0)
std::cout << " " << check << " compilation " << std::endl;
else {
// remove folder
std::string command = "rm -Rf " + Package;
if (execute_command(command) != 0) {
std::string msg = " fail to clean up with \"" + command + "\"";
throw nptool::Error("nptool", msg);
}
throw nptool::Error("nptool", "fail to compile package ");
}
// install
command = make_bin + " install -C " + Build;
if (execute_command(command) == 0)
std::cout << " " << check << " installation " << std::endl;
else
throw nptool::Error("nptool", "fail to install package ");
// plugin registration
auto plugins = parser.GetAllBlocksWithToken("Plugin");
for (auto it = plugins.begin(), end = plugins.end(); it != end; it++) {
auto token = (*it)->GetString("token", true);
auto lib = (*it)->GetString("library", true);
command =
"echo \"" + package + " " + token + " lib" + lib + lib_extention + "\" >> " + Env + "/plugin_lib.list ";
if (execute_command(command, true, false) == 0)
std::cout << " " << check << " plugin " << token << " registration " << std::endl;
else
throw nptool::Error("nptool", "fail to register plugin");
}
}
else
throw nptool::Error("nptool", "Package \"" + package + "\" file does not contain appropriate token");
// Success, record the package
command = "echo \"" + package + "\" >> " + Env + "/installed_package.list ";
execute_command(command, true, false);
return;
}
}
throw nptool::Error("nptool", "Package \"" + package + "\" does not exist");
}
else
throw nptool::Error("nptool", "Fail to open " + Path);
}
void reinstall(std::string package, bool allow_branch) {
// check if package is installed
if (check_install(package)) {
std::cout << check << "Package " << package << " is installed, proceeding to reinstall" << std::endl;
}
else{
throw nptool::Error("nptool", "Failed: Package "+ package + " is not installed, please use: nptool --install " + package);
}
uninstall(package);
install(package, allow_branch);
}
void update(std::string package) {
std::cout << nptool::cli_blue;
std::cout << "** " << package << std::endl;
// Userful variables
// path
std::string Package = Env + "/" + package;
std::string Build = Package + "/build";
std::string Sources = Package + "/sources";
// check if package is installed
if (check_install(package)) {
std::cout << check << " " << package << " installed" << std::endl;
}
else {
std::cout << cross << " " << package << " not installed" << std::endl;
return;
}
// exe and cmake option
std::string cmake_generator = getenv("NPTOOL_GENERATOR");
std::string cmake, make_bin;
if (cmake_generator.compare("Ninja") == 0) {
make_bin = "ninja ";
cmake = "cmake -GNinja -Dnptool=1";
}
else {
make_bin = "make ";
cmake = "cmake -Dnptool=1";
}
std::string command;
// git status
command = "LANG=\"en_EN\" git --git-dir=" + Sources + "/.git --work-tree=" + Sources +
" fetch --dry-run 2>&1 | grep 'From' ";
if (execute_command(command) != 0) {
std::cout << check << " already up to date " << Sources << std::endl;
return;
}
// git pull
command = "git --git-dir=" + Sources + "/.git --work-tree=" + Sources + " pull ";
if (execute_command(command) == 0)
std::cout << check << " pull from repository in " << std::endl;
else
throw nptool::Error("nptool", "fail to pull from repository");
// compile
command = make_bin + " -C " + Build;
if (execute_command(command) == 0)
std::cout << check << " compilation " << std::endl;
else
throw nptool::Error("nptool", "fail to compile package ");
// install
command = make_bin + " install -C " + Build;
if (execute_command(command) == 0)
std::cout << check << " installation " << std::endl;
else
throw nptool::Error("nptool", "fail to install package ");
return;
}
void new_env(std::string env) {
std::string command, command2;
std::cout << nptool::cli_blue;
std::cout << "**** Setting up new environment: \"" << env << "\"\n";
/*
// test if Home folder exist
command = "ls " + Home + " > /dev/null 2>&1";
command2 = "mkdir " + Home;
if (execute_command(command, false, false) == 0)
std::cout << check << Home << " folder present " << std::endl;
else if (execute_command(command, false, false) == 0)
std::cout << check << Home << " successfully created " << std::endl;
else {
std::string msg = "Failed to create " + Home + " folder";
throw nptool::Error("nptool", msg);
}
*/
// create the $NPTOOL_ENV/<env>
command = "mkdir -p " + Home + env;
if (execute_command(command, false, false) == 0)
std::cout << check << Home << env << " successfully created " << std::endl;
else {
std::string msg = "Failed to create " + Home + env + " folder";
throw nptool::Error("nptool", msg);
}
/* // test if .config exist
command = "ls ~/.config > /dev/null 2>&1";
if (execute_command(command, false, false) == 0)
std::cout << check << "~/.config folder present " << std::endl;
else if (system("mkdir ~/.config") == 0)
std::cout << check << "~/.config successfully created " << std::endl;
else
throw nptool::Error("nptool", "Failed to create .config folder");
// create the .config/nptool
command = "ls ~/.config/nptool > /dev/null 2>&1";
if (execute_command(command, false, false) == 0)
std::cout << check << "~/.config/nptool folder present " << std::endl;
else if (system("mkdir ~/.config/nptool") == 0)
std::cout << check << "~/.config/nptool successfully created " <<
std::endl; else throw nptool::Error("nptool", "Failed to create
~/.config/nptool folder");
// create the .config/nptool/<env>.config
command = "touch ~/.config/nptool/" + env + ".config";
if (execute_command(command, false, false) == 0)
std::cout << check << "~/.config/nptool/" + env + ".config successfully
created " << std::endl; else throw nptool::Error("nptool", "Failed to create
~/.config/nptool/" + env + ".config");
*/
std::cout << std::flush << nptool::cli_restore_color;
}
void remove_env(std::string env) {
std::cout << nptool::cli_blue;
std::cout << "**** Removing environment: \"" << env << "\"\n";
std::string command;
command = "rm -Rf " + Home + env + " > /dev/null 2>&1";
if (execute_command(command, false, false) == 0)
std::cout << check << Home + env + " removed" << std::endl;
command = "rm -Rf " + Home + env + " > /dev/null 2>&1";
if (execute_command(command, false, false) == 0)
std::cout << check << "~/.config/nptool/" + env + " removed" << std::endl;
}
bool check_env(std::string env) {
std::string command;
command = "ls " + Home + env + " > /dev/null 2>&1";
std::cout << command << std::endl;
if (execute_command(command, false, false) == 0)
return true;
command = "ls " + Home + env + " > /dev/null 2>&1";
std::cout << command << std::endl;
if (execute_command(command, false, false) == 0)
return true;
return false;
}
bool check_install(std::string package) {
// Userful varialbe
// path
std::string Package = Env + "/" + package;
std::string Build = Package + "/build";
std::string Sources = Package + "/sources";
// check if package is installed
std::string Installed = Env + "/installed_package.list";
std::ifstream installed(Installed);
std::string line;
while (installed >> line) {
if (line.compare(package) == 0) {
return true;
}
}
return false;
}
std::string check_project(std::string project) {
// Userful varialbe
// path
std::string Package = Env + "/" + project;
// check if project exist
std::string List = Env + "/project.list";
std::ifstream existing(List);
std::string name, ppath;
while (existing >> name >> ppath) {
if (name.compare(project) == 0) {
return ppath;
}
}
return "_none_";
}
void new_project(std::string project) {
std::string command;
std::cout << nptool::cli_blue;
std::cout << "**** Setting up new project: \"" << project << "\"\n";
// check if a project with same name exist:
if (check_project(project).compare("_none_") != 0) {
std::string msg = "A project exist already with the same name at " + check_project(project);
throw nptool::Error("nptool", msg, "remove existing project or choose a different name");
}
// copy base file from resources
command = "cp -R " + Path + "/resources/default_project " + project;
if (execute_command(command) == 0)
std::cout << check << project << " main direcotry successfully created " << std::endl;
else
throw nptool::Error("nptool", "Failed to create " + project + " directory");
// make additional empty folder
command = "mkdir " + project + "/calibration ";
command += project + "/energy_loss ";
command += project + "/detector ";
command += project + "/output ";
command += project + "/output/analysis ";
command += project + "/output/simulation ";
command += project + "/output/conversion ";
if (execute_command(command) == 0)
std::cout << check << project << " directory arborescence successfully created " << std::endl;
else
throw nptool::Error("nptool", "Failed to create " + project + " directory arborescence");
// append folder path and name to list of project:
command = "echo \"" + project + " " + PWD + "/" + project + "\" >> " + Env + "/project.list";
// test if .config exist
if (execute_command(command, true, false) == 0)
std::cout << check << project << " append to projects list" << std::endl;
else
throw nptool::Error("nptool", "Failed to append to projects list");
std::cout << std::flush << nptool::cli_restore_color;
}
void remove_project(std::string project) {
std::string command;
std::cout << nptool::cli_blue;
std::cout << "**** Removing project: \"" << project << "\"\n";
// check if a project with same name exist:
if (check_project(project).compare("_none_") == 0) {
std::string msg = "Project " + project + " not found in project list";
throw nptool::Error("nptool", msg);
}
// remove the project folder
command = "rm -Rf " + check_project(project) + LogShell;
if (execute_command(command) == 0)
std::cout << check << project << " folder successfully suppressed " << std::endl;
else
throw nptool::Error("nptool", "Failed to suppress " + check_project(project));
// remove folder path and name from list of project:
command = sed + "\'/^" + project + "/d\' " + Env + "/project.list";
if (execute_command(command) == 0)
std::cout << check << project << " removed from projects list" << std::endl;
else
throw nptool::Error("nptool", "Failed to removed from projects list");
std::cout << std::flush << nptool::cli_restore_color;
}
void duplicate(std::vector<std::string> List) {
std::string command, command2, command3;
std::cout << nptool::cli_blue;
// check if a project.yaml file is present in the current folder
command = "ls project.yaml > /dev/null 2>&1";
if (execute_command(command, false, false) == 0)
std::cout << check << "project.yaml file present: valid project " << std::endl;
else {
std::string msg = "Not in a valid project folder ";
std::string fix = "Run this command from a valid project folder";
throw nptool::Error("nptool", msg, fix);
}
// load the project file
nptool::InputParser parser("project.yaml", true);
// loop over all plugin to duplicate locally
for (auto it = List.begin(), end = List.end(); it != end; it++) {
std::cout << " ** Duplicating plugin: \"" << *it << "\"\n";
// check that the plugin exist
if (check_install(*it)) {
std::cout << " " << check << *it << " is an installed plugin " << std::endl;
auto blocks = parser.GetAllBlocksWithToken("project");
auto already_duplicate = blocks[0]->GetVectorString("duplicate plugin", true);
bool valid = true;
for (auto itt = already_duplicate.begin(), aend = already_duplicate.end(); itt != aend; itt++) {
if (*itt == *it) {
std::cout << " " << cross << *it << " is already duplicated. Ignoring " << std::endl;
valid = false;
break;
}
}
// Copy of the folder
if (valid) {
command = "cp -r " + Env + "/" + *it + "/sources " + *it;
if (execute_command(command, true, false) == 0)
std::cout << " " << check << *it << " folder copied " << std::endl;
else {
std::cout << " " << cross << *it << " folder copy failed " << std::endl;
valid = false;
}
}
// Adding the relevant folder to CMake
if (valid) {
command = "echo \"add_subdirectory(\"" + *it + "\")\">>duplicate.cmake";
command2 = "echo \"include_directories(\\${duplicate_include_dir})\" >> "
"duplicate.cmake ";
command3 = "touch CMakeLists.txt";
if (execute_command(command, false, false) == 0 && execute_command(command2, false, false) == 0 &&
execute_command(command3, false, false) == 0)
std::cout << " " << check << "CMakeLists.txt modified " << std::endl;
else
throw(nptool::Error("npanalysis", "Failed to modify CMakeLists.txt"));
command = sed + "\'s/duplicate plugin:/duplicate plugin: " + *it + "/g\' ./project.yaml";
if (execute_command(command, false, false) == 0)
std::cout << " " << check << "Added to project.yaml " << std::endl;
else
throw(nptool::Error("npanalysis", "Failed to modify project.yaml"));
}
}
else
std::cout << " " << cross << *it << " is not an installed plugin. Ignoring " << std::endl;
}
}
bool update_remote() {
std::string url = "https://gitlab.in2p3.fr/nptool/remote-package/-/raw/main/"
"remote_package.list";
std::string command = "curl -s " + url + " > " + Env + "/remote_package.list ";
auto val = system(command.c_str());
if (val == 0)
return true;
else
return false;
}