34#include <boost/algorithm/string/regex.hpp>
35#include <boost/interprocess/managed_shared_memory.hpp>
36#include <boost/interprocess/sync/file_lock.hpp>
37#include <boost/interprocess/sync/interprocess_condition.hpp>
38#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
39#include <boost/interprocess/sync/scoped_lock.hpp>
40#include <boost/interprocess/sync/sharable_lock.hpp>
41#include <boost/regex.hpp>
42#include <boost/thread/thread_time.hpp>
44#include <SimoxUtility/algorithm/string/string_tools.h>
56#define SCRIPT_PATH "ArmarXCore/core/system/cmake/FindPackageX.cmake"
57#define CACHE_PATH "/ArmarXCMakeCache_" + (getenv("USER") ? getenv("USER") : "DUMMY_USER")
66 char* homePathC = getenv(
"HOME");
75 std::shared_ptr<boost::interprocess::file_lock>
82 std::cout <<
"getFileLock( path = " << path <<
")" << std::endl;
84 if (!std::filesystem::exists(path))
88 std::cout <<
"path does not exist" << std::endl;
90 if (!std::filesystem::create_directories(path))
94 std::cout <<
"failed to create directories" << std::endl;
96 return std::shared_ptr<boost::interprocess::file_lock>();
99 path +=
"/" + lockName;
102 std::cout <<
"lock file = " << path << std::endl;
104 if (!std::filesystem::exists(path))
108 std::cout <<
"touch " << path << std::endl;
111 std::ofstream file(path);
116 std::cout <<
"build lock with " << path << std::endl;
118 return std::shared_ptr<boost::interprocess::file_lock>(
119 new boost::interprocess::file_lock(path.c_str()));
122 std::shared_ptr<boost::interprocess::file_lock>
126 std::shared_ptr<boost::interprocess::file_lock> lock(
getFileLock(name,
false));
136 const std::shared_ptr<boost::interprocess::file_lock>&
145 const std::shared_ptr<boost::interprocess::file_lock>&
157 static std::mutex mx;
162 std::shared_ptr<boost::interprocess::scoped_lock<boost::interprocess::file_lock>>;
170 new boost::interprocess::scoped_lock<boost::interprocess::file_lock>(
171 *
CMakeFileLock(), boost::get_system_time() + boost::posix_time::milliseconds(50)));
172 while (!lockPtr->owns())
174 lockPtr->timed_lock(boost::get_system_time() + boost::posix_time::milliseconds(50));
182 auto start = IceUtil::Time::now();
183 std::string path = std::filesystem::temp_directory_path().string() +
CACHE_PATH;
184 path +=
"/" + packageName;
185 if (!std::filesystem::exists(path))
192 const auto writeTime = std::filesystem::last_write_time(path);
193 const auto now =
decltype(writeTime)::clock::now();
194 long age = std::chrono::duration_cast<std::chrono::seconds>(now - writeTime).count();
196 boost::interprocess::sharable_lock<boost::interprocess::file_lock> e_lock(
199 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
202 ARMARX_INFO_S << packageName <<
" from cache locked for " << dura;
218 auto start = IceUtil::Time::now();
219 std::string path = std::filesystem::temp_directory_path().string() +
CACHE_PATH;
220 if (!std::filesystem::exists(path))
222 if (!std::filesystem::create_directories(path))
227 path = path +
"/" + packageName;
228 boost::interprocess::scoped_lock<boost::interprocess::file_lock> e_lock(*
CacheFileLock());
232 std::ofstream file(path);
233 file << packageContent;
236 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
239 ARMARX_INFO_S << packageName <<
" update cache locked for " << dura;
244 boost::interprocess::interprocess_upgradable_mutex*
memoryMutex =
nullptr;
249 const std::filesystem::path& packagePath,
251 bool usePackagePathOnlyAsHint) :
254 if (this->packageName.empty())
256 ARMARX_WARNING <<
"CMakePackageFinder: Package name must not be empty";
258 static std::string scriptPath;
261 std::unique_lock lock(cmakePackageMutex);
263 if (scriptPath.empty())
267 auto start = IceUtil::Time::now();
268 auto list = FindPackageIncludePathList(
"ArmarXCore");
269 auto dura = (IceUtil::Time::now() - start);
271 if (dura.toMilliSeconds() > 10000)
273 ARMARX_INFO_S <<
"Cmakefinder for initial core search took long - Duration: "
274 << dura.toMilliSeconds();
277 if (!ArmarXDataPath::getAbsolutePath(SCRIPT_PATH, scriptPath, list, false))
280 <<
"Finding FindPackageX.cmake failed - trying again with different path";
282 if (!ArmarXDataPath::getAbsolutePath(std::string(
"../source/") + SCRIPT_PATH,
293 auto start = IceUtil::Time::now();
294 std::string resultStr;
295 std::string tmpDir = getArmarXCMakeTempDir();
298 if (!packagePath.empty())
300 resultStr = ExecCommand(
"cd " + tmpDir +
"; cmake -DPACKAGE=" + this->packageName +
301 " -DPACKAGEBUILDPATH" +
302 (usePackagePathOnlyAsHint ?
"Hint" :
"") +
"=" +
303 packagePath.string() +
" -P " + scriptPath,
309 resultStr = ExecCommand(
"cd " + tmpDir +
"; cmake -DPACKAGE=" + this->packageName +
321 auto dura = (IceUtil::Time::now() - start);
323 if (dura.toMilliSeconds() > 10000)
325 ARMARX_INFO_S <<
"Cmakefinder took long for package " << packagePath
326 <<
" - Duration: " << dura.toMilliSeconds();
329 std::vector<std::string> resultList;
330 resultList = extractVariables(resultStr);
335 std::vector<std::string>
341 std::vector<std::string> result;
342 boost::split_regex(result, output, boost::regex(
"-I"));
344 for (
size_t i = 0; i < result.size(); i++)
346 simox::alg::trim(result[i]);
348 if (result[i].empty())
350 result.erase(result.begin() + i);
363 auto start = IceUtil::Time::now();
367 std::stringstream
str;
369 <<
" -DLANGUAGE=CXX -DCOMPILER_ID=GNU -DMODE=LINK";
379 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
395 auto start = IceUtil::Time::now();
401 start = IceUtil::Time::now();
405 std::stringstream
str;
406 str <<
"cd " + tmpDir +
";cmake --find-package -DNAME=" <<
packageName
407 <<
" -DLANGUAGE=CXX -DCOMPILER_ID=GNU -DMODE=COMPILE";
418 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
436 auto start = IceUtil::Time::now();
439 command +=
" 2>/dev/null";
442 FILE* fp = popen(command.c_str(),
"r");
446 while (fgets(line,
sizeof line, fp))
451 result = pclose(fp) / 256;
452 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
455 ARMARX_INFO_S <<
"ExecCommand took " << dura <<
" \n command: " << command;
460 std::vector<std::string>
463 std::vector<std::string> result;
464 using namespace std::filesystem;
472 const std::string envVar(*env);
473 ARMARX_DEBUG <<
"Retrieved environment variable " << envVar;
475 const std::vector<std::string> elements = simox::alg::split(envVar,
"=");
476 if (not(elements.size() == 2))
481 const std::string& envVarName = elements.front();
482 const std::string& envVarValue = elements.back();
484 if (simox::alg::ends_with(envVarName,
"_DIR"))
486 const std::string
packageName = simox::alg::remove_suffix(envVarName,
"_DIR");
493 if (pckFinder.packageFound() && !pckFinder.getBuildDir().empty())
516 const std::map<std::string, std::string>&
525 std::map<std::string, std::string>::const_iterator it =
vars.find(varName);
527 if (it !=
vars.end())
535 std::vector<std::string>
538 auto depListString =
getVar(
"DEPENDENCIES");
539 std::vector<std::string> resultList =
armarx::Split(depListString,
";",
true,
true);
544 std::map<std::string, std::string>
548 auto depListString =
getVar(
"PACKAGE_DEPENDENCY_PATHS");
549 std::vector<std::string> resultList = simox::alg::split(depListString,
";");
550 std::map<std::string, std::string> resultMap;
552 for (
auto& depPairString : resultList)
554 std::vector<std::string> depPair = simox::alg::split(depPairString,
":");
556 if (depPair.size() < 2)
561 resultMap[depPair.at(0)] = depPair.at(1);
569 std::string& varName,
570 std::string& content)
573 const boost::regex e(
"\\-\\- ([a-zA-Z0-9_]+):(.+)");
574 boost::match_results<std::string::const_iterator> what;
576 bool found = boost::regex_search(input, what, e);
578 for (
size_t i = 1; i < what.size(); i++)
593 simox::alg::trim(varName);
594 simox::alg::trim(content);
603 std::vector<std::string>
606 std::vector<std::string> resultList = simox::alg::split(cmakeVarString,
"\n");
608 for (
size_t i = 0; i < resultList.size(); i++)
610 simox::alg::trim(resultList[i]);
612 if (resultList[i].empty())
614 resultList.erase(resultList.begin() + i);
633 std::vector<std::string>
637 std::vector<std::string> resultList = simox::alg::split(depListString,
";");
644 const std::string tmpDir =
"/tmp";
645 std::string result = tmpDir;
646 char* username = getenv(
"USER");
649 std::string usernameString = std::string(username);
650 simox::alg::trim(usernameString);
651 result +=
"/armarxcmake-" + usernameString;
652 if (!std::filesystem::exists(result))
654 if (!std::filesystem::create_directories(result))
666 const boost::regex e(
"\\$C\\{([a-zA-Z0-9_\\-]+):([a-zA-Z0-9_\\-]+)\\}");
667 boost::match_results<std::string::const_iterator> what;
668 bool found = boost::regex_search(
string, what, e);
669 std::map<std::string, CMakePackageFinder> finders;
672 for (
size_t i = 1; i < what.size(); i += 3)
674 std::string
package = what[i];
675 auto it = finders.find(package);
676 if (it == finders.end())
680 .insert(std::make_pair(
685 std::string var = what[i + 1];
688 auto envVar = it->second.getVar(var);
689 string = boost::regex_replace(
string, e, std::string(envVar));
690 ARMARX_DEBUG <<
"Replacing '" << var <<
"' with '" << std::string(envVar) <<
"'";
699 return getVar(
"EXECUTABLE");
702 std::vector<std::string>
705 namespace fs = std::filesystem;
707 const fs::path componentReportFilename =
708 fs::path(
getBuildDir()) /
"component_executables_report.txt";
709 if (fs::exists(componentReportFilename))
711 std::ifstream componentReportFile(componentReportFilename);
712 if (componentReportFile.bad())
714 ARMARX_WARNING <<
"Could not load file: " << componentReportFilename;
718 const std::string content(std::istreambuf_iterator<char>{componentReportFile}, {});
719 return simox::alg::split(content,
"\n");
723 if (
vars.count(
"EXECUTABLE") > 0)
725 return simox::alg::split(
getVar(
"EXECUTABLE"),
" ");
729 <<
"/build/component_executables_report.txt` is generated properly and "
730 "EXECUTABLE variable (legacy).";
static CMakePackageFinderCache GlobalCache
static std::vector< std::string > FindPackageIncludePathList(const std::string &packageName)
Static function to find the include path with cmake.
std::vector< std::string > getDependencies() const
std::vector< std::string > getComponentExecutableNames() const
const std::map< std::string, std::string > & getVars() const
static std::string getArmarXCMakeTempDir()
return the path where the temporary cmake files are stored that are automically created by cmake.
std::map< std::string, std::string > getDependencyPaths() const
static std::string FindPackageLibs(const std::string &packageName)
std::string getVar(const std::string &varName) const
Returns the content of a CMake variable.
static std::string ExecCommand(std::string command, int &result, bool suppressStdErr=false)
std::string getName() const
Returns the name of the given package.
CMakePackageFinder(const std::string &packageName, const std::filesystem::path &packagePath="", bool suppressStdErr=false, bool usePackagePathOnlyAsHint=false)
The package with name packageName is searched with cmake during construction of this class.
std::string getBuildDir() const
std::vector< std::string > getIncludePathList() const
Return the include paths in a vector.
bool packageFound() const
Returns whether or not this package was found with cmake.
std::map< std::string, std::string > vars
static bool _ParseString(const std::string &input, std::string &varName, std::string &content)
static bool ReplaceCMakePackageFinderVars(std::string &string)
Replaces occurrences like $C{PACKAGE_NAME:VAR_NAME} with their CMakePackageFinder value.
std::vector< std::string > extractVariables(const std::string &cmakeVarString)
std::string getExecutables() const
static std::string FindPackageIncludePaths(const std::string &packageName)
static void _CreateSharedMutex()
static std::vector< std::string > FindAllArmarXSourcePackages()
std::string getIncludePaths() const
Returns the include paths separated by semi-colons.
static std::string ReadFileContents(const std::string &path)
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_IMPORTANT_S
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
This file offers overloads of toIce() and fromIce() functions for STL container types.
fs::path remove_trailing_separator(fs::path p)
std::shared_ptr< boost::interprocess::file_lock > getFileLock(std::string lockName, bool verbose=false)
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
const std::shared_ptr< boost::interprocess::file_lock > & CacheFileLock()
std::mutex & CMakeMutex()
std::mutex cmakePackageMutex
ScopedFileLockPtr lockCMake()
std::shared_ptr< boost::interprocess::file_lock > CreateAndCheckFileLock(const std::string &name)
std::shared_ptr< boost::interprocess::scoped_lock< boost::interprocess::file_lock > > ScopedFileLockPtr
std::shared_ptr< boost::interprocess::managed_shared_memory > sharedMemorySegment
const std::shared_ptr< boost::interprocess::file_lock > & CMakeFileLock()
bool readCMakeCache(const std::string &packageName, std::string &packageContent)
bool updateCMakeCache(const std::string &packageName, const std::string &packageContent)
boost::interprocess::interprocess_upgradable_mutex * memoryMutex