| /***************************************************************************** |
| * Copyright (c) 2020 CEA LIST. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * Contributors: |
| * Ansgar Radermacher ansgar.radermacher@cea.fr |
| * |
| *****************************************************************************/ |
| |
| package org.eclipse.papyrus.robotics.ros2.base; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.InputStreamReader; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.papyrus.designer.infra.base.StringUtils; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.FileDialog; |
| import org.eclipse.swt.widgets.Shell; |
| |
| import com.google.common.collect.Lists; |
| |
| /** |
| * Manage an environment for sub-processes |
| */ |
| public class EnvironmentUtils { |
| |
| public static final String BASH_ENV = "BASH_ENV"; //$NON-NLS-1$ |
| |
| public static final String PATH = "PATH"; //$NON-NLS-1$ |
| |
| public static final String ROS_PREFIX = "ROS_"; //$NON-NLS-1$ |
| |
| public static final String DECLARE_X = "declare -x"; //$NON-NLS-1$ |
| |
| protected static Map<String, String> localEnv = null; |
| |
| protected static Job setupJob; |
| |
| /** |
| * @return an environment map |
| */ |
| public static Map<String, String> getenv() { |
| if (localEnv == null) { |
| localEnv = new HashMap<String, String>(); |
| localEnv.putAll(System.getenv()); |
| } |
| return localEnv; |
| } |
| |
| /** |
| * Return an environment value from a key |
| * |
| * @param key |
| * @return the environment value |
| */ |
| public static String get(String key) { |
| return getenv().get(key); |
| } |
| |
| // update the following environment values (and those starting with ROS_) |
| public static final List<String> checkValues = Lists.newArrayList( |
| Ros2Constants.CMAKE_PREFIX_PATH, |
| Ros2Constants.AMENT_PREFIX_PATH, |
| Ros2Constants.PYTHON_PATH, |
| "PATH", //$NON-NLS-1$ |
| "LD_LIBRARY_PATH"); //$NON-NLS-1$ |
| |
| /** |
| * Run setup application in a new job |
| */ |
| public static void runCheckAndApplySetupJob(final String pathList) { |
| setupJob = new Job("check environment") { //$NON-NLS-1$ |
| |
| @Override |
| public IStatus run(IProgressMonitor monitor) { |
| boolean ok = EnvironmentUtils.checkAndApplySetup(pathList, monitor); |
| setupJob = null; |
| if (ok) { |
| return Status.OK_STATUS; |
| } else { |
| return Status.CANCEL_STATUS; |
| } |
| } |
| }; |
| setupJob.schedule(); |
| } |
| |
| /** |
| * block, until an eventually running setup job is finished |
| */ |
| public static void waitForSetupJob() { |
| if (setupJob != null) { |
| try { |
| setupJob.join(); |
| } catch (InterruptedException e) { |
| Activator.log.error(e); |
| } |
| } |
| } |
| |
| /** |
| * Apply setup parameters |
| * |
| * @param pathList |
| * a colon (File.pathSeparator) separated list of setup paths |
| */ |
| @SuppressWarnings("nls") |
| public static boolean checkAndApplySetup(String pathList, IProgressMonitor monitor) { |
| if (pathList.length() == 0) { |
| // empty, nothing to do |
| return true; |
| } |
| String[] pathListArray = pathList.split(File.pathSeparator); |
| resetEnvironment(); |
| monitor.beginTask("source setup.file", pathListArray.length); |
| for (String setupPath : pathListArray) { |
| String setupFile = setupPath + "/setup.bash"; //$NON-NLS-1$ |
| monitor.subTask(setupFile); |
| if (new File(setupFile).exists()) { |
| sourceScript(setupFile); |
| } else { |
| // execute error message asynchronously to avoid blocking the tests (that run |
| // on an CI server without a ROS2 installation) |
| Display.getDefault().asyncExec(new Runnable() { |
| |
| @Override |
| public void run() { |
| final Shell shell = Display.getCurrent().getActiveShell(); |
| MessageDialog.openInformation(shell, "ROS setup", String.format( //$NON-NLS-1$ |
| "Papyrus for Robotics could not find a setup.bash file at location (%s). " + |
| "Please verify the PATH in the ROS2 preferences.\n" + |
| "=> Window => Preferences => type \"ros\" in filter => setup path list.", |
| setupPath)); |
| } |
| }); |
| } |
| if (monitor.isCanceled()) { |
| return false; |
| } |
| monitor.worked(1); |
| } |
| return true; |
| } |
| |
| /** |
| * reset the local environment variables back to the system values |
| */ |
| public static void resetEnvironment() { |
| if (localEnv != null) { |
| for (String var : System.getenv().keySet()) { |
| if (var.startsWith(ROS_PREFIX) || checkValues.contains(var)) { |
| String value = System.getenv(var); |
| localEnv.put(var, value); |
| } |
| } |
| } |
| } |
| |
| @SuppressWarnings("nls") |
| public static void selectAndSourceScript(String initialFile, Shell shell) { |
| if (shell != null) { |
| FileDialog fd = new FileDialog(shell, SWT.OPEN); |
| fd.setText("Open"); |
| String[] filterExt = { "*.bash;*.sh" }; |
| String[] filterNames = { "setup files" }; |
| fd.setFilterExtensions(filterExt); |
| fd.setFilterNames(filterNames); |
| fd.setFileName(initialFile); |
| String fileName = fd.open(); |
| if (fileName != null) { |
| sourceScript(fileName); |
| } |
| } |
| } |
| |
| /** |
| * Source the ROS2 setup.bash created within the Eclipse workspace |
| * (if it exists) |
| */ |
| public static void sourceWorkspace() { |
| String fileName = ResourcesPlugin.getWorkspace().getRoot().getLocation().toString(); |
| fileName += "/install/setup.bash"; //$NON-NLS-1$ |
| if (new File(fileName).exists()) { |
| sourceScript(fileName); |
| } |
| } |
| |
| /** |
| * Source a ROS script and update the environment accordingly. |
| * |
| * @param scriptFile |
| */ |
| public static void sourceScript(String scriptFile) { |
| ProcessBuilder pb = new ProcessBuilder("bash", //$NON-NLS-1$ |
| "-c", //$NON-NLS-1$ |
| "export"); //$NON-NLS-1$ |
| Map<String, String> pbEnv = pb.environment(); |
| // the file in BASH_ENV is read by bash |
| pbEnv.put(BASH_ENV, scriptFile); |
| pbEnv.putAll(getenv()); |
| |
| try { |
| Process p = pb.start(); |
| BufferedReader results = new BufferedReader(new InputStreamReader(p.getInputStream())); |
| boolean error = ProcessUtils.logErrors(p); |
| if (error) { |
| return; |
| } |
| String line; |
| // Map<String, String> env = EnvironmentUtils.getModifiableEnvironmentMap(); |
| while ((line = results.readLine()) != null) { |
| line = line.substring(DECLARE_X.length()); |
| String lineArray[] = line.split("="); //$NON-NLS-1$ |
| if (lineArray.length == 2) { |
| String var = lineArray[0].trim(); |
| String value = StringUtils.unquote(lineArray[1]); |
| |
| if (var.startsWith(ROS_PREFIX) || checkValues.contains(var)) { |
| localEnv.put(var, value); |
| } |
| } |
| } |
| } catch (Exception e) { |
| Activator.log.error(e); |
| } |
| } |
| |
| /** |
| * Return the executable or null |
| * |
| * @param executable |
| * the name of the executable |
| * @return the absolute path to the executable or null |
| */ |
| public static String getFromPath(final String executable) { |
| String path = get(PATH); |
| if (path != null) { |
| String[] pathParts = path.split(File.pathSeparator); |
| for (String pathPart : pathParts) { |
| File file = new File(pathPart + File.separator + executable); |
| if (file.exists()) { |
| return file.getAbsolutePath(); |
| } |
| } |
| } |
| return null; |
| } |
| } |