Skip to content

Commit f44e44c

Browse files
committed
Standalone version fixes and improvements
1 parent bf195c8 commit f44e44c

File tree

5 files changed

+205
-16
lines changed

5 files changed

+205
-16
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ log
33
build
44
.idea
55
src/ros2cs
6+
**/metadata*.xml
67
src/Ros2ForUnity/Plugins
78
!src/Ros2ForUnity/Plugins/.gitkeep

build.ps1

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ if($clean_install) {
3434
Write-Host "Cleaning install directory..." -ForegroundColor White
3535
Remove-Item -Path "$scriptPath\install" -Force -Recurse -ErrorAction Ignore
3636
}
37+
38+
if($standalone) {}
39+
& "python3 $SCRIPTPATH\src\scripts\metadata_generator.py" --standalone
40+
} else {
41+
& "python3 $SCRIPTPATH\src\scripts\metadata_generator.py"
42+
}
43+
3744
& "$scriptPath\src\ros2cs\build.ps1" @options
3845
if($?) {
3946
md -Force $scriptPath\install\asset | Out-Null
@@ -42,6 +49,9 @@ if($?) {
4249
$plugin_path=Join-Path -Path $scriptPath -ChildPath "\install\asset\Ros2ForUnity\Plugins\"
4350
Write-Host "Deploying build to $plugin_path" -ForegroundColor Green
4451
& "$scriptPath\deploy_unity_plugins.ps1" $plugin_path
52+
53+
Copy-Item -Path $scriptPath\src\Ros2ForUnity\metadata_ros2cs.xml -Destination $scriptPath\install\asset\Ros2ForUnity\Plugins\Linux\x86_64\
54+
Copy-Item -Path $scriptPath\src\Ros2ForUnity\metadata_ros2cs.xml -Destination $scriptPath\install\asset\Ros2ForUnity\Plugins\
4555
} else {
4656
Write-Host "Ros2cs build failed!" -ForegroundColor Red
4757
exit 1

build.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,18 @@ if [ $CLEAN_INSTALL == 1 ]; then
5555
echo "Cleaning install directory..."
5656
rm -rf $SCRIPTPATH/install/*
5757
fi
58+
59+
if [ $STANDALONE == 1 ]; then
60+
python3 $SCRIPTPATH/src/scripts/metadata_generator.py --standalone
61+
else
62+
python3 $SCRIPTPATH/src/scripts/metadata_generator.py
63+
fi
64+
5865
if $SCRIPTPATH/src/ros2cs/build.sh $OPTIONS; then
5966
mkdir -p $SCRIPTPATH/install/asset && cp -R $SCRIPTPATH/src/Ros2ForUnity $SCRIPTPATH/install/asset/
6067
$SCRIPTPATH/deploy_unity_plugins.sh $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/
68+
cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/Linux/x86_64/metadata_ros2cs.xml
69+
cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/metadata_ros2cs.xml
6170
else
6271
echo "Ros2cs build failed!"
6372
exit 1

src/Ros2ForUnity/Scripts/ROS2ForUnity.cs

Lines changed: 113 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Collections.Generic;
1818
using UnityEngine;
1919
using UnityEditor;
20+
using System.Xml;
2021

2122
namespace ROS2
2223
{
@@ -28,6 +29,8 @@ internal class ROS2ForUnity
2829
{
2930
private static bool isInitialized = false;
3031
private static string ros2ForUnityAssetFolderName = "Ros2ForUnity";
32+
private XmlDocument ros2csMetadata = new XmlDocument();
33+
private XmlDocument ros2ForUnityMetadata = new XmlDocument();
3134

3235
enum Platform
3336
{
@@ -80,7 +83,7 @@ private string GetEnvPathVariableValue()
8083
return Environment.GetEnvironmentVariable(GetEnvPathVariableName());
8184
}
8285

83-
private string GetPluginPath()
86+
private string GetRos2ForUnityPath()
8487
{
8588
char separator = Path.DirectorySeparatorChar;
8689
string appDataPath = Application.dataPath;
@@ -89,6 +92,14 @@ private string GetPluginPath()
8992
if (InEditor()) {
9093
pluginPath += separator + ros2ForUnityAssetFolderName;
9194
}
95+
return pluginPath;
96+
}
97+
98+
private string GetPluginPath()
99+
{
100+
char separator = Path.DirectorySeparatorChar;
101+
string ros2ForUnityPath = GetRos2ForUnityPath();
102+
string pluginPath = ros2ForUnityPath;
92103

93104
pluginPath += separator + "Plugins";
94105

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

146+
public bool IsStandalone() {
147+
return Convert.ToBoolean(Convert.ToInt16(GetMetadataValue(ros2csMetadata, "/ros2cs/standalone")));
148+
}
149+
150+
public string GetROSVersion()
151+
{
152+
string ros2SourcedCodename = GetROSVersionSourced();
153+
string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, "/ros2_for_unity/ros2");
154+
155+
// Sourced ROS2 libs takes priority
156+
if (string.IsNullOrEmpty(ros2SourcedCodename)) {
157+
return ros2FromRos4UMetadata;
158+
}
159+
160+
return ros2SourcedCodename;
161+
}
162+
163+
/// <summary>
164+
/// Checks if both ros2cs and ros2-for-unity were build for the same ros version as well as
165+
/// the current sourced ros version matches ros2cs binaries.
166+
/// </summary>
167+
public void CheckIntegrity()
168+
{
169+
string ros2SourcedCodename = GetROSVersionSourced();
170+
string ros2FromRos2csMetadata = GetMetadataValue(ros2csMetadata, "/ros2cs/ros2");
171+
string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, "/ros2_for_unity/ros2");
172+
173+
if (ros2FromRos4UMetadata != ros2FromRos2csMetadata) {
174+
Debug.LogError(
175+
"ROS2 versions in 'ros2cs' and 'ros2-for-unity' metadata files are not the same. " +
176+
"This is caused by mixing versions/builds. Plugin might not work correctly."
177+
);
178+
}
179+
180+
if(!IsStandalone() && ros2SourcedCodename != ros2FromRos2csMetadata) {
181+
Debug.LogError(
182+
"ROS2 version in 'ros2cs' metadata doesn't match currently sourced version. " +
183+
"This is caused by mixing versions/builds. Plugin might not work correctly."
184+
);
185+
}
186+
187+
if (IsStandalone() && !string.IsNullOrEmpty(ros2SourcedCodename)) {
188+
Debug.LogError(
189+
"You should not source ROS2 in 'ros2-for-unity' standalone build. " +
190+
"Plugin might not work correctly."
191+
);
192+
}
193+
}
194+
195+
public string GetROSVersionSourced()
196+
{
197+
return Environment.GetEnvironmentVariable("ROS_DISTRO");
198+
}
199+
135200
/// <summary>
136201
/// Check if the ros version is supported, only applicable to non-standalone plugin versions
137202
/// (i. e. without ros2 libraries included in the plugin).
138203
/// </summary>
139-
private string CheckROSVersionSourced()
204+
private void CheckROSSupport(string ros2Codename)
140205
{
141-
string currentVersion = Environment.GetEnvironmentVariable("ROS_DISTRO");
142206
List<string> supportedVersions = new List<string>() { "foxy", "galactic" };
143207
var supportedVersionsString = String.Join(", ", supportedVersions);
144-
if (string.IsNullOrEmpty(currentVersion))
208+
if (string.IsNullOrEmpty(ros2Codename))
145209
{
146210
string errMessage = "No ROS environment sourced. You need to source your ROS2 " + supportedVersionsString
147211
+ " environment before launching Unity (ROS_DISTRO env variable not found)";
@@ -155,9 +219,9 @@ private string CheckROSVersionSourced()
155219
#endif
156220
}
157221

158-
if (!supportedVersions.Contains(currentVersion))
222+
if (!supportedVersions.Contains(ros2Codename))
159223
{
160-
string errMessage = "Currently sourced ROS version differs from supported one. Sourced: " + currentVersion
224+
string errMessage = "Currently sourced ROS version differs from supported one. Sourced: " + ros2Codename
161225
+ ", supported: " + supportedVersionsString + ".";
162226
Debug.LogError(errMessage);
163227
#if UNITY_EDITOR
@@ -168,8 +232,6 @@ private string CheckROSVersionSourced()
168232
Application.Quit(ROS_BAD_VERSION_CODE);
169233
#endif
170234
}
171-
Debug.Log("Running with a supported ROS 2 version: " + currentVersion);
172-
return currentVersion;
173235
}
174236

175237
private void RegisterCtrlCHandler()
@@ -192,28 +254,63 @@ private void ConnectLoggers()
192254
Ros2csLogger.LogLevel = LogLevel.WARNING;
193255
}
194256

257+
private string GetMetadataValue(XmlDocument doc, string valuePath)
258+
{
259+
return doc.DocumentElement.SelectSingleNode(valuePath).InnerText;
260+
}
261+
262+
private void LoadMetadata()
263+
{
264+
char separator = Path.DirectorySeparatorChar;
265+
try
266+
{
267+
ros2csMetadata.Load(GetPluginPath() + separator + "metadata_ros2cs.xml");
268+
ros2ForUnityMetadata.Load(GetRos2ForUnityPath() + separator + "metadata_ros2_for_unity.xml");
269+
}
270+
catch (System.IO.FileNotFoundException)
271+
{
272+
var errMessage = "Could not find metadata files.";
273+
#if UNITY_EDITOR
274+
EditorApplication.isPlaying = false;
275+
throw new System.IO.FileNotFoundException(errMessage);
276+
#else
277+
const int NO_METADATA = 1;
278+
Application.Quit(NO_METADATA);
279+
#endif
280+
}
281+
}
282+
195283
internal ROS2ForUnity()
196284
{
197-
// TODO: Find a way to determine whether we run standalone build
285+
// Load metadata
286+
LoadMetadata();
287+
string currentRos2Version = GetROSVersion();
288+
string standalone = IsStandalone() ? "standalone" : "non-standalone";
289+
290+
// Self checks
291+
CheckROSSupport(currentRos2Version);
292+
CheckIntegrity();
293+
294+
// Library loading
198295
if (GetOS() == Platform.Windows) {
199296
// Windows version can run standalone, modifies PATH to ensure all plugins visibility
200297
SetEnvPathVariable();
201298
} else {
202-
// Linux version needs to have ros2 sourced, which is checked here. It also loads plugins by absolute path
203-
// since LD_LIBRARY_PATH cannot be set dynamically within the process for dlopen() which is used under the hood.
204-
// Since libraries are built with -rpath=".", dependencies will be correcly located within plugins directory.
205-
// For foxy, it is also necessary to use modified version of librcpputils to resolve custom msgs packages.
206-
string currentRosVersion = CheckROSVersionSourced();
299+
// For foxy, it is necessary to use modified version of librcpputils to resolve custom msgs packages.
207300
ROS2.GlobalVariables.absolutePath = GetPluginPath() + "/";
208-
if (currentRosVersion == "foxy") {
301+
if (currentRos2Version == "foxy") {
209302
ROS2.GlobalVariables.preloadLibrary = true;
210303
ROS2.GlobalVariables.preloadLibraryName = "librcpputils.so";
211304
}
212305
}
306+
307+
// Initialize
213308
ConnectLoggers();
214309
Ros2cs.Init();
215310
RegisterCtrlCHandler();
216311

312+
Ros2csLogger.GetInstance().LogInfo("ROS2 version: " + currentRos2Version + ". Build type: " + standalone);
313+
217314
#if UNITY_EDITOR
218315
EditorApplication.playModeStateChanged += this.EditorPlayStateChanged;
219316
EditorApplication.quitting += this.DestroyROS2ForUnity;

src/scripts/metadata_generator.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Copyright 2019-2022 Robotec.ai.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import argparse
16+
import xml.etree.ElementTree as ET
17+
from xml.dom import minidom
18+
import subprocess
19+
import pathlib
20+
21+
parser = argparse.ArgumentParser(description='Generate metadata file for ros2-for-unity.')
22+
parser.add_argument('--standalone', action='store_true', help='is a standalone build')
23+
args = parser.parse_args()
24+
25+
def get_git_commit(working_directory) -> str:
26+
return subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=working_directory).decode('ascii').strip()
27+
28+
def get_git_description(working_directory) -> str:
29+
return subprocess.check_output(['git', 'describe', '--tags', '--always'], cwd=working_directory).decode('ascii').strip()
30+
31+
def get_commit_date(working_directory) -> str:
32+
return subprocess.check_output(['git', 'show', '-s', '--format=%ci'], cwd=working_directory).decode('ascii').strip()
33+
34+
def get_git_abbrev(working_directory) -> str:
35+
return subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=working_directory).decode('ascii').strip()
36+
37+
def get_ros2_for_unity_root_path() -> pathlib.Path:
38+
return pathlib.Path(__file__).parents[2]
39+
40+
def get_ros2_for_unity_path() -> pathlib.Path:
41+
return pathlib.Path(__file__).parents[1].joinpath("Ros2ForUnity")
42+
43+
def get_ros2cs_path() -> pathlib.Path:
44+
return pathlib.Path(__file__).parents[1].joinpath("ros2cs")
45+
46+
def get_ros2_path() -> pathlib.Path:
47+
return get_ros2cs_path().joinpath("src").joinpath("ros2").joinpath("rcl_interfaces")
48+
49+
ros2_for_unity = ET.Element("ros2_for_unity")
50+
ET.SubElement(ros2_for_unity, "ros2").text = get_git_abbrev(get_ros2_path())
51+
ros2_for_unity_version = ET.SubElement(ros2_for_unity, "version")
52+
ET.SubElement(ros2_for_unity_version, "sha").text = get_git_commit(get_ros2_for_unity_root_path())
53+
ET.SubElement(ros2_for_unity_version, "desc").text = get_git_description(get_ros2_for_unity_root_path())
54+
ET.SubElement(ros2_for_unity_version, "date").text = get_commit_date(get_ros2_for_unity_root_path())
55+
56+
ros2_cs = ET.Element("ros2cs")
57+
ET.SubElement(ros2_cs, "ros2").text = get_git_abbrev(get_ros2_path())
58+
ros2_cs_version = ET.SubElement(ros2_cs, "version")
59+
ET.SubElement(ros2_cs_version, "sha").text = get_git_commit(get_ros2cs_path())
60+
ET.SubElement(ros2_cs_version, "desc").text = get_git_description(get_ros2cs_path())
61+
ET.SubElement(ros2_cs_version, "date").text = get_commit_date(get_ros2cs_path())
62+
ET.SubElement(ros2_cs, "standalone").text = str(int(args.standalone))
63+
64+
rf2u_xmlstr = minidom.parseString(ET.tostring(ros2_for_unity)).toprettyxml(indent=" ")
65+
metadata_rf2u_file = get_ros2_for_unity_path().joinpath("metadata_ros2_for_unity.xml")
66+
with open(str(metadata_rf2u_file), "w") as f:
67+
f.write(rf2u_xmlstr)
68+
69+
r2cs_xmlstr = minidom.parseString(ET.tostring(ros2_cs)).toprettyxml(indent=" ")
70+
metadata_r2cs_file = get_ros2_for_unity_path().joinpath("metadata_ros2cs.xml")
71+
with open(str(metadata_r2cs_file), "w") as f:
72+
f.write(r2cs_xmlstr)

0 commit comments

Comments
 (0)