25 #include <SimoxUtility/algorithm/string.h>
46 defs->required(
armarxPackageName, prefix +
"10_ArmarXPackageName",
"Name of the ArmarX package.");
47 defs->optional(
armarxPythonPackagesDir, prefix +
"11_ArmarXPythonPackagesDir",
"Directory where python packages are located (relative to the root of the ArmarX package).");
49 defs->required(
pythonPackageName, prefix +
"20_PythonPackageName",
"Name of the Python package in the ArmarXPackage/python/ directory.");
50 defs->required(
pythonScriptPath, prefix +
"21_PythonScriptPath",
"Path to the python script (inside the python package).");
52 defs->optional(
pythonPathEntriesString, prefix +
"23_PythonPathEntries",
"Colon-separated list of paths to add to the PYTHONPATH.");
53 defs->optional(
pythonPoetry, prefix +
"24_PythonPoetry",
"Use python poetry.");
55 defs->optional(
venvName, prefix +
"30_venv.Name",
"Name of the virtual environment.");
58 ss <<
"Type of the virtual environment."
67 defs->optional(
workingDir, prefix +
"40_WorkingDirectory",
68 "If set, this path is used as working directory for the python script (overriding BinaryPathAsWorkingDirectory)."
69 "\n${HOME} for env vars, $C{RobotAPI:BINARY_DIR} for CMakePackageFinder vars");
76 if (props.
getProperty<std::string>(prefix +
"40_WorkingDirectory").isSet())
78 workingDir = props.
getProperty<std::string>(
"WorkingDirectory").getValueAndReplaceAllVars();
81 pythonScriptArgumentsVector =
simox::alg::split(pythonScriptArgumentsString,
" ",
true,
true);
82 pythonPathEntriesVector =
simox::alg::split(pythonPathEntriesString,
":",
true,
true);
92 defs->defineOptionalProperty<
bool>(
"BinaryPathAsWorkingDirectory",
false,
"If true the path of the binary is set as the working directory.");
93 defs->defineOptionalProperty<
bool>(
"RestartInCaseOfCrash",
false,
"Whether the application should be restarted in case it crashed.");
95 defs->defineOptionalProperty<
bool>(
"DisconnectInCaseOfCrash",
true,
"Whether this component should disconnect as long as the application is not running.");
96 defs->defineOptionalProperty<
int>(
"FakeObjectStartDelay", 0,
"Delay in ms after which the fake armarx object is started on which other apps can depend. Not used if property FakeObjectDelayedStartKeyword is used.");
97 defs->defineOptionalProperty<std::string>(
"FakeObjectDelayedStartKeyword",
"",
"If not empty, the start up of the fake armarx object will be delayed until this keyword is found in the stdout of the subprocess.");
98 defs->defineOptionalProperty<
int>(
"KillDelay", 2000,
"Delay ins ms before the subprocess is killed after sending the stop signal");
99 defs->defineOptionalProperty<
bool>(
"PythonUnbuffered",
true,
"If true, PYTHONUNBUFFERED=1 is added to the environment variables.");
100 defs->defineOptionalProperty<
bool>(
"RedirectToArmarXLog",
true,
"If true, all outputs from the subprocess are printed with ARMARX_LOG, otherwise with std::cout");
101 defs->defineOptionalProperty<Ice::StringSeq>(
"AdditionalEnvVars", {},
"Comma-seperated list of env-var assignment, e.g. MYVAR=1,ADDPATH=/tmp");
102 defs->defineOptionalProperty<Ice::StringSeq>(
"Dependencies", {},
"Comma-seperated list of Ice Object dependencies. The external app will only be started after all dependencies have been found.");
112 std::filesystem::path armarxCoreRootPath = finder.
getPackageDir();
113 std::filesystem::path armarxCliPath = armarxCoreRootPath /
"etc" /
"python";
114 return armarxCliPath;
122 properties.
read(*
this,
"py.");
127 std::stringstream originalPythonPath;
128 if (
char* pp = getenv(
"PYTHONPATH"))
130 originalPythonPath << pp;
132 std::vector<std::string> pythonPath =
simox::alg::split(originalPythonPath.str(),
":");
147 if (
auto it = std::find(pythonPath.begin(), pythonPath.end(), armarxCliPath.string()); it != pythonPath.end())
149 message <<
"(Removed '" << *it <<
"' from PYTHONPATH.)";
150 pythonPath.erase(it);
153 pythonPath.push_back(this->paths.pythonPackagePath.string());
156 const std::string newPythonpath = simox::alg::join(pythonPath,
":");
158 ARMARX_INFO <<
"Setting PYTHONPATH:\n\t" << newPythonpath
159 <<
"\nOriginal PYTHONPATH:\n\t" << originalPythonPath.str()
162 setenv(
"PYTHONPATH", newPythonpath.c_str(), 1);
226 os <<
"\nVenv path: \t" << paths.
venvPath;
228 os <<
"\nWorking directory: \t" << paths.
workingDir;
236 namespace fs = std::filesystem;
238 armarxPackagePath = findArmarXPackagePath(properties);
239 pythonPackagePath = findPythonPackagePath(properties);
240 pythonScriptPath = findPythonScriptPath(properties);
244 std::string cmd = std::string(
"cd ")
245 +
reinterpret_cast<const char*
>(pythonPackagePath.u8string().c_str())
246 +
";poetry env info -p --ansi" ;
248 std::array<char, 128> buffer;
249 std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(),
"r"), pclose);
252 throw std::runtime_error(
"popen() failed!");
254 while (fgets(buffer.data(), buffer.size(), pipe.get()) !=
nullptr)
256 std::string
s = buffer.data();
257 s.erase(std::remove(
s.begin(),
s.end(),
'\n'),
s.end());
263 venvPath = findVenvPath(properties);
265 pythonBinPath = findPythonBinaryPath(properties);
269 workingDir = pythonScriptPath.parent_path();
282 std::stringstream ss;
284 throw armarx::LocalException(ss.str());
291 return armarxPackagePath
298 std::vector<path> candidates
307 if (std::optional<path> p = checkCandidateFiles(candidates))
313 std::stringstream ss;
314 ss <<
"Could not find python script '" << properties.
pythonScriptPath <<
"' "
317 <<
"Checked candidates: ";
318 for (
const path& candidate : candidates)
320 ss <<
"\n" << candidate;
322 throw armarx::LocalException(ss.str());
334 venvPath = getDedicatedVenvPath(properties);
338 venvPath = getSharedVenvPath(properties);
343 std::vector<path> candidates
345 getDedicatedVenvPath(properties),
346 getSharedVenvPath(properties),
348 if (std::optional<path> p = checkCandidateDirectories(candidates))
350 venvPath = p.value();
354 std::stringstream ss;
355 ss <<
"Could not find dedicated or shared venv '" << properties.
venvName <<
"' "
358 <<
"Checked candidates: ";
359 for (
const path& candidate : candidates)
361 ss <<
"\n" << candidate;
363 throw armarx::LocalException(ss.str());
369 if (std::filesystem::is_directory(venvPath))
375 std::stringstream ss;
377 <<
" venv '" << properties.
venvName <<
"' ";
389 ss <<
"Expected path: \n" << venvPath;
390 throw armarx::LocalException(ss.str());
398 const path pythonBinPath = venvPath /
"bin" /
"python";
400 if (std::filesystem::is_regular_file(pythonBinPath))
402 return pythonBinPath;
406 std::stringstream ss;
407 ss <<
"Could not find python binary "
409 <<
" venv '" << properties.
venvName <<
"' ";
421 ss <<
"Expected path: \n" << pythonBinPath;
422 throw armarx::LocalException(ss.str());
428 return pythonPackagePath
434 return armarxPackagePath
442 return checkCandidatePaths(candidates, [](
const path & p)
444 return std::filesystem::is_regular_file(p);
450 return checkCandidatePaths(candidates, [](
const path & p)
452 return std::filesystem::is_directory(p);
458 auto it = std::find_if(candidates.begin(), candidates.end(), existsFn);
459 if (it != candidates.end())