comparison SConstruct @ 5:33684971cdb1

Replaced the shell script launcher with a cross-platform C executable. * Replaced the Unix-only shell script launcher with a cross-platform compiled executable written in C. * Added several new configuration variables to the SConsctruct script which allows the user to specify where their Python site-packages and executable are located; * Cleaned up the SConstruct script and made it easier to grok.
author M. George Hansen <technopolitica@gmail.com>
date Tue, 17 May 2011 14:18:25 -0700
parents e2a8e3805b04
children dd4ed4945411
comparison
equal deleted inserted replaced
4:20c2b7a42c63 5:33684971cdb1
2 import os 2 import os
3 import platform 3 import platform
4 import compileall 4 import compileall
5 import fnmatch 5 import fnmatch
6 from collections import Sequence 6 from collections import Sequence
7 from types import StringType
7 from multiprocessing import cpu_count 8 from multiprocessing import cpu_count
8 9
9 def InstallChmod(env, dest, source, mode): 10 def InstallChmod(env, dest, source, mode):
10 targets = env.Install(dest, source) 11 targets = env.Install(dest, source)
11 for target in targets: 12 for target in targets:
49 file_path = os.path.join(dir_path, file_name) 50 file_path = os.path.join(dir_path, file_name)
50 env.Clean(path, file_path) 51 env.Clean(path, file_path)
51 source_file_path = file_path.rstrip('oc') 52 source_file_path = file_path.rstrip('oc')
52 env.Depends(file_path, source_file_path) 53 env.Depends(file_path, source_file_path)
53 54
54 if not isinstance(source, Sequence): 55 if not isinstance(source, Sequence) or isinstance(source, StringType):
55 source = [source] 56 source = [source]
56 for dir in source: 57 for dir in source:
57 _remove_compiled_modules(str(dir)) 58 _remove_compiled_modules(str(dir))
58 if compile: 59 if compile:
59 compileall.compile_dir(str(dir), ddir=str(dest), force=True) 60 compileall.compile_dir(str(dir), ddir=str(dest), force=True)
79 AddOption( 80 AddOption(
80 '--no-compile', 81 '--no-compile',
81 dest='compile', 82 dest='compile',
82 action='store_false', 83 action='store_false',
83 default=True, 84 default=True,
84 help='don\'t compile any Python modules into .pyc files', 85 help='don\'t compile any Python modules into .pyc files before installing',
85 ) 86 )
86 SetOption('num_jobs', cpu_count()) 87 SetOption('num_jobs', cpu_count())
87 88
88 def is_abs_path(key, val, env): 89 def is_abs_path(key, val, env):
89 if not os.path.isabs(val): 90 if not os.path.isabs(val):
113 dist_name, dist_version, dist_id = \ 114 dist_name, dist_version, dist_id = \
114 platform.linux_distribution(full_distribution_name=False) 115 platform.linux_distribution(full_distribution_name=False)
115 if dist_name in ['debian', 'Ubuntu']: 116 if dist_name in ['debian', 'Ubuntu']:
116 # Debian uses dist-packages instead of site-packages for Python 117 # Debian uses dist-packages instead of site-packages for Python
117 # versions > 2.5. 118 # versions > 2.5.
118 PY_LIB_DIR_DEFAULT = '$EXEC_PREFIX/lib/python$PY_VERSION_SHORT/' \ 119 PY_PACKAGES_DIR_DEFAULT = '$EXEC_PREFIX/lib/python$PY_VERSION_SHORT/' \
119 'dist-packages' 120 'dist-packages'
120 else: 121 else:
121 PY_LIB_DIR_DEFAULT = '$EXEC_PREFIX/lib/python$PY_VERSION_SHORT/' \ 122 PY_PACKAGES_DIR_DEFAULT = '$EXEC_PREFIX/lib/python$PY_VERSION_SHORT/' \
122 'site-packages' 123 'site-packages'
124 PY_HEADERS_DIR_DEFAULT = '/usr/include/python$PY_VERSION_SHORT'
125 PY_LIB_DIR_DEFAULT = '/usr/lib'
126 PY_LIB_NAME = 'python$PY_VERSION_SHORT'
123 elif platform_name == 'Windows': 127 elif platform_name == 'Windows':
124 try: 128 try:
125 PREFIX_DEFAULT = os.environ['ProgramFiles'] + r'\$PROGRAM_NAME' 129 PREFIX_DEFAULT = os.environ['PROGRAMFILES'] + r'\$PROGRAM_NAME'
126 except KeyError: 130 except KeyError:
127 PREFIX_DEFAULT = '' 131 PREFIX_DEFAULT = ''
128 error_message = '%ProgramFiles% environmental variable is not ' \ 132 error_message = '%PROGRAMFILES% environmental variable is not ' \
129 'set, unable to determine path to Program Files ' \ 133 'set, unable to determine path to Program Files ' \
130 'folder (use --prefix to override)' 134 'folder'
131 raise SConfWarning(error_message) 135 raise SConfWarning(error_message)
132 BIN_DIR_DEFAULT = '$EXEC_PREFIX' 136 BIN_DIR_DEFAULT = '$EXEC_PREFIX'
133 DATA_ROOT_DIR_DEFAULT = '$PREFIX/data' 137 DATA_ROOT_DIR_DEFAULT = '$PREFIX/data'
134 SYS_CONF_DIR_DEFAULT = '$PREFIX/config' 138 SYS_CONF_DIR_DEFAULT = '$PREFIX/config'
135 INCLUDE_DIR_DEFAULT = '$PREFIX/include' 139 INCLUDE_DIR_DEFAULT = '$PREFIX/include'
136 DOC_DIR_DEFAULT = '$PREFIX/doc/' 140 DOC_DIR_DEFAULT = '$PREFIX/doc/'
137 LIB_DIR_DEFAULT = '$EXEC_PREFIX/lib' 141 LIB_DIR_DEFAULT = '$EXEC_PREFIX/lib'
138 PY_LIB_DIR_DEFAULT = '$EXEC_PREFIX/lib' 142 PY_LIB_DIR_DEFAULT = '$EXEC_PREFIX/lib'
143 # FIXME M. George Hansen 2011-05-12: Does sys.prefix include the
144 # PythonX.Y part on Windows?
145 python_prefix = sys.prefix
139 if GetOption('stand_alone'): 146 if GetOption('stand_alone'):
140 PY_LIB_DIR_DEFAULT = '$PREFIX/lib' 147 PY_PACKAGES_DIR_DEFAULT = '$PREFIX/lib'
141 else: 148 else:
142 python_prefix = sys.prefix 149 PY_PACKAGES_DIR_DEFAULT = \
143 # FIXME M. George Hansen 2011-05-12: Does sys.prefix include the
144 # PythonX.Y part on Windows?
145 PY_LIB_DIR_DEFAULT = \
146 os.path.join(python_prefix, 'Lib', 'site-packages') 150 os.path.join(python_prefix, 'Lib', 'site-packages')
151 PY_HEADERS_DIR_DEFAULT = os.path.join(python_prefix, 'include')
152 try:
153 PY_LIB_DIR_DEFAULT = os.environ['SYSTEMROOT']
154 except KeyError:
155 PY_LIB_DIR_DEFAULT = ''
156 error_message = '%SYSTEMROOT% environmental variable is not set, ' \
157 'unable to determine path to Windows system folder'
158 raise SConfWarning(error_message)
159 PY_LIB_NAME = 'python$PY_VERSION_MAJOR$PY_VERSION_MINOR'
147 160
148 # Platform-independant variables: 161 # Platform-independant variables:
149 variables.AddVariables( 162 variables.AddVariables(
150 PathVariable( 163 PathVariable(
151 'PREFIX', 164 'PREFIX',
206 'files should be installed', 219 'files should be installed',
207 LIB_DIR_DEFAULT, 220 LIB_DIR_DEFAULT,
208 is_abs_path, 221 is_abs_path,
209 ), 222 ),
210 PathVariable( 223 PathVariable(
211 'PY_LIB_DIR', 224 'PY_PACKAGES_DIR',
212 'directory where pure Python modules and packages should be installed', 225 'directory where pure Python modules and packages should be installed',
213 PY_LIB_DIR_DEFAULT, 226 PY_PACKAGES_DIR_DEFAULT,
214 is_abs_path, 227 is_abs_path,
215 ), 228 ),
216 PathVariable( 229 PathVariable(
217 'INCLUDE_DIR', 230 'INCLUDE_DIR',
218 'directory where C/C++ header files should be installed', 231 'directory where C/C++ header files should be installed',
219 INCLUDE_DIR_DEFAULT, 232 INCLUDE_DIR_DEFAULT,
220 is_abs_path, 233 is_abs_path,
221 ), 234 ),
235 PathVariable(
236 'PY_HEADERS_DIR',
237 'directory where Python.h can be found',
238 PY_HEADERS_DIR_DEFAULT,
239 ),
240 PathVariable(
241 'PY_LIB_DIR',
242 'directory where the Python shared library can be found',
243 PY_LIB_DIR_DEFAULT,
244 ),
245 BoolVariable(
246 'DEBUG',
247 'if True, compile the program launcher executable with debugging '
248 'symbols',
249 False,
250 ),
222 ) 251 )
223 252
224 platform_name = platform.system() 253 platform_name = platform.system()
225 python_version_tuple = platform.python_version_tuple() 254 python_version_tuple = platform.python_version_tuple()
226 255
227 environment = Environment( 256 environment = Environment(
228 tools=['default', 'textfile', 'packaging'], 257 tools=['default', 'cc', 'textfile', 'packaging'],
229 variables=variables, 258 variables=variables,
230 PROJECT_NAME='parpg', 259 PROJECT_NAME='parpg',
231 PROJECT_VERSION_MAJOR=0, 260 PROJECT_VERSION_MAJOR=0,
232 PROJECT_VERSION_MINOR=2, 261 PROJECT_VERSION_MINOR=2,
233 PROJECT_VERSION_PATCH=0, 262 PROJECT_VERSION_PATCH=0,
234 PROJECT_VERSION_SHORT='$PROJECT_VERSION_MAJOR.$PROJECT_VERSION_MAJOR', 263 PROJECT_VERSION_SHORT='${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MAJOR}',
235 PROJECT_VERSION_LONG='$PROJECT_VERSION_MAJOR.$PROJECT_VERSION_MINOR.' 264 PROJECT_VERSION_LONG='${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.'
236 '$PROJECT_VERSION_PATCH', 265 '${PROJECT_VERSION_PATCH}',
237 PYTHON=sys.executable, 266 PYTHON=sys.executable,
238 PY_VERSION_SHORT='.'.join(python_version_tuple[:-1]), 267 PY_VERSION_MAJOR=python_version_tuple[0],
239 PY_VERSION_LONG='.'.join(python_version_tuple), 268 PY_VERSION_MINOR=python_version_tuple[1],
269 PY_VERSION_PATCH=python_version_tuple[2],
270 PY_VERSION_SHORT='${PY_VERSION_MAJOR}.${PY_VERSION_MINOR}',
271 PY_VERSION_LONG='${PY_VERSION_MAJOR}.${PY_VERSION_MINOR}.'
272 '${PY_VERSION_PATCH}',
273 PY_LIB_NAME=PY_LIB_NAME,
240 PY_PACKAGES=[], 274 PY_PACKAGES=[],
241 CONFIG_FILES=[], 275 CONFIG_FILES=[],
242 DATA_FILES=[], 276 DATA_FILES=[],
243 EXECUTABLES=[], 277 EXECUTABLES=[],
244 ) 278 )
279 if environment['DEBUG']:
280 environment.AppendUnique(CCFLAGS=['-gdwarf-2', '-g3'])
281
245 Help(variables.GenerateHelpText(environment)) 282 Help(variables.GenerateHelpText(environment))
246 283
247 config_dict = [('@{0}@'.format(key), environment.Dictionary()[key]) for key in 284 config_dict = [('@{0}@'.format(key), environment.Dictionary()[key]) for key in
248 ('PREFIX', 'LIB_DIR', 'PY_LIB_DIR', 'BIN_DIR', 'SYS_CONF_DIR', 285 ('PREFIX', 'LIB_DIR', 'PY_PACKAGES_DIR', 'BIN_DIR',
249 'DATA_DIR', 'PYTHON')] 286 'SYS_CONF_DIR', 'DATA_DIR', 'PYTHON')]
250 287
251 environment['PY_PACKAGES'] += [ 288 install_py_packages = environment.InstallPyPackages(
252 Dir('src/parpg'), 289 '$PY_PACKAGES_DIR',
253 ] 290 'src/parpg',
254 config_file_template = File('system.cfg.in')
255 subst_config_file = environment.Substfile(config_file_template,
256 SUBST_DICT=config_dict)
257 environment['CONFIG_FILES'] += [subst_config_file]
258 environment['DATA_FILES'] += Glob('data/*')
259 # TODO M. George Hansen 2011-05-12: Implement windows executable.
260 executable_template = File('bin/unix/parpg.in')
261 subst_executable = environment.Substfile(executable_template,
262 SUBST_DICT=config_dict)
263 environment['EXECUTABLES'] += [subst_executable]
264
265 sources = environment.InstallPyPackages(
266 '$PY_LIB_DIR',
267 environment['PY_PACKAGES'],
268 compile=GetOption('compile'), 291 compile=GetOption('compile'),
269 ) 292 )
270 config_files = environment.InstallChmod( 293
294 subst_config_file = environment.Substfile(
295 'system.cfg.in',
296 SUBST_DICT=config_dict
297 )
298 install_config_files = environment.InstallChmod(
271 '$SYS_CONF_DIR', 299 '$SYS_CONF_DIR',
272 environment['CONFIG_FILES'], 300 subst_config_file,
273 mode=0755, 301 mode=0755
274 ) 302 )
275 Requires(config_files, subst_config_file) 303 Requires(install_config_files, subst_config_file)
276 data_files = environment.InstallReadOnly( 304
305 install_data = environment.InstallReadOnly(
277 '$DATA_DIR', 306 '$DATA_DIR',
278 environment['DATA_FILES'], 307 Glob('data/*'),
279 ) 308 )
280 executables = environment.InstallExecutable( 309
310 executable_source = environment.Substfile(
311 'bin/parpg.c.in',
312 SUBST_DICT=config_dict,
313 )
314 build_executable = environment.Program(
315 executable_source,
316 LIBS=['$PY_LIB_NAME'],
317 LIBPATH='$PY_LIB_DIR',
318 CPPPATH=['$PY_HEADERS_DIR'],
319 )
320 # FIXME M. George Hansen 2011-05-17: Should the executable target be hard-coded
321 # like this?
322 install_executable = environment.InstallExecutable(
281 '$BIN_DIR', 323 '$BIN_DIR',
282 environment['EXECUTABLES'], 324 'bin/parpg',
283 ) 325 )
284 Requires(executables, subst_executable)
285 326
286 # TODO M. George Hansen 2011-05-12: Implement package builder. 327 # TODO M. George Hansen 2011-05-12: Implement package builder.
287 #package = environment.Package( 328 #package = environment.Package(
288 # NAME='parpg', 329 # NAME='parpg',
289 # VERSION='0.2.0', 330 # VERSION='0.2.0',
292 # SUMMARY='', 333 # SUMMARY='',
293 # DESCRIPTION='', 334 # DESCRIPTION='',
294 # X_RPM_GROUP='Application/parpg', 335 # X_RPM_GROUP='Application/parpg',
295 #) 336 #)
296 337
297 Default([sources, config_files, data_files, executables]) 338 build = Alias('build', [build_executable])
298 Alias('install', [sources, config_files, data_files, executables]) 339 install = Alias('install', [build, install_executable, install_py_packages,
340 install_config_files, install_data])
341
342 Default(install)