Beast SCons tools:

* Add VSProject SCons Builder
* Add Protoc SCons Builder
* Add Beast build utilities python module
This commit is contained in:
Vinnie Falco
2014-04-24 18:02:13 -07:00
parent 5db677d74d
commit 0c06939f38
35 changed files with 10412 additions and 6617 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,10 @@
from __future__ import absolute_import, division, print_function, unicode_literals from __future__ import absolute_import, division, print_function, unicode_literals
import copy import copy
import itertools
import ntpath
import os import os
import random
import sys import sys
def add_beast_to_path(): def add_beast_to_path():
@@ -24,7 +27,7 @@ VARIANT_DIRECTORIES = {
'modules': ('bin', 'modules'), 'modules': ('bin', 'modules'),
} }
BOOST_LIBRARIES = 'boost_system', BOOST_LIBRARIES = '' #boost_system'
MAIN_PROGRAM_FILE = 'beast/unit_test/tests/main.cpp' MAIN_PROGRAM_FILE = 'beast/unit_test/tests/main.cpp'
DOTFILE = '~/.scons' DOTFILE = '~/.scons'
@@ -43,6 +46,94 @@ def main():
for name, path in VARIANT_DIRECTORIES.items(): for name, path in VARIANT_DIRECTORIES.items():
env.VariantDir(os.path.join(*path), name, duplicate=0) env.VariantDir(os.path.join(*path), name, duplicate=0)
env.Replace(PRINT_CMD_LINE_FUNC=Print.print_cmd_line) 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()

View File

@@ -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

View File

@@ -59,12 +59,13 @@ private:
// Called at program exit when destructors for objects // Called at program exit when destructors for objects
// with static storage duration are invoked. // with static storage duration are invoked.
// //
void doStaticDetruction () void doStaticDestruction ()
{ {
// In theory this shouldn't be needed (?) // In theory this shouldn't be needed (?)
ScopedLockType lock (m_mutex); ScopedLockType lock (m_mutex);
bassert (! m_didStaticDestruction); bassert (! m_didStaticDestruction);
m_didStaticDestruction = true;
for (List <Item>::iterator iter (m_list.begin()); iter != m_list.end();) for (List <Item>::iterator iter (m_list.begin()); iter != m_list.end();)
{ {
@@ -82,7 +83,7 @@ private:
{ {
~StaticDestructor () ~StaticDestructor ()
{ {
Manager::get().doStaticDetruction(); Manager::get().doStaticDestruction();
} }
}; };

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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"'])

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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 <tom@swirly.com>
#
# 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')

View File

@@ -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))

View File

@@ -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')

View File

@@ -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)

View File

@@ -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)))

View File

@@ -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))

View File

@@ -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

View File

@@ -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 []

View File

@@ -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'])

View File

@@ -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))

View File

@@ -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)

View File

@@ -1,5 +0,0 @@
#!/bin/bash
# Remove all the compiled .pyc files at or below this directory.
find . -name \*.pyc | xargs rm

View File

@@ -1,5 +0,0 @@
#!/bin/bash
# Run all the beast Python unit tests.
python -m unittest discover -p \*_test.py

View File

@@ -0,0 +1,180 @@
# Beast.py
# Copyright 2014 by:
# Vinnie Falco <vinnie.falco@gmail.com>
# 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('<target>'), env.File('<sources>'))
print (green(cmdline))

View File

@@ -28,7 +28,7 @@
# Date : 2014--4-25 # Date : 2014--4-25
""" """
protoc.py: Protoc Builder for SCons Protoc.py: Protoc Builder for SCons
This Builder invokes protoc to generate C++ and Python from a .proto file. This Builder invokes protoc to generate C++ and Python from a .proto file.
@@ -72,9 +72,10 @@ def ProtocEmitter(target, source, env):
return target, source return target, source
ProtocBuilder = SCons.Builder.Builder(action = ProtocAction, ProtocBuilder = SCons.Builder.Builder(
emitter = ProtocEmitter, action = ProtocAction,
srcsuffix = '$PROTOCSRCSUFFIX') emitter = ProtocEmitter,
srcsuffix = '$PROTOCSRCSUFFIX')
def generate(env): def generate(env):
"""Add Builders and construction variables for protoc to an Environment.""" """Add Builders and construction variables for protoc to an Environment."""

View File

@@ -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 = """\
<?xml version="1.0" encoding="%(encoding)s"?>\r
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
"""
V12DSPProjectConfiguration = """\
<ProjectConfiguration Include="%(variant)s|%(platform)s">\r
<Configuration>%(variant)s</Configuration>\r
<Platform>%(platform)s</Platform>\r
</ProjectConfiguration>\r
"""
V12DSPGlobals = """\
<PropertyGroup Label="Globals">\r
<ProjectGuid>%(project_guid)s</ProjectGuid>\r
<Keyword>Win32Proj</Keyword>\r
<RootNamespace>%(name)s</RootNamespace>\r
<IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename>\r
</PropertyGroup>\r
"""
V12DSPPropertyGroup = """\
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'" Label="Configuration">\r
<CharacterSet>MultiByte</CharacterSet>\r
<ConfigurationType>Application</ConfigurationType>\r
<PlatformToolset>v120</PlatformToolset>\r
<LinkIncremental>False</LinkIncremental>\r
<UseDebugLibraries>%(use_debug_libs)s</UseDebugLibraries>\r
<UseOfMfc>False</UseOfMfc>\r
<WholeProgramOptimization>false</WholeProgramOptimization>\r
<IntDir>%(int_dir)s</IntDir>\r
<OutDir>%(out_dir)s</OutDir>\r
</PropertyGroup>\r
"""
V12DSPImportGroup= """\
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'" Label="PropertySheets">\r
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
</ImportGroup>\r
"""
V12DSPItemDefinitionGroup= """\
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'">\r
"""
V12CustomBuildProtoc= """\
<FileType>Document</FileType>\r
<Command Condition="'$(Configuration)|$(Platform)'=='%(name)s'">protoc --cpp_out=%(cpp_out)s --proto_path=%%(RelativeDir) %%(Identity)</Command>\r
<Outputs Condition="'$(Configuration)|$(Platform)'=='%(name)s'">%(base_out)s.pb.h;%(base_out)s.pb.cc.</Outputs>\r
<Message Condition="'$(Configuration)|$(Platform)'=='%(name)s'">protoc --cpp_out=%(cpp_out)s --proto_path=%%(RelativeDir) %%(Identity)</Message>\r
<LinkObjects Condition="'$(Configuration)|$(Platform)'=='%(name)s'">false</LinkObjects>\r
"""
V12DSPFiltersHeader = (
'''<?xml version="1.0" encoding="utf-8"?>\r
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\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</%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)</%(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 '<VSProject.Item "%s" %s>' % (
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 += '</%(tag)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(' <ItemGroup Label="ProjectConfigurations">\r\n')
for config in self.configs:
variant = config.variant
platform = config.platform
f.write(V12DSPProjectConfiguration % locals())
f.write(' </ItemGroup>\r\n')
f.write(V12DSPGlobals % locals())
f.write(' <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\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(' <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r\n')
f.write(' <ImportGroup Label="ExtensionSettings" />\r\n')
for config in self.configs:
variant = config.variant
platform = config.platform
f.write(V12DSPImportGroup % locals())
f.write(' <PropertyGroup Label="UserMacros" />\r\n')
for config in self.configs:
variant = config.variant
platform = config.platform
f.write(V12DSPItemDefinitionGroup % locals())
# Cl options
f.write(' <ClCompile>\r\n')
f.write(
#' <PrecompiledHeader />\r\n'
' <PreprocessorDefinitions>%s%%(PreprocessorDefinitions)</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(' </ClCompile>\r\n')
f.write(' <Link>\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(' </Link>\r\n')
f.write(' </ItemDefinitionGroup>\r\n')
def writeProject(self):
self.writeHeader()
f = self.project_file
self.project_file.write(' <ItemGroup>\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 = ' <ExcludedFromBuild>True</ExcludedFromBuild>\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(' </%(tag)s>\r\n' % locals())
f.write(' </ItemGroup>\r\n')
f.write(
' <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r\n'
' <ImportGroup Label="ExtensionTargets">\r\n'
' </ImportGroup>\r\n'
'</Project>\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(' <ItemGroup>\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(
' <Filter Include="%(group)s">\r\n'
' <UniqueIdentifier>%(guid)s</UniqueIdentifier>\r\n'
' </Filter>\r\n' % locals())
f.write(' </ItemGroup>\r\n')
f.write(' <ItemGroup>\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'
' <Filter>%(group)s</Filter>\r\n'
' </%(tag)s>\r\n' % locals())
f.write(' </ItemGroup>\r\n')
f.write('</Project>\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