Skip to content

Commit

Permalink
Replace explicit use of shell+’which’ with ‘finder’ module
Browse files Browse the repository at this point in the history
- previously, used popen() and ‘which’ to detect the presence of python, dot and CppTransport-sendmail on $PATH

- this works, but involves a call to fork() which does not play nicely with OpenMPI on some systems. To reduce the number of calls to fork() we can search for executables ourselves by parsing $PATH. The finder module from the translator already has this functionality so it can be brought across wholesale
  • Loading branch information
ds283 committed Aug 14, 2017
1 parent d957d25 commit 1f82131
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 64 deletions.
1 change: 1 addition & 0 deletions CppTransport/CMakeLists.txt
Expand Up @@ -1266,6 +1266,7 @@ SET(TRANSPORT_RUNTIME_TRANSACTIONS_FILES

SET(TRANSPORT_RUNTIME_UTILITIES_FILES
transport-runtime/utilities/asciitable.h
transport-runtime/utilities/finder.h
transport-runtime/utilities/formatter.h
transport-runtime/utilities/host_information.h
transport-runtime/utilities/latex_output.h
Expand Down
95 changes: 31 additions & 64 deletions CppTransport/transport-runtime/manager/environment.h
Expand Up @@ -36,6 +36,7 @@
#include <pwd.h>

#include "transport-runtime/defaults.h"
#include "transport-runtime/utilities/finder.h"
#include "transport-runtime/utilities/to_printable.h"

#include "boost/algorithm/string.hpp"
Expand Down Expand Up @@ -200,6 +201,11 @@ namespace transport
// INTERNAL DATA

protected:

// DELGATES

//! filesystem object finder
finder find;

// USERS AND AUTHENTICATION

Expand All @@ -208,7 +214,8 @@ namespace transport

// ENVIRONMENT PATHS

//! user home directory

//! user home directory, if detected
boost::optional< boost::filesystem::path > home;

//! CppTransport resource installation paths
Expand Down Expand Up @@ -286,6 +293,9 @@ namespace transport
dot_available(false),
sendmail_available(false)
{
// add $PATH to object finder
find.add_environment_variable("PATH");

// detect user id
this->detect_userid();

Expand Down Expand Up @@ -370,7 +380,8 @@ namespace transport

std::string term_type(term_type_cstr);

this->colour_output = term_type == "xterm"
this->colour_output =
term_type == "xterm"
|| term_type == "xterm-color"
|| term_type == "xterm-256color"
|| term_type == "screen"
Expand All @@ -382,96 +393,51 @@ namespace transport
void local_environment::detect_graphviz()
{
// TODO: platform introspection
FILE* f = popen("which dot", "r");
auto dot = this->find.find("dot");

if(!f)
if(!dot)
{
this->dot_available = false;
this->dot_location = CPPTRANSPORT_DEFAULT_DOT_PATH;
return;
}
else
{
char buffer[1024];
char* line = fgets(buffer, sizeof(buffer), f);
pclose(f);

if(line != nullptr)
{
this->dot_available = true;
std::string temp = std::string(line);
boost::algorithm::trim_right(temp);
this->dot_location = temp;
}
else
{
this->dot_available = false;
this->dot_location = CPPTRANSPORT_DEFAULT_DOT_PATH;
}
}
this->dot_available = true;
this->dot_location = *dot;
}


void local_environment::detect_sendmail()
{
// TODO: platform introspection
FILE* f = popen("which CppTransport-sendmail", "r");
auto sendmail = this->find.find("CppTransport-sendmail");

if(!f)
if(!sendmail)
{
this->sendmail_available = false;
this->sendmail_location = CPPTRANSPORT_DEFAULT_SENDMAIL_PATH;
return;
}
else
{
char buffer[1024];
char* line = fgets(buffer, sizeof(buffer), f);
pclose(f);

if(line != nullptr)
{
this->sendmail_available = true;
std::string temp = std::string(line);
boost::algorithm::trim_right(temp);
this->sendmail_location = temp;
}
else
{
this->sendmail_available = false;
this->sendmail_location = CPPTRANSPORT_DEFAULT_SENDMAIL_PATH;
}
}

this->sendmail_available = true;
this->sendmail_location = *sendmail;
}


void local_environment::detect_python()
{
// TODO: Platform introspection
FILE* f = popen("which python", "r");
auto python = this->find.find("python");

if(!f)
if(!python)
{
this->python_available = false;
this->python_location = CPPTRANSPORT_DEFAULT_PYTHON_PATH;
return;
}
else
{
char buffer[1024];
char* line = fgets(buffer, sizeof(buffer), f);
pclose(f);

if(line != nullptr)
{
this->python_available = true;
std::string temp = std::string(line);
boost::algorithm::trim_right(temp);
this->python_location = temp;
}
else
{
this->python_available = false;
this->python_location = CPPTRANSPORT_DEFAULT_PYTHON_PATH;
}
}

this->python_available = true;
this->python_location = *python;

this->python_cached = true;
}
Expand Down Expand Up @@ -578,6 +544,7 @@ namespace transport

int local_environment::execute_python(const boost::filesystem::path& script)
{
// python detection is lazy; we don't look for it until we need it
if(!this->python_cached) this->detect_python();

if(!this->python_available) return EXIT_FAILURE;
Expand Down
181 changes: 181 additions & 0 deletions CppTransport/transport-runtime/utilities/finder.h
@@ -0,0 +1,181 @@
//
// Created by David Seery on 14/08/2017.
// --@@
// Copyright (c) 2017 University of Sussex. All rights reserved.
//
// This file is part of the CppTransport platform.
//
// CppTransport is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// CppTransport is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with CppTransport. If not, see <http://www.gnu.org/licenses/>.
//
// @license: GPL-2
// @contributor: David Seery <D.Seery@sussex.ac.uk>
// --@@
//

#ifndef CPPTRANSPORT_FINDER_H
#define CPPTRANSPORT_FINDER_H


#include <iostream>
#include <string>
#include <list>

#include "boost/optional.hpp"
#include "boost/filesystem/operations.hpp"
#include "boost/algorithm/string.hpp"


namespace transport
{

class finder
{

// CONSTRUCTOR, DESTRUCTOR

public:

//! default constructor
finder() = default;

//! destructor is default
~finder() = default;


// INTERFACE -- ADD PATHS

public:

//! add a path to the search list
finder& add(boost::filesystem::path p);

//! add a container of paths to the search list
template <typename Container>
finder& add(const Container& plist);

//! add the current working directory to the list of search paths
finder& add_cwd();

//! add a :-separated list from a system environment variable, optionally offset
//! by a fixed relative path
finder& add_environment_variable(const std::string var, const boost::filesystem::path tail = boost::filesystem::path{});


// INTERFACE -- FIND FULLY-QUALIFIED PATHS

public:

// find fully-qualified path name corresponding to a given leafname;
// if present, returns the FQPN, otherwise returns boost::none
boost::optional< boost::filesystem::path >
find(const boost::filesystem::path& leaf);


// INTERNAL DATA

private:

//! list of paths to search
std::list<boost::filesystem::path> paths;

};


template <typename Container>
finder& finder::add(const Container& plist)
{
for(const auto& t : plist)
{
this->add(t);
}

return *this;
}


inline finder& finder::add(boost::filesystem::path p)
{
// add path to list, if it isn't already present
auto t = std::find(this->paths.begin(), this->paths.end(), p);

if(t == this->paths.end()) this->paths.emplace_back(std::move(p));

return *this;
}


inline finder& finder::add_environment_variable(const std::string var, const boost::filesystem::path leaf)
{
// query value of environment variable
char* resource_path_cstr = std::getenv(var.c_str());

// return if environment variable did not exist
if(resource_path_cstr == nullptr) return *this;

std::string template_paths{resource_path_cstr};

// parse environment variable into a : separated list of directories
for(auto t = boost::algorithm::make_split_iterator(template_paths,
boost::algorithm::first_finder(":", boost::algorithm::is_equal()));
t != boost::algorithm::split_iterator<std::string::iterator>(); ++t)
{
std::string path = boost::copy_range<std::string>(*t);

// add 'templates' directory to each root
boost::filesystem::path rpath(path);

// if path is not absolute, make relative to current working directory
if(!rpath.is_absolute()) rpath = boost::filesystem::absolute(rpath);

if(!leaf.empty()) this->add(rpath / leaf);
else this->add(rpath);
}

return *this;
}


inline finder& finder::add_cwd()
{
boost::filesystem::path cwd{ boost::filesystem::initial_path<boost::filesystem::path>() };

return this->add(cwd);
}


inline boost::optional< boost::filesystem::path >
finder::find(const boost::filesystem::path& leaf)
{
if(leaf.is_absolute())
{
auto fqpn = boost::filesystem::canonical(leaf);

if(boost::filesystem::exists(leaf)) return fqpn;
return boost::none;
}

for(const auto& path : this->paths)
{
auto file = path / leaf;

if(boost::filesystem::exists(file)) return boost::filesystem::canonical(file);
}

return boost::none;
}

} // namespace transport


#endif //CPPTRANSPORT_FINDER_H

0 comments on commit 1f82131

Please sign in to comment.