Mercurial > parpg-core
comparison SConstruct @ 11:4706e0194af3
Various improvements to the build process including support for self-contained builds.
* Note that despite all of these changes PARPG still does not run because asset paths are not standardized,
* Modified the SCons script so that by default running `scons` with no arguments creates a self-contained "build" under a build subdirectory to make in-source testing easier. To install PARPG, use `scons install` instead.
* Got rid of the binary launcher and replaced it with a shell script for unix and a batch script for Windows (batch script is untested). The binary turned out to be too much trouble to maintain.
* Modified the parpg.settings module and parpg.main entry script so that PARPG searches through several default search paths for configuration file(s). PARPG thus no longer crashes if it can't find a configuration file in any particular search path, but will crash it if can't find any configuration files.
* Paths supplied to parpg.main are now appended as search paths for the configuration file(s).
* Changed the default configuration file name to "parpg.cfg" to simplify searches.
* Created the site_scons directory tree where SCons extensions and tools should be placed.
* Created a new SCons builder, CopyRecurse, which can copy only certain files and folders from a directory tree using filters (files and folders that start with a leading dot "." e.g. ".svn" are ignored by default).
* Added the CPython SCons tool (stands for Compile-Python - I didn't name it!), which provides the InstallPython builder for pre-compiling python sources before they are installed. However, it is currently broken and only installs the python sources.
author | M. George Hansen <technopolitica@gmail.com> |
---|---|
date | Tue, 31 May 2011 02:46:20 -0700 |
parents | dd4ed4945411 |
children | d60f1dab8469 |
comparison
equal
deleted
inserted
replaced
10:5deaf494934a | 11:4706e0194af3 |
---|---|
1 import sys | 1 import sys |
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 copy import copy | |
6 from collections import Sequence | 7 from collections import Sequence |
7 from types import StringType | 8 from types import StringType |
8 from multiprocessing import cpu_count | 9 from multiprocessing import cpu_count |
9 | 10 |
10 from SCons.Util import is_Sequence, is_Dict | 11 from SCons.Util import is_Sequence, is_Dict |
12 from SCons.Tool.install import copyFunc | |
13 | |
14 #def recursive_glob(topdir_path, include_pattern='*', exclude_pattern=''): | |
15 # """ | |
16 # Locate all files matching supplied filename pattern in and below | |
17 # supplied root directory. | |
18 # """ | |
19 # for dir_path, subdir_names, file_names in \ | |
20 # os.walk(os.path.abspath(topdir_path)): | |
21 # for file_name in fnmatch.filter(file_names, include_pattern): | |
22 # if not fnmatch.fnmatch(file_name, exclude_pattern): | |
23 # file_path = os.path.join(dir_path, file_name) | |
24 # yield File(file_path) | |
25 # for subdir_name in copy(subdir_names): | |
26 # if not fnmatch.fnmatch(subdir_name, include_pattern) or \ | |
27 # fnmatch.fnmatch(subdir_name, exclude_pattern): | |
28 # subdir_names.remove(subdir_name) | |
29 # else: | |
30 # subdir_path = os.path.join(dir_path, subdir_name) | |
11 | 31 |
12 def InstallChmod(env, dest, source, mode): | 32 def InstallChmod(env, dest, source, mode): |
13 targets = env.Install(dest, source) | 33 targets = env.Install(dest, source) |
14 for target in targets: | 34 for target in targets: |
15 env.AddPostAction(target, Chmod(target, mode)) | 35 env.AddPostAction(target, Chmod(target, mode)) |
33 # File... (Note: Yes this can happen!) | 53 # File... (Note: Yes this can happen!) |
34 error_message = \ | 54 error_message = \ |
35 'expected entry to be a Dir or a File, but got {0!r}' | 55 'expected entry to be a Dir or a File, but got {0!r}' |
36 raise ValueError(error_message.format(entry)) | 56 raise ValueError(error_message.format(entry)) |
37 targets.append(target) | 57 targets.append(target) |
38 return targets | |
39 | |
40 def InstallPyPackages(env, dest, source, compile=True): | |
41 # Remove all existing *.pyc and *.pyo files for a sanitary install | |
42 # environment. | |
43 def _remove_compiled_modules(path): | |
44 for dir_path, dir_names, file_names in os.walk(path): | |
45 for file_name in fnmatch.filter(file_names, '*.py[co]'): | |
46 file_path = os.path.join(dir_path, file_name) | |
47 os.remove(file_path) | |
48 | |
49 def _add_targets(path): | |
50 for dir_path, dir_names, file_names in os.walk(path): | |
51 for file_name in fnmatch.filter(file_names, '*.py[co]'): | |
52 file_path = os.path.join(dir_path, file_name) | |
53 env.Clean(path, file_path) | |
54 source_file_path = file_path.rstrip('oc') | |
55 env.Depends(file_path, source_file_path) | |
56 | |
57 if not isinstance(source, Sequence) or isinstance(source, StringType): | |
58 source = [source] | |
59 for dir in source: | |
60 _remove_compiled_modules(str(dir)) | |
61 if compile: | |
62 compileall.compile_dir(str(dir), ddir=str(dest), force=True) | |
63 _add_targets(str(dir)) | |
64 targets = env.InstallReadOnly(dest, source) | |
65 return targets | 58 return targets |
66 | 59 |
67 def SubstfileEscape(env, *args, **kwargs): | 60 def SubstfileEscape(env, *args, **kwargs): |
68 subst_dict = kwargs.get('SUBST_DICT') or env.get('SUBST_DICT') | 61 subst_dict = kwargs.get('SUBST_DICT') or env.get('SUBST_DICT') |
69 escape_sequences = kwargs.get('ESCAPE_SEQUENCES') or env.get('ESCAPE_SEQUENCES') | 62 escape_sequences = kwargs.get('ESCAPE_SEQUENCES') or env.get('ESCAPE_SEQUENCES') |
85 return target | 78 return target |
86 | 79 |
87 AddMethod(Environment, InstallChmod) | 80 AddMethod(Environment, InstallChmod) |
88 AddMethod(Environment, InstallExecutable) | 81 AddMethod(Environment, InstallExecutable) |
89 AddMethod(Environment, InstallReadOnly) | 82 AddMethod(Environment, InstallReadOnly) |
90 AddMethod(Environment, InstallPyPackages) | |
91 AddMethod(Environment, SubstfileEscape) | 83 AddMethod(Environment, SubstfileEscape) |
92 | 84 |
93 EnsurePythonVersion(2, 6) | 85 EnsurePythonVersion(2, 6) |
94 | 86 |
95 AddOption( | 87 AddOption( |
267 ) | 259 ) |
268 | 260 |
269 python_version_tuple = platform.python_version_tuple() | 261 python_version_tuple = platform.python_version_tuple() |
270 | 262 |
271 environment = Environment( | 263 environment = Environment( |
272 tools=['default', 'textfile', 'packaging'], | 264 tools=['default', 'cpython', 'copyrecurse', 'textfile', 'packaging'], |
273 variables=variables, | 265 variables=variables, |
274 PROJECT_NAME='parpg', | 266 PROJECT_NAME='parpg', |
275 PROJECT_VERSION_MAJOR=0, | 267 PROJECT_VERSION_MAJOR=0, |
276 PROJECT_VERSION_MINOR=2, | 268 PROJECT_VERSION_MINOR=2, |
277 PROJECT_VERSION_PATCH=0, | 269 PROJECT_VERSION_PATCH=0, |
284 PY_VERSION_PATCH=python_version_tuple[2], | 276 PY_VERSION_PATCH=python_version_tuple[2], |
285 PY_VERSION_SHORT='${PY_VERSION_MAJOR}.${PY_VERSION_MINOR}', | 277 PY_VERSION_SHORT='${PY_VERSION_MAJOR}.${PY_VERSION_MINOR}', |
286 PY_VERSION_LONG='${PY_VERSION_MAJOR}.${PY_VERSION_MINOR}.' | 278 PY_VERSION_LONG='${PY_VERSION_MAJOR}.${PY_VERSION_MINOR}.' |
287 '${PY_VERSION_PATCH}', | 279 '${PY_VERSION_PATCH}', |
288 PY_LIB_NAME=PY_LIB_NAME, | 280 PY_LIB_NAME=PY_LIB_NAME, |
289 PY_PACKAGES=[], | 281 BUILD_DIR='build', |
290 CONFIG_FILES=[], | |
291 DATA_FILES=[], | |
292 EXECUTABLES=[], | |
293 ) | 282 ) |
294 if environment['DEBUG']: | 283 if environment['DEBUG']: |
295 if platform_name == 'Windows': | 284 if platform_name == 'Windows': |
296 environment['CCPDBFLAGS'] = '/Z7 /Od' | 285 environment['CCPDBFLAGS'] = '/Z7 /Od' |
297 environment.AppendUnique(LINKFLAGS='/DEBUG') | 286 environment.AppendUnique(LINKFLAGS='/DEBUG') |
302 | 291 |
303 config_dict = [('@{0}@'.format(key), environment.Dictionary()[key]) for key in | 292 config_dict = [('@{0}@'.format(key), environment.Dictionary()[key]) for key in |
304 ('PREFIX', 'LIB_DIR', 'PY_PACKAGES_DIR', 'BIN_DIR', | 293 ('PREFIX', 'LIB_DIR', 'PY_PACKAGES_DIR', 'BIN_DIR', |
305 'SYS_CONF_DIR', 'DATA_DIR', 'PYTHON')] | 294 'SYS_CONF_DIR', 'DATA_DIR', 'PYTHON')] |
306 | 295 |
307 install_py_packages = environment.InstallPyPackages( | 296 copy_py_packages = environment.Install( |
297 '$BUILD_DIR', | |
298 'src/parpg', | |
299 ) | |
300 install_py_packages = environment.InstallPython( | |
308 '$PY_PACKAGES_DIR', | 301 '$PY_PACKAGES_DIR', |
309 'src/parpg', | 302 copy_py_packages, |
310 compile=GetOption('compile'), | 303 PYTHON_COMPILE=GetOption('compile'), |
304 ) | |
305 | |
306 copy_data = environment.CopyRecurse( | |
307 '$BUILD_DIR', | |
308 'data', | |
311 ) | 309 ) |
312 | 310 |
313 subst_config_file = environment.Substfile( | 311 subst_config_file = environment.Substfile( |
314 'system.cfg.in', | 312 '$BUILD_DIR/parpg.cfg', |
315 SUBST_DICT=config_dict | 313 'parpg.cfg.in', |
314 SUBST_DICT=config_dict, | |
316 ) | 315 ) |
317 install_config_files = environment.InstallChmod( | 316 install_config_files = environment.InstallChmod( |
318 '$SYS_CONF_DIR', | 317 '$SYS_CONF_DIR', |
319 subst_config_file, | 318 subst_config_file, |
320 mode=0755 | 319 mode=0755, |
321 ) | 320 ) |
322 Requires(install_config_files, subst_config_file) | 321 Requires(install_config_files, subst_config_file) |
323 | 322 |
324 install_data = environment.InstallReadOnly( | 323 if platform_name == 'Windows': |
325 '$DATA_DIR', | 324 launcher_name = 'parpg.bat' |
326 Glob('data/*'), | 325 else: |
327 ) | 326 launcher_name = 'parpg.sh' |
328 | 327 # FIXME M. George Hansen 2011-05-20: Do any other sequences need to be escaped? |
329 # FIXME M. George Hansen 2011-05-20: Do any other sequences need to be escaped | 328 launcher = environment.SubstfileEscape( |
330 # in a C string? | 329 'build/$LAUNCHER_NAME', |
331 executable_source = environment.SubstfileEscape( | 330 'bin/${LAUNCHER_NAME}.in', |
332 'bin/parpg.c.in', | 331 LAUNCHER_NAME=launcher_name, |
333 SUBST_DICT=config_dict, | 332 SUBST_DICT=config_dict, |
334 ESCAPE_SEQUENCES={'\\': r'\\\\', '"': r'\\"'}, | 333 ESCAPE_SEQUENCES={'\\': r'\\\\', '"': r'\\"'}, |
335 ) | 334 ) |
336 build_executable = environment.Program( | |
337 executable_source, | |
338 LIBS=['$PY_LIB_NAME'], | |
339 LIBPATH='$PY_LIB_DIR', | |
340 CPPPATH=['$PY_HEADERS_DIR'], | |
341 ) | |
342 # Clean up any files created by the MSVC compiler on Windows. | |
343 if platform_name == 'Windows': | 335 if platform_name == 'Windows': |
344 environment.Clean(build_executable, 'bin/parpg.ilk') | 336 install_launcher = environment.InstallExecutable( |
345 environment.Clean(build_executable, 'bin/parpg.pdb') | 337 '$BIN_DIR', |
346 install_executable = environment.InstallExecutable( | 338 launcher, |
347 '$BIN_DIR', | 339 ) |
348 build_executable, | 340 else: |
349 ) | 341 # Remove the .sh suffix, since it isn't needed on unix platforms. |
342 install_launcher = environment.InstallAs( | |
343 '$BIN_DIR/parpg', | |
344 launcher, | |
345 ) | |
350 | 346 |
351 # TODO M. George Hansen 2011-05-12: Implement package builder. | 347 # TODO M. George Hansen 2011-05-12: Implement package builder. |
352 #package = environment.Package( | 348 #package = environment.Package( |
353 # NAME='parpg', | 349 # NAME='parpg', |
354 # VERSION='0.2.0', | 350 # VERSION='0.2.0', |
357 # SUMMARY='', | 353 # SUMMARY='', |
358 # DESCRIPTION='', | 354 # DESCRIPTION='', |
359 # X_RPM_GROUP='Application/parpg', | 355 # X_RPM_GROUP='Application/parpg', |
360 #) | 356 #) |
361 | 357 |
362 build = Alias('build', [build_executable]) | 358 build = Alias('build', [launcher, subst_config_file, copy_py_packages, |
363 install = Alias('install', [build, install_executable, install_py_packages, | 359 copy_data]) |
364 install_config_files, install_data]) | 360 install = Alias('install', [build, install_launcher, install_py_packages, |
365 | 361 install_config_files]) |
366 Default(install) | 362 |
363 Default(build) |