Package rosdeb :: Module source_deb
[frames] | no frames]

Source Code for Module rosdeb.source_deb

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2010, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32  # 
 33  # Revision $Id: __init__.py 10652 2010-08-11 22:01:37Z kwc $ 
 34  from __future__ import with_statement 
 35   
 36  import os 
 37  import sys 
 38  import time 
 39  from subprocess import check_call 
 40  import hashlib 
 41   
 42  import yaml 
 43   
 44  import roslib.packages 
 45   
 46  from rosdeb.rosutil import convert_html_to_text, stack_rosdeps 
 47  from rosdeb.core import ubuntu_release, debianize_name, debianize_version, platforms, ubuntu_release_name 
 48   
49 -def make_source_deb(distro_name, stack_name, stack_version, os_platform_name, staging_dir):
50 """ 51 @param os_platform_name: Name of OS platform/version, e.g. 'lucid' 52 @type os_platform_name: str 53 @return: list of source-deb files 54 @rtype: [str] 55 """ 56 debian_name = 'ros-%s-%s'%(distro_name, debianize_name(stack_name)) 57 58 tmpl_d = os.path.join(roslib.packages.get_pkg_dir('rosdeb'), 'resources', 'source_deb') 59 60 tarball = os.path.join(staging_dir, "%s-%s.tar.bz2"%(stack_name, stack_version)) 61 if not os.path.exists(tarball): 62 raise Exception("tarball must be copied to staging directory first") 63 64 # keep track of files we've copied in to modify 65 files = [] 66 67 # make STACK/debian 68 stack_d = os.path.join(staging_dir, stack_name) 69 debian_d = os.path.join(stack_d, 'debian') 70 if not os.path.exists(debian_d): 71 os.makedirs(debian_d) 72 73 # Files which go into debian dir 74 for f in ['rules', 'compat', 'postinst']: 75 files.append( (os.path.join(tmpl_d, f), os.path.join(debian_d, f)) ) 76 77 # Files which go into stack dir 78 for f in ['fixpc.py', 'fixbinpath.py', 'fixrpath.py', 'Makefile', 'setup_deb.sh', 'purge_build.py', 'update_version.py', 'gen_versioned_debs.py']: 79 files.append( (os.path.join(tmpl_d, f), os.path.join(stack_d, f)) ) 80 81 # Files which go into stack dir and are different for ros stack 82 if stack_name == 'ros': 83 for f in ['setup_deb.sh', 'Makefile']: 84 f_src = f+'-ros' 85 files.append( (os.path.join(tmpl_d, f_src), os.path.join(stack_d, f)) ) 86 87 # Files which go into stack dir and only exist for ros 88 if stack_name == 'ros': 89 for f in ['setup.sh','setup.bash','setup.zsh','.rosinstall']: 90 files.append( (os.path.join(tmpl_d, f), os.path.join(stack_d, f))) 91 92 for src, dst in files: 93 with open(src, 'r') as f: 94 src_text = f.read() 95 96 dst_text = src_text.replace('${ROS_DISTRO_NAME}', distro_name) 97 dst_text = dst_text.replace('${ROS_STACK_NAME}', stack_name) 98 dst_text = dst_text.replace('${ROS_STACK_DEBIAN_NAME}', debian_name) 99 dst_text = dst_text.replace('${ROS_STACK_VERSION}', stack_version) 100 with open(dst, 'w') as f: 101 f.write(dst_text) 102 103 # copy permission modes 104 s = os.stat(src) 105 os.chmod(dst, s.st_mode) 106 107 # CONTROL: read in the control YAML data and convert it to an actual control file 108 control_yaml = os.path.join(staging_dir, '%s-%s.yaml'%(stack_name, stack_version)) 109 with open(control_yaml, 'r') as f: 110 metadata = yaml.load(f.read()) 111 if not type(metadata) == dict: 112 raise Exception("invalid control file: %s\nMetadata is [%s]"%(control_yaml, metadata)) 113 114 # make distro-specific 115 metadata['package'] = debian_name 116 with open(os.path.join(debian_d, 'control'), 'w') as f: 117 f.write(control_file(metadata, distro_name, os_platform_name).encode('utf-8')) 118 119 # CHANGELOG 120 with open(os.path.join(debian_d, 'changelog'), 'w') as f: 121 f.write(changelog_file(metadata, os_platform_name).encode('utf-8')) 122 123 # We must use a build-version starting with a letter greater than r 124 build_version='s$BUILD_VERSION' 125 126 with open(os.path.join(debian_d, 'changelog.tmp'), 'w') as f: 127 f.write(changelog_file(metadata, os_platform_name, build_version)) 128 129 # MD5Sum of original stack tar.bz2: 130 with open(os.path.join(stack_d, '%s-%s.md5'%(stack_name, stack_version)),'w') as mf: 131 with open(os.path.join(staging_dir, '%s-%s.tar.bz2'%(stack_name, stack_version)),'r') as f: 132 m = hashlib.md5() 133 m.update(f.read()) 134 mf.write('%s %s\n'%(m.hexdigest(), '../%s-%s.tar.bz2'%(stack_name, stack_version))) 135 136 # Note: this creates 3 files. A .dsc, a .tar.gz, and a .changes 137 check_call(['dpkg-buildpackage', '-S', '-uc', '-us'], cwd=stack_d) 138 139 140 # SOURCE DEB: .dsc plus tarball of debian dir. Ignore the changes for now 141 f_name = "%s_%s-0~%s"%(debian_name, stack_version, os_platform_name) 142 files = [os.path.join(staging_dir, f_name+ext) for ext in ('.dsc', '.tar.gz')] 143 for f in files: 144 assert os.path.exists(f), "File: %s does not exist"%f 145 146 return files
147
148 -def supported_platforms(control):
149 return [version for version in control['rosdeps'].keys()]
150
151 -def changelog_file(metadata, platform='lucid', build_version='0'):
152 #TODO: want to use BeautifulSoup to rip actual changelog 153 data = metadata.copy() 154 #day-of-week, dd month yyyy hh:mm:ss +zzzz 155 data['date'] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) 156 data['platform'] = platform 157 data['supported'] = platform 158 data['build-version'] = build_version 159 160 return """%(package)s (%(version)s-%(build-version)s~%(platform)s) %(supported)s; urgency=low 161 162 * Please see https://ros.org/wiki/%(stack)s/ChangeList 163 \t 164 -- See website <ros-users@code.ros.org> %(date)s 165 \t 166 """%data
167
168 -def deb_depends(metadata, distro_name, platform_name):
169 """ 170 @return: list of debian package dependencies, or None if not supported on that platform 171 @rtype: [str] 172 """ 173 # if a control file does not specify deb depends for the platform, it is not valid on that platform 174 if 'rosdeps' not in metadata: 175 return None 176 if platform_name not in metadata['rosdeps']: 177 # hack to get around bug in ubuntu_map that polluted control files with bad maverick keys 178 if platform_name == 'maverick' and 'mighty' in metadata['rosdeps']: 179 platform_name = 'mighty' 180 else: 181 return None 182 rosdeps = metadata['rosdeps'][platform_name] 183 # support version-locking syntax 184 rosdeps_fixed = [] 185 for r in rosdeps: 186 if '=' in r: 187 # example libeigen3-dev=3.0.1-1+ros4~lucid 188 if '*' in r: 189 raise Exception("cannot include glob patterns in debian control file") 190 rosdep_name, version = r.split('=') 191 rosdeps_fixed.append("%s (=%s)"%(rosdep_name, version)) 192 else: 193 rosdeps_fixed.append(r) 194 return rosdeps_fixed
195
196 -def stack_depends(metadata, distro_name, platform_name):
197 """ 198 @return: list of debian stack dependencies 199 @rtype: [str] 200 """ 201 stackdeps = metadata.get('depends', []) 202 stackdeps = ['ros-%s-%s'%(distro_name, debianize_name(s)) for s in stackdeps] 203 204 return stackdeps
205
206 -def download_control(stack_name, stack_version):
207 url = 'https://code.ros.org/svn/release/download/stacks/%(stack_name)s/%(stack_name)s-%(stack_version)s/%(stack_name)s-%(stack_version)s.yaml' 208 url = url%locals() 209 import urllib2 210 try: 211 return yaml.load(urllib2.urlopen(url)) 212 except: 213 raise Exception("Problem fetching yaml info for %s %s (% s).\nThis yaml info is usually created when a release is uploaded. If it is missing, either the stack version is wrong, or the release did not occur correctly."%(stack_name, stack_version, url))
214
215 -def control_file(metadata, distro_name, platform_name):
216 data = metadata.copy() 217 data['description-full'] = metadata['description-full'].rstrip() 218 data['distro_name'] = distro_name 219 if data['maintainer'].startswith('Maintained by '): 220 data['maintainer'] = data['maintainer'][len('Maintained by '):] 221 222 try: 223 depends = deb_depends(metadata, distro_name, platform_name) 224 stacks = stack_depends(metadata, distro_name, platform_name) 225 if depends is None: 226 raise Exception("stack [%s] does not have valid debian package dependencies for release [%s]"%(metadata['stack'], platform_name)) 227 data['all-depends'] = ', '.join(depends + stacks) 228 data['deb-depends'] = ', '.join(depends) 229 except KeyError: 230 raise Exception("stack [%s] does not have rosdeps for release [%s]"%(metadata['stack'], platform_name)) 231 232 return """Source: %(package)s 233 Section: unknown 234 Priority: %(priority)s 235 Maintainer: %(maintainer)s 236 Build-Depends: debhelper (>= 5), chrpath, %(all-depends)s 237 Standards-Version: 3.7.2 238 XBC-WG-rosdistro: %(distro_name)s 239 240 Package: %(package)s 241 Architecture: any 242 Depends: ${shlibs:Depends}, ${misc:Depends}, ${rosstack:Depends}, %(deb-depends)s 243 Description: %(description-brief)s 244 %(description-full)s 245 """%data
246
247 -def control_data(stack_name, stack_version, md5sum, stack_file=None):
248 """ 249 Generate metadata for control file. Cannot generate debian dependencies as these are platform specific. 250 251 @type stack_name: name of stack 252 @type stack_name: str 253 @type stack_version: stack version id 254 @type stack_version: str 255 @param stack_file: location of stack file, or None to use default rosstack lookup 256 @type stack_file: str 257 """ 258 import roslib.stack_manifest 259 if stack_file is None: 260 stack_file = roslib.stack_manifest.stack_file(stack_name) 261 m = roslib.stack_manifest.parse_file(stack_file) 262 263 metadata = {} 264 265 metadata['md5sum'] = md5sum #3301 266 metadata['stack'] = stack_name 267 metadata['package'] = debianize_name(stack_name) 268 metadata['version'] = stack_version 269 metadata['homepage'] = m.url 270 if m.author.startswith('Maintained by '): 271 metadata['maintainer'] = m.author[len('Maintained by '):] 272 else: 273 metadata['maintainer'] = m.author 274 metadata['priority'] = 'optional' 275 if m.brief: 276 # 60-char limit on control files 277 metadata['description-brief'] = m.brief[:60] 278 else: 279 metadata['description-brief'] = m.brief[:60] 280 281 try: 282 description = convert_html_to_text(m.description).rstrip() 283 except: 284 description = "unknown" 285 286 # per debian spec, single-space pad to signal paragraph 287 desc_padded = '' 288 for l in description.split('\n'): 289 desc_padded += ' '+l+'\n' 290 metadata['description-full'] = desc_padded.rstrip() 291 292 # do deps in two parts as ros stack depends need to become version 293 # locked later on due to lack of ABI compat 294 metadata['depends'] = [d.stack for d in m.depends] 295 metadata['rosdeps'] = rosdeps = {} 296 for platform in platforms(): 297 try: 298 rosdeps[platform] = stack_rosdeps(stack_name, os.path.dirname(stack_file), platform) 299 except Exception as e: 300 # #3435 301 # TODO: this is a hack that should be turned into a typed exception 302 if "cannot generate" in str(e): 303 sys.stderr.write("Error with platform [%s]: %s\n"%(platform, str(e))) 304 # ignore other failures as these are generally unsupported 305 # platforms. Later logic is responsible for erroring if 306 # control file is missing bindings, and unit tests on 307 # stack_rosdeps verify its functionality 308 309 return metadata
310