Skip to content

Commit

Permalink
Merge pull request #47 from schmrlng/precompile
Browse files Browse the repository at this point in the history
rostypegen within arbitrary modules (default: Main) for __precompile__ compatibility
  • Loading branch information
jdlangs authored Aug 10, 2018
2 parents df32f3c + 6620b79 commit 3b123e3
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 39 deletions.
25 changes: 25 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ generated modules will be overwritten after `rostypegen()` is called again. Kee
in mind that names cannot be cleared once defined so if a module is not
regenerated, the first version will remain.

### Compatibility with Package Precompilation
As described above, by default `rostypegen` creates modules in `Main` -- however,
this behavior is incompatible with Julia package precompilation. If you are using
`RobotOS` in your own module or package, as opposed to a script, you may reduce
load-time latency (useful for real-life applications!) by generating the ROS type
modules inside your package module using an approach similar to the example below:

# MyROSPackage.jl
__precompile__()
module MyROSPackage

using RobotOS

@rosimport geometry_msgs.msg: Pose
rostypegen(current_module())
import .geometry_msgs.msg: Pose
# ...

end

In this case, we have provided `rostypegen` with a root module (`MyROSPackage`)
for type generation. The Julia type corresponding to `geometry_msgs/Pose` now
lives at `MyROSPackage.geometry_msgs.msg.Pose`; note the extra dot in
`import .geometry_msgs.msg: Pose`.

## Usage: ROS API

In general, the API functions provided directly match those provided in rospy,
Expand Down
85 changes: 47 additions & 38 deletions src/gentypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,24 @@ const _rospy_imports = Dict{String,ROSPackage}()
const _rospy_objects = Dict{String,PyObject}()
const _rospy_modules = Dict{String,PyObject}()

const _ros_builtin_types = Dict{String, Symbol}(
"bool" => :Bool,
"int8" => :Int8,
"int16" => :Int16,
"int32" => :Int32,
"int64" => :Int64,
"uint8" => :UInt8,
"uint16" => :UInt16,
"uint32" => :UInt32,
"uint64" => :UInt64,
"float32" => :Float32,
"float64" => :Float64,
"string" => :String,
"time" => :Time,
"duration"=> :Duration,
const _ros_builtin_types = Dict{String,DataType}(
"bool" => Bool,
"int8" => Int8,
"int16" => Int16,
"int32" => Int32,
"int64" => Int64,
"uint8" => UInt8,
"uint16" => UInt16,
"uint32" => UInt32,
"uint64" => UInt64,
"float32" => Float32,
"float64" => Float64,
"string" => String,
"time" => Time,
"duration"=> Duration,
#Deprecated by ROS but supported here
"char" => :UInt8,
"byte" => :Int8,
"char" => UInt8,
"byte" => Int8,
)

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

"""
rostypegen()
rostypegen(rosrootmod::Module=Main)
Initiate the Julia type generation process after importing some ROS types. Creates modules in `Main`
with the same behavior as imported ROS modules in python. Should only be called once, after all
`@rosimport` statements are done.
Initiate the Julia type generation process after importing some ROS types. Creates modules in
rootrosmod (default is `Main`) with the same behavior as imported ROS modules in python.
Should only be called once, after all `@rosimport` statements are done.
"""
function rostypegen()
function rostypegen(rosrootmod::Module=Main)
global _rospy_imports
pkgdeps = _collectdeps(_rospy_imports)
pkglist = _order(pkgdeps)
for pkg in pkglist
buildpackage(_rospy_imports[pkg])
buildpackage(_rospy_imports[pkg], rosrootmod)
end
end

Expand Down Expand Up @@ -277,38 +277,47 @@ function _import_rospy_pkg(package::String)
end

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

#Create the top-level module for the package in Main
pkgsym = Symbol(_name(pkg))
pkgcode = Expr(:toplevel, :(module ($pkgsym) end))
Main.eval(pkgcode)
pkgmod = Main.eval(pkgsym)
pkgcode = :(module ($pkgsym) end)
pkginitcode = :(function __init__() end)

#Add msg and srv submodules if needed
@debug_addindent
if length(pkg.msg.members) > 0
msgmod = :(module msg end)
msgcode = modulecode(pkg.msg)
msgcode = modulecode(pkg.msg, rosrootmod)
for expr in msgcode
push!(msgmod.args[3].args, expr)
end
eval(pkgmod, msgmod)
push!(pkgcode.args[3].args, msgmod)
for typ in pkg.msg.members
push!(pkginitcode.args[2].args, :(@rosimport $(pkgsym).msg: $(Symbol(typ))))
end
end
if length(pkg.srv.members) > 0
srvmod = :(module srv end)
srvcode = modulecode(pkg.srv)
srvcode = modulecode(pkg.srv, rosrootmod)
for expr in srvcode
push!(srvmod.args[3].args, expr)
end
eval(pkgmod, srvmod)
push!(pkgcode.args[3].args, srvmod)
for typ in pkg.srv.members
push!(pkginitcode.args[2].args, :(@rosimport $(pkgsym).srv: $(Symbol(typ))))
end
end
push!(pkgcode.args[3].args, :(import RobotOS.@rosimport))
push!(pkgcode.args[3].args, pkginitcode)
pkgcode = Expr(:toplevel, pkgcode)
rosrootmod.eval(pkgcode)
@debug_subindent
end

#Generate all code for a .msg or .srv module
function modulecode(mod::ROSModule)
function modulecode(mod::ROSModule, rosrootmod::Module)
@debug("submodule: ", _fullname(mod))
modcode = Expr[]

Expand All @@ -325,7 +334,7 @@ function modulecode(mod::ROSModule)
end
)
#Import statement specific to the module
append!(modcode, _importexprs(mod))
append!(modcode, _importexprs(mod, rosrootmod))
#The exported names
push!(modcode, _exportexpr(mod))

Expand All @@ -340,20 +349,20 @@ function modulecode(mod::ROSModule)
end

#The imports specific to each module, including dependant packages
function _importexprs(mod::ROSMsgModule)
function _importexprs(mod::ROSMsgModule, rosrootmod::Module)
imports = Expr[Expr(:import, :RobotOS, :AbstractMsg)]
othermods = filter(d -> d != _name(mod), mod.deps)
append!(imports, [Expr(:using,:Main,Symbol(m),:msg) for m in othermods])
append!(imports, [Expr(:using,fullname(rosrootmod)...,Symbol(m),:msg) for m in othermods])
imports
end
function _importexprs(mod::ROSSrvModule)
function _importexprs(mod::ROSSrvModule, rosrootmod::Module)
imports = Expr[
Expr(:import, :RobotOS, :AbstractSrv),
Expr(:import, :RobotOS, :AbstractService),
Expr(:import, :RobotOS, :_srv_reqtype),
Expr(:import, :RobotOS, :_srv_resptype),
]
append!(imports, [Expr(:using,:Main,Symbol(m),:msg) for m in mod.deps])
append!(imports, [Expr(:using,fullname(rosrootmod)...,Symbol(m),:msg) for m in mod.deps])
imports
end

Expand Down Expand Up @@ -519,7 +528,7 @@ function _addtypemember!(exprs, namestr, typestr)
end
j_typ = _ros_builtin_types[typestr]
#Compute the default value now
j_def = @eval _typedefault($j_typ)
j_def = _typedefault(j_typ)
end

namesym = Symbol(namestr)
Expand Down
2 changes: 1 addition & 1 deletion test/rospy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ init_node("jltest", anonymous=true)
#Parameters
@test length(RobotOS.get_param_names()) > 0
@test has_param("rosdistro")
@test chomp(get_param("rosdistro")) in ["hydro", "indigo", "jade", "kinetic", "lunar"]
@test chomp(get_param("rosdistro")) in ["hydro", "indigo", "jade", "kinetic", "lunar", "melodic"]
@test ! has_param("some_param")
@test_throws KeyError get_param("some_param")
@test_throws KeyError delete_param("some_param")
Expand Down
10 changes: 10 additions & 0 deletions test/typegeneration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ rostypegen()
@test isdefined(nav_msgs.srv, :GetPlanRequest)
@test isdefined(nav_msgs.srv, :GetPlanResponse)

#type generation in a non-Main module
module TestModule
using RobotOS
@rosimport std_msgs.msg: Float32
rostypegen(current_module())
end
@test !isdefined(std_msgs.msg, :Float32Msg)
@test isdefined(TestModule, :std_msgs)
@test isdefined(TestModule.std_msgs.msg, :Float32Msg)

#message creation
posestamp = geometry_msgs.msg.PoseStamped()
@test typeof(posestamp.pose) == geometry_msgs.msg.Pose
Expand Down

0 comments on commit 3b123e3

Please sign in to comment.