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__ = "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 list(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 list(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