4
4
# SPDX-License-Identifier: BSD-3-Clause
5
5
6
6
"""
7
- A script with various methods of installing dependencies
8
- defined in an extension.toml
7
+ This script is a utility to install dependencies mentioned in an extension.toml file of an extension.
8
+
9
+ The script takes in two arguments:
10
+
11
+ 1. type: The type of dependencies to install. It can be one of the following: ['all', 'apt', 'rosdep'].
12
+ 2. extensions_dir: The path to the directory beneath which we search for extensions.
13
+
14
+ The script will search for all extensions in the extensions_dir and then look for an extension.toml file in each
15
+ extension's config directory. If the extension.toml file exists, the script will look for the following keys in the
16
+ [isaac_lab_settings] section:
17
+
18
+ * **apt_deps**: A list of apt packages to install.
19
+ * **ros_ws**: The path to the ROS workspace in the extension. If the path is not absolute, the script assumes that
20
+ the path is relative to the extension root and resolves it accordingly.
21
+
22
+ If the type is 'all', the script will install both apt and rosdep packages. If the type is 'apt', the script will only
23
+ install apt packages. If the type is 'rosdep', the script will only install rosdep packages.
24
+
25
+ For more information, please check the `documentation`_.
26
+
27
+ .. _documentation: https://isaac-sim.github.io/IsaacLab/source/setup/developer.html#extension-dependency-management
9
28
"""
10
29
11
30
import argparse
15
34
from subprocess import run
16
35
17
36
# add argparse arguments
18
- parser = argparse .ArgumentParser (description = "Utility to install dependencies based on an extension.toml" )
19
- parser .add_argument ("type" , type = str , choices = ["all" , "apt" , "rosdep" ], help = "The type of packages to install" )
20
- parser .add_argument ("extensions_dir" , type = str , help = "The path to the directory beneath which we search for extensions" )
37
+ parser = argparse .ArgumentParser (description = "A utility to install dependencies based on extension.toml files." )
38
+ parser .add_argument ("type" , type = str , choices = ["all" , "apt" , "rosdep" ], help = "The type of packages to install." )
39
+ parser .add_argument ("extensions_dir" , type = str , help = "The path to the directory containing extensions." )
40
+ parser .add_argument ("--ros_distro" , type = str , default = "humble" , help = "The ROS distribution to use for rosdep." )
21
41
22
42
23
43
def install_apt_packages (paths : list [str ]):
24
- """Attempts to install apt packages for Isaac Lab extensions.
25
- For each path in arg paths, it looks in {extension_root}/config/extension.toml for [isaac_lab_settings][apt_deps]
26
- and then attempts to install them. Exits on failure to stop the build process
27
- from continuing despite missing dependencies.
44
+ """Installs apt packages listed in the extension.toml file for Isaac Lab extensions.
45
+
46
+ For each path in the input list of paths, the function looks in ``{path}/config/extension.toml`` for
47
+ the ``[isaac_lab_settings][apt_deps]`` key. It then attempts to install the packages listed in the
48
+ value of the key. The function exits on failure to stop the build process from continuing despite missing
49
+ dependencies.
28
50
29
51
Args:
30
- paths: A list of paths to the extension root
52
+ paths: A list of paths to the extension's root.
31
53
32
54
Raises:
33
- RuntimeError: If 'apt' is not a known command
55
+ FileNotFoundError: If the extension.toml file is not found.
56
+ SystemError: If 'apt' is not a known command. This is a system error.
34
57
"""
35
58
for path in paths :
36
59
if shutil .which ("apt" ):
60
+ # Check if the extension.toml file exists
37
61
if not os .path .exists (f"{ path } /config/extension.toml" ):
38
- raise RuntimeError (
39
- "During the installation of an IsaacSim extension's dependencies, an extension.toml was unable to"
40
- " be found. All IsaacSim extensions must have a configuring .toml at"
41
- " (extension_root)/config/extension.toml"
62
+ raise FileNotFoundError (
63
+ "During the installation of 'apt' dependencies, unable to find a"
64
+ f" valid file at: { path } /config/extension.toml."
42
65
)
66
+ # Load the extension.toml file and check for apt_deps
43
67
with open (f"{ path } /config/extension.toml" ) as fd :
44
68
ext_toml = toml .load (fd )
45
69
if "isaac_lab_settings" in ext_toml and "apt_deps" in ext_toml ["isaac_lab_settings" ]:
@@ -48,77 +72,105 @@ def install_apt_packages(paths: list[str]):
48
72
run_and_print (["apt-get" , "update" ])
49
73
run_and_print (["apt-get" , "install" , "-y" ] + deps )
50
74
else :
51
- print ("[INFO] No apt packages to install " )
75
+ print (f "[INFO] No apt packages specified for the extension at: { path } " )
52
76
else :
53
- raise RuntimeError ("Exiting because 'apt' is not a known command" )
77
+ raise SystemError ("Unable to find 'apt' command. Please ensure that 'apt' is installed on your system." )
78
+
54
79
80
+ def install_rosdep_packages (paths : list [str ], ros_distro : str = "humble" ):
81
+ """Installs ROS dependencies listed in the extension.toml file for Isaac Lab extensions.
55
82
56
- def install_rosdep_packages (paths : list [str ]):
57
- """Attempts to install rosdep packages for Isaac Lab extensions.
58
- For each path in arg paths, it looks in {extension_root}/config/extension.toml for [isaac_lab_settings][ros_ws]
59
- and then attempts to install all rosdeps under that workspace.
60
- Exits on failure to stop the build process from continuing despite missing dependencies.
83
+ For each path in the input list of paths, the function looks in ``{path}/config/extension.toml`` for
84
+ the ``[isaac_lab_settings][ros_ws]`` key. It then attempts to install the ROS dependencies under the workspace
85
+ listed in the value of the key. The function exits on failure to stop the build process from continuing despite
86
+ missing dependencies.
87
+
88
+ If the path to the ROS workspace is not absolute, the function assumes that the path is relative to the extension
89
+ root and resolves it accordingly. The function also checks if the ROS workspace exists before proceeding with
90
+ the installation of ROS dependencies. If the ROS workspace does not exist, the function raises an error.
61
91
62
92
Args:
63
- path: A list of paths to the extension roots
93
+ path: A list of paths to the extension roots.
94
+ ros_distro: The ROS distribution to use for rosdep. Default is 'humble'.
64
95
65
96
Raises:
66
- RuntimeError: If 'rosdep' is not a known command
97
+ FileNotFoundError: If the extension.toml file is not found under the path.
98
+ FileNotFoundError: If a valid ROS workspace is not found while installing ROS dependencies.
99
+ SystemError: If 'rosdep' is not a known command. This is raised if 'rosdep' is not installed on the system.
67
100
"""
68
101
for path in paths :
69
102
if shutil .which ("rosdep" ):
103
+ # Check if the extension.toml file exists
70
104
if not os .path .exists (f"{ path } /config/extension.toml" ):
71
- raise RuntimeError (
72
- "During the installation of an IsaacSim extension's dependencies, an extension.toml was unable to"
73
- " be found. All IsaacSim extensions must have a configuring .toml at"
74
- " (extension_root)/config/extension.toml"
105
+ raise FileNotFoundError (
106
+ "During the installation of 'rosdep' dependencies, unable to find a"
107
+ f" valid file at: { path } /config/extension.toml."
75
108
)
109
+ # Load the extension.toml file and check for ros_ws
76
110
with open (f"{ path } /config/extension.toml" ) as fd :
77
111
ext_toml = toml .load (fd )
78
112
if "isaac_lab_settings" in ext_toml and "ros_ws" in ext_toml ["isaac_lab_settings" ]:
113
+ # resolve the path to the ROS workspace
79
114
ws_path = ext_toml ["isaac_lab_settings" ]["ros_ws" ]
115
+ if not os .path .abspath (ws_path ):
116
+ ws_path = os .path .join (path , ws_path )
117
+ # check if the workspace exists
118
+ if not os .path .exists (f"{ ws_path } /src" ):
119
+ raise FileNotFoundError (
120
+ "During the installation of 'rosdep' dependencies, unable to find a"
121
+ f" valid ROS workspace at: { path } /{ ws_path } ."
122
+ )
123
+ # install rosdep if not already installed
80
124
if not os .path .exists ("/etc/ros/rosdep/sources.list.d/20-default.list" ):
81
125
run_and_print (["rosdep" , "init" ])
82
- run_and_print (["rosdep" , "update" , "--rosdistro=humble" ])
126
+ run_and_print (["rosdep" , "update" , f"--rosdistro={ ros_distro } " ])
127
+ # install rosdep packages
83
128
run_and_print ([
84
129
"rosdep" ,
85
130
"install" ,
86
131
"--from-paths" ,
87
- f"{ path } / { ws_path } /src" ,
132
+ f"{ ws_path } /src" ,
88
133
"--ignore-src" ,
89
134
"-y" ,
90
- "--rosdistro=humble " ,
135
+ f "--rosdistro={ ros_distro } " ,
91
136
])
92
137
else :
93
- print ("[INFO] No rosdep packages to install " )
138
+ print (f "[INFO] No rosdep packages specified for the extension at: { path } " )
94
139
else :
95
- raise RuntimeError ("Exiting because 'rosdep' is not a known command" )
140
+ raise SystemError (
141
+ "Unable to find 'rosdep' command. Please ensure that 'rosdep' is installed on your system."
142
+ "You can install it by running:\n \t sudo apt-get install python3-rosdep"
143
+ )
96
144
97
145
98
146
def run_and_print (args : list [str ]):
99
- """Runs a subprocess.run(args=args, capture_output=True, check=True),
100
- and prints the output
147
+ """Runs a subprocess and prints the output to stdout.
148
+
149
+ This function wraps subprocess.run() and prints the output to stdout.
101
150
102
151
Args:
103
- args: a list of arguments to be passed to subprocess.run()
152
+ args: A list of arguments to pass to subprocess.run().
104
153
"""
105
154
completed_process = run (args = args , capture_output = True , check = True )
106
155
print (f"{ str (completed_process .stdout , encoding = 'utf-8' )} " )
107
156
108
157
109
158
def main ():
159
+ # Parse the command line arguments
110
160
args = parser .parse_args ()
111
161
# Get immediate children of args.extensions_dir
112
162
extension_paths = [os .path .join (args .extensions_dir , x ) for x in next (os .walk (args .extensions_dir ))[1 ]]
163
+
164
+ # Install dependencies based on the type
113
165
if args .type == "all" :
114
166
install_apt_packages (extension_paths )
115
- install_rosdep_packages (extension_paths )
167
+ install_rosdep_packages (extension_paths , args . ros_distro )
116
168
elif args .type == "apt" :
117
169
install_apt_packages (extension_paths )
118
170
elif args .type == "rosdep" :
119
- install_rosdep_packages (extension_paths )
171
+ install_rosdep_packages (extension_paths , args . ros_distro )
120
172
else :
121
- raise ValueError (f"'Invalid type dependency: '{ args .type } '. Available options: ['all', 'apt', 'rosdep']." )
173
+ raise ValueError (f"'Invalid dependency type : '{ args .type } '. Available options: ['all', 'apt', 'rosdep']." )
122
174
123
175
124
176
if __name__ == "__main__" :
0 commit comments