From bf4dd0cbdeed816de93d83e3e27ef235589e2fd6 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 24 Apr 2014 18:02:13 -0700 Subject: [PATCH] Beast SCons tools: * Add VSProject SCons Builder * Add Protoc SCons Builder * Add Beast build utilities python module --- SConstruct | 97 ++- modules/beast_core/beast_core.unity.mm | 8 +- modules/beast_core/time/AtExitHook.cpp | 9 +- python/README | 10 - python/beast/__init__.py | 0 python/beast/env/AddCommonFlags.py | 11 - python/beast/env/AddUserEnv.py | 38 -- python/beast/env/Print.py | 41 -- python/beast/env/ReadEnvFile.py | 34 - python/beast/env/ReadEnvFile_test.py | 51 -- python/beast/env/Tags.py | 17 - python/beast/env/__init__.py | 0 python/beast/platform/GetEnvironment.py | 59 -- python/beast/platform/Platform.py | 27 - python/beast/platform/__init__.py | 0 python/beast/util/Boost.py | 61 -- python/beast/util/CleanCPPFiles.py | 77 --- python/beast/util/Dict.py | 17 - python/beast/util/Dict_test.py | 56 -- python/beast/util/Execute.py | 14 - python/beast/util/File.py | 42 -- python/beast/util/Git.py | 9 - python/beast/util/Iter.py | 7 - python/beast/util/String.py | 60 -- python/beast/util/String_test.py | 36 - python/beast/util/Terminal.py | 36 - python/beast/util/Tests.py | 31 - python/beast/util/__init__.py | 0 python/clean-python.py | 5 - python/run-tests.sh | 5 - site_scons/Beast.py | 180 +++++ site_scons/site_tools/Protoc.py | 97 +++ site_scons/site_tools/VSProject.py | 862 ++++++++++++++++++++++++ 33 files changed, 1245 insertions(+), 752 deletions(-) delete mode 100644 python/README delete mode 100644 python/beast/__init__.py delete mode 100644 python/beast/env/AddCommonFlags.py delete mode 100644 python/beast/env/AddUserEnv.py delete mode 100644 python/beast/env/Print.py delete mode 100644 python/beast/env/ReadEnvFile.py delete mode 100644 python/beast/env/ReadEnvFile_test.py delete mode 100644 python/beast/env/Tags.py delete mode 100644 python/beast/env/__init__.py delete mode 100644 python/beast/platform/GetEnvironment.py delete mode 100644 python/beast/platform/Platform.py delete mode 100644 python/beast/platform/__init__.py delete mode 100644 python/beast/util/Boost.py delete mode 100644 python/beast/util/CleanCPPFiles.py delete mode 100644 python/beast/util/Dict.py delete mode 100644 python/beast/util/Dict_test.py delete mode 100644 python/beast/util/Execute.py delete mode 100644 python/beast/util/File.py delete mode 100644 python/beast/util/Git.py delete mode 100644 python/beast/util/Iter.py delete mode 100644 python/beast/util/String.py delete mode 100644 python/beast/util/String_test.py delete mode 100644 python/beast/util/Terminal.py delete mode 100644 python/beast/util/Tests.py delete mode 100644 python/beast/util/__init__.py delete mode 100755 python/clean-python.py delete mode 100755 python/run-tests.sh create mode 100644 site_scons/Beast.py create mode 100644 site_scons/site_tools/Protoc.py create mode 100644 site_scons/site_tools/VSProject.py diff --git a/SConstruct b/SConstruct index 1513509394..80b5919836 100644 --- a/SConstruct +++ b/SConstruct @@ -1,7 +1,10 @@ from __future__ import absolute_import, division, print_function, unicode_literals import copy +import itertools +import ntpath import os +import random import sys def add_beast_to_path(): @@ -24,7 +27,7 @@ VARIANT_DIRECTORIES = { 'modules': ('bin', 'modules'), } -BOOST_LIBRARIES = 'boost_system', +BOOST_LIBRARIES = '' #boost_system' MAIN_PROGRAM_FILE = 'beast/unit_test/tests/main.cpp' DOTFILE = '~/.scons' @@ -43,6 +46,94 @@ def main(): for name, path in VARIANT_DIRECTORIES.items(): env.VariantDir(os.path.join(*path), name, duplicate=0) env.Replace(PRINT_CMD_LINE_FUNC=Print.print_cmd_line) - Tests.run_tests(env, MAIN_PROGRAM_FILE, '.', '.test.cpp') + #Tests.run_tests(env, MAIN_PROGRAM_FILE, '.', '.test.cpp') + +#main() + +#------------------------------------------------------------------------------- + +def is_unity(path): + b, e = os.path.splitext(path) + return os.path.splitext(b)[1] == '.unity' and e in ['.c', '.cpp'] + +def files(base): + for parent, _, files in os.walk(base): + for path in files: + path = os.path.join(parent, path) + yield os.path.normpath(path) + +#------------------------------------------------------------------------------- + +''' +/MP /GS /W3 /wd"4018" /wd"4244" /wd"4267" /Gy- /Zc:wchar_t +/I"D:\lib\OpenSSL-Win64\include" /I"D:\lib\boost_1_55_0" +/I"..\..\src\protobuf\src" /I"..\..\src\protobuf\vsprojects" +/I"..\..\src\leveldb" /I"..\..\src\leveldb\include" /I"..\..\build\proto" +/Zi /Gm- /Od /Fd"..\..\build\obj\VisualStudio2013\Debug.x64\vc120.pdb" +/fp:precise /D "_CRTDBG_MAP_ALLOC" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" +/D "_VARIADIC_MAX=10" /D "_WIN32_WINNT=0x0600" /D "_SCL_SECURE_NO_WARNINGS" +/D "_CRT_SECURE_NO_WARNINGS" /D "_MBCS" /errorReport:prompt /WX- /Zc:forScope +/RTC1 /GR /Gd /MTd /openmp- /Fa"..\..\build\obj\VisualStudio2013\Debug.x64\" +/EHa /nologo /Fo"..\..\build\obj\VisualStudio2013\Debug.x64\" +/Fp"..\..\build\obj\VisualStudio2013\Debug.x64\rippled.pch" +''' + +# Path to this SConstruct file +base_dir = Dir('#').srcnode().get_abspath() + +base_env = Environment( + tools = ['default', 'VSProject'], + CCCOMSTR = '', + CMDLINE_QUIET = 1, + CPPPATH = [ + os.environ['BOOST_ROOT'], + os.environ['OPENSSL_ROOT'] + ], + CPPDEFINES = [ + '_WIN32_WINNT=0x6000'] + ) + +#base_env.Replace(PRINT_CMD_LINE_FUNC=Print.print_cmd_line) + +env = base_env + +bin_dir = os.path.join(base_dir, 'bin') + +srcs = filter(is_unity, list(files('beast')) + list(files('modules'))) +for variant in ['Debug']: #, 'Release']: + for platform in ['Win32']: + #env = base_env.Clone() + #env.Replace(PRINT_CMD_LINE_FUNC=Print.print_cmd_line) + variant_dir = os.path.join(bin_dir, variant + '.' + platform) + env.VariantDir(os.path.join(variant_dir, 'beast'), 'beast', duplicate=0) + env.VariantDir(os.path.join(variant_dir, 'modules'), 'modules', duplicate=0) + env.Append(CCFLAGS=[ + '/EHsc', + '/bigobj', + '/Fd${TARGET}.pdb' + ]) + if variant == 'Debug': + env.Append(CCFLAGS=[ + '/MTd', + '/Od', + '/Zi' + ]) + else: + env.Append(CCFLAGS=[ + '/MT', + '/Ox' + ]) + variant_srcs = [os.path.join(variant_dir, os.path.relpath(f, base_dir)) for f in srcs] + + beast = env.StaticLibrary( + target = os.path.join(variant_dir, 'beast.lib'), + source = variant_srcs) + +env.VSProject ( + 'out', + buildtarget = beast, + source = filter(is_unity, list(files('beast')) + list(files('modules')))) + +env.Default ('out.vcxproj') +#env.Default (os.path.join(bin_dir,'Debug.Win32', 'beast.lib')) -main() diff --git a/modules/beast_core/beast_core.unity.mm b/modules/beast_core/beast_core.unity.mm index 037588b9c9..2875d31481 100644 --- a/modules/beast_core/beast_core.unity.mm +++ b/modules/beast_core/beast_core.unity.mm @@ -21,4 +21,10 @@ */ //============================================================================== -#include "beast_core.beast_core.unity.cpp" +#undef BEAST_COMPILE_OBJECTIVE_CPP +#define BEAST_COMPILE_OBJECTIVE_CPP 1 + +#include "beast_core.unity.cpp" + +#undef BEAST_COMPILE_OBJECTIVE_CPP +#define BEAST_COMPILE_OBJECTIVE_CPP 0 diff --git a/modules/beast_core/time/AtExitHook.cpp b/modules/beast_core/time/AtExitHook.cpp index 5ee6e68e15..de391c110f 100644 --- a/modules/beast_core/time/AtExitHook.cpp +++ b/modules/beast_core/time/AtExitHook.cpp @@ -30,7 +30,7 @@ public: : m_didStaticDestruction (false) { } - + static inline Manager& get () { return StaticObject ::get(); @@ -59,12 +59,13 @@ private: // Called at program exit when destructors for objects // with static storage duration are invoked. // - void doStaticDetruction () + void doStaticDestruction () { // In theory this shouldn't be needed (?) ScopedLockType lock (m_mutex); bassert (! m_didStaticDestruction); + m_didStaticDestruction = true; for (List ::iterator iter (m_list.begin()); iter != m_list.end();) { @@ -82,7 +83,7 @@ private: { ~StaticDestructor () { - Manager::get().doStaticDetruction(); + Manager::get().doStaticDestruction(); } }; @@ -100,7 +101,7 @@ private: // When it gets destroyed, we will call into the Manager to // call all of the AtExitHook items in the list. // -AtExitHook::Manager::StaticDestructor AtExitHook::Manager::s_staticDestructor; +AtExitHook::Manager::StaticDestructor AtExitHook::Manager::s_staticDestructor; //------------------------------------------------------------------------------ diff --git a/python/README b/python/README deleted file mode 100644 index 494b1cb07d..0000000000 --- a/python/README +++ /dev/null @@ -1,10 +0,0 @@ -Python code for the beast and scons. - -Scripts you can run from this directory. - -./run-tests.sh - Runs the unit tests. - -./clean-python.sh - If you remove or rename any Python files, you should run this script to - prevent old .pyc files from hiding bugs. \ No newline at end of file diff --git a/python/beast/__init__.py b/python/beast/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/python/beast/env/AddCommonFlags.py b/python/beast/env/AddCommonFlags.py deleted file mode 100644 index d541262a62..0000000000 --- a/python/beast/env/AddCommonFlags.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from beast.util import Boost -from beast.util import Git - -def add_common_flags(env): - git_flag = '-DTIP_BRANCH="%s"' % Git.describe() - env['CPPFLAGS'] = '%s %s' % (env['CPPFLAGS'], git_flag) - env['CPPPATH'].insert(0, Boost.CPPPATH) - env['LIBPATH'].append(Boost.LIBPATH) - env['BOOST_HOME'] = Boost.CPPPATH diff --git a/python/beast/env/AddUserEnv.py b/python/beast/env/AddUserEnv.py deleted file mode 100644 index 8d5056a708..0000000000 --- a/python/beast/env/AddUserEnv.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import os -import shlex - -from beast.env.ReadEnvFile import read_env_file -from beast.util.String import is_string -from beast.util.Terminal import warn - -_BAD_VARS_ERROR = """ -the following variables appearing in %s were not understood: - %s""" - -def add_user_env(env, dotfile, print=print): - df = os.path.expanduser(dotfile) - try: - with open(df, 'r') as f: - dotvars = read_env_file(f.read()) - except IOError: - if os.path.exists(df): - warn("Dotfile %s exists but can't be read." % dotfile, print) - dotvars = {} - - bad_names = [] - for name, value in dotvars.items(): - if name in env: - if is_string(env[name]): - env[name] = value - else: - env[name] = shlex.split(value) - else: - bad_names.append(name) - if bad_names: - error = _BAD_VARS_ERROR % (dotfile, '\n '.join(bad_names)) - warn(error, print) - - for name, default in env.items(): - env[name] = os.environ.get(name, default) diff --git a/python/beast/env/Print.py b/python/beast/env/Print.py deleted file mode 100644 index 163b73bfab..0000000000 --- a/python/beast/env/Print.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import textwrap - -from beast.util import String -from beast.util import Terminal - -FIELD_WIDTH = 10 -LINE_WIDTH = 69 - -EMPTY_NAME = ' ' * FIELD_WIDTH - -TEXT_WRAPPER = textwrap.TextWrapper( - break_long_words=False, - break_on_hyphens=False, - width=LINE_WIDTH, -) - -DISPLAY_EMPTY_ENVS = True - -def print_build_vars(name, value, same, print=print): - """Pretty-print values as a build configuration.""" - name = '%s' % name.rjust(FIELD_WIDTH) - color = Terminal.blue if same else Terminal.green - - for line in TEXT_WRAPPER.wrap(String.stringify(value, ' ')): - print(' '.join([name, color(line)])) - name = EMPTY_NAME - -def print_cmd_line(s, target, source, env): - print(EMPTY_NAME + Terminal.blue(String.stringify(target))) - -def print_build_config(env, original, print=print): - print('\nConfiguration:') - for name, value in env.items(): - if value or DISPLAY_EMPTY_ENVS: - same = (value == original[name]) - if not same: - print('"%s" != "%s"' % (value, original[name])) - print_build_vars(name, value, same, print=print) - print() diff --git a/python/beast/env/ReadEnvFile.py b/python/beast/env/ReadEnvFile.py deleted file mode 100644 index fce83e2172..0000000000 --- a/python/beast/env/ReadEnvFile.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import json -import re - -from beast.util import String -from beast.util.Terminal import warn - -ENV_LINE_MATCH = re.compile(r'(?: export \s+)? \s* ([^=\s]*) \s* = (.*)', - re.VERBOSE) - -def read_env_file(data, print=print): - try: - return json.loads(data) - except ValueError: - pass - - bad_lines = [] - results = {} - for number, raw_line in enumerate(data.splitlines()): - line = String.remove_comment(raw_line).strip() - if line: - match = ENV_LINE_MATCH.match(line) - if match: - name, value = match.groups() - results[name.strip()] = String.remove_quotes(value.strip()) - else: - bad_lines.append([number, raw_line]) - if bad_lines: - warn("Didn't understand the following environment file lines:", print) - for number, line in bad_lines: - print('%d. >>> %s' % (number + 1, line)) - - return results diff --git a/python/beast/env/ReadEnvFile_test.py b/python/beast/env/ReadEnvFile_test.py deleted file mode 100644 index 7c419b5717..0000000000 --- a/python/beast/env/ReadEnvFile_test.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from unittest import TestCase -from beast.env.ReadEnvFile import read_env_file - -from beast.util import Terminal -Terminal.CAN_CHANGE_COLOR = False - -JSON = """ -{ - "FOO": "foo", - "BAR": "bar bar bar", - "CPPFLAGS": "-std=c++11 -frtti -fno-strict-aliasing -DWOMBAT" -}""" - -ENV = """ -# An env file. - -FOO=foo -export BAR="bar bar bar" -CPPFLAGS=-std=c++11 -frtti -fno-strict-aliasing -DWOMBAT - -# export BAZ=baz should be ignored. - -""" - -RESULT = { - 'FOO': 'foo', - 'BAR': 'bar bar bar', - 'CPPFLAGS': '-std=c++11 -frtti -fno-strict-aliasing -DWOMBAT', - } - -BAD_ENV = ENV + """ -This line isn't right. -NO SPACES IN NAMES="valid value" -""" - -class test_ReadEnvFile(TestCase): - def test_read_json(self): - self.assertEqual(read_env_file(JSON), RESULT) - - def test_read_env(self): - self.assertEqual(read_env_file(ENV), RESULT) - - def test_read_env_error(self): - errors = [] - self.assertEqual(read_env_file(BAD_ENV, errors.append), RESULT) - self.assertEqual(errors, [ - "WARNING: Didn't understand the following environment file lines:", - "11. >>> This line isn't right.", - '12. >>> NO SPACES IN NAMES="valid value"']) diff --git a/python/beast/env/Tags.py b/python/beast/env/Tags.py deleted file mode 100644 index 6bd0861e75..0000000000 --- a/python/beast/env/Tags.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from beast.util.Terminal import warn - -_TAGS = frozenset(['debug', 'optimize']) - -def _to_tag(name, value): - return '%s%s' % ('' if value else 'no', name) - -def get_tags(arguments, print=print): - result = {} - bad_tags = set(arguments) - _TAGS - if bad_tags: - warn("don't understand tags " + ' '.join(bad_tags), print=print) - debug = result.get('debug', True) - optimize = result.get('optimize', not debug) - return _to_tag('debug', debug), _to_tag('optimize', optimize) diff --git a/python/beast/env/__init__.py b/python/beast/env/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/python/beast/platform/GetEnvironment.py b/python/beast/platform/GetEnvironment.py deleted file mode 100644 index 89b14f1964..0000000000 --- a/python/beast/platform/GetEnvironment.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from beast.platform import Platform -from beast.util import Dict -from beast.env import Tags - -_DEFAULTS = { - '': { - 'BOOST_HOME': None, - 'CC': 'gcc', - 'CCFLAGS': None, - 'CFLAGS': None, - 'CXX': 'g++', - 'CPPFLAGS': '-std=c++11 -frtti -fno-strict-aliasing', - 'CPPPATH': [], - 'LIBPATH': [], - 'LIBS': [], - 'LINKFLAGS': '', - }, - - 'Darwin': { - 'CC': 'clang', - 'CXX': 'clang++', - 'CPPFLAGS': '-x c++ -stdlib=libc++ -std=c++11 -frtti', - 'LINKFLAGS': '-stdlib=libc++', - }, - - 'FreeBSD': { - 'CC': 'gcc46', - 'CXX': 'g++46', - 'CCFLAGS': '-Wl,-rpath=/usr/local/lib/gcc46', - 'LINKFLAGS': '-Wl,-rpath=/usr/local/lib/gcc46', - 'LIBS': ['kvm'], - }, - - # TODO: specific flags for Windows, Linux platforms. -} - -TAGS = { - 'debug': { - 'CPPFLAGS': '-g -DDEBUG' - }, - - 'optimize': { - 'CPPFLAGS': '-O3', - }, - - 'nooptimize': { - 'CPPFLAGS': '-O0', - } - } - -def get_environment(arguments): - tags = Tags.get_tags(arguments) - env = Dict.compose_prefix_dicts(Platform.PLATFORM, _DEFAULTS) - for tag in tags or []: - for k, v in TAGS.get(tag, {}).items(): - env[k] = '%s %s' % (env[k], v) - return env diff --git a/python/beast/platform/Platform.py b/python/beast/platform/Platform.py deleted file mode 100644 index 1b16e9f6ac..0000000000 --- a/python/beast/platform/Platform.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import platform - -def _get_platform_string(): - system = platform.system() - parts = [system] - linux = system == 'Linux' - if linux: - flavor, version, _ = platform.linux_distribution() - # Arch still has issues with the platform module - parts[0] = flavor.capitalize() or 'Archlinux' - parts.extend(version.split('.')) - elif system == 'Darwin': - ten, major, minor = platform.mac_ver()[0].split('.') - parts.extend([ten, major, minor]) - elif system == 'Windows': - release, version, csd, ptype = platform.win32_ver() - parts.extend([release, version, csd, ptype]) - elif system == 'FreeBSD': - # No other variables to pass with FreeBSD that Python provides and I could find - pass - else: - raise Exception("Don't understand how to build for platform " + system) - return '.'.join(parts), linux - -PLATFORM, IS_LINUX = _get_platform_string() diff --git a/python/beast/platform/__init__.py b/python/beast/platform/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/python/beast/util/Boost.py b/python/beast/util/Boost.py deleted file mode 100644 index d7faf17af7..0000000000 --- a/python/beast/util/Boost.py +++ /dev/null @@ -1,61 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import os -import re - -ROOT_ENV_VARIABLE = 'BOOST_ROOT' -MINIMUM_VERSION = 1, 55, 0 -VERSION_FILE = 'boost', 'version.hpp' -LIBRARY_PATH_SEGMENT = 'stage', 'lib' -VERSION_MATCHER = re.compile(r'#define\s+BOOST_VERSION\s+(\d+)') - -CANT_OPEN_VERSION_FILE_ERROR = """Unable to open boost version file %s. -You have set the environment variable BOOST_ROOT to be %s. -Please check to make sure that this points to a valid installation of boost.""" - -CANT_UNDERSTAND_VERSION_ERROR = ( - "Didn't understand version string '%s' from file %s'") -VERSION_TOO_OLD_ERROR = ('Your version of boost, %s, is older than the minimum ' - 'required, %s.') - -def _text(major, minor, release): - return '%d.%02d.%02d' % (major, minor, release) - -def _raw_boost_path(): - try: - path = os.environ[ROOT_ENV_VARIABLE] - if path: - return os.path.normpath(path) - except KeyError: - pass - raise KeyError('%s environment variable is not set.' % ROOT_ENV_VARIABLE) - -def _get_version_number(path): - version_file = os.path.join(path, *VERSION_FILE) - try: - with open(version_file) as f: - for line in f: - match = VERSION_MATCHER.match(line) - if match: - version = match.group(1) - try: - return int(version) - except ValueError: - raise Exception(CANT_UNDERSTAND_VERSION_ERROR % - (version, version_file)) - except IOError: - raise Exception(CANT_OPEN_VERSION_FILE_ERROR % (version_file, path)) - -def _validate_version(v): - version = v // 100000, (v // 100) % 100, v % 100 - if version < MINIMUM_VERSION: - raise Exception(VERSION_TOO_OLD_ERROR % ( - _text(*version), _text(*MINIMUM_VERSION))) - -def _boost_path(): - path = _raw_boost_path() - _validate_version(_get_version_number(path)) - return path - -CPPPATH = _boost_path() -LIBPATH = os.path.join(CPPPATH, *LIBRARY_PATH_SEGMENT) diff --git a/python/beast/util/CleanCPPFiles.py b/python/beast/util/CleanCPPFiles.py deleted file mode 100644 index de11b02346..0000000000 --- a/python/beast/util/CleanCPPFiles.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -#------------------------------------------------------------------------------ -# -# This file is part of Beast: https://github.com/vinniefalco/Beast -# Copyright 2014, Tom Ritchford -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -#------------------------------------------------------------------------------ - -""" -Cleans C++ files according to a consistent standard. - -Trims trailing whitespace; converts files encoded in Latin-1 to UTF-8. - -""" - -import os -import sys -import tempfile - -def process_file(filename, process): - with open(filename) as infile: - lines = infile.read().strip().splitlines() - outfd, outname = tempfile.mkstemp(dir=os.path.dirname(filename)) - with os.fdopen(outfd, 'w') as outfile: - for number, line in enumerate(lines): - try: - result = process(line, filename, number).encode('utf-8') - outfile.write(result) - except Exception as e: - if True: raise - raise Exception('%s on line %d in file %s' % - (e.message, number + 1, filename)) - os.rename(outname, filename) - -def walk_and_process(root, process, condition, message=None): - for root, dirs, files in os.walk(root): - for f in files: - filename = os.path.join(root, f) - if condition(filename): - if message: - print(message % filename) - process_file(filename, process) - -def clean_line_endings(root, endings=None, message=None): - if endings == None: - endings = '.h', '.cpp', '.sh' - def condition(filename): - return os.path.splitext(filename)[1] in endings - - def process(line, filename, number): - try: - return line.decode('utf-8').rstrip() + '\n' - except: - result = line.decode('latin-1').rstrip() + '\n' - print('Found a non UTF-8 line at %s:%d' % (filename, number + 1)) - return result - - walk_and_process(root, process, condition, message) - -if __name__ == "__main__": - path = '.' - if len(sys.argv) > 1: - path = sys.argv[1] - clean_line_endings(os.path.abspath(path)) #, message='processing %s') diff --git a/python/beast/util/Dict.py b/python/beast/util/Dict.py deleted file mode 100644 index 67ee13369c..0000000000 --- a/python/beast/util/Dict.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -def compose(*dicts): - result = {} - for d in dicts: - result.update(**d) - return result - -def get_items_with_prefix(key, mapping): - """Get all elements from the mapping whose keys are a prefix of the given - key, sorted by increasing key length.""" - for k, v in sorted(mapping.items()): - if key.startswith(k): - yield v - -def compose_prefix_dicts(key, mapping): - return compose(*get_items_with_prefix(key, mapping)) diff --git a/python/beast/util/Dict_test.py b/python/beast/util/Dict_test.py deleted file mode 100644 index 690921e229..0000000000 --- a/python/beast/util/Dict_test.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from unittest import TestCase - -from beast.util import Dict - -DICT = { - '': { - 'foo': 'foo-default', - 'bar': 'bar-default', - }, - - 'Darwin': { - 'foo': 'foo-darwin', - 'baz': 'baz-darwin', - }, - - 'Darwin.10.8': { - 'foo': 'foo-darwin-10.8', - 'bing': 'bing-darwin-10.8', - }, - } - -class test_Dict(TestCase): - def computeMapValue(self, config, key): - return Dict.compose(*Dict.get_items_with_prefix(config, DICT))[key] - - def assertMapValue(self, config, key, result): - self.assertEquals(self.computeMapValue(config, key), result) - - def testDefault1(self): - self.assertMapValue('', 'foo', 'foo-default') - - def testDefault2(self): - self.assertMapValue('Darwin.10.8', 'bar', 'bar-default') - - def testPrefix1(self): - self.assertMapValue('Darwin', 'foo', 'foo-darwin') - - def testPrefix2(self): - self.assertMapValue('Darwin.10.8', 'foo', 'foo-darwin-10.8') - - def testPrefix3(self): - self.assertMapValue('Darwin', 'baz', 'baz-darwin') - - def testPrefix4(self): - self.assertMapValue('Darwin.10.8', 'bing', 'bing-darwin-10.8') - - def testFailure1(self): - self.assertRaises(KeyError, self.computeMapValue, '', 'baz') - - def testFailure2(self): - self.assertRaises(KeyError, self.computeMapValue, '', 'bing') - - def testFailure2(self): - self.assertRaises(KeyError, self.computeMapValue, 'Darwin', 'bing') diff --git a/python/beast/util/Execute.py b/python/beast/util/Execute.py deleted file mode 100644 index 65c6adfd1b..0000000000 --- a/python/beast/util/Execute.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import subprocess - -from beast.util import String - -def execute(args, include_errors=True, **kwds): - """Execute a shell command and return the value. If args is a string, - it's split on spaces - if some of your arguments contain spaces, args should - instead be a list of arguments.""" - if String.is_string(args): - args = args.split() - stderr = subprocess.STDOUT if include_errors else None - return subprocess.check_output(args, stderr=stderr, **kwds) diff --git a/python/beast/util/File.py b/python/beast/util/File.py deleted file mode 100644 index b8c99f54b0..0000000000 --- a/python/beast/util/File.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from beast.util import String - -import os - -LIBRARY_PATTERNS = 'lib%s.a', 'lib%s.dylib' - -def first_fields_after_prefix(filename, prefix): - with open(filename, 'r') as f: - return String.first_fields_after_prefix(prefix, f) - -def find_files_with_suffix(base, suffix): - for parent, _, files in os.walk(base): - for path in files: - path = os.path.join(parent, path) - if path.endswith(suffix): - yield os.path.normpath(path) - -def child_files(parent, files): - return [os.path.normpath(os.path.join(parent, f)) for f in files] - -def sibling_files(path, files): - return child_files(os.path.dirname(path), files) - -def replace_extension(file, ext): - return os.path.splitext(file)[0] + ext - -def validate_libraries(path, libraries): - bad = [] - for lib in libraries: - found = False - for pat in LIBRARY_PATTERNS: - libfile = os.path.join(path, pat % lib) - if os.path.isfile(libfile): - found = True - break - if not found: - bad.append(libfile) - if bad: - libs = 'library' if len(bad) == 1 else 'libraries' - raise Exception('Missing %s: %s' % (libs, ', '.join(bad))) diff --git a/python/beast/util/Git.py b/python/beast/util/Git.py deleted file mode 100644 index 31cecad47a..0000000000 --- a/python/beast/util/Git.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import os - -from beast.util import Execute -from beast.util import String - -def describe(**kwds): - return String.single_line(Execute.execute('git describe --tags', **kwds)) diff --git a/python/beast/util/Iter.py b/python/beast/util/Iter.py deleted file mode 100644 index 976165cb53..0000000000 --- a/python/beast/util/Iter.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -def first(condition, sequence): - for i in sequence: - result = condition(i) - if result: - return result diff --git a/python/beast/util/String.py b/python/beast/util/String.py deleted file mode 100644 index 63d89084fa..0000000000 --- a/python/beast/util/String.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import functools - -from beast.util import Iter -from beast.util.Terminal import warn - -def is_string(s): - """Is s a string? - in either Python 2.x or 3.x.""" - return isinstance(s, (str, unicode)) - -def stringify(item, joiner=''): - """If item is not a string, stringify its members and join them.""" - try: - len(item) - except: - return str(item) - if not item or is_string(item): - return item or '' - else: - return joiner.join(str(i) for i in item) - -def single_line(line, report_errors=True, joiner='+'): - """Force a string to be a single line with no carriage returns, and report - a warning if there was more than one line.""" - lines = line.strip().splitlines() - if report_errors and len(lines) > 1: - print('multiline result:', lines) - return joiner.join(lines) - -# Copied from -# https://github.com/lerugray/pickett/blob/master/pickett/ParseScript.py -def remove_comment(line): - """Remove trailing comments from one line.""" - start = 0 - while True: - loc = line.find('#', start) - if loc == -1: - return line.replace('\\#', '#') - elif not (loc and line[loc - 1] == '\\'): - return line[:loc].replace('\\#', '#') - start = loc + 1 - -def remove_quotes(line, quote='"', print=print): - if not line.startswith(quote): - return line - if line.endswith(quote): - return line[1:-1] - - warn('line started with %s but didn\'t end with one:' % quote, print) - print(line) - return line[1:] - -def fields_after_prefix(prefix, line): - line = line.strip() - return line.startswith(prefix) and line[len(prefix):].split() - -def first_fields_after_prefix(prefix, sequence): - condition = functools.partial(fields_after_prefix, prefix) - return Iter.first(condition, sequence) or [] diff --git a/python/beast/util/String_test.py b/python/beast/util/String_test.py deleted file mode 100644 index 9e88b37be0..0000000000 --- a/python/beast/util/String_test.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from unittest import TestCase - -from beast.util import String -from beast.util import Terminal - -Terminal.CAN_CHANGE_COLOR = False - -class String_test(TestCase): - def test_comments(self): - self.assertEqual(String.remove_comment(''), '') - self.assertEqual(String.remove_comment('#'), '') - self.assertEqual(String.remove_comment('# a comment'), '') - self.assertEqual(String.remove_comment('hello # a comment'), 'hello ') - self.assertEqual(String.remove_comment( - r'hello \# not a comment # a comment'), - 'hello # not a comment ') - - def test_remove_quotes(self): - errors = [] - self.assertEqual(String.remove_quotes('hello', print=errors.append), - 'hello') - self.assertEqual(String.remove_quotes('"hello"', print=errors.append), - 'hello') - self.assertEqual(String.remove_quotes('hello"', print=errors.append), - 'hello"') - self.assertEqual(errors, []) - - def test_remove_quotes_error(self): - errors = [] - self.assertEqual(String.remove_quotes('"hello', print=errors.append), - 'hello') - self.assertEqual(errors, - ['WARNING: line started with " but didn\'t end with one:', - '"hello']) diff --git a/python/beast/util/Terminal.py b/python/beast/util/Terminal.py deleted file mode 100644 index d5b33c0894..0000000000 --- a/python/beast/util/Terminal.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import sys - -from beast.platform.Platform import PLATFORM - -# See https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python -CAN_CHANGE_COLOR = ( - hasattr(sys.stderr, "isatty") - and sys.stderr.isatty() - and not PLATFORM.startswith('Windows')) - - -# See https://en.wikipedia.org/wiki/ANSI_escape_code -RED = 91 -GREEN = 92 -BLUE = 94 - -def add_mode(text, *modes): - if CAN_CHANGE_COLOR: - modes = ';'.join(str(m) for m in modes) - return '\033[%sm%s\033[0m' % (modes, text) - else: - return text - -def blue(text): - return add_mode(text, BLUE) - -def green(text): - return add_mode(text, GREEN) - -def red(text): - return add_mode(text, RED) - -def warn(text, print=print): - print('%s %s' % (red('WARNING:'), text)) diff --git a/python/beast/util/Tests.py b/python/beast/util/Tests.py deleted file mode 100644 index 2ad1986ae9..0000000000 --- a/python/beast/util/Tests.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import os - -from beast.util import File - -LIBS_PREFIX = '// LIBS:' -MODS_PREFIX = '// MODULES:' - -def build_executable(env, path, main_program_file): - """Build a stand alone executable that runs - all the test suites in one source file.""" - libs = File.first_fields_after_prefix(path, LIBS_PREFIX) - source_modules = File.first_fields_after_prefix(path, MODS_PREFIX) - source_modules = File.sibling_files(path, source_modules) - - bin = os.path.basename(os.path.splitext(path)[0]) - bin = os.path.join('bin', bin) - - # All paths get normalized here, so we can use posix - # forward slashes for everything including on Windows - srcs = File.child_files('bin', [main_program_file, path] + source_modules) - objs = [File.replace_extension(f, '.o') for f in srcs] - if libs: - env.Append(LIBS=libs) # DANGER: will append the file over and over. - env.Program(bin, srcs) - -def run_tests(env, main_program_file, root, suffix): - root = os.path.normpath(root) - for path in File.find_files_with_suffix(root, suffix): - build_executable(env, path, main_program_file) diff --git a/python/beast/util/__init__.py b/python/beast/util/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/python/clean-python.py b/python/clean-python.py deleted file mode 100755 index 1a18804354..0000000000 --- a/python/clean-python.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# Remove all the compiled .pyc files at or below this directory. - -find . -name \*.pyc | xargs rm diff --git a/python/run-tests.sh b/python/run-tests.sh deleted file mode 100755 index 0b181ca7ab..0000000000 --- a/python/run-tests.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# Run all the beast Python unit tests. - -python -m unittest discover -p \*_test.py diff --git a/site_scons/Beast.py b/site_scons/Beast.py new file mode 100644 index 0000000000..3a4ba9d004 --- /dev/null +++ b/site_scons/Beast.py @@ -0,0 +1,180 @@ +# Beast.py +# Copyright 2014 by: +# Vinnie Falco +# Tom Ritchford +# Nik Bougalis +# This file is part of Beast: http://github.com/vinniefalco/Beast + +from __future__ import absolute_import, division, print_function, unicode_literals + +import os +import platform +import subprocess +import sys +import SCons.Node +import SCons.Util + +#------------------------------------------------------------------------------- +# +# Environment +# +#------------------------------------------------------------------------------- + +def _execute(args, include_errors=True, **kwds): + """Execute a shell command and return the value. If args is a string, + it's split on spaces - if some of your arguments contain spaces, args should + instead be a list of arguments.""" + def single_line(line, report_errors=True, joiner='+'): + """Force a string to be a single line with no carriage returns, and report + a warning if there was more than one line.""" + lines = line.strip().splitlines() + if report_errors and len(lines) > 1: + print('multiline result:', lines) + return joiner.join(lines) + def is_string(s): + """Is s a string? - in either Python 2.x or 3.x.""" + return isinstance(s, (str, unicode)) + if is_string(args): + args = args.split() + stderr = subprocess.STDOUT if include_errors else None + return single_line(subprocess.check_output(args, stderr=stderr, **kwds)) + +class __System(object): + """Provides information about the host platform""" + def __init__(self): + self.name = platform.system() + self.linux = self.name == 'Linux' + self.osx = self.name == 'Darwin' + self.windows = self.name == 'Windows' + self.distro = None + self.version = None + + # True if building under the Travis CI (http://travis-ci.org) + self.travis = ( + os.environ.get('TRAVIS', '0') == 'true') and ( + os.environ.get('CI', '0') == 'true') + + if self.linux: + self.distro, self.version, _ = platform.linux_distribution() + self.__display = '%s %s (%s)' % (self.distro, self.version, self.name) + + elif self.osx: + ten, major, minor = platform.mac_ver()[0].split('.') + self.__display = '%s %s.%s.%s' % (self.name, ten, major, minor) + + elif self.windows: + release, version, csd, ptype = platform.win32_ver() + self.__display = '%s %s %s (%s)' % (self.name, release, version, ptype) + + else: + raise Exception('Unknown system platform "' + self.name + '"') + + def __str__(self): + return self.__display + +class Git(object): + """Provides information about git and the repository we are called from""" + def __init__(self, env): + self.exists = env.Detect('git') + if self.exists: + self.commit_id = _execute('git describe --tags') + else: + self.commit_id = None + +system = __System() + +#------------------------------------------------------------------------------- + +def printChildren(target): + def doPrint(tgt, level, found): + for item in tgt: + if SCons.Util.is_List(item): + doPrint(item, level, found) + else: + if item.abspath in found: + continue + found[item.abspath] = False + print('\t'*level + item.path) + #DoPrint(item.children(scan=1), level+1, found) + item.scan() + doPrint(item.all_children(), level+1, found) + doPrint(target, 0, {}) + +def variantFile(path, variant_dirs): + '''Returns the path to the corresponding dict entry in variant_dirs''' + path = str(path) + for dest, source in variant_dirs.iteritems(): + common = os.path.commonprefix([path, source]) + if common == source: + return os.path.join(dest, path[len(common)+1:]) + return path + +def variantFiles(files, variant_dirs): + '''Returns a list of files remapped to their variant directories''' + result = [] + for path in files: + result.append(variantFile(path, variant_dirs)) + return result + +def printEnv(env, keys): + if type(keys) != list: + keys = list(keys) + s = '' + for key in keys: + if key in env: + value = env[key] + else: + value = '' + s+=('%s=%s, ' % (key, value)) + print('[' + s + ']') + +#------------------------------------------------------------------------------- +# +# Output +# +#------------------------------------------------------------------------------- + +# See https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python +CAN_CHANGE_COLOR = ( + hasattr(sys.stderr, "isatty") + and sys.stderr.isatty() + and not system.windows + and not os.environ.get('INSIDE_EMACS') + ) + +# See https://en.wikipedia.org/wiki/ANSI_escape_code +BLUE = 94 +GREEN = 92 +RED = 91 +YELLOW = 93 + +def add_mode(text, *modes): + if CAN_CHANGE_COLOR: + modes = ';'.join(str(m) for m in modes) + return '\033[%sm%s\033[0m' % (modes, text) + else: + return text + +def blue(text): + return add_mode(text, BLUE) + +def green(text): + return add_mode(text, GREEN) + +def red(text): + return add_mode(text, RED) + +def yellow(text): + return add_mode(text, YELLOW) + +def warn(text, print=print): + print('%s %s' % (red('WARNING:'), text)) + +# Prints command lines using environment substitutions +def print_coms(coms, env): + if type(coms) is str: + coms=list(coms) + for key in coms: + cmdline = env.subst(env[key], 0, + env.File(''), env.File('')) + print (green(cmdline)) diff --git a/site_scons/site_tools/Protoc.py b/site_scons/site_tools/Protoc.py new file mode 100644 index 0000000000..32d77f3a4c --- /dev/null +++ b/site_scons/site_tools/Protoc.py @@ -0,0 +1,97 @@ +# +# Copyright (c) 2009 Scott Stafford +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +# Author : Scott Stafford +# Date : 2009-12-09 20:36:14 +# +# Changes : Vinnie Falco +# Date : 2014--4-25 + +""" +Protoc.py: Protoc Builder for SCons + +This Builder invokes protoc to generate C++ and Python from a .proto file. + +NOTE: Java is not currently supported. +""" + +__author__ = "Scott Stafford" + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Node.FS +import SCons.Util + +from SCons.Script import File, Dir + +import os.path + +protocs = 'protoc' + +ProtocAction = SCons.Action.Action('$PROTOCCOM', '$PROTOCCOMSTR') + +def ProtocEmitter(target, source, env): + PROTOCOUTDIR = env['PROTOCOUTDIR'] + PROTOCPYTHONOUTDIR = env['PROTOCPYTHONOUTDIR'] + for source_path in [str(x) for x in source]: + base = os.path.splitext(os.path.basename(source_path))[0] + if PROTOCOUTDIR: + target.extend([os.path.join(PROTOCOUTDIR, base + '.pb.cc'), + os.path.join(PROTOCOUTDIR, base + '.pb.h')]) + if PROTOCPYTHONOUTDIR: + target.append(os.path.join(PROTOCPYTHONOUTDIR, base + '_pb2.py')) + + try: + target.append(env['PROTOCFDSOUT']) + except KeyError: + pass + + #print "PROTOC SOURCE:", [str(s) for s in source] + #print "PROTOC TARGET:", [str(s) for s in target] + + return target, source + +ProtocBuilder = SCons.Builder.Builder( + action = ProtocAction, + emitter = ProtocEmitter, + srcsuffix = '$PROTOCSRCSUFFIX') + +def generate(env): + """Add Builders and construction variables for protoc to an Environment.""" + try: + bld = env['BUILDERS']['Protoc'] + except KeyError: + bld = ProtocBuilder + env['BUILDERS']['Protoc'] = bld + + env['PROTOC'] = env.Detect(protocs) or 'protoc' + env['PROTOCFLAGS'] = SCons.Util.CLVar('') + env['PROTOCPROTOPATH'] = SCons.Util.CLVar('') + env['PROTOCCOM'] = '$PROTOC ${["-I%s"%x for x in PROTOCPROTOPATH]} $PROTOCFLAGS --cpp_out=$PROTOCCPPOUTFLAGS$PROTOCOUTDIR ${PROTOCPYTHONOUTDIR and ("--python_out="+PROTOCPYTHONOUTDIR) or ""} ${PROTOCFDSOUT and ("-o"+PROTOCFDSOUT) or ""} ${SOURCES}' + env['PROTOCOUTDIR'] = '${SOURCE.dir}' + env['PROTOCPYTHONOUTDIR'] = "python" + env['PROTOCSRCSUFFIX'] = '.proto' + +def exists(env): + return env.Detect(protocs) \ No newline at end of file diff --git a/site_scons/site_tools/VSProject.py b/site_scons/site_tools/VSProject.py new file mode 100644 index 0000000000..9753ae4f46 --- /dev/null +++ b/site_scons/site_tools/VSProject.py @@ -0,0 +1,862 @@ +# Copyright 2014 Vinnie Falco (vinnie.falco@gmail.com) +# Portions Copyright The SCons Foundation +# Portions Copyright Google, Inc. +# This file is part of beast + +""" +A SCons tool to provide a family of scons builders that +generate Visual Studio project files +""" + +import collections +import hashlib +import io +import itertools +import ntpath +import os +import pprint +import random +import re + +import SCons.Builder +import SCons.Node.FS +import SCons.Node +import SCons.Script.Main +import SCons.Util + +import sys + +#------------------------------------------------------------------------------- + +# Adapted from msvs.py + +UnicodeByteMarker = '\xEF\xBB\xBF' + +V12DSPHeader = """\ +\r +\r +""" + +V12DSPProjectConfiguration = """\ + \r + %(variant)s\r + %(platform)s\r + \r +""" + +V12DSPGlobals = """\ + \r + %(project_guid)s\r + Win32Proj\r + %(name)s\r + true\r + \r +""" + +V12DSPPropertyGroup = """\ + \r + MultiByte\r + Application\r + v120\r + False\r + %(use_debug_libs)s\r + False\r + false\r + %(int_dir)s\r + %(out_dir)s\r + \r +""" + +V12DSPImportGroup= """\ + \r + \r + \r +""" + +V12DSPItemDefinitionGroup= """\ + \r +""" + +V12CustomBuildProtoc= """\ + Document\r + protoc --cpp_out=%(cpp_out)s --proto_path=%%(RelativeDir) %%(Identity)\r + %(base_out)s.pb.h;%(base_out)s.pb.cc.\r + protoc --cpp_out=%(cpp_out)s --proto_path=%%(RelativeDir) %%(Identity)\r + false\r +""" + +V12DSPFiltersHeader = ( +'''\r +\r +''') + +#------------------------------------------------------------------------------- + +def is_subdir(child, parent): + '''Determine if child is a subdirectory of parent''' + return os.path.commonprefix([parent, child]) == parent + +def itemList(items, sep): + if type(items) == str: # Won't work in Python 3. + return items + def gen(): + for item in sorted(items): + if type(item) == dict: + for k, v in item.items(): + yield k + '=' + v + else: + yield item + yield sep + return ''.join(gen()) + +#------------------------------------------------------------------------------- + +class SwitchConverter(object): + '''Converts command line switches to MSBuild XML, using tables''' + + def __init__(self, table, booltable): + self.table = {} + for key in table: + self.table[key] = table[key] + for key in booltable: + value = booltable[key] + self.table[key] = [value[0], 'True'] + self.table[key + '-'] = [value[0], 'False'] + + def getXml(self, switches, prefix = ''): + if type(switches) != list: + switches = list(switches) + xml = [] + unknown = [] + for switch in switches: + try: + value = self.table[switch] + xml.append ( + '%s<%s>%s\r\n' % ( + prefix, value[0], value[1], value[0])) + except: + unknown.append(switch) + if unknown: + s = itemList(unknown, ' ') + tag = 'AdditionalOptions' + xml.append('%(prefix)s<%(tag)s>%(s)s%%(%(tag)s)\r\n' % locals()) + if xml: + xml.sort() + return ''.join(xml) + return '' + +class ClSwitchConverter(SwitchConverter): + def __init__(self): + booltable = { + '/C' : ['KeepComments'], + '/doc' : ['GenerateXMLDocumentationFiles'], + '/FAu' : ['UseUnicodeForAssemblerListing'], + '/FC' : ['UseFullPaths'], + '/FR' : ['BrowseInformation'], + '/Fr' : ['BrowseInformation'], + '/Fx' : ['ExpandAttributedSource'], + '/GF' : ['StringPooling'], + '/GL' : ['WholeProgramOptimization'], + '/Gm' : ['MinimalRebuild'], + '/GR' : ['RuntimeTypeInfo'], + '/GS' : ['BufferSecurityCheck'], + '/GT' : ['EnableFiberSafeOptimizations'], + '/Gy' : ['FunctionLevelLinking'], + '/MP' : ['MultiProcessorCompilation'], + '/Oi' : ['IntrinsicFunctions'], + '/Oy' : ['OmitFramePointers'], + '/RTCc' : ['SmallerTypeCheck'], + '/u' : ['UndefineAllPreprocessorDefinitions'], + '/X' : ['IgnoreStandardIncludePath'], + '/WX' : ['TreatWarningAsError'], + '/Za' : ['DisableLanguageExtensions'], + '/Zl' : ['OmitDefaultLibName'], + '/fp:except' : ['FloatingPointExceptions'], + '/hotpatch' : ['CreateHotpatchableImage'], + '/nologo' : ['SuppressStartupBanner'], + '/openmp' : ['OpenMPSupport'], + '/showIncludes' : ['ShowIncludes'], + '/Zc:forScope' : ['ForceConformanceInForLoopScope'], + '/Zc:wchar_t' : ['TreatWChar_tAsBuiltInType'], + } + table = { + '/EHsc' : ['ExceptionHandling', 'Sync'], + '/EHa' : ['ExceptionHandling', 'Async'], + '/EHs' : ['ExceptionHandling', 'SyncCThrow'], + '/FA' : ['AssemblerOutput', 'AssemblyCode'], + '/FAcs' : ['AssemblerOutput', 'All'], + '/FAc' : ['AssemblerOutput', 'AssemblyAndMachineCode'], + '/FAs' : ['AssemblerOutput', 'AssemblyAndSourceCode'], + '/Gd' : ['CallingConvention', 'Cdecl'], + '/Gr' : ['CallingConvention', 'FastCall'], + '/Gz' : ['CallingConvention', 'StdCall'], + '/MT' : ['RuntimeLibrary', 'MultiThreaded'], + '/MTd' : ['RuntimeLibrary', 'MultiThreadedDebug'], + '/MD' : ['RuntimeLibrary', 'MultiThreadedDLL'], + '/MDd' : ['RuntimeLibrary', 'MultiThreadedDebugDLL'], + '/Od' : ['Optimization', 'Disabled'], + '/O1' : ['Optimization', 'MinSpace'], + '/O2' : ['Optimization', 'MaxSpeed'], + '/Ox' : ['Optimization', 'Full'], + '/Ob1' : ['InlineFunctionExpansion', 'OnlyExplicitInline'], + '/Ob2' : ['InlineFunctionExpansion', 'AnySuitable'], + '/Ot' : ['FavorSizeOrSpeed', 'Speed'], + '/Os' : ['FavorSizeOrSpeed', 'Size'], + '/RTCs' : ['BasicRuntimeChecks', 'StackFrameRuntimeCheck'], + '/RTCu' : ['BasicRuntimeChecks', 'UninitializedLocalUsageCheck'], + '/RTC1' : ['BasicRuntimeChecks', 'EnableFastChecks'], + '/TC' : ['CompileAs', 'CompileAsC'], + '/TP' : ['CompileAs', 'CompileAsCpp'], + '/W0' : [ 'WarningLevel', 'TurnOffAllWarnings'], + '/W1' : [ 'WarningLevel', 'Level1'], + '/W2' : [ 'WarningLevel', 'Level2'], + '/W3' : [ 'WarningLevel', 'Level3'], + '/W4' : [ 'WarningLevel', 'Level4'], + '/Wall' : [ 'WarningLevel', 'EnableAllWarnings'], + '/Yc' : ['PrecompiledHeader', 'Create'], + '/Yu' : ['PrecompiledHeader', 'Use'], + '/Z7' : ['DebugInformationFormat', 'OldStyle'], + '/Zi' : ['DebugInformationFormat', 'ProgramDatabase'], + '/ZI' : ['DebugInformationFormat', 'EditAndContinue'], + '/Zp1' : ['StructMemberAlignment', '1Byte'], + '/Zp2' : ['StructMemberAlignment', '2Bytes'], + '/Zp4' : ['StructMemberAlignment', '4Bytes'], + '/Zp8' : ['StructMemberAlignment', '8Bytes'], + '/Zp16' : ['StructMemberAlignment', '16Bytes'], + '/arch:IA32' : ['EnableEnhancedInstructionSet', 'NoExtensions'], + '/arch:SSE' : ['EnableEnhancedInstructionSet', 'StreamingSIMDExtensions'], + '/arch:SSE2' : ['EnableEnhancedInstructionSet', 'StreamingSIMDExtensions2'], + '/arch:AVX' : ['EnableEnhancedInstructionSet', 'AdvancedVectorExtensions'], + '/clr' : ['CompileAsManaged', 'True'], + '/clr:pure' : ['CompileAsManaged', 'Pure'], + '/clr:safe' : ['CompileAsManaged', 'Safe'], + '/clr:oldSyntax' : ['CompileAsManaged', 'OldSyntax'], + '/fp:fast' : ['FloatingPointModel', 'Fast'], + '/fp:precise' : ['FloatingPointModel', 'Precise'], + '/fp:strict' : ['FloatingPointModel', 'Strict'], + '/errorReport:none' : ['ErrorReporting', 'None'], + '/errorReport:prompt' : ['ErrorReporting', 'Prompt'], + '/errorReport:queue' : ['ErrorReporting', 'Queue'], + '/errorReport:send' : ['ErrorReporting', 'Send'], + } + # Ideas from Google's Generate Your Project + ''' + _Same(_compile, 'AdditionalIncludeDirectories', _folder_list) # /I + + _Same(_compile, 'PreprocessorDefinitions', _string_list) # /D + _Same(_compile, 'DisableSpecificWarnings', _string_list) # /wd + _Same(_compile, 'ProgramDataBaseFileName', _file_name) # /Fd + + _Same(_compile, 'AdditionalOptions', _string_list) + _Same(_compile, 'AdditionalUsingDirectories', _folder_list) # /AI + _Same(_compile, 'AssemblerListingLocation', _file_name) # /Fa + _Same(_compile, 'BrowseInformationFile', _file_name) + _Same(_compile, 'ForcedIncludeFiles', _file_list) # /FI + _Same(_compile, 'ForcedUsingFiles', _file_list) # /FU + _Same(_compile, 'UndefinePreprocessorDefinitions', _string_list) # /U + _Same(_compile, 'XMLDocumentationFileName', _file_name) + '' : ['EnablePREfast', _boolean) # /analyze Visible='false' + _Renamed(_compile, 'ObjectFile', 'ObjectFileName', _file_name) # /Fo + _Renamed(_compile, 'PrecompiledHeaderThrough', 'PrecompiledHeaderFile', + _file_name) # Used with /Yc and /Yu + _Renamed(_compile, 'PrecompiledHeaderFile', 'PrecompiledHeaderOutputFile', + _file_name) # /Fp + _ConvertedToAdditionalOption(_compile, 'DefaultCharIsUnsigned', '/J') + _MSBuildOnly(_compile, 'ProcessorNumber', _integer) # the number of processors + _MSBuildOnly(_compile, 'TrackerLogDirectory', _folder_name) + _MSBuildOnly(_compile, 'TreatSpecificWarningsAsErrors', _string_list) # /we + _MSBuildOnly(_compile, 'PreprocessOutputPath', _string) # /Fi + ''' + SwitchConverter.__init__(self, table, booltable) + +class LinkSwitchConverter(SwitchConverter): + def __init__(self): + # Based on code in Generate Your Project + booltable = { + '/DEBUG' : ['GenerateDebugInformation'], + '/DYNAMICBASE' : ['RandomizedBaseAddress'], + '/DYNAMICBASE' : ['RandomizedBaseAddress'], + '/DYNAMICBASE' : ['RandomizedBaseAddress'], + '/DYNAMICBASE' : ['RandomizedBaseAddress'], + '/DYNAMICBASE' : ['RandomizedBaseAddress'], + '/NOLOGO' : ['SuppressStartupBanner'], + '/nologo' : ['SuppressStartupBanner'], + } + table = { + '/ERRORREPORT:NONE' : ['ErrorReporting', 'NoErrorReport'], + '/ERRORREPORT:PROMPT' : ['ErrorReporting', 'PromptImmediately'], + '/ERRORREPORT:QUEUE' : ['ErrorReporting', 'QueueForNextLogin'], + '/ERRORREPORT:SEND' : ['ErrorReporting', 'SendErrorReport'], + '/MACHINE:X86' : ['TargetMachine', 'MachineX86'], + '/MACHINE:ARM' : ['TargetMachine', 'MachineARM'], + '/MACHINE:EBC' : ['TargetMachine', 'MachineEBC'], + '/MACHINE:IA64' : ['TargetMachine', 'MachineIA64'], + '/MACHINE:MIPS' : ['TargetMachine', 'MachineMIPS'], + '/MACHINE:MIPS16' : ['TargetMachine', 'MachineMIPS16'], + '/MACHINE:MIPSFPU' : ['TargetMachine', 'MachineMIPSFPU'], + '/MACHINE:MIPSFPU16' : ['TargetMachine', 'MachineMIPSFPU16'], + '/MACHINE:SH4' : ['TargetMachine', 'MachineSH4'], + '/MACHINE:THUMB' : ['TargetMachine', 'MachineTHUMB'], + '/MACHINE:X64' : ['TargetMachine', 'MachineX64'], + '/NXCOMPAT' : ['DataExecutionPrevention', 'true'], + '/NXCOMPAT:NO' : ['DataExecutionPrevention', 'false'], + '/SUBSYSTEM:CONSOLE' : ['SubSystem', 'Console'], + '/SUBSYSTEM:WINDOWS' : ['SubSystem', 'Windows'], + '/SUBSYSTEM:NATIVE' : ['SubSystem', 'Native'], + '/SUBSYSTEM:EFI_APPLICATION' : ['SubSystem', 'EFI Application'], + '/SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER' : ['SubSystem', 'EFI Boot Service Driver'], + '/SUBSYSTEM:EFI_ROM' : ['SubSystem', 'EFI ROM'], + '/SUBSYSTEM:EFI_RUNTIME_DRIVER' : ['SubSystem', 'EFI Runtime'], + '/SUBSYSTEM:WINDOWSCE' : ['SubSystem', 'WindowsCE'], + '/SUBSYSTEM:POSIX' : ['SubSystem', 'POSIX'], + } + ''' + /TLBID:1 /MANIFEST /MANIFESTUAC:level='asInvoker' uiAccess='false' + + _Same(_link, 'AllowIsolation', _boolean) # /ALLOWISOLATION + _Same(_link, 'CLRUnmanagedCodeCheck', _boolean) # /CLRUNMANAGEDCODECHECK + _Same(_link, 'DelaySign', _boolean) # /DELAYSIGN + _Same(_link, 'EnableUAC', _boolean) # /MANIFESTUAC + _Same(_link, 'GenerateMapFile', _boolean) # /MAP + _Same(_link, 'IgnoreAllDefaultLibraries', _boolean) # /NODEFAULTLIB + _Same(_link, 'IgnoreEmbeddedIDL', _boolean) # /IGNOREIDL + _Same(_link, 'MapExports', _boolean) # /MAPINFO:EXPORTS + _Same(_link, 'StripPrivateSymbols', _file_name) # /PDBSTRIPPED + _Same(_link, 'PerUserRedirection', _boolean) + _Same(_link, 'Profile', _boolean) # /PROFILE + _Same(_link, 'RegisterOutput', _boolean) + _Same(_link, 'SetChecksum', _boolean) # /RELEASE + _Same(_link, 'SupportUnloadOfDelayLoadedDLL', _boolean) # /DELAY:UNLOAD + + _Same(_link, 'SwapRunFromCD', _boolean) # /SWAPRUN:CD + _Same(_link, 'TurnOffAssemblyGeneration', _boolean) # /NOASSEMBLY + _Same(_link, 'UACUIAccess', _boolean) # /uiAccess='true' + _Same(_link, 'EnableCOMDATFolding', _newly_boolean) # /OPT:ICF + _Same(_link, 'FixedBaseAddress', _newly_boolean) # /FIXED + _Same(_link, 'LargeAddressAware', _newly_boolean) # /LARGEADDRESSAWARE + _Same(_link, 'OptimizeReferences', _newly_boolean) # /OPT:REF + _Same(_link, 'TerminalServerAware', _newly_boolean) # /TSAWARE + + _Same(_link, 'AdditionalDependencies', _file_list) + _Same(_link, 'AdditionalLibraryDirectories', _folder_list) # /LIBPATH + _Same(_link, 'AdditionalManifestDependencies', _file_list) # /MANIFESTDEPENDENCY: + _Same(_link, 'AdditionalOptions', _string_list) + _Same(_link, 'AddModuleNamesToAssembly', _file_list) # /ASSEMBLYMODULE + _Same(_link, 'AssemblyLinkResource', _file_list) # /ASSEMBLYLINKRESOURCE + _Same(_link, 'BaseAddress', _string) # /BASE + _Same(_link, 'DelayLoadDLLs', _file_list) # /DELAYLOAD + _Same(_link, 'EmbedManagedResourceFile', _file_list) # /ASSEMBLYRESOURCE + _Same(_link, 'EntryPointSymbol', _string) # /ENTRY + _Same(_link, 'ForceSymbolReferences', _file_list) # /INCLUDE + _Same(_link, 'FunctionOrder', _file_name) # /ORDER + _Same(_link, 'HeapCommitSize', _string) + _Same(_link, 'HeapReserveSize', _string) # /HEAP + _Same(_link, 'ImportLibrary', _file_name) # /IMPLIB + _Same(_link, 'KeyContainer', _file_name) # /KEYCONTAINER + _Same(_link, 'KeyFile', _file_name) # /KEYFILE + _Same(_link, 'ManifestFile', _file_name) # /ManifestFile + _Same(_link, 'MapFileName', _file_name) + _Same(_link, 'MergedIDLBaseFileName', _file_name) # /IDLOUT + _Same(_link, 'MergeSections', _string) # /MERGE + _Same(_link, 'MidlCommandFile', _file_name) # /MIDL + _Same(_link, 'ModuleDefinitionFile', _file_name) # /DEF + _Same(_link, 'OutputFile', _file_name) # /OUT + _Same(_link, 'ProfileGuidedDatabase', _file_name) # /PGD + _Same(_link, 'ProgramDatabaseFile', _file_name) # /PDB + _Same(_link, 'StackCommitSize', _string) + _Same(_link, 'StackReserveSize', _string) # /STACK + _Same(_link, 'TypeLibraryFile', _file_name) # /TLBOUT + _Same(_link, 'TypeLibraryResourceID', _integer) # /TLBID + _Same(_link, 'Version', _string) # /VERSION + + + _Same(_link, 'AssemblyDebug', + _Enumeration(['', + 'true', # /ASSEMBLYDEBUG + 'false'])) # /ASSEMBLYDEBUG:DISABLE + _Same(_link, 'CLRImageType', + _Enumeration(['Default', + 'ForceIJWImage', # /CLRIMAGETYPE:IJW + 'ForcePureILImage', # /Switch="CLRIMAGETYPE:PURE + 'ForceSafeILImage'])) # /Switch="CLRIMAGETYPE:SAFE + _Same(_link, 'CLRThreadAttribute', + _Enumeration(['DefaultThreadingAttribute', # /CLRTHREADATTRIBUTE:NONE + 'MTAThreadingAttribute', # /CLRTHREADATTRIBUTE:MTA + 'STAThreadingAttribute'])) # /CLRTHREADATTRIBUTE:STA + _Same(_link, 'Driver', + _Enumeration(['NotSet', + 'Driver', # /Driver + 'UpOnly', # /DRIVER:UPONLY + 'WDM'])) # /DRIVER:WDM + _Same(_link, 'LinkTimeCodeGeneration', + _Enumeration(['Default', + 'UseLinkTimeCodeGeneration', # /LTCG + 'PGInstrument', # /LTCG:PGInstrument + 'PGOptimization', # /LTCG:PGOptimize + 'PGUpdate'])) # /LTCG:PGUpdate + _Same(_link, 'ShowProgress', + _Enumeration(['NotSet', + 'LinkVerbose', # /VERBOSE + 'LinkVerboseLib'], # /VERBOSE:Lib + new=['LinkVerboseICF', # /VERBOSE:ICF + 'LinkVerboseREF', # /VERBOSE:REF + 'LinkVerboseSAFESEH', # /VERBOSE:SAFESEH + 'LinkVerboseCLR'])) # /VERBOSE:CLR + _Same(_link, 'UACExecutionLevel', + _Enumeration(['AsInvoker', # /level='asInvoker' + 'HighestAvailable', # /level='highestAvailable' + 'RequireAdministrator'])) # /level='requireAdministrator' + _Same(_link, 'MinimumRequiredVersion', _string) + _Same(_link, 'TreatLinkerWarningAsErrors', _boolean) # /WX + + + # Options found in MSVS that have been renamed in MSBuild. + _Renamed(_link, 'IgnoreDefaultLibraryNames', 'IgnoreSpecificDefaultLibraries', + _file_list) # /NODEFAULTLIB + _Renamed(_link, 'ResourceOnlyDLL', 'NoEntryPoint', _boolean) # /NOENTRY + _Renamed(_link, 'SwapRunFromNet', 'SwapRunFromNET', _boolean) # /SWAPRUN:NET + + _Moved(_link, 'GenerateManifest', '', _boolean) + _Moved(_link, 'IgnoreImportLibrary', '', _boolean) + _Moved(_link, 'LinkIncremental', '', _newly_boolean) + _Moved(_link, 'LinkLibraryDependencies', 'ProjectReference', _boolean) + _Moved(_link, 'UseLibraryDependencyInputs', 'ProjectReference', _boolean) + + # MSVS options not found in MSBuild. + _MSVSOnly(_link, 'OptimizeForWindows98', _newly_boolean) + _MSVSOnly(_link, 'UseUnicodeResponseFiles', _boolean) + + # MSBuild options not found in MSVS. + _MSBuildOnly(_link, 'BuildingInIDE', _boolean) + _MSBuildOnly(_link, 'ImageHasSafeExceptionHandlers', _boolean) # /SAFESEH + _MSBuildOnly(_link, 'LinkDLL', _boolean) # /DLL Visible='false' + _MSBuildOnly(_link, 'LinkStatus', _boolean) # /LTCG:STATUS + _MSBuildOnly(_link, 'PreventDllBinding', _boolean) # /ALLOWBIND + _MSBuildOnly(_link, 'SupportNobindOfDelayLoadedDLL', _boolean) # /DELAY:NOBIND + _MSBuildOnly(_link, 'TrackerLogDirectory', _folder_name) + _MSBuildOnly(_link, 'MSDOSStubFileName', _file_name) # /STUB Visible='false' + _MSBuildOnly(_link, 'SectionAlignment', _integer) # /ALIGN + _MSBuildOnly(_link, 'SpecifySectionAttributes', _string) # /SECTION + _MSBuildOnly(_link, 'ForceFileOutput', + _Enumeration([], new=['Enabled', # /FORCE + # /FORCE:MULTIPLE + 'MultiplyDefinedSymbolOnly', + 'UndefinedSymbolOnly'])) # /FORCE:UNRESOLVED + _MSBuildOnly(_link, 'CreateHotPatchableImage', + _Enumeration([], new=['Enabled', # /FUNCTIONPADMIN + 'X86Image', # /FUNCTIONPADMIN:5 + 'X64Image', # /FUNCTIONPADMIN:6 + 'ItaniumImage'])) # /FUNCTIONPADMIN:16 + _MSBuildOnly(_link, 'CLRSupportLastError', + _Enumeration([], new=['Enabled', # /CLRSupportLastError + 'Disabled', # /CLRSupportLastError:NO + # /CLRSupportLastError:SYSTEMDLL + 'SystemDlls'])) + + ''' + SwitchConverter.__init__(self, table, booltable) + +CLSWITCHES = ClSwitchConverter() +LINKSWITCHES = LinkSwitchConverter() + +#------------------------------------------------------------------------------- + +# Return a Windows path from a native path +def winpath(path): + drive, rest = ntpath.splitdrive(path) + result = [] + while rest and rest != ntpath.sep: + rest, part = ntpath.split(rest) + result.insert(0, part) + if rest: + result.insert(0, rest) + return ntpath.join(drive.upper(), *result) + +def makeList(x): + if not x: + return [] + if type(x) is not list: + return [x] + return x + +#------------------------------------------------------------------------------- + +class Configuration(object): + def __init__(self, variant, platform, target, env): + self.name = '%s|%s' % (variant, platform) + self.variant = variant + self.platform = platform + self.target = target + self.env = env + +#------------------------------------------------------------------------------- + +class Item(object): + '''Represents a file item in the Solution Explorer''' + def __init__(self, path, builder): + self._path = path + self._builder = builder + self.node = dict() + + if builder == 'Object': + self._tag = 'ClCompile' + self._excluded = False + elif builder == 'Protoc': + self._tag = 'CustomBuild' + self._excluded = False + else: + ext = os.path.splitext(self._path)[1] + if ext in ['.c', '.cc', '.cpp']: + self._tag = 'ClCompile' + self._excluded = True + else: + if ext in ['.h', '.hpp', '.hxx', '.inl', '.inc']: + self._tag = 'ClInclude' + else: + self._tag = 'None' + self._excluded = False; + + def __repr__(self): + return '' % ( + self.path, self.tag, str(self.node)) + + def path(self): + return self._path + + def tag(self): + return self._tag + + def builder(self): + return self._builder + + def is_compiled(self): + return self._builder == 'Object' + + def is_excluded(self): + return self._excluded + +#------------------------------------------------------------------------------- + +def _guid(seed, name = None): + m = hashlib.md5() + m.update(seed) + if name: + m.update(name) + d = m.hexdigest().upper() + guid = "{%s-%s-%s-%s-%s}" % (d[:8], d[8:12], d[12:16], d[16:20], d[20:32]) + return guid + +class _ProjectGenerator(object): + '''Generates a project file for Visual Studio 2013''' + + def __init__(self, project_node, filters_node, env): + try: + self.configs = sorted(env['VSPROJECT_CONFIGS'], key=lambda x: x.name) + except KeyError: + raise ValueError ('Missing VSPROJECT_CONFIGS') + self.root_dir = os.getcwd() + self.root_dirs = [os.path.abspath(x) for x in makeList(env['VSPROJECT_ROOT_DIRS'])] + self.project_dir = os.path.dirname(os.path.abspath(str(project_node))) + self.project_node = project_node + self.project_file = None + self.filters_node = filters_node + self.filters_file = None + self.guid = _guid(os.path.basename(str(self.project_node))) + self.cpppath = [] + for path in [os.path.abspath(x) for x in makeList(env['CPPPATH'])]: + common = os.path.commonprefix([path, self.root_dir]) + if len(common) == len(self.root_dir): + self.cpppath.append(winpath(os.path.relpath(path, self.project_dir))) + #else: + # self.cpppath.append(path) + self.buildItemList(env) + + def buildItemList(self, env): + '''Build the Item set associated with the configurations''' + items = {} + def _walk(target, items, prefix=''): + if os.path.isabs(str(target)): + return + if target.has_builder(): + builder = target.get_builder().get_name(env) + bsources = target.get_binfo().bsources + if builder == 'Program': + for child in bsources: + _walk(child, items, prefix+' ') + else: + for child in bsources: + item = items.setdefault(str(child), Item(str(child), builder=builder)) + item.node[config] = target + _walk(child, items, prefix+' ') + for child in target.children(scan=1): + if not os.path.isabs(str(child)): + item = items.setdefault(str(child), Item(str(child), builder=None)) + _walk(child, items, prefix+' ') + for config in self.configs: + targets = config.target + for target in targets: + _walk(target, items) + self.items = sorted([v for k, v in items.iteritems()], key=lambda x: x.path) + + def makeListTag(self, items, tag, prefix='', inherit=True): + '''Builds an XML tag string from a list of items. If items is + empty, then the returned string is empty.''' + if not items: + return '' + s = '%(prefix)s<%(tag)s>' % locals() + s += ';'.join(items) + if inherit: + s += ';%%(%(tag)s)' % locals() + s += '\r\n' % locals() + return s + + def relPaths(self, paths): + items = [] + for path in paths: + if not os.path.isabs(path): + items.append(winpath(os.path.relpath(path, self.project_dir))) + items.sort() + return items + + def makePaths(self, paths, extra = None): + '''Returns a semicolon delimited string formed from a list + of root relative paths converted to be project relative.''' + s = '' + sep = '' + root_dir = os.getcwd() + for path in sorted(paths): + common = os.path.commonprefix([os.path.abspath(path), root_dir]) + if len(common) == len(root_dir): + s += sep + s += winpath(os.path.relpath(path, self.project_dir)) + sep = ';' + if extra: + s += sep + extra + return s + + def writeHeader(self): + global clSwitches + + encoding = 'utf-8' + project_guid = self.guid + name = 'RippleD' + + f = self.project_file + f.write(UnicodeByteMarker) + f.write(V12DSPHeader % locals()) + f.write(' \r\n') + for config in self.configs: + variant = config.variant + platform = config.platform + f.write(V12DSPProjectConfiguration % locals()) + f.write(' \r\n') + f.write(V12DSPGlobals % locals()) + + f.write(' \r\n') + for config in self.configs: + variant = config.variant + platform = config.platform + use_debug_libs = variant == 'Debug' + variant_dir = os.path.relpath(os.path.dirname( + config.target[0].get_abspath()), self.project_dir) + out_dir = winpath(variant_dir) + ntpath.sep + int_dir = winpath(os.path.join(variant_dir, 'src')) + ntpath.sep + f.write(V12DSPPropertyGroup % locals()) + + f.write(' \r\n') + f.write(' \r\n') + for config in self.configs: + variant = config.variant + platform = config.platform + f.write(V12DSPImportGroup % locals()) + + f.write(' \r\n') + for config in self.configs: + variant = config.variant + platform = config.platform + f.write(V12DSPItemDefinitionGroup % locals()) + # Cl options + f.write(' \r\n') + f.write( + #' \r\n' + ' %s%%(PreprocessorDefinitions)\r\n' % ( + itemList(config.env['CPPDEFINES'], ';'))) + props = '' + props += self.makeListTag( + [x for x in config.env['CPPPATH'] if is_subdir(x, self.root_dir) + ], 'AdditionalIncludeDirectories', ' ', True) + f.write(props) + f.write(CLSWITCHES.getXml(config.env['CCFLAGS'], ' ')) + f.write(' \r\n') + + f.write(' \r\n') + props = '' + props += self.makeListTag([x for x in config.env['LIBS'] + ], 'AdditionalDependencies', ' ', True) + props += self.makeListTag( + [x for x in config.env['LIBPATH'] if is_subdir(x, self.root_dir) + ], 'AdditionalLibraryDirectories', ' ', True) + f.write(props) + f.write(LINKSWITCHES.getXml(config.env['LINKFLAGS'], ' ')) + f.write(' \r\n') + + f.write(' \r\n') + + def writeProject(self): + self.writeHeader() + + f = self.project_file + self.project_file.write(' \r\n') + for item in self.items: + path = winpath(os.path.relpath(item.path(), self.project_dir)) + props = '' + tag = item.tag() + if item.is_excluded(): + props = ' True\r\n' + elif item.builder() == 'Object': + props = '' + for config, output in item.node.iteritems(): + name = config.name + env = output.get_build_env() + inc_dirs = self.makePaths(env['CPPPATH']) + props += self.makeListTag(self.relPaths(env['CPPPATH']), + 'AdditionalIncludeDirectories', ' ', True) + elif item.builder() == 'Protoc': + for config, output in item.node.iteritems(): + name = config.name + out_dir = os.path.relpath(os.path.dirname(str(output)), self.project_dir) + cpp_out = winpath(out_dir) + base_out = os.path.join(out_dir, os.path.splitext(os.path.basename(item.path()))[0]) + props += V12CustomBuildProtoc % locals() + + f.write(' <%(tag)s Include="%(path)s">\r\n' % locals()) + f.write(props) + f.write(' \r\n' % locals()) + f.write(' \r\n') + + f.write( + ' \r\n' + ' \r\n' + ' \r\n' + '\r\n') + + def writeFilters(self): + def getGroup(abspath): + abspath = os.path.dirname(abspath) + for d in self.root_dirs: + common = os.path.commonprefix([abspath, d]) + if common == d: + return winpath(os.path.relpath(abspath, common)) + return winpath(os.path.split(abspath)[1]) + + f = self.filters_file + f.write(UnicodeByteMarker) + f.write(V12DSPFiltersHeader) + + f.write(' \r\n') + groups = set() + for item in self.items: + group = getGroup(os.path.abspath(item.path())) + while group != '': + groups.add(group) + group = ntpath.split(group)[0] + for group in sorted(groups): + guid = _guid(self.guid, group) + f.write( + ' \r\n' + ' %(guid)s\r\n' + ' \r\n' % locals()) + f.write(' \r\n') + + f.write(' \r\n') + for item in self.items: + path = os.path.abspath(item.path()) + group = getGroup(path) + path = winpath(os.path.relpath(path, self.project_dir)) + tag = item.tag() + f.write ( + ' <%(tag)s Include="%(path)s">\r\n' + ' %(group)s\r\n' + ' \r\n' % locals()) + f.write(' \r\n') + f.write('\r\n') + + def build(self): + try: + self.project_file = open(str(self.project_node), 'wb') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + + str(self.project_node) + '" for writing:' + str(detail)) + try: + self.filters_file = open(str(self.filters_node), 'wb') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + + str(self.filters_node) + '" for writing:' + str(detail)) + self.writeProject() + self.writeFilters() + self.project_file.close() + self.filters_file.close() + +#------------------------------------------------------------------------------- + +class _SolutionGenerator(object): + def __init__(self, slnfile, projfile, env): + pass + + def build(self): + pass + +#------------------------------------------------------------------------------- + +# Generate the VS2013 project +def buildProject(target, source, env): + if env.get('auto_build_solution', 1): + if len(target) != 3: + raise ValueError ("Unexpected len(target) != 3") + if not env.get('auto_build_solution', 1): + if len(target) != 2: + raise ValueError ("Unexpected len(target) != 2") + + g = _ProjectGenerator (target[0], target[1], env) + g.build() + + if env.get('auto_build_solution', 1): + g = _SolutionGenerator (target[2], target[0], env) + g.build() + +def projectEmitter(target, source, env): + if len(target) != 1: + raise ValueError ("Exactly one target must be specified") + + # If source is unspecified this condition will be true + if not source or source[0] == target[0]: + source = [] + + outputs = [] + for node in list(target): + path = env.GetBuildPath(node) + outputs.extend([ + path + '.vcxproj', + path + '.vcxproj.filters']) + if env.get('auto_build_solution', 1): + outputs.append(path + '.sln') + return outputs, source + +projectBuilder = SCons.Builder.Builder( + action = SCons.Action.Action(buildProject, "Building ${TARGET}"), + emitter = projectEmitter) + +def createConfig(self, variant, platform, target, env): + return Configuration(variant, platform, target, env) + +def generate(env): + '''Add Builders and construction variables for Microsoft Visual + Studio project files to an Environment.''' + try: + env['BUILDERS']['VSProject'] + except KeyError: + env['BUILDERS']['VSProject'] = projectBuilder + env.AddMethod(createConfig, 'VSProjectConfig') + +def exists(env): + return True