# HG changeset patch # User M. George Hansen # Date 1306050839 25200 # Node ID dd4ed4945411798723b192d84471b5dcbd730225 # Parent 33684971cdb16a05ee51bb9011bb369d9e36320c Ported binary launcher to Windows. * Reorganized the parpg executable source so that it now compiles with the MSVC compiler (tested with MSVC Express 2010). * Cleaned up the launcher source by adding a bunch of const statements and removing the superfluous NULL_TERMINATE macro. * Fixed a few memory leaks in the launcher source by freeing malloced/calloced pointers; * Added a new SubstfileEscape builder to the SConstruct script used to escape certain sequences when substituting a template file. This is used to escape backslashes ("\") in c strings in the bin/parpg.c.in template for Windows paths. * Fixed the PY_LIB_DIR_DEFAULT for Windows so that it now correctly points to the default Python installation path. * Modified the SConstruct script to support compiling the launcher executable with debugging symbols in Windows with the DEBUG flag. diff -r 33684971cdb1 -r dd4ed4945411 SConstruct --- a/SConstruct Tue May 17 14:18:25 2011 -0700 +++ b/SConstruct Sun May 22 00:53:59 2011 -0700 @@ -7,6 +7,8 @@ from types import StringType from multiprocessing import cpu_count +from SCons.Util import is_Sequence, is_Dict + def InstallChmod(env, dest, source, mode): targets = env.Install(dest, source) for target in targets: @@ -62,10 +64,31 @@ targets = env.InstallReadOnly(dest, source) return targets +def SubstfileEscape(env, *args, **kwargs): + subst_dict = kwargs.get('SUBST_DICT') or env.get('SUBST_DICT') + escape_sequences = kwargs.get('ESCAPE_SEQUENCES') or env.get('ESCAPE_SEQUENCES') + if subst_dict is not None and escape_sequences is not None: + if not is_Dict(subst_dict) and is_Sequence(subst_dict): + subst_dict = dict(subst_dict) + else: + error_message = 'SUBST_DICT must be dict or sequence' + raise SCons.Errors.UserError(error_message) + escaped_subst_dict = {} + for key, value in subst_dict.items(): + escaped_value = value + for seq, escaped_seq in escape_sequences.items(): + escaped_value = escaped_value.replace(seq, escaped_seq) + escaped_subst_dict[key] = escaped_value + kwargs['SUBST_DICT'] = escaped_subst_dict + + target = env.Substfile(*args, **kwargs) + return target + AddMethod(Environment, InstallChmod) AddMethod(Environment, InstallExecutable) AddMethod(Environment, InstallReadOnly) AddMethod(Environment, InstallPyPackages) +AddMethod(Environment, SubstfileEscape) EnsurePythonVersion(2, 6) @@ -139,7 +162,6 @@ INCLUDE_DIR_DEFAULT = '$PREFIX/include' DOC_DIR_DEFAULT = '$PREFIX/doc/' LIB_DIR_DEFAULT = '$EXEC_PREFIX/lib' - PY_LIB_DIR_DEFAULT = '$EXEC_PREFIX/lib' # FIXME M. George Hansen 2011-05-12: Does sys.prefix include the # PythonX.Y part on Windows? python_prefix = sys.prefix @@ -149,13 +171,7 @@ PY_PACKAGES_DIR_DEFAULT = \ os.path.join(python_prefix, 'Lib', 'site-packages') PY_HEADERS_DIR_DEFAULT = os.path.join(python_prefix, 'include') - try: - PY_LIB_DIR_DEFAULT = os.environ['SYSTEMROOT'] - except KeyError: - PY_LIB_DIR_DEFAULT = '' - error_message = '%SYSTEMROOT% environmental variable is not set, ' \ - 'unable to determine path to Windows system folder' - raise SConfWarning(error_message) + PY_LIB_DIR_DEFAULT = os.path.join(python_prefix, 'libs') PY_LIB_NAME = 'python$PY_VERSION_MAJOR$PY_VERSION_MINOR' # Platform-independant variables: @@ -250,11 +266,10 @@ ), ) -platform_name = platform.system() python_version_tuple = platform.python_version_tuple() environment = Environment( - tools=['default', 'cc', 'textfile', 'packaging'], + tools=['default', 'textfile', 'packaging'], variables=variables, PROJECT_NAME='parpg', PROJECT_VERSION_MAJOR=0, @@ -277,7 +292,11 @@ EXECUTABLES=[], ) if environment['DEBUG']: - environment.AppendUnique(CCFLAGS=['-gdwarf-2', '-g3']) + if platform_name == 'Windows': + environment['CCPDBFLAGS'] = '/Z7 /Od' + environment.AppendUnique(LINKFLAGS='/DEBUG') + else: + environment.AppendUnique(CCFLAGS=['-gdwarf-2', '-g3']) Help(variables.GenerateHelpText(environment)) @@ -307,9 +326,12 @@ Glob('data/*'), ) -executable_source = environment.Substfile( +# FIXME M. George Hansen 2011-05-20: Do any other sequences need to be escaped +# in a C string? +executable_source = environment.SubstfileEscape( 'bin/parpg.c.in', SUBST_DICT=config_dict, + ESCAPE_SEQUENCES={'\\': r'\\\\', '"': r'\\"'}, ) build_executable = environment.Program( executable_source, @@ -317,11 +339,13 @@ LIBPATH='$PY_LIB_DIR', CPPPATH=['$PY_HEADERS_DIR'], ) -# FIXME M. George Hansen 2011-05-17: Should the executable target be hard-coded -# like this? +# Clean up any files created by the MSVC compiler on Windows. +if platform_name == 'Windows': + environment.Clean(build_executable, 'bin/parpg.ilk') + environment.Clean(build_executable, 'bin/parpg.pdb') install_executable = environment.InstallExecutable( '$BIN_DIR', - 'bin/parpg', + build_executable, ) # TODO M. George Hansen 2011-05-12: Implement package builder. diff -r 33684971cdb1 -r dd4ed4945411 bin/parpg.c.in --- a/bin/parpg.c.in Tue May 17 14:18:25 2011 -0700 +++ b/bin/parpg.c.in Sun May 22 00:53:59 2011 -0700 @@ -1,37 +1,36 @@ #include #include #include -#ifdef _MSC_VER +#ifdef _WIN32 # include #else # include # include #endif -#define CONCAT_ARRAYS(TYPE, ARRAY_A, SIZE_A, ARRAY_B, SIZE_B) \ - (TYPE*)concatArrays((const void**)(ARRAY_A), (SIZE_A), \ - (const void**)(ARRAY_B), (SIZE_B), sizeof(TYPE)) - -#define NULL_TERMINATE(TYPE, ARRAY, SIZE) \ - (TYPE*)realloc(ARRAY, sizeof(TYPE) * (SIZE + 1)) - #define ARRAY_LEN(ARRAY) (sizeof(ARRAY) / sizeof *(ARRAY)) +#define CONCAT_ARRAYS(TYPE, ARRAY_A, ARRAY_B) \ + (TYPE*)concatArrays((const void** const)(ARRAY_A), \ + ARRAY_LEN(ARRAY_A) * sizeof(TYPE), (const void** const)(ARRAY_B), \ + ARRAY_LEN(ARRAY_B) * sizeof(TYPE), sizeof(TYPE)) + void* -concatArrays(const void* arrayA, size_t sizeA, - const void* arrayB, size_t sizeB, size_t sizeType) { - char* concatenatedArray = malloc(sizeType * (sizeA + sizeB)); +concatArrays(const void* const arrayA, const size_t sizeA, + const void* const arrayB, const size_t sizeB, const size_t sizeType) { + char* const concatenatedArray = malloc(sizeType * (sizeA + sizeB)); memcpy(concatenatedArray, arrayA, sizeA * sizeType); memcpy(concatenatedArray + sizeA * sizeType, arrayB, sizeB * sizeType); return concatenatedArray; } char* -joinStr(char* strA, char* strB, char* separator) { - int lenA = strlen(strA); - int lenB = strlen(strB); - int lenSeparator = strlen(separator); - char* concatenatedStr = malloc(lenA + lenB + lenSeparator + 1); +joinStr(const char* const strA, const char* const strB, + const char* const separator) { + const int lenA = strlen(strA); + const int lenB = strlen(strB); + const int lenSeparator = strlen(separator); + char* const concatenatedStr = malloc(lenA + lenB + lenSeparator + 1); memcpy(concatenatedStr, strA, lenA); memcpy(concatenatedStr + lenA, separator, lenSeparator); memcpy(concatenatedStr + lenA + lenSeparator, strB, lenB + 1); @@ -39,38 +38,69 @@ } int -spawnParpgProcess(char* pythonExecutable, char* configDir, int argc, - char* argv[]) { - char* baseArgs[] = {pythonExecutable, "-m", "parpg.main", configDir}; - int nBaseArgs = ARRAY_LEN(baseArgs); - int nAdditionalArgs = argc - 1; - char* additionalArgs[nAdditionalArgs]; +spawnParpgProcess(char* const pythonExecutable, char* const configDir, + const int argc, char* const argv[]) { + const int sizeCharPtr = sizeof(char*); + const int nBaseArgs = 4; + char** const baseArgs = malloc(nBaseArgs * sizeCharPtr); + const int nAdditionalArgs = argc - 1; + char** const additionalArgs = + (char**)malloc(nAdditionalArgs * sizeCharPtr); + const int nArgs = nBaseArgs + nAdditionalArgs; + char* const oldPythonPath = getenv("PYTHONPATH"); + char* newPythonPath; + // Use calloc so that the last element is NULL. + char** const env = calloc(2, sizeCharPtr); + int exitStatus; + // Use calloc so that the last element is NULL. + char** const args = (char**)calloc(nArgs + 1, sizeCharPtr); int i; - for (i = 1; i < argc; i++) { - additionalArgs[i - 1] = argv[i]; + + baseArgs[0] = pythonExecutable; + baseArgs[1] = "-m"; + baseArgs[2] = "parpg.main"; + baseArgs[3] = configDir; + if (nAdditionalArgs > 0) { + for (i = 1; i < argc; i++) { + additionalArgs[i - 1] = argv[i]; + } + memcpy(args, CONCAT_ARRAYS(char*, baseArgs, additionalArgs), + nArgs * sizeCharPtr); + } else { + // Use memcpy so that the last NULL element in args is preserved. + memcpy(args, baseArgs, nArgs * sizeCharPtr); } - int nArgs = nBaseArgs + nAdditionalArgs; - char** args = NULL_TERMINATE(char*, CONCAT_ARRAYS(char*, baseArgs, - nBaseArgs, additionalArgs, nAdditionalArgs), nArgs); - char* oldPythonPath = getenv("PYTHONPATH"); - char* newPythonPath = joinStr(oldPythonPath, "@PY_PACKAGES_DIR@", ":"); - int exitStatus; - char* env[2] = {joinStr("PYTHONPATH=", newPythonPath, ""), (char*)(0)}; -#ifdef _MSC_VER + if (oldPythonPath != 0) { + newPythonPath = joinStr(oldPythonPath, "@PY_PACKAGES_DIR@", ":"); + } else { + newPythonPath = "@PY_PACKAGES_DIR@"; + } + env[0] = joinStr("PYTHONPATH=", newPythonPath, ""); +#ifdef _WIN32 +# ifdef _MSC_VER // MSVC deprecated POSIX spawn functions. + exitStatus = _spawnve(_P_WAIT, pythonExecutable, args, env); +# else exitStatus = spawnve(P_WAIT, pythonExecutable, args, env); +# endif #else exitStatus = execve(pythonExecutable, args, env); #endif + free(baseArgs); + free(additionalArgs); + free(env); + free(args); return exitStatus; } int main(int argc, char* argv[]) { - int exitStatus = spawnParpgProcess("@PYTHON@", "@SYS_CONF_DIR@", argc, - argv); - if (exitStatus) { - perror("parpg exited with error"); + const int exitStatus = spawnParpgProcess("@PYTHON@", "@SYS_CONF_DIR@", + argc, argv); + if (exitStatus < 0) { + perror("failed to execute subprocess"); + exit(EXIT_FAILURE); + } else { + exit(EXIT_SUCCESS); } - return exitStatus; }