$search
00001 # Software License Agreement (BSD License) 00002 # 00003 # Copyright (c) 2010, Willow Garage, Inc. 00004 # All rights reserved. 00005 # 00006 # Redistribution and use in source and binary forms, with or without 00007 # modification, are permitted provided that the following conditions 00008 # are met: 00009 # 00010 # * Redistributions of source code must retain the above copyright 00011 # notice, this list of conditions and the following disclaimer. 00012 # * Redistributions in binary form must reproduce the above 00013 # copyright notice, this list of conditions and the following 00014 # disclaimer in the documentation and/or other materials provided 00015 # with the distribution. 00016 # * Neither the name of Willow Garage, Inc. nor the names of its 00017 # contributors may be used to endorse or promote products derived 00018 # from this software without specific prior written permission. 00019 # 00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00031 # POSSIBILITY OF SUCH DAMAGE. 00032 # 00033 # Revision $Id: __init__.py 4136 2010-10-20 00:13:02Z kwc $ 00034 # $Author: kwc $ 00035 00036 from __future__ import with_statement 00037 00038 import os 00039 import subprocess 00040 import tempfile 00041 import re 00042 import tarfile 00043 import shutil 00044 import urllib2 00045 import re 00046 import hashlib 00047 00048 import yaml 00049 00050 import roslib.packages 00051 import roslib.stacks 00052 import roslib.stack_manifest 00053 00054 from rosdeb import control_data 00055 # we export this symbol to create.py as well 00056 from rosdeb.rosutil import checkout_svn_to_tmp, checkout_dev_to_tmp 00057 00058 class ReleaseException(Exception): pass 00059 00060 def get_source_version(distro, stack_name): 00061 dev_svn = distro.stacks[stack_name].dev_svn 00062 print "Reading version number from %s"%(dev_svn) 00063 00064 source_url = dev_svn + '/stack.xml' 00065 try: 00066 f = urllib2.urlopen(source_url) 00067 text = f.read() 00068 f.close() 00069 except urllib2.HTTPError: 00070 raise ReleaseException("failed to fetch stack.xml for stack.\nA common cause is the '_rules' are not set correctly for this stack.\nThe URL was %s"%(source_url)) 00071 00072 try: 00073 m = roslib.stack_manifest.parse(text) 00074 if m.version: 00075 return m.version 00076 except: 00077 raise ReleaseException("stack.xml failed validation. Read from URL [%s]"%(source_url)) 00078 00079 version = get_stack_manifest_version(text) 00080 if version: 00081 return version 00082 00083 # fallback on CMakeLists.txt version 00084 source_url = dev_svn + '/CMakeLists.txt' 00085 try: 00086 f = urllib2.urlopen(source_url) 00087 text = f.read() 00088 f.close() 00089 except urllib2.HTTPError: 00090 raise ReleaseException("failed to fetch CMakeLists.txt for stack.\nA common cause is the '_rules' are not set correctly for this stack.\nThe URL was %s"%(source_url)) 00091 00092 return get_cmake_version(text) 00093 00094 # this is mostly a copy of the roscreatestack version, but as it has 00095 # different error behavior, I decided to copy it and slim it down (kwc) 00096 def compute_stack_depends(stack_dir): 00097 """ 00098 @return: depends, licenses 00099 @rtype: {str: [str]}, [str] 00100 @raise ReleaseException: if error occurs detecting dependencies 00101 """ 00102 stack = os.path.basename(os.path.abspath(stack_dir)) 00103 if os.path.exists(stack_dir): 00104 packages = roslib.packages.list_pkgs_by_path(os.path.abspath(stack_dir)) 00105 depends = _compute_stack_depends_and_licenses(stack, packages) 00106 else: 00107 depends = dict() 00108 # add in bare ros dependency into any stack as an implicit depend 00109 if not 'ros' in depends and stack != 'ros': 00110 depends['ros'] = [] 00111 return depends 00112 00113 def _compute_stack_depends_and_licenses(stack, packages): 00114 pkg_depends = [] 00115 for pkg in packages: 00116 m = roslib.manifest.parse_file(roslib.manifest.manifest_file(pkg)) 00117 pkg_depends.extend([d.package for d in m.depends]) 00118 00119 stack_depends = {} 00120 for pkg in pkg_depends: 00121 if pkg in packages: 00122 continue 00123 try: 00124 st = roslib.stacks.stack_of(pkg) 00125 except roslib.packages.InvalidROSPkgException: 00126 raise ReleaseException("cannot locate package [%s], which is a dependency in the [%s] stack"%(pkg, stack)) 00127 if not st: 00128 raise ReleaseException("WARNING: stack depends on [%s], which is not in a stack"%pkg) 00129 if st == stack: 00130 continue 00131 if not st in stack_depends: 00132 stack_depends[st] = [] 00133 stack_depends[st].append(pkg) 00134 return stack_depends 00135 00136 00137 def get_stack_version(stack_dir, stack_name): 00138 return get_stack_version_by_dir(stack_dir) 00139 00140 # NOTE: this is a copy of roslib.stacks.get_stack_version_by_dir(). We copy 00141 # it here for compatibility with cturtle/diamondback 00142 def get_stack_version_by_dir(stack_dir): 00143 """ 00144 Get stack version where stack_dir points to root directory of stack. 00145 00146 @param env: override environment variables 00147 @type env: {str: str} 00148 00149 @return: version number of stack, or None if stack is unversioned. 00150 @rtype: str 00151 """ 00152 # REP 109: check for <version> tag first, then CMakeLists.txt 00153 manifest_filename = os.path.join(stack_dir, roslib.stacks.STACK_FILE) 00154 if os.path.isfile(manifest_filename): 00155 m = roslib.stack_manifest.parse_file(manifest_filename) 00156 try: 00157 # version attribute only available in ROS 1.5.1+ 00158 if m.version: 00159 return m.version 00160 except AttributeError: 00161 pass 00162 00163 cmake_filename = os.path.join(stack_dir, 'CMakeLists.txt') 00164 if os.path.isfile(cmake_filename): 00165 with open(cmake_filename) as f: 00166 return get_cmake_version(f.read()) 00167 else: 00168 return None 00169 00170 def get_cmake_version(text): 00171 for l in text.split('\n'): 00172 if l.strip().startswith('rosbuild_make_distribution'): 00173 x_re = re.compile(r'[()]') 00174 lsplit = x_re.split(l.strip()) 00175 if len(lsplit) < 2: 00176 raise ReleaseException("couldn't find version number in CMakeLists.txt:\n\n%s"%l) 00177 return lsplit[1] 00178 00179 def update_rosdistro_yaml(stack_name, version, distro_file): 00180 """ 00181 Update distro file for new stack version 00182 """ 00183 if not os.path.exists(distro_file): 00184 raise ReleaseException("[%s] does not exist"%distro_file) 00185 00186 with open(distro_file) as f: 00187 d = [d for d in yaml.load_all(f.read())] 00188 if len(d) != 1: 00189 raise ReleaseException("found more than one release document in [%s]"%distro_file) 00190 d = d[0] 00191 00192 distro_d = d 00193 if not 'stacks' in d: 00194 d['stacks'] = {} 00195 d = d['stacks'] 00196 if not stack_name in d: 00197 d[stack_name] = {} 00198 d = d[stack_name] 00199 # set the version key, assume not overriding properties 00200 d['version'] = str(version) 00201 00202 print "Writing new release properties to [%s]"%distro_file 00203 with open(distro_file, 'w') as f: 00204 f.write(yaml.safe_dump(distro_d)) 00205 00206 def make_dist(name, version, distro_stack, repair=False): 00207 """ 00208 Create tarball in a temporary directory. 00209 00210 @param repair: if True, repair tarball from tagged release. 00211 @type repair: bool 00212 @return: tarball file path, control data. 00213 """ 00214 tmp_dir = checkout_stack(name, distro_stack, repair) 00215 00216 return make_dist_of_dir(tmp_dir, name, version, distro_stack) 00217 00218 def checkout_stack(name, distro_stack): 00219 """ 00220 Checkout the stack into a tempdir 00221 00222 @return The temporary directory to find the stack inside 00223 """ 00224 tmp_dir = checkout_dev_to_tmp(name, distro_stack) 00225 return tmp_dir 00226 00227 def md5sum_file(filename): 00228 m = hashlib.md5() 00229 with open(filename, 'rb') as f: 00230 data = f.read(4096) 00231 while data: 00232 m.update(data) 00233 data = f.read(4096) 00234 return m.hexdigest() 00235 00236 def make_dist_of_dir(tmp_dir, name, version, distro_stack): 00237 """ 00238 Create tarball in a temporary directory. 00239 It is expected the tempdir has a fresh checkout of the stack. 00240 00241 @param name: stack name 00242 @param version: stack version 00243 @return: tarball file path, control data. 00244 """ 00245 tmp_source_dir = os.path.join(tmp_dir, name) 00246 print 'Building a distribution for %s in %s'%(name, tmp_source_dir) 00247 tarball = create_stack_tarball(tmp_source_dir, name, version) 00248 md5sum = md5sum_file(tarball) 00249 control = control_data(name, version, md5sum, stack_file=os.path.join(tmp_source_dir, 'stack.xml')) 00250 00251 # move tarball outside tmp_dir so we can clean it up 00252 dst = os.path.join(tempfile.gettempdir(), os.path.basename(tarball)) 00253 shutil.copyfile(tarball, dst) 00254 return dst, control 00255 00256 TAR_IGNORE_TOP=['build'] 00257 TAR_IGNORE_ALL=['.svn', '.git', '.hg'] 00258 00259 def tar_exclude(name): 00260 if name.split('/')[-1] in TAR_IGNORE_ALL: 00261 return True 00262 else: 00263 return False 00264 00265 def create_stack_tarball(path, stack_name, stack_version): 00266 """ 00267 Create a source tarball from a stack at a particular path. 00268 00269 @param name: name of stack 00270 @param stack_version: version number of stack 00271 @param path: the path of the stack to package up 00272 @return: the path of the resulting tarball, or else None 00273 """ 00274 00275 # Verify that the stack has both a stack.xml and CMakeLists.txt file 00276 stack_xml_path = os.path.join(path, roslib.stack_manifest.STACK_FILE) 00277 cmake_lists_path = os.path.join(path, 'CMakeLists.txt') 00278 00279 if not os.path.exists(stack_xml_path): 00280 raise ReleaseException("Could not find stack manifest, expected [%s]."%(stack_xml_path)) 00281 if not os.path.exists(cmake_lists_path): 00282 raise ReleaseException("Could not find CMakeLists.txt file, expected [%s]."%(cmake_lists_path)) 00283 00284 # Create the build directory 00285 build_dir = os.path.join(path, 'build') 00286 if not os.path.exists(build_dir): 00287 os.makedirs(build_dir) 00288 00289 tarfile_name = os.path.join(build_dir,'%s-%s.tar.bz2'%(stack_name, stack_version)) 00290 archive_dir = '%s-%s'%(stack_name, stack_version) 00291 00292 tar = tarfile.open(tarfile_name, 'w:bz2') 00293 00294 for x in os.listdir(path): 00295 if x not in TAR_IGNORE_TOP + TAR_IGNORE_ALL: 00296 # path of item 00297 p = os.path.join(path,x) 00298 # tar path of item 00299 tp = os.path.join(archive_dir, x) 00300 00301 tar.add(p, tp, exclude=tar_exclude) 00302 00303 tar.close() 00304 return tarfile_name 00305