00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 """
00037 Implements the roscreate-stack tool.
00038
00039 The focus of this module is on supporting the command-line tool. The
00040 code API of this module is *not* stable.
00041 """
00042
00043 from __future__ import with_statement
00044 import roslib; roslib.load_manifest('roscreate')
00045
00046 NAME='roscreate-stack'
00047
00048 import os
00049 import sys
00050 import roslib.manifest
00051 import roslib.packages
00052 import roslib.stacks
00053 import roslib.stack_manifest
00054
00055 from roscreate.core import read_template, author_name, print_warning, on_ros_path
00056
00057 def get_templates():
00058 """
00059 @return: mapping of file names to templates to instantiate
00060 @rtype: {str: str}
00061 """
00062 templates = {}
00063 templates['stack.xml'] = read_template('stack.tmpl')
00064 templates['CMakeLists.txt'] = read_template('CMakeLists.stack.tmpl')
00065 templates['Makefile'] = read_template('Makefile.stack.tmpl')
00066 return templates
00067
00068 def instantiate_template(template, stack, brief, description, author, depends, licenses, review):
00069 """
00070 @return: template instantiated with properties
00071 @rtype: str
00072 """
00073 return template%locals()
00074
00075 def _update_depends(depends):
00076 new_depends = []
00077 for s, pkgs in depends.iteritems():
00078 d = roslib.stack_manifest.StackDepend(s)
00079 d.annotation = ', '.join(set(pkgs))
00080 new_depends.append(d)
00081 new_depends.sort(lambda x, y: -1 if x.stack < y.stack else 1)
00082 return ''.join([" %s\n"%d.xml() for d in new_depends])
00083
00084 def create_stack(stack, stack_dir, stack_manifest, author, depends, licenses, show_deps):
00085 """
00086 @param stack: name of stack
00087 @type stack: str
00088 @param stack_dir: path to stack
00089 @type stack_dir: str
00090 @param stack_manifest: existing stack manifest or None
00091 @type stack_manifest: L{roslib.stack_manifest.StackManifest}
00092 @param author: name of stack maintainer. Overrides stack_manifest.
00093 @type author: str
00094 @param depends: map of stack name to packages that use that stack. Overrides stack_manifest.
00095 @type depends: {str: [str]}
00096 @param licenses: list of licenses present in stack
00097 @type licenses: set(str)
00098 """
00099
00100 if show_deps:
00101 print ''.join([' <depend stack="%s"/> <!-- %s --> \n'%(s, ', '.join(set(pkgs))) for s, pkgs in depends.iteritems()])
00102 return
00103
00104
00105 if stack_manifest is not None:
00106 try:
00107 licenses.update([l.strip() for l in stack_manifest.license.split(',')])
00108 except: pass
00109 brief = stack_manifest.brief or stack
00110 description = stack_manifest.description
00111 review = ' <review status="%s" notes="%s"/>'%(stack_manifest.status, stack_manifest.notes)
00112 else:
00113 stack_manifest = roslib.stack_manifest.StackManifest()
00114 brief = description = stack
00115 review = ' <review status="unreviewed" notes=""/>'
00116
00117 licenses = ','.join(licenses)
00118 depends = _update_depends(depends)
00119
00120 p = os.path.abspath(stack_dir)
00121 if not os.path.exists(p):
00122 print "Creating stack directory", p
00123 os.makedirs(p)
00124
00125 templates = get_templates()
00126 for filename, template in templates.iteritems():
00127 contents = instantiate_template(template, stack, brief, description, author, depends, licenses, review)
00128 p = os.path.abspath(os.path.join(stack_dir, filename))
00129 if not os.path.exists(filename) or filename == 'stack.xml':
00130 print "Creating stack file", p
00131 with open(p, 'w') as f:
00132 f.write(contents.encode('utf-8'))
00133 print "\nPlease edit %s/stack.xml to finish creating your stack"%stack
00134
00135 def compute_stack_depends_and_licenses(stack_dir):
00136 """
00137 @return: depends, licenses
00138 @rtype: {str: [str]}, [str]
00139 @raise: roslib.packages.InvalidROSPkgException
00140 """
00141 stack = os.path.basename(os.path.abspath(stack_dir))
00142 if os.path.exists(stack_dir):
00143 packages = roslib.packages.list_pkgs_by_path(os.path.abspath(stack_dir))
00144 depends, licenses = _compute_stack_depends_and_licenses(stack, packages)
00145 else:
00146 depends = dict()
00147 licenses = ['BSD']
00148
00149 if not 'ros' in depends and stack != 'ros':
00150 depends['ros'] = []
00151 return depends, licenses
00152
00153 def _compute_stack_depends_and_licenses(stack, packages):
00154 pkg_depends = []
00155 licenses = []
00156 stack_depends = {}
00157 for pkg in packages:
00158 m = roslib.manifest.parse_file(roslib.manifest.manifest_file(pkg))
00159 pkg_depends.extend([d.package for d in m.depends])
00160 licenses.extend([l.strip() for l in m.license.split(',')])
00161
00162
00163 pkg_dir = roslib.packages.get_pkg_dir(pkg)
00164 if (os.path.isdir(os.path.join(pkg_dir, 'msg')) or \
00165 os.path.isdir(os.path.join(pkg_dir, 'srv'))) and \
00166 stack not in ['ros', 'ros_comm']:
00167 if not 'ros_comm' in stack_depends:
00168 stack_depends['ros_comm'] = []
00169
00170
00171
00172 for pkg in pkg_depends:
00173 if pkg in packages:
00174 continue
00175 try:
00176 st = roslib.stacks.stack_of(pkg)
00177 except roslib.packages.InvalidROSPkgException:
00178 print_warning("WARNING: cannot locate package [%s], which is a dependency in the [%s] stack"%(pkg, stack))
00179 continue
00180 if not st:
00181 print_warning("WARNING: stack depends on [%s], which is not in a stack"%pkg)
00182 continue
00183 if st == stack:
00184 continue
00185 if not st in stack_depends:
00186 stack_depends[st] = []
00187 stack_depends[st].append(pkg)
00188
00189 return stack_depends, set(licenses)
00190
00191 def roscreatestack_main():
00192 from optparse import OptionParser
00193 parser = OptionParser(usage="usage: %prog <path-to-stack>", prog=NAME)
00194 parser.add_option("--show-deps",
00195 dest="show_deps", default=False,
00196 action="store_true",
00197 help="show stack dependencies, instead of generating stack.xml")
00198 options, args = parser.parse_args()
00199 if not args:
00200 parser.error("you must specify the path to a stack")
00201 stack_dir = args[0]
00202 stack = os.path.basename(os.path.abspath(stack_dir))
00203
00204 if not on_ros_path(stack_dir):
00205 print >> sys.stderr, "ERROR: roscreate-stack only work in directories in ROS_PACKAGE_PATH\nPlease update your ROS_PACKAGE_PATH environment variable."
00206 sys.exit(1)
00207
00208 try:
00209 depends, licenses = compute_stack_depends_and_licenses(stack_dir)
00210 except roslib.packages.InvalidROSPkgException, e:
00211 print >> sys.stderr, str(e)
00212 sys.exit(1)
00213
00214
00215 stack_manifest = None
00216 author = "Maintained by %s"%author_name()
00217
00218 if not options.show_deps:
00219
00220 stack_xml_path = os.path.join(stack_dir, 'stack.xml')
00221 if os.path.exists(stack_xml_path):
00222 import shutil
00223 stack_xml_path_bak = os.path.join(stack_dir, 'stack.xml.bak')
00224 print 'Backing up existing stack.xml to %s'%(stack_xml_path_bak)
00225 shutil.copyfile(stack_xml_path, stack_xml_path_bak)
00226
00227
00228 stack_manifest = roslib.stack_manifest.parse_file(stack_xml_path)
00229 author = stack_manifest.author
00230
00231 create_stack(stack, stack_dir, stack_manifest, author, depends, licenses, options.show_deps)
00232
00233 if __name__ == "__main__":
00234 roscreatestack_main()