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