Skip to content

Commit 8d04836

Browse files
committed
rostypegen within arbitrary modules (default: Main) for __precompile__ compatibility
1 parent df32f3c commit 8d04836

File tree

4 files changed

+82
-39
lines changed

4 files changed

+82
-39
lines changed

docs/src/index.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,39 @@ generated modules will be overwritten after `rostypegen()` is called again. Kee
6969
in mind that names cannot be cleared once defined so if a module is not
7070
regenerated, the first version will remain.
7171

72+
### Compatibility with Package Precompilation
73+
As described above, by default `rostypegen` creates modules in `Main` -- however,
74+
this behavior is incompatible with Julia package precompilation. If you are using
75+
`RobotOS` in the context of a module, as opposed to a script, you may reduce
76+
load-time latency (useful for real-life applications!) by generating the ROS type
77+
modules inside your package module using an approach similar to the example below:
78+
79+
# MyROSPackage.jl
80+
__precompile__()
81+
module MyROSPackage
82+
83+
using RobotOS
84+
85+
@rosimport geometry_msgs.msg: Pose
86+
rostypegen(current_module())
87+
import .geometry_msgs.msg: Pose
88+
# ...
89+
90+
function __init__()
91+
@rosimport geometry_msgs.msg: Pose
92+
# ...
93+
end
94+
95+
end
96+
97+
In this case, we have provided `rostypegen` with a root module (`MyROSPackage`)
98+
for type generation. The Julia type corresponding to `geometry_msgs/Pose` now
99+
lives at `MyROSPackage.geometry_msgs.msg.Pose`; note the extra dot in
100+
`import .geometry_msgs.msg: Pose`. A precompiled package using `RobotOS` must
101+
also duplicate any `@rosimport` statements within the `__init__` function in
102+
its module so that necessary memory/objects in the Python runtime can be
103+
allocated each time the package is used.
104+
72105
## Usage: ROS API
73106

74107
In general, the API functions provided directly match those provided in rospy,

src/gentypes.jl

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,24 @@ const _rospy_imports = Dict{String,ROSPackage}()
3939
const _rospy_objects = Dict{String,PyObject}()
4040
const _rospy_modules = Dict{String,PyObject}()
4141

42-
const _ros_builtin_types = Dict{String, Symbol}(
43-
"bool" => :Bool,
44-
"int8" => :Int8,
45-
"int16" => :Int16,
46-
"int32" => :Int32,
47-
"int64" => :Int64,
48-
"uint8" => :UInt8,
49-
"uint16" => :UInt16,
50-
"uint32" => :UInt32,
51-
"uint64" => :UInt64,
52-
"float32" => :Float32,
53-
"float64" => :Float64,
54-
"string" => :String,
55-
"time" => :Time,
56-
"duration"=> :Duration,
42+
const _ros_builtin_types = Dict{String,DataType}(
43+
"bool" => Bool,
44+
"int8" => Int8,
45+
"int16" => Int16,
46+
"int32" => Int32,
47+
"int64" => Int64,
48+
"uint8" => UInt8,
49+
"uint16" => UInt16,
50+
"uint32" => UInt32,
51+
"uint64" => UInt64,
52+
"float32" => Float32,
53+
"float64" => Float64,
54+
"string" => String,
55+
"time" => Time,
56+
"duration"=> Duration,
5757
#Deprecated by ROS but supported here
58-
"char" => :UInt8,
59-
"byte" => :Int8,
58+
"char" => UInt8,
59+
"byte" => Int8,
6060
)
6161

6262
#Abstract supertypes of all generated types
@@ -143,18 +143,18 @@ function _rosimport(package::String, ismsg::Bool, names::String...)
143143
end
144144

145145
"""
146-
rostypegen()
146+
rostypegen(rosrootmod::Module=Main)
147147
148-
Initiate the Julia type generation process after importing some ROS types. Creates modules in `Main`
149-
with the same behavior as imported ROS modules in python. Should only be called once, after all
150-
`@rosimport` statements are done.
148+
Initiate the Julia type generation process after importing some ROS types. Creates modules in
149+
rootrosmod (default is `Main`) with the same behavior as imported ROS modules in python.
150+
Should only be called once, after all `@rosimport` statements are done.
151151
"""
152-
function rostypegen()
152+
function rostypegen(rosrootmod::Module=Main)
153153
global _rospy_imports
154154
pkgdeps = _collectdeps(_rospy_imports)
155155
pkglist = _order(pkgdeps)
156156
for pkg in pkglist
157-
buildpackage(_rospy_imports[pkg])
157+
buildpackage(_rospy_imports[pkg], rosrootmod)
158158
end
159159
end
160160

@@ -277,38 +277,38 @@ function _import_rospy_pkg(package::String)
277277
end
278278

279279
#The function that creates and fills the generated top-level modules
280-
function buildpackage(pkg::ROSPackage)
280+
function buildpackage(pkg::ROSPackage, rosrootmod::Module=Main)
281281
@debug("Building package: ", _name(pkg))
282282

283283
#Create the top-level module for the package in Main
284284
pkgsym = Symbol(_name(pkg))
285-
pkgcode = Expr(:toplevel, :(module ($pkgsym) end))
286-
Main.eval(pkgcode)
287-
pkgmod = Main.eval(pkgsym)
285+
pkgcode = :(module ($pkgsym) end)
288286

289287
#Add msg and srv submodules if needed
290288
@debug_addindent
291289
if length(pkg.msg.members) > 0
292290
msgmod = :(module msg end)
293-
msgcode = modulecode(pkg.msg)
291+
msgcode = modulecode(pkg.msg, rosrootmod)
294292
for expr in msgcode
295293
push!(msgmod.args[3].args, expr)
296294
end
297-
eval(pkgmod, msgmod)
295+
push!(pkgcode.args[3].args, msgmod)
298296
end
299297
if length(pkg.srv.members) > 0
300298
srvmod = :(module srv end)
301-
srvcode = modulecode(pkg.srv)
299+
srvcode = modulecode(pkg.srv, rosrootmod)
302300
for expr in srvcode
303301
push!(srvmod.args[3].args, expr)
304302
end
305-
eval(pkgmod, srvmod)
303+
push!(pkgcode.args[3].args, srvmod)
306304
end
305+
pkgcode = Expr(:toplevel, pkgcode)
306+
rosrootmod.eval(pkgcode)
307307
@debug_subindent
308308
end
309309

310310
#Generate all code for a .msg or .srv module
311-
function modulecode(mod::ROSModule)
311+
function modulecode(mod::ROSModule, rosrootmod::Module=Main)
312312
@debug("submodule: ", _fullname(mod))
313313
modcode = Expr[]
314314

@@ -325,7 +325,7 @@ function modulecode(mod::ROSModule)
325325
end
326326
)
327327
#Import statement specific to the module
328-
append!(modcode, _importexprs(mod))
328+
append!(modcode, _importexprs(mod, rosrootmod))
329329
#The exported names
330330
push!(modcode, _exportexpr(mod))
331331

@@ -340,20 +340,20 @@ function modulecode(mod::ROSModule)
340340
end
341341

342342
#The imports specific to each module, including dependant packages
343-
function _importexprs(mod::ROSMsgModule)
343+
function _importexprs(mod::ROSMsgModule, rosrootmod::Module=Main)
344344
imports = Expr[Expr(:import, :RobotOS, :AbstractMsg)]
345345
othermods = filter(d -> d != _name(mod), mod.deps)
346-
append!(imports, [Expr(:using,:Main,Symbol(m),:msg) for m in othermods])
346+
append!(imports, [Expr(:using,Symbol(rosrootmod),Symbol(m),:msg) for m in othermods])
347347
imports
348348
end
349-
function _importexprs(mod::ROSSrvModule)
349+
function _importexprs(mod::ROSSrvModule, rosrootmod::Module=Main)
350350
imports = Expr[
351351
Expr(:import, :RobotOS, :AbstractSrv),
352352
Expr(:import, :RobotOS, :AbstractService),
353353
Expr(:import, :RobotOS, :_srv_reqtype),
354354
Expr(:import, :RobotOS, :_srv_resptype),
355355
]
356-
append!(imports, [Expr(:using,:Main,Symbol(m),:msg) for m in mod.deps])
356+
append!(imports, [Expr(:using,Symbol(rosrootmod),Symbol(m),:msg) for m in mod.deps])
357357
imports
358358
end
359359

@@ -519,7 +519,7 @@ function _addtypemember!(exprs, namestr, typestr)
519519
end
520520
j_typ = _ros_builtin_types[typestr]
521521
#Compute the default value now
522-
j_def = @eval _typedefault($j_typ)
522+
j_def = _typedefault(j_typ)
523523
end
524524

525525
namesym = Symbol(namestr)

test/rospy.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ init_node("jltest", anonymous=true)
44
#Parameters
55
@test length(RobotOS.get_param_names()) > 0
66
@test has_param("rosdistro")
7-
@test chomp(get_param("rosdistro")) in ["hydro", "indigo", "jade", "kinetic", "lunar"]
7+
@test chomp(get_param("rosdistro")) in ["hydro", "indigo", "jade", "kinetic", "lunar", "melodic"]
88
@test ! has_param("some_param")
99
@test_throws KeyError get_param("some_param")
1010
@test_throws KeyError delete_param("some_param")

test/typegeneration.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ rostypegen()
2828
@test isdefined(nav_msgs.srv, :GetPlanRequest)
2929
@test isdefined(nav_msgs.srv, :GetPlanResponse)
3030

31+
#type generation in a non-Main module
32+
module TestModule
33+
using RobotOS
34+
@rosimport std_msgs.msg: Float32
35+
rostypegen(current_module())
36+
end
37+
@test !isdefined(std_msgs.msg, :Float32Msg)
38+
@test isdefined(TestModule, :std_msgs)
39+
@test isdefined(TestModule.std_msgs.msg, :Float32Msg)
40+
3141
#message creation
3242
posestamp = geometry_msgs.msg.PoseStamped()
3343
@test typeof(posestamp.pose) == geometry_msgs.msg.Pose

0 commit comments

Comments
 (0)