1"""SCons.Tool.zip
2
3Tool-specific initialization for zip.
4
5There normally shouldn't be any need to import this module directly.
6It will usually be imported through the generic SCons.Tool.Tool()
7selection method.
8
9"""
10
11# MIT License
12#
13# Copyright The SCons Foundation
14#
15# Permission is hereby granted, free of charge, to any person obtaining
16# a copy of this software and associated documentation files (the
17# "Software"), to deal in the Software without restriction, including
18# without limitation the rights to use, copy, modify, merge, publish,
19# distribute, sublicense, and/or sell copies of the Software, and to
20# permit persons to whom the Software is furnished to do so, subject to
21# the following conditions:
22#
23# The above copyright notice and this permission notice shall be included
24# in all copies or substantial portions of the Software.
25#
26# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
34import os
35
36import SCons.Builder
37import SCons.Defaults
38import SCons.Node.FS
39import SCons.Util
40
41import time
42import zipfile
43
44
45zip_compression = zipfile.ZIP_DEFLATED
46
47
48def _create_zipinfo_for_file(fname, arcname, date_time, compression):
49    st = os.stat(fname)
50    if not date_time:
51        mtime = time.localtime(st.st_mtime)
52        date_time = mtime[0:6]
53    zinfo = zipfile.ZipInfo(filename=arcname, date_time=date_time)
54    zinfo.external_attr = (st.st_mode & 0xFFFF) << 16  # Unix attributes
55    zinfo.compress_type = compression
56    zinfo.file_size = st.st_size
57    return zinfo
58
59
60def zip_builder(target, source, env):
61    compression = env.get('ZIPCOMPRESSION', zipfile.ZIP_STORED)
62    zip_root = str(env.get('ZIPROOT', ''))
63    date_time = env.get('ZIP_OVERRIDE_TIMESTAMP')
64
65    files = []
66    for s in source:
67        if s.isdir():
68            for dirpath, dirnames, filenames in os.walk(str(s)):
69                for fname in filenames:
70                    path = os.path.join(dirpath, fname)
71                    if os.path.isfile(path):
72                        files.append(path)
73        else:
74            files.append(str(s))
75
76    with zipfile.ZipFile(str(target[0]), 'w', compression) as zf:
77        for fname in files:
78            arcname = os.path.relpath(fname, zip_root)
79            # TODO: Switch to ZipInfo.from_file when 3.6 becomes the base python version
80            zinfo = _create_zipinfo_for_file(fname, arcname, date_time, compression)
81            with open(fname, "rb") as f:
82                zf.writestr(zinfo, f.read())
83
84
85# Fix PR #3569 - If you don't specify ZIPCOM and ZIPCOMSTR when creating
86# env, then it will ignore ZIPCOMSTR set afterwards.
87zipAction = SCons.Action.Action(zip_builder, "$ZIPCOMSTR",
88                                varlist=['ZIPCOMPRESSION', 'ZIPROOT', 'ZIP_OVERRIDE_TIMESTAMP'])
89
90ZipBuilder = SCons.Builder.Builder(action=SCons.Action.Action('$ZIPCOM', '$ZIPCOMSTR'),
91                                   source_factory=SCons.Node.FS.Entry,
92                                   source_scanner=SCons.Defaults.DirScanner,
93                                   suffix='$ZIPSUFFIX',
94                                   multi=1)
95
96
97def generate(env):
98    """Add Builders and construction variables for zip to an Environment."""
99    try:
100        bld = env['BUILDERS']['Zip']
101    except KeyError:
102        bld = ZipBuilder
103        env['BUILDERS']['Zip'] = bld
104
105    env['ZIP'] = 'zip'
106    env['ZIPFLAGS'] = SCons.Util.CLVar('')
107    env['ZIPCOM'] = zipAction
108    env['ZIPCOMPRESSION'] = zip_compression
109    env['ZIPSUFFIX'] = '.zip'
110    env['ZIPROOT'] = SCons.Util.CLVar('')
111
112
113def exists(env):
114    return True
115
116# Local Variables:
117# tab-width:4
118# indent-tabs-mode:nil
119# End:
120# vim: set expandtab tabstop=4 shiftwidth=4:
121