Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ log
build
.idea
src/ros2cs
**/metadata*.xml
src/Ros2ForUnity/Plugins
!src/Ros2ForUnity/Plugins/.gitkeep
10 changes: 10 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ if($clean_install) {
Write-Host "Cleaning install directory..." -ForegroundColor White
Remove-Item -Path "$scriptPath\install" -Force -Recurse -ErrorAction Ignore
}

if($standalone) {}
& "python3 $SCRIPTPATH\src\scripts\metadata_generator.py" --standalone
} else {
& "python3 $SCRIPTPATH\src\scripts\metadata_generator.py"
}

& "$scriptPath\src\ros2cs\build.ps1" @options
if($?) {
md -Force $scriptPath\install\asset | Out-Null
Expand All @@ -42,6 +49,9 @@ if($?) {
$plugin_path=Join-Path -Path $scriptPath -ChildPath "\install\asset\Ros2ForUnity\Plugins\"
Write-Host "Deploying build to $plugin_path" -ForegroundColor Green
& "$scriptPath\deploy_unity_plugins.ps1" $plugin_path

Copy-Item -Path $scriptPath\src\Ros2ForUnity\metadata_ros2cs.xml -Destination $scriptPath\install\asset\Ros2ForUnity\Plugins\Linux\x86_64\
Copy-Item -Path $scriptPath\src\Ros2ForUnity\metadata_ros2cs.xml -Destination $scriptPath\install\asset\Ros2ForUnity\Plugins\
} else {
Write-Host "Ros2cs build failed!" -ForegroundColor Red
exit 1
Expand Down
9 changes: 9 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,18 @@ if [ $CLEAN_INSTALL == 1 ]; then
echo "Cleaning install directory..."
rm -rf $SCRIPTPATH/install/*
fi

if [ $STANDALONE == 1 ]; then
python3 $SCRIPTPATH/src/scripts/metadata_generator.py --standalone
else
python3 $SCRIPTPATH/src/scripts/metadata_generator.py
fi

if $SCRIPTPATH/src/ros2cs/build.sh $OPTIONS; then
mkdir -p $SCRIPTPATH/install/asset && cp -R $SCRIPTPATH/src/Ros2ForUnity $SCRIPTPATH/install/asset/
$SCRIPTPATH/deploy_unity_plugins.sh $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/
cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/Linux/x86_64/metadata_ros2cs.xml
cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/metadata_ros2cs.xml
else
echo "Ros2cs build failed!"
exit 1
Expand Down
50 changes: 50 additions & 0 deletions src/Ros2ForUnity/Scripts/PostInstall.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2019-2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#if UNITY_EDITOR
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;

namespace ROS2
{

/// <summary>
/// An internal class responsible for installing ros2-for-unity metadata files
/// </summary>
internal class PostInstall : IPostprocessBuildWithReport
{
public int callbackOrder { get { return 0; } }
public void OnPostprocessBuild(BuildReport report)
{
var r2fuMetadataName = "metadata_ros2_for_unity.xml";
var r2csMetadataName = "metadata_ros2cs.xml";

// FileUtil.CopyFileOrDirectory: All file separators should be forward ones "/".
var r2fuMeta = ROS2ForUnity.GetRos2ForUnityPath() + "/" + r2fuMetadataName;
var r2csMeta = ROS2ForUnity.GetPluginPath() + "/" + r2csMetadataName;
var outputDir = Directory.GetParent(report.summary.outputPath);
var execFilename = Path.GetFileNameWithoutExtension(report.summary.outputPath);
FileUtil.CopyFileOrDirectory(
r2fuMeta, outputDir + "/" + execFilename + "_Data/" + r2fuMetadataName);
FileUtil.CopyFileOrDirectory(
r2csMeta, outputDir + "/" + execFilename + "_Data/Plugins/" + r2csMetadataName);
}

}

}
#endif
137 changes: 118 additions & 19 deletions src/Ros2ForUnity/Scripts/ROS2ForUnity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Xml;

namespace ROS2
{
Expand All @@ -28,14 +29,16 @@ internal class ROS2ForUnity
{
private static bool isInitialized = false;
private static string ros2ForUnityAssetFolderName = "Ros2ForUnity";
private XmlDocument ros2csMetadata = new XmlDocument();
private XmlDocument ros2ForUnityMetadata = new XmlDocument();

enum Platform
{
Windows,
Linux
}

private Platform GetOS()
private static Platform GetOS()
{
if (Application.platform == RuntimePlatform.LinuxEditor || Application.platform == RuntimePlatform.LinuxPlayer)
{
Expand All @@ -48,11 +51,11 @@ private Platform GetOS()
throw new System.NotSupportedException("Only Linux and Windows are supported");
}

private bool InEditor() {
private static bool InEditor() {
return Application.isEditor;
}

private string GetOSName()
private static string GetOSName()
{
switch (GetOS())
{
Expand Down Expand Up @@ -80,7 +83,7 @@ private string GetEnvPathVariableValue()
return Environment.GetEnvironmentVariable(GetEnvPathVariableName());
}

private string GetPluginPath()
public static string GetRos2ForUnityPath()
{
char separator = Path.DirectorySeparatorChar;
string appDataPath = Application.dataPath;
Expand All @@ -89,6 +92,14 @@ private string GetPluginPath()
if (InEditor()) {
pluginPath += separator + ros2ForUnityAssetFolderName;
}
return pluginPath;
}

public static string GetPluginPath()
{
char separator = Path.DirectorySeparatorChar;
string ros2ForUnityPath = GetRos2ForUnityPath();
string pluginPath = ros2ForUnityPath;

pluginPath += separator + "Plugins";

Expand Down Expand Up @@ -116,7 +127,7 @@ private string GetPluginPath()
/// Note that on Linux, LD_LIBRARY_PATH as used for dlopen() is determined on process start and this change won't
/// affect it. Ros2 looks for rmw implementation based on this variable (independently) and the change
/// is effective for this process, however rmw implementation's dependencies itself are loaded by dynamic linker
/// anyway so setting it for Linux is pointless.
/// anyway so setting it for Linux is pointless.
/// </description>
private void SetEnvPathVariable()
{
Expand All @@ -132,16 +143,69 @@ private void SetEnvPathVariable()
Environment.SetEnvironmentVariable(GetEnvPathVariableName(), pluginPath + envPathSep + currentPath);
}

public bool IsStandalone() {
return Convert.ToBoolean(Convert.ToInt16(GetMetadataValue(ros2csMetadata, "/ros2cs/standalone")));
}

public string GetROSVersion()
{
string ros2SourcedCodename = GetROSVersionSourced();
string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, "/ros2_for_unity/ros2");

// Sourced ROS2 libs takes priority
if (string.IsNullOrEmpty(ros2SourcedCodename)) {
return ros2FromRos4UMetadata;
}

return ros2SourcedCodename;
}

/// <summary>
/// Checks if both ros2cs and ros2-for-unity were build for the same ros version as well as
/// the current sourced ros version matches ros2cs binaries.
/// </summary>
public void CheckIntegrity()
{
string ros2SourcedCodename = GetROSVersionSourced();
string ros2FromRos2csMetadata = GetMetadataValue(ros2csMetadata, "/ros2cs/ros2");
string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, "/ros2_for_unity/ros2");

if (ros2FromRos4UMetadata != ros2FromRos2csMetadata) {
Debug.LogError(
"ROS2 versions in 'ros2cs' and 'ros2-for-unity' metadata files are not the same. " +
"This is caused by mixing versions/builds. Plugin might not work correctly."
);
}

if(!IsStandalone() && ros2SourcedCodename != ros2FromRos2csMetadata) {
Debug.LogError(
"ROS2 version in 'ros2cs' metadata doesn't match currently sourced version. " +
"This is caused by mixing versions/builds. Plugin might not work correctly."
);
}

if (IsStandalone() && !string.IsNullOrEmpty(ros2SourcedCodename)) {
Debug.LogError(
"You should not source ROS2 in 'ros2-for-unity' standalone build. " +
"Plugin might not work correctly."
);
}
}

public string GetROSVersionSourced()
{
return Environment.GetEnvironmentVariable("ROS_DISTRO");
}

/// <summary>
/// Check if the ros version is supported, only applicable to non-standalone plugin versions
/// (i. e. without ros2 libraries included in the plugin).
/// </summary>
private string CheckROSVersionSourced()
private void CheckROSSupport(string ros2Codename)
{
string currentVersion = Environment.GetEnvironmentVariable("ROS_DISTRO");
List<string> supportedVersions = new List<string>() { "foxy", "galactic" };
var supportedVersionsString = String.Join(", ", supportedVersions);
if (string.IsNullOrEmpty(currentVersion))
if (string.IsNullOrEmpty(ros2Codename))
{
string errMessage = "No ROS environment sourced. You need to source your ROS2 " + supportedVersionsString
+ " environment before launching Unity (ROS_DISTRO env variable not found)";
Expand All @@ -155,9 +219,9 @@ private string CheckROSVersionSourced()
#endif
}

if (!supportedVersions.Contains(currentVersion))
if (!supportedVersions.Contains(ros2Codename))
{
string errMessage = "Currently sourced ROS version differs from supported one. Sourced: " + currentVersion
string errMessage = "Currently sourced ROS version differs from supported one. Sourced: " + ros2Codename
+ ", supported: " + supportedVersionsString + ".";
Debug.LogError(errMessage);
#if UNITY_EDITOR
Expand All @@ -168,8 +232,6 @@ private string CheckROSVersionSourced()
Application.Quit(ROS_BAD_VERSION_CODE);
#endif
}
Debug.Log("Running with a supported ROS 2 version: " + currentVersion);
return currentVersion;
}

private void RegisterCtrlCHandler()
Expand All @@ -192,28 +254,65 @@ private void ConnectLoggers()
Ros2csLogger.LogLevel = LogLevel.WARNING;
}

private string GetMetadataValue(XmlDocument doc, string valuePath)
{
return doc.DocumentElement.SelectSingleNode(valuePath).InnerText;
}

private void LoadMetadata()
{
char separator = Path.DirectorySeparatorChar;
try
{
ros2csMetadata.Load(GetPluginPath() + separator + "metadata_ros2cs.xml");
ros2ForUnityMetadata.Load(GetRos2ForUnityPath() + separator + "metadata_ros2_for_unity.xml");
}
catch (System.IO.FileNotFoundException)
{
var errMessage = "Could not find metadata files.";
#if UNITY_EDITOR
EditorApplication.isPlaying = false;
throw new System.IO.FileNotFoundException(errMessage);
#else
const int NO_METADATA = 1;
Application.Quit(NO_METADATA);
#endif
}
}

internal ROS2ForUnity()
{
// TODO: Find a way to determine whether we run standalone build
// Load metadata
LoadMetadata();
string currentRos2Version = GetROSVersion();
string standalone = IsStandalone() ? "standalone" : "non-standalone";

// Self checks
CheckROSSupport(currentRos2Version);
CheckIntegrity();

// Library loading
if (GetOS() == Platform.Windows) {
// Windows version can run standalone, modifies PATH to ensure all plugins visibility
SetEnvPathVariable();
} else {
// Linux version needs to have ros2 sourced, which is checked here. It also loads plugins by absolute path
// since LD_LIBRARY_PATH cannot be set dynamically within the process for dlopen() which is used under the hood.
// Since libraries are built with -rpath=".", dependencies will be correcly located within plugins directory.
// For foxy, it is also necessary to use modified version of librcpputils to resolve custom msgs packages.
string currentRosVersion = CheckROSVersionSourced();
// For foxy, it is necessary to use modified version of librcpputils to resolve custom msgs packages.
ROS2.GlobalVariables.absolutePath = GetPluginPath() + "/";
if (currentRosVersion == "foxy") {
if (currentRos2Version == "foxy") {
ROS2.GlobalVariables.preloadLibrary = true;
ROS2.GlobalVariables.preloadLibraryName = "librcpputils.so";
}
}

// Initialize
ConnectLoggers();
Ros2cs.Init();
RegisterCtrlCHandler();

string rmwImpl = Ros2cs.GetRMWImplementation();

Debug.Log("ROS2 version: " + currentRos2Version + ". Build type: " + standalone + ". RMW: " + rmwImpl);

#if UNITY_EDITOR
EditorApplication.playModeStateChanged += this.EditorPlayStateChanged;
EditorApplication.quitting += this.DestroyROS2ForUnity;
Expand Down
Loading