Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 34 additions & 40 deletions src/eckit/system/Library.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,29 +74,42 @@ std::string Library::prefixDirectory() const {
return prefixDirectory_;
}

std::string Library::home() const {
const std::vector<std::string>& Library::homes() const {
AutoLock<Mutex> lock(mutex_);

if (!homes_.empty()) {
return homes_;
}

std::string libhome = prefix_ + "_HOME";
char* home = ::getenv(libhome.c_str());
if (home) {
return home;
char* home_env = ::getenv(libhome.c_str());

// Home is a colon separated list of directories
if (home_env) {
std::stringstream ss(home_env);
std::string item;
while (std::getline(ss, item, ':')) {
homes_.push_back(item);
}
}
else {
// Default homes
LocalPathName prefix = prefixDirectory();
homes_ = {prefix, prefix.dirName(), prefix.dirName().dirName(), "/"};
}

return home_; // may return empty string (meaning not set)
ASSERT(!homes_.empty());
return homes_;
}

std::string Library::libraryHome() const {
std::string h = home();
if (!h.empty()) {
return h;
}
return prefixDirectory();
const std::vector<std::string>& h = homes();
return h[0];
}

void Library::libraryHome(const std::string& home) {
AutoLock<Mutex> lock(mutex_);
home_ = home;
homes_ = {home};
}

std::string Library::libraryPath() const {
Expand Down Expand Up @@ -155,39 +168,20 @@ std::string Library::expandPath(const std::string& p) const {
ASSERT(p.substr(0, s.size()) == s);
ASSERT(p.size() == s.size() || p[s.size()] == '/');

// 1. if HOME is set for this library, either via env variable LIBNAME_HOME exists
// or set in code expand ~lib/ to its content

const std::string h = home();
if (!h.empty()) {
std::string result = h + "/" + p.substr(s.size());
return result;
}

// 2. try to walk up the path and check for paths that exist
// If HOME is set for this library, either via env variable LIBNAME_HOME
// or set in code, expand ~lib/ to its content
std::string tail = "/" + p.substr(s.size());
const std::vector<std::string>& h = homes();

const std::string extra = "/" + p.substr(s.size());

eckit::LocalPathName path = prefixDirectory();
eckit::LocalPathName root("/");

while (true) {
LocalPathName tmp = path + extra;

if (tmp.exists()) {
return tmp;
for (const auto& home : h) {
LocalPathName result = LocalPathName(home + tail);
if (result.exists()) {
return result;
}

if (path == root) {
break;
}

path = path.dirName();
}

// 3. as a last resort expand with prefix directory although we know the path doesn't exist

return prefixDirectory() + extra;
// As a last resort expand with the first home entry, although we know the path doesn't exist
return h[0] + tail;
}

void Library::print(std::ostream& out) const {
Expand Down
5 changes: 3 additions & 2 deletions src/eckit/system/Library.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class Library : private eckit::NonCopyable {
void unlock() { mutex_.unlock(); }

protected: // methods
virtual std::string home() const;

virtual const std::vector<std::string>& homes() const;

virtual const void* addr() const;

Expand All @@ -94,7 +95,7 @@ class Library : private eckit::NonCopyable {
private: // members
std::string name_;
std::string prefix_;
std::string home_; // if not set explicitly, will be empty
mutable std::vector<std::string> homes_;

bool debug_;

Expand Down
32 changes: 32 additions & 0 deletions tests/system/test_system_library.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "eckit/system/ResourceUsage.h"
#include "eckit/system/SystemInfo.h"
#include "eckit/testing/Test.h"
#include "eckit/filesystem/TmpDir.h"

using namespace std;
using namespace eckit;
Expand Down Expand Up @@ -85,6 +86,37 @@ CASE("test_eckit_system_info") {
Log::info() << "execPath is " << execPath << std::endl;
}

CASE("Test colon-seperated ECKIT_HOME expansion") {

eckit::TmpDir td;
LocalPathName tmpdir = td.localPath();

// create files $tmpdir/dir{i}/file{i}
for (int i = 0; i < 3; i++) {
LocalPathName dir = tmpdir + "/dir" + std::to_string(i);
LocalPathName file = dir + "/file" + std::to_string(i);
file.touch();
}

std::stringstream ss;
ss << tmpdir << "/dir0:" << tmpdir << "/dir1:" << tmpdir << "/dir2";

setenv("ECKIT_HOME", ss.str().c_str(), 1);

EXPECT(LocalPathName("~eckit/file0").exists());
EXPECT(LocalPathName("~eckit/file1").exists());
EXPECT(LocalPathName("~eckit/file2").exists());

EXPECT(LocalPathName("~eckit/file0") == tmpdir + "/dir0/file0");
EXPECT(LocalPathName("~eckit/file1") == tmpdir + "/dir1/file1");
EXPECT(LocalPathName("~eckit/file2") == tmpdir + "/dir2/file2");

// add file3 to dir1 and dir2 and test that the first one is returned
LocalPathName(tmpdir + "/dir1/file3").touch();
LocalPathName(tmpdir + "/dir2/file3").touch();
EXPECT(LocalPathName("~eckit/file3") == tmpdir + "/dir1/file3");
}

CASE("test_eckit_system_library") {

std::vector<std::string> libs = LibraryManager::list();
Expand Down
Loading