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