Skip to content

Commit 3b123e3

Browse files
authored
Merge pull request #47 from schmrlng/precompile
rostypegen within arbitrary modules (default: Main) for __precompile__ compatibility
2 parents df32f3c + 6620b79 commit 3b123e3

File tree

4 files changed

+83
-39
lines changed

4 files changed

+83
-39
lines changed

docs/src/index.md

+25
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,31 @@ 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 your own module or package, 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+
end
91+
92+
In this case, we have provided `rostypegen` with a root module (`MyROSPackage`)
93+
for type generation. The Julia type corresponding to `geometry_msgs/Pose` now
94+
lives at `MyROSPackage.geometry_msgs.msg.Pose`; note the extra dot in
95+
`import .geometry_msgs.msg: Pose`.
96+
7297
## Usage: ROS API
7398

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

src/gentypes.jl

+47-38
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,47 @@ 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)
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)
286+
pkginitcode = :(function __init__() end)
288287

289288
#Add msg and srv submodules if needed
290289
@debug_addindent
291290
if length(pkg.msg.members) > 0
292291
msgmod = :(module msg end)
293-
msgcode = modulecode(pkg.msg)
292+
msgcode = modulecode(pkg.msg, rosrootmod)
294293
for expr in msgcode
295294
push!(msgmod.args[3].args, expr)
296295
end
297-
eval(pkgmod, msgmod)
296+
push!(pkgcode.args[3].args, msgmod)
297+
for typ in pkg.msg.members
298+
push!(pkginitcode.args[2].args, :(@rosimport $(pkgsym).msg: $(Symbol(typ))))
299+
end
298300
end
299301
if length(pkg.srv.members) > 0
300302
srvmod = :(module srv end)
301-
srvcode = modulecode(pkg.srv)
303+
srvcode = modulecode(pkg.srv, rosrootmod)
302304
for expr in srvcode
303305
push!(srvmod.args[3].args, expr)
304306
end
305-
eval(pkgmod, srvmod)
307+
push!(pkgcode.args[3].args, srvmod)
308+
for typ in pkg.srv.members
309+
push!(pkginitcode.args[2].args, :(@rosimport $(pkgsym).srv: $(Symbol(typ))))
310+
end
306311
end
312+
push!(pkgcode.args[3].args, :(import RobotOS.@rosimport))
313+
push!(pkgcode.args[3].args, pkginitcode)
314+
pkgcode = Expr(:toplevel, pkgcode)
315+
rosrootmod.eval(pkgcode)
307316
@debug_subindent
308317
end
309318

310319
#Generate all code for a .msg or .srv module
311-
function modulecode(mod::ROSModule)
320+
function modulecode(mod::ROSModule, rosrootmod::Module)
312321
@debug("submodule: ", _fullname(mod))
313322
modcode = Expr[]
314323

@@ -325,7 +334,7 @@ function modulecode(mod::ROSModule)
325334
end
326335
)
327336
#Import statement specific to the module
328-
append!(modcode, _importexprs(mod))
337+
append!(modcode, _importexprs(mod, rosrootmod))
329338
#The exported names
330339
push!(modcode, _exportexpr(mod))
331340

@@ -340,20 +349,20 @@ function modulecode(mod::ROSModule)
340349
end
341350

342351
#The imports specific to each module, including dependant packages
343-
function _importexprs(mod::ROSMsgModule)
352+
function _importexprs(mod::ROSMsgModule, rosrootmod::Module)
344353
imports = Expr[Expr(:import, :RobotOS, :AbstractMsg)]
345354
othermods = filter(d -> d != _name(mod), mod.deps)
346-
append!(imports, [Expr(:using,:Main,Symbol(m),:msg) for m in othermods])
355+
append!(imports, [Expr(:using,fullname(rosrootmod)...,Symbol(m),:msg) for m in othermods])
347356
imports
348357
end
349-
function _importexprs(mod::ROSSrvModule)
358+
function _importexprs(mod::ROSSrvModule, rosrootmod::Module)
350359
imports = Expr[
351360
Expr(:import, :RobotOS, :AbstractSrv),
352361
Expr(:import, :RobotOS, :AbstractService),
353362
Expr(:import, :RobotOS, :_srv_reqtype),
354363
Expr(:import, :RobotOS, :_srv_resptype),
355364
]
356-
append!(imports, [Expr(:using,:Main,Symbol(m),:msg) for m in mod.deps])
365+
append!(imports, [Expr(:using,fullname(rosrootmod)...,Symbol(m),:msg) for m in mod.deps])
357366
imports
358367
end
359368

@@ -519,7 +528,7 @@ function _addtypemember!(exprs, namestr, typestr)
519528
end
520529
j_typ = _ros_builtin_types[typestr]
521530
#Compute the default value now
522-
j_def = @eval _typedefault($j_typ)
531+
j_def = _typedefault(j_typ)
523532
end
524533

525534
namesym = Symbol(namestr)

test/rospy.jl

+1-1
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

+10
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)