Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(201)

Side by Side Diff: recipe_engine/third_party/setuptools/command/build_py.py

Issue 1344583003: Recipe package system. (Closed) Base URL: git@github.com:luci/recipes-py.git@master
Patch Set: Recompiled proto Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 from glob import glob
2 from distutils.util import convert_path
3 import distutils.command.build_py as orig
4 import os
5 import sys
6 import fnmatch
7 import textwrap
8
9 try:
10 from setuptools.lib2to3_ex import Mixin2to3
11 except ImportError:
12 class Mixin2to3:
13 def run_2to3(self, files, doctests=True):
14 "do nothing"
15
16
17 class build_py(orig.build_py, Mixin2to3):
18 """Enhanced 'build_py' command that includes data files with packages
19
20 The data files are specified via a 'package_data' argument to 'setup()'.
21 See 'setuptools.dist.Distribution' for more details.
22
23 Also, this version of the 'build_py' command allows you to specify both
24 'py_modules' and 'packages' in the same setup operation.
25 """
26
27 def finalize_options(self):
28 orig.build_py.finalize_options(self)
29 self.package_data = self.distribution.package_data
30 self.exclude_package_data = (self.distribution.exclude_package_data or
31 {})
32 if 'data_files' in self.__dict__:
33 del self.__dict__['data_files']
34 self.__updated_files = []
35 self.__doctests_2to3 = []
36
37 def run(self):
38 """Build modules, packages, and copy data files to build directory"""
39 if not self.py_modules and not self.packages:
40 return
41
42 if self.py_modules:
43 self.build_modules()
44
45 if self.packages:
46 self.build_packages()
47 self.build_package_data()
48
49 self.run_2to3(self.__updated_files, False)
50 self.run_2to3(self.__updated_files, True)
51 self.run_2to3(self.__doctests_2to3, True)
52
53 # Only compile actual .py files, using our base class' idea of what our
54 # output files are.
55 self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0))
56
57 def __getattr__(self, attr):
58 if attr == 'data_files': # lazily compute data files
59 self.data_files = files = self._get_data_files()
60 return files
61 return orig.build_py.__getattr__(self, attr)
62
63 def build_module(self, module, module_file, package):
64 outfile, copied = orig.build_py.build_module(self, module, module_file,
65 package)
66 if copied:
67 self.__updated_files.append(outfile)
68 return outfile, copied
69
70 def _get_data_files(self):
71 """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
72 self.analyze_manifest()
73 data = []
74 for package in self.packages or ():
75 # Locate package source directory
76 src_dir = self.get_package_dir(package)
77
78 # Compute package build directory
79 build_dir = os.path.join(*([self.build_lib] + package.split('.')))
80
81 # Length of path to strip from found files
82 plen = len(src_dir) + 1
83
84 # Strip directory from globbed filenames
85 filenames = [
86 file[plen:] for file in self.find_data_files(package, src_dir)
87 ]
88 data.append((package, src_dir, build_dir, filenames))
89 return data
90
91 def find_data_files(self, package, src_dir):
92 """Return filenames for package's data files in 'src_dir'"""
93 globs = (self.package_data.get('', [])
94 + self.package_data.get(package, []))
95 files = self.manifest_files.get(package, [])[:]
96 for pattern in globs:
97 # Each pattern has to be converted to a platform-specific path
98 files.extend(glob(os.path.join(src_dir, convert_path(pattern))))
99 return self.exclude_data_files(package, src_dir, files)
100
101 def build_package_data(self):
102 """Copy data files into build directory"""
103 for package, src_dir, build_dir, filenames in self.data_files:
104 for filename in filenames:
105 target = os.path.join(build_dir, filename)
106 self.mkpath(os.path.dirname(target))
107 srcfile = os.path.join(src_dir, filename)
108 outf, copied = self.copy_file(srcfile, target)
109 srcfile = os.path.abspath(srcfile)
110 if (copied and
111 srcfile in self.distribution.convert_2to3_doctests):
112 self.__doctests_2to3.append(outf)
113
114 def analyze_manifest(self):
115 self.manifest_files = mf = {}
116 if not self.distribution.include_package_data:
117 return
118 src_dirs = {}
119 for package in self.packages or ():
120 # Locate package source directory
121 src_dirs[assert_relative(self.get_package_dir(package))] = package
122
123 self.run_command('egg_info')
124 ei_cmd = self.get_finalized_command('egg_info')
125 for path in ei_cmd.filelist.files:
126 d, f = os.path.split(assert_relative(path))
127 prev = None
128 oldf = f
129 while d and d != prev and d not in src_dirs:
130 prev = d
131 d, df = os.path.split(d)
132 f = os.path.join(df, f)
133 if d in src_dirs:
134 if path.endswith('.py') and f == oldf:
135 continue # it's a module, not data
136 mf.setdefault(src_dirs[d], []).append(path)
137
138 def get_data_files(self):
139 pass # kludge 2.4 for lazy computation
140
141 if sys.version < "2.4": # Python 2.4 already has this code
142 def get_outputs(self, include_bytecode=1):
143 """Return complete list of files copied to the build directory
144
145 This includes both '.py' files and data files, as well as '.pyc'
146 and '.pyo' files if 'include_bytecode' is true. (This method is
147 needed for the 'install_lib' command to do its job properly, and to
148 generate a correct installation manifest.)
149 """
150 return orig.build_py.get_outputs(self, include_bytecode) + [
151 os.path.join(build_dir, filename)
152 for package, src_dir, build_dir, filenames in self.data_files
153 for filename in filenames
154 ]
155
156 def check_package(self, package, package_dir):
157 """Check namespace packages' __init__ for declare_namespace"""
158 try:
159 return self.packages_checked[package]
160 except KeyError:
161 pass
162
163 init_py = orig.build_py.check_package(self, package, package_dir)
164 self.packages_checked[package] = init_py
165
166 if not init_py or not self.distribution.namespace_packages:
167 return init_py
168
169 for pkg in self.distribution.namespace_packages:
170 if pkg == package or pkg.startswith(package + '.'):
171 break
172 else:
173 return init_py
174
175 f = open(init_py, 'rbU')
176 if 'declare_namespace'.encode() not in f.read():
177 from distutils.errors import DistutilsError
178
179 raise DistutilsError(
180 "Namespace package problem: %s is a namespace package, but "
181 "its\n__init__.py does not call declare_namespace()! Please "
182 'fix it.\n(See the setuptools manual under '
183 '"Namespace Packages" for details.)\n"' % (package,)
184 )
185 f.close()
186 return init_py
187
188 def initialize_options(self):
189 self.packages_checked = {}
190 orig.build_py.initialize_options(self)
191
192 def get_package_dir(self, package):
193 res = orig.build_py.get_package_dir(self, package)
194 if self.distribution.src_root is not None:
195 return os.path.join(self.distribution.src_root, res)
196 return res
197
198 def exclude_data_files(self, package, src_dir, files):
199 """Filter filenames for package's data files in 'src_dir'"""
200 globs = (self.exclude_package_data.get('', [])
201 + self.exclude_package_data.get(package, []))
202 bad = []
203 for pattern in globs:
204 bad.extend(
205 fnmatch.filter(
206 files, os.path.join(src_dir, convert_path(pattern))
207 )
208 )
209 bad = dict.fromkeys(bad)
210 seen = {}
211 return [
212 f for f in files if f not in bad
213 and f not in seen and seen.setdefault(f, 1) # ditch dupes
214 ]
215
216
217 def assert_relative(path):
218 if not os.path.isabs(path):
219 return path
220 from distutils.errors import DistutilsSetupError
221
222 msg = textwrap.dedent("""
223 Error: setup script specifies an absolute path:
224
225 %s
226
227 setup() arguments must *always* be /-separated paths relative to the
228 setup.py directory, *never* absolute paths.
229 """).lstrip() % path
230 raise DistutilsSetupError(msg)
OLDNEW
« no previous file with comments | « recipe_engine/third_party/setuptools/command/build_ext.py ('k') | recipe_engine/third_party/setuptools/command/develop.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698