f90c56643236bebc49ae3004fff650d58ecd95d2
[SubU] /
1 """distutils.command.build_clib
2
3 Implements the Distutils 'build_clib' command, to build a C/C++ library
4 that is included in the module distribution and needed by an extension
5 module."""
6
7
8 # XXX this module has *lots* of code ripped-off quite transparently from
9 # build_ext.py -- not surprisingly really, as the work required to build
10 # a static library from a collection of C source files is not really all
11 # that different from what's required to build a shared object file from
12 # a collection of C source files.  Nevertheless, I haven't done the
13 # necessary refactoring to account for the overlap in code between the
14 # two modules, mainly because a number of subtle details changed in the
15 # cut 'n paste.  Sigh.
16
17 import os
18 from ..core import Command
19 from ..errors import DistutilsSetupError
20 from ..sysconfig import customize_compiler
21 from distutils._log import log
22
23
24 def show_compilers():
25     from ..ccompiler import show_compilers
26
27     show_compilers()
28
29
30 class build_clib(Command):
31
32     description = "build C/C++ libraries used by Python extensions"
33
34     user_options = [
35         ('build-clib=', 'b', "directory to build C/C++ libraries to"),
36         ('build-temp=', 't', "directory to put temporary build by-products"),
37         ('debug', 'g', "compile with debugging information"),
38         ('force', 'f', "forcibly build everything (ignore file timestamps)"),
39         ('compiler=', 'c', "specify the compiler type"),
40     ]
41
42     boolean_options = ['debug', 'force']
43
44     help_options = [
45         ('help-compiler', None, "list available compilers", show_compilers),
46     ]
47
48     def initialize_options(self):
49         self.build_clib = None
50         self.build_temp = None
51
52         # List of libraries to build
53         self.libraries = None
54
55         # Compilation options for all libraries
56         self.include_dirs = None
57         self.define = None
58         self.undef = None
59         self.debug = None
60         self.force = 0
61         self.compiler = None
62
63     def finalize_options(self):
64         # This might be confusing: both build-clib and build-temp default
65         # to build-temp as defined by the "build" command.  This is because
66         # I think that C libraries are really just temporary build
67         # by-products, at least from the point of view of building Python
68         # extensions -- but I want to keep my options open.
69         self.set_undefined_options(
70             'build',
71             ('build_temp', 'build_clib'),
72             ('build_temp', 'build_temp'),
73             ('compiler', 'compiler'),
74             ('debug', 'debug'),
75             ('force', 'force'),
76         )
77
78         self.libraries = self.distribution.libraries
79         if self.libraries:
80             self.check_library_list(self.libraries)
81
82         if self.include_dirs is None:
83             self.include_dirs = self.distribution.include_dirs or []
84         if isinstance(self.include_dirs, str):
85             self.include_dirs = self.include_dirs.split(os.pathsep)
86
87         # XXX same as for build_ext -- what about 'self.define' and
88         # 'self.undef' ?
89
90     def run(self):
91         if not self.libraries:
92             return
93
94         # Yech -- this is cut 'n pasted from build_ext.py!
95         from ..ccompiler import new_compiler
96
97         self.compiler = new_compiler(
98             compiler=self.compiler, dry_run=self.dry_run, force=self.force
99         )
100         customize_compiler(self.compiler)
101
102         if self.include_dirs is not None:
103             self.compiler.set_include_dirs(self.include_dirs)
104         if self.define is not None:
105             # 'define' option is a list of (name,value) tuples
106             for (name, value) in self.define:
107                 self.compiler.define_macro(name, value)
108         if self.undef is not None:
109             for macro in self.undef:
110                 self.compiler.undefine_macro(macro)
111
112         self.build_libraries(self.libraries)
113
114     def check_library_list(self, libraries):
115         """Ensure that the list of libraries is valid.
116
117         `library` is presumably provided as a command option 'libraries'.
118         This method checks that it is a list of 2-tuples, where the tuples
119         are (library_name, build_info_dict).
120
121         Raise DistutilsSetupError if the structure is invalid anywhere;
122         just returns otherwise.
123         """
124         if not isinstance(libraries, list):
125             raise DistutilsSetupError("'libraries' option must be a list of tuples")
126
127         for lib in libraries:
128             if not isinstance(lib, tuple) and len(lib) != 2:
129                 raise DistutilsSetupError("each element of 'libraries' must a 2-tuple")
130
131             name, build_info = lib
132
133             if not isinstance(name, str):
134                 raise DistutilsSetupError(
135                     "first element of each tuple in 'libraries' "
136                     "must be a string (the library name)"
137                 )
138
139             if '/' in name or (os.sep != '/' and os.sep in name):
140                 raise DistutilsSetupError(
141                     "bad library name '%s': "
142                     "may not contain directory separators" % lib[0]
143                 )
144
145             if not isinstance(build_info, dict):
146                 raise DistutilsSetupError(
147                     "second element of each tuple in 'libraries' "
148                     "must be a dictionary (build info)"
149                 )
150
151     def get_library_names(self):
152         # Assume the library list is valid -- 'check_library_list()' is
153         # called from 'finalize_options()', so it should be!
154         if not self.libraries:
155             return None
156
157         lib_names = []
158         for (lib_name, build_info) in self.libraries:
159             lib_names.append(lib_name)
160         return lib_names
161
162     def get_source_files(self):
163         self.check_library_list(self.libraries)
164         filenames = []
165         for (lib_name, build_info) in self.libraries:
166             sources = build_info.get('sources')
167             if sources is None or not isinstance(sources, (list, tuple)):
168                 raise DistutilsSetupError(
169                     "in 'libraries' option (library '%s'), "
170                     "'sources' must be present and must be "
171                     "a list of source filenames" % lib_name
172                 )
173
174             filenames.extend(sources)
175         return filenames
176
177     def build_libraries(self, libraries):
178         for (lib_name, build_info) in libraries:
179             sources = build_info.get('sources')
180             if sources is None or not isinstance(sources, (list, tuple)):
181                 raise DistutilsSetupError(
182                     "in 'libraries' option (library '%s'), "
183                     "'sources' must be present and must be "
184                     "a list of source filenames" % lib_name
185                 )
186             sources = list(sources)
187
188             log.info("building '%s' library", lib_name)
189
190             # First, compile the source code to object files in the library
191             # directory.  (This should probably change to putting object
192             # files in a temporary build directory.)
193             macros = build_info.get('macros')
194             include_dirs = build_info.get('include_dirs')
195             objects = self.compiler.compile(
196                 sources,
197                 output_dir=self.build_temp,
198                 macros=macros,
199                 include_dirs=include_dirs,
200                 debug=self.debug,
201             )
202
203             # Now "link" the object files together into a static library.
204             # (On Unix at least, this isn't really linking -- it just
205             # builds an archive.  Whatever.)
206             self.compiler.create_static_lib(
207                 objects, lib_name, output_dir=self.build_clib, debug=self.debug
208             )