1# -*- coding: ascii -*- 2# 3# Copyright 2007, 2008, 2009, 2010, 2011 4# Andr\xe9 Malo or his licensors, as applicable 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17""" 18=================== 19 Data distribution 20=================== 21 22This module provides tools to simplify data distribution. 23""" 24__author__ = u"Andr\xe9 Malo" 25__docformat__ = "restructuredtext en" 26 27from distutils import filelist as _filelist 28import os as _os 29import posixpath as _posixpath 30import sys as _sys 31 32from _setup import commands as _commands 33 34 35def splitpath(path): 36 """ Split a path """ 37 drive, path = '', _os.path.normpath(path) 38 try: 39 splitunc = _os.path.splitunc 40 except AttributeError: 41 pass 42 else: 43 drive, path = splitunc(path) 44 if not drive: 45 drive, path = _os.path.splitdrive(path) 46 elems = [] 47 try: 48 sep = _os.path.sep 49 except AttributeError: 50 sep = _os.path.join('1', '2')[1:-1] 51 while 1: 52 prefix, path = _os.path.split(path) 53 elems.append(path) 54 if prefix in ('', sep): 55 drive = _os.path.join(drive, prefix) 56 break 57 path = prefix 58 elems.reverse() 59 return drive, elems 60 61 62def finalizer(installer): 63 """ Finalize install_data """ 64 data_files = [] 65 for item in installer.data_files: 66 if not isinstance(item, Data): 67 data_files.append(item) 68 continue 69 data_files.extend(item.flatten(installer)) 70 installer.data_files = data_files 71 72 73class Data(object): 74 """ File list container """ 75 76 def __init__(self, files, target=None, preserve=0, strip=0, 77 prefix=None): 78 """ Initialization """ 79 self._files = files 80 self._target = target 81 self._preserve = preserve 82 self._strip = strip 83 self._prefix = prefix 84 self.fixup_commands() 85 86 def fixup_commands(self): 87 pass 88 89 def from_templates(cls, *templates, **kwargs): 90 """ Initialize from template """ 91 files = _filelist.FileList() 92 for tpl in templates: 93 for line in tpl.split(';'): 94 files.process_template_line(line.strip()) 95 files.sort() 96 files.remove_duplicates() 97 result = [] 98 for filename in files.files: 99 _, elems = splitpath(filename) 100 if '.svn' in elems or '.git' in elems: 101 continue 102 result.append(filename) 103 return cls(result, **kwargs) 104 from_templates = classmethod(from_templates) 105 106 def flatten(self, installer): 107 """ Flatten the file list to (target, file) tuples """ 108 # pylint: disable = W0613 109 if self._prefix: 110 _, prefix = splitpath(self._prefix) 111 telems = prefix 112 else: 113 telems = [] 114 115 tmap = {} 116 for fname in self._files: 117 (_, name), target = splitpath(fname), telems 118 if self._preserve: 119 if self._strip: 120 name = name[max(0, min(self._strip, len(name) - 1)):] 121 if len(name) > 1: 122 target = telems + name[:-1] 123 tmap.setdefault(_posixpath.join(*target), []).append(fname) 124 return tmap.items() 125 126 127class Documentation(Data): 128 """ Documentation container """ 129 130 def fixup_commands(self): 131 _commands.add_option('install_data', 'without-docs', 132 help_text='Do not install documentation files', 133 inherit='install', 134 ) 135 _commands.add_finalizer('install_data', 'documentation', finalizer) 136 137 def flatten(self, installer): 138 """ Check if docs should be installed at all """ 139 if installer.without_docs: 140 return [] 141 return Data.flatten(self, installer) 142 143 144class Manpages(Documentation): 145 """ Manpages container """ 146 147 def dispatch(cls, files): 148 """ Automatically dispatch manpages to their target directories """ 149 mpmap = {} 150 for manpage in files: 151 normalized = _os.path.normpath(manpage) 152 _, ext = _os.path.splitext(normalized) 153 if ext.startswith(_os.path.extsep): 154 ext = ext[len(_os.path.extsep):] 155 mpmap.setdefault(ext, []).append(manpage) 156 return [cls(manpages, prefix=_posixpath.join( 157 'share', 'man', 'man%s' % section, 158 )) for section, manpages in mpmap.items()] 159 dispatch = classmethod(dispatch) 160 161 def flatten(self, installer): 162 """ Check if manpages are suitable """ 163 if _sys.platform == 'win32': 164 return [] 165 return Documentation.flatten(self, installer) 166