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"
65 char* homePathC = getenv(
"HOME");
77 const char* user = getenv(
"USER");
78 std::string dir = fs::temp_directory_path().string() +
"/ArmarXCMakeCache_" +
79 (user ? user :
"DUMMY_USER");
83 const char* workspace = getenv(
"ARMARX_WORKSPACE");
86 const std::string workspaceName =
88 if (not workspaceName.empty())
90 dir +=
"_" + workspaceName;
97 std::shared_ptr<boost::interprocess::file_lock>
103 std::cout <<
"getFileLock( path = " << path <<
")" << std::endl;
105 if (!std::filesystem::exists(path))
109 std::cout <<
"path does not exist" << std::endl;
111 if (!std::filesystem::create_directories(path))
115 std::cout <<
"failed to create directories" << std::endl;
117 return std::shared_ptr<boost::interprocess::file_lock>();
120 path +=
"/" + lockName;
123 std::cout <<
"lock file = " << path << std::endl;
125 if (!std::filesystem::exists(path))
129 std::cout <<
"touch " << path << std::endl;
132 std::ofstream file(path);
137 std::cout <<
"build lock with " << path << std::endl;
139 return std::shared_ptr<boost::interprocess::file_lock>(
140 new boost::interprocess::file_lock(path.c_str()));
143 std::shared_ptr<boost::interprocess::file_lock>
147 std::shared_ptr<boost::interprocess::file_lock> lock(
getFileLock(name,
false));
157 const std::shared_ptr<boost::interprocess::file_lock>&
166 const std::shared_ptr<boost::interprocess::file_lock>&
178 static std::mutex mx;
183 std::shared_ptr<boost::interprocess::scoped_lock<boost::interprocess::file_lock>>;
191 new boost::interprocess::scoped_lock<boost::interprocess::file_lock>(
192 *
CMakeFileLock(), boost::get_system_time() + boost::posix_time::milliseconds(50)));
193 while (!lockPtr->owns())
195 lockPtr->timed_lock(boost::get_system_time() + boost::posix_time::milliseconds(50));
203 auto start = IceUtil::Time::now();
205 path +=
"/" + packageName;
206 if (!std::filesystem::exists(path))
213 const auto writeTime = std::filesystem::last_write_time(path);
214 const auto now =
decltype(writeTime)::clock::now();
215 long age = std::chrono::duration_cast<std::chrono::seconds>(now - writeTime).count();
217 boost::interprocess::sharable_lock<boost::interprocess::file_lock> e_lock(
220 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
223 ARMARX_INFO_S << packageName <<
" from cache locked for " << dura;
239 auto start = IceUtil::Time::now();
241 if (!std::filesystem::exists(path))
243 if (!std::filesystem::create_directories(path))
248 path = path +
"/" + packageName;
249 boost::interprocess::scoped_lock<boost::interprocess::file_lock> e_lock(*
CacheFileLock());
253 std::ofstream file(path);
254 file << packageContent;
257 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
260 ARMARX_INFO_S << packageName <<
" update cache locked for " << dura;
265 boost::interprocess::interprocess_upgradable_mutex*
memoryMutex =
nullptr;
270 const std::filesystem::path& packagePath,
272 bool usePackagePathOnlyAsHint) :
275 if (this->packageName.empty())
277 ARMARX_WARNING <<
"CMakePackageFinder: Package name must not be empty";
279 static std::string scriptPath;
282 std::unique_lock lock(cmakePackageMutex);
284 if (scriptPath.empty())
288 auto start = IceUtil::Time::now();
289 auto list = FindPackageIncludePathList(
"ArmarXCore");
290 auto dura = (IceUtil::Time::now() - start);
292 if (dura.toMilliSeconds() > 10000)
294 ARMARX_INFO_S <<
"Cmakefinder for initial core search took long - Duration: "
295 << dura.toMilliSeconds();
298 if (!ArmarXDataPath::getAbsolutePath(SCRIPT_PATH, scriptPath, list, false))
301 <<
"Finding FindPackageX.cmake failed - trying again with different path";
303 if (!ArmarXDataPath::getAbsolutePath(std::string(
"../source/") + SCRIPT_PATH,
314 auto start = IceUtil::Time::now();
315 std::string resultStr;
316 std::string tmpDir = getArmarXCMakeTempDir();
319 if (!packagePath.empty())
321 resultStr = ExecCommand(
"cd " + tmpDir +
"; cmake -DPACKAGE=" + this->packageName +
322 " -DPACKAGEBUILDPATH" +
323 (usePackagePathOnlyAsHint ?
"Hint" :
"") +
"=" +
324 packagePath.string() +
" -P " + scriptPath,
330 resultStr = ExecCommand(
"cd " + tmpDir +
"; cmake -DPACKAGE=" + this->packageName +
342 auto dura = (IceUtil::Time::now() - start);
344 if (dura.toMilliSeconds() > 10000)
346 ARMARX_INFO_S <<
"Cmakefinder took long for package " << packagePath
347 <<
" - Duration: " << dura.toMilliSeconds();
350 std::vector<std::string> resultList;
351 resultList = extractVariables(resultStr);
356 std::vector<std::string>
362 std::vector<std::string> result;
363 boost::split_regex(result, output, boost::regex(
"-I"));
365 for (
size_t i = 0; i < result.size(); i++)
367 simox::alg::trim(result[i]);
369 if (result[i].empty())
371 result.erase(result.begin() + i);
384 auto start = IceUtil::Time::now();
388 std::stringstream
str;
390 <<
" -DLANGUAGE=CXX -DCOMPILER_ID=GNU -DMODE=LINK";
400 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
416 auto start = IceUtil::Time::now();
422 start = IceUtil::Time::now();
426 std::stringstream
str;
427 str <<
"cd " + tmpDir +
";cmake --find-package -DNAME=" <<
packageName
428 <<
" -DLANGUAGE=CXX -DCOMPILER_ID=GNU -DMODE=COMPILE";
439 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
457 auto start = IceUtil::Time::now();
460 command +=
" 2>/dev/null";
463 FILE* fp = popen(command.c_str(),
"r");
467 while (fgets(line,
sizeof line, fp))
472 result = pclose(fp) / 256;
473 auto dura = (IceUtil::Time::now() - start).toMilliSecondsDouble();
476 ARMARX_INFO_S <<
"ExecCommand took " << dura <<
" \n command: " << command;
481 std::vector<std::string>
484 std::vector<std::string> result;
485 using namespace std::filesystem;
493 const std::string envVar(*env);
494 ARMARX_DEBUG <<
"Retrieved environment variable " << envVar;
496 const std::vector<std::string> elements = simox::alg::split(envVar,
"=");
497 if (not(elements.size() == 2))
502 const std::string& envVarName = elements.front();
503 const std::string& envVarValue = elements.back();
505 if (simox::alg::ends_with(envVarName,
"_DIR"))
507 const std::string
packageName = simox::alg::remove_suffix(envVarName,
"_DIR");
514 if (pckFinder.packageFound() && !pckFinder.getBuildDir().empty())
537 const std::map<std::string, std::string>&
546 std::map<std::string, std::string>::const_iterator it =
vars.find(varName);
548 if (it !=
vars.end())
556 std::vector<std::string>
559 auto depListString =
getVar(
"DEPENDENCIES");
560 std::vector<std::string> resultList =
armarx::Split(depListString,
";",
true,
true);
565 std::map<std::string, std::string>
569 auto depListString =
getVar(
"PACKAGE_DEPENDENCY_PATHS");
570 std::vector<std::string> resultList = simox::alg::split(depListString,
";");
571 std::map<std::string, std::string> resultMap;
573 for (
auto& depPairString : resultList)
575 std::vector<std::string> depPair = simox::alg::split(depPairString,
":");
577 if (depPair.size() < 2)
582 resultMap[depPair.at(0)] = depPair.at(1);
590 std::string& varName,
591 std::string& content)
594 const boost::regex e(
"\\-\\- ([a-zA-Z0-9_]+):(.+)");
595 boost::match_results<std::string::const_iterator> what;
597 bool found = boost::regex_search(input, what, e);
599 for (
size_t i = 1; i < what.size(); i++)
614 simox::alg::trim(varName);
615 simox::alg::trim(content);
624 std::vector<std::string>
627 std::vector<std::string> resultList = simox::alg::split(cmakeVarString,
"\n");
629 for (
size_t i = 0; i < resultList.size(); i++)
631 simox::alg::trim(resultList[i]);
633 if (resultList[i].empty())
635 resultList.erase(resultList.begin() + i);
654 std::vector<std::string>
658 std::vector<std::string> resultList = simox::alg::split(depListString,
";");
665 const std::string tmpDir =
"/tmp";
666 std::string result = tmpDir;
667 char* username = getenv(
"USER");
670 std::string usernameString = std::string(username);
671 simox::alg::trim(usernameString);
672 result +=
"/armarxcmake-" + usernameString;
673 if (!std::filesystem::exists(result))
675 if (!std::filesystem::create_directories(result))
687 const boost::regex e(
"\\$C\\{([a-zA-Z0-9_\\-]+):([a-zA-Z0-9_\\-]+)\\}");
688 boost::match_results<std::string::const_iterator> what;
689 bool found = boost::regex_search(
string, what, e);
690 std::map<std::string, CMakePackageFinder> finders;
693 for (
size_t i = 1; i < what.size(); i += 3)
695 std::string
package = what[i];
696 auto it = finders.find(package);
697 if (it == finders.end())
701 .insert(std::make_pair(
706 std::string var = what[i + 1];
709 auto envVar = it->second.getVar(var);
710 string = boost::regex_replace(
string, e, std::string(envVar));
711 ARMARX_DEBUG <<
"Replacing '" << var <<
"' with '" << std::string(envVar) <<
"'";
720 return getVar(
"EXECUTABLE");
723 std::vector<std::string>
726 namespace fs = std::filesystem;
728 const fs::path componentReportFilename =
729 fs::path(
getBuildDir()) /
"component_executables_report.txt";
730 if (fs::exists(componentReportFilename))
732 std::ifstream componentReportFile(componentReportFilename);
733 if (componentReportFile.bad())
735 ARMARX_WARNING <<
"Could not load file: " << componentReportFilename;
739 const std::string content(std::istreambuf_iterator<char>{componentReportFile}, {});
740 return simox::alg::split(content,
"\n");
744 if (
vars.count(
"EXECUTABLE") > 0)
746 return simox::alg::split(
getVar(
"EXECUTABLE"),
" ");
750 <<
"/build/component_executables_report.txt` is generated properly and "
751 "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
std::string getCMakeCacheDir()